Hey all,
I was at my wit's end trying to figure out why my RM57x was seemingly corrupting memory whenever I disabled and subsequently re-enabled its caches. I believe I found a solution to my problem and was hoping to run it past someone more knowledgeable.
I started out by using the cache enable/disable code provided in the RM57 Family User's Guide, spnu562. This code did not work. Instead, it corrupted recently written values.
http://www.ti.com/lit/ug/spnu562/spnu562.pdf
Disable caches - spnu562 p.392 section 9.3.1
MRC p15, 0, r1, c1, c0, 0 ; Read system control register configuration data BIC r1, r1, #0x1 << 12 ; Instruction cache disable BIC r1, r1, #0x1 << 2 ; Data cache disable DSB MRC p15, 0, r1, c1, c0, 0 ; Disable cache RAMs
Invalidate and enable caches - spnu562 p.393 section 9.3.1
MRC p15, 0, r1, c1, c0, 1 ; Read auxiliary control register BIC r1, r1, #0x1 << 5 ; Bit is default set to disable ECC. Clearing bit 5. MCR p15, 0, r1, c1, c0, 1 ; Enable ECC, generate abort on ECC errors, enable hardware recovery. MRC p15, 0, r1, c1, c0, 0 ; Read system control register configuration data ORR r1, r1, #0x1 << 12 ; Instruction cache enable ORR r1, r1, #0x1 << 2 ; Data cache enable DSB MCR p15, 0, r0, c15, c5, 0 ; Invalidate entire data cache MCR p15, 0, r0, c7, c5, 0 ; Invalidate entire instruction cache MCR p15, 0, r1, c1, c0, 0 ; Enable cache RAM ISB ; You must issue an ISB instruction to flush the pipeline. This ensures that all subsequent instruction fetches see the effect of enabling the instruction cache.
This code does not agree with the ARM Cortex-R5 documentation on how to enable and disable caches. It also does not work. Here is a partial list of suspicious-looking things:
- When disabling caches, the last instruction should logically be "MCR" to store the modified register values back to the CP15. The provided code instead uses "MRC" meaning that the modified register values are not stored back to the CP15 and the cache is never disabled.
- When disabling a write-back cache, user code is required to "clean" the cache, copying any changed memory values from cache into the backing RAM. The provided code does not do this, so when cache is re-enabled, any dirty values in the cache will take precedence over changes made to memory while the cache was disabled.
Following the recommendations in the ARM Cortex-R5 Technical Reference Manual and the ARM Architecture Reference Manual, I came up with some different code instead and tested it on the RM57L843 in my RM57x HDK. This new code seems to work correctly in a debugger.
ARM Cortex-R5 Technical Reference Manual - http://infocenter.arm.com/help/topic/com.arm.doc.ddi0460d/DDI0460D_cortex_r5_r1p2_trm.pdf
ARM Architecture Reference Manual, ARMv7-A and ARMv7-R edition - http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0406c/index.html
// ----- Cache manipulation ----- // Enable the instruction and data caches w/o ESM.
// Based on the ARM Cortex-R5 Technical Reference Manual, page 231 (8-31, section 8.5.5) #define CACHE_ON \ asm("mov r0, #0"); \ asm("MRC p15, #0, R1, c1, c0, #0"); \ asm("ORR R1, R1, #0x1 << 12"); \ asm("ORR R1, R1, #0x1 << 2"); \ asm("DSB"); \ asm("MCR p15, #0, r0, c15, c5, #0"); \ asm("DSB"); \ asm("MCR p15, #0, r0, c7, c5, #0"); \ asm("DSB"); \ asm("MCR p15, #0, R1, c1, c0, #0"); \ asm("ISB") // Enable the instruction and data caches w/ ESM.
// Based on the ARM Cortex-R5 Technical Reference Manual, page 231 (8-31, section 8.5.5) #define CACHE_ON_ESM \ asm("MOV r0, 0"); \ asm("MRC p15, 0, r1, c1, c0, 1"); \ asm("BIC r1, r1, #0x1 << 5"); \ asm("MCR p15, 0, r1, c1, c0, 1"); \ asm("MRC p15, 0, r1, c1, c0, 0"); \ asm("ORR r1, r1, #0x1 << 12"); \ asm("ORR r1, r1, #0x1 << 2"); \ asm("DSB"); \ asm("MCR p15, 0, r0, c15, c5, 0"); \ asm("DSB"); \ asm("MCR p15, 0, r0, c7, c5, 0"); \ asm("DSB"); \ asm("MCR p15, 0, r1, c1, c0, 0"); \ asm("ISB") // Disable the instruction and data caches. (based on a combination of spnu562 9.3.1 and the ARM Cortex-R5 Technical Reference Manual). // (ISB added from the ARM Cortex-R5 Technical Reference Manual, p. 232 (8-32, section 8.5.5), // "Disabling or enabling instruction cache"). #define CACHE_OFF \ asm("MRC p15, 0, r1, c1, c0, 0"); \ asm("BIC r1, r1, #0x1 << 12"); \ asm("BIC r1, r1, #0x1 << 2"); \ asm("DSB"); \ asm("MCR p15, 0, r1, c1, c0, 0"); \ asm("ISB") // Clean the data cache. // ARM Architecture Reference Manual // B2.2.7 (B2-1286) Performing Cache Maintenance Operations #define CACHE_CLEAN \ asm("MRC p15, 1, R0, c0, c0, 1"); \ asm("ANDS R3, R0, #0x07000000"); \ asm("MOV R3, R3, LSR #23"); \ asm("BEQ Finished"); \ asm("MOV R10, #0"); \ asm("Loop1"); \ asm("ADD R2, R10, R10, LSR #1"); \ asm("MOV R1, R0, LSR R2"); \ asm("AND R1, R1, #7"); \ asm("CMP R1, #2"); \ asm("BLT Skip"); \ asm("MCR p15, 2, R10, c0, c0, 0"); \ asm("ISB"); \ asm("MRC p15, 1, R1, c0, c0, 0"); \ asm("AND R2, R1, #7"); \ asm("ADD R2, R2, #4"); \ asm("LDR R4, =0x3FF"); \ asm("ANDS R4, R4, R1, LSR #3"); \ asm("CLZ R5, R4"); \ asm("MOV R9, R4"); \ asm("Loop2"); \ asm("LDR R7, =0x00007fff"); \ asm("ANDS R7, R7, R1, LSR #13"); \ asm("Loop3"); \ asm("ORR R11, R10, R9, LSL R5"); \ asm("ORR R11, R11, R7, LSL R2"); \ asm("MCR p15, 0, R11, c7, c10, 2");\ asm("SUBS R7, R7, #1"); \ asm("BGE Loop3"); \ asm("SUBS R9, R9, #1"); \ asm("BGE Loop2"); \ asm("Skip"); \ asm("ADD R10, R10, #2"); \ asm("CMP R3, R10"); \ asm("BGT Loop1"); \ asm("DSB"); \ asm("Finished") void cache_clean(void) { CACHE_CLEAN; } // ----- Test harness ----- #define STORE_123 \ asm("EOR R4,R4,R4"); \ asm("ADD R4,R4,1"); \ asm("ADD R5,R4,1"); \ asm("ADD R6,R5,1"); \ asm("STMDB SP!, {R4,R5,R6}") #define STORE_456 \ asm("EOR R4,R4,R4"); \ asm("ADD R4,R4,4"); \ asm("ADD R5,R4,1"); \ asm("ADD R6,R5,1"); \ asm("STMDB SP!, {R4,R5,R6}") #define STORE_789 \ asm("EOR R4,R4,R4"); \ asm("ADD R4,R4,7"); \ asm("ADD R5,R4,1"); \ asm("ADD R6,R5,1"); \ asm("STMDB SP!, {R4,R5,R6}") #define LOAD \ asm("LDMIA SP!, {R4,R5,R6}") #define CLEAR_REGS \ asm("EOR R4,R4,R4"); \ asm("MOV R5,R4"); \ asm("MOV R6,R4"); int main() { // At startup, cache is off. // Store 1,2,3 straight into RAM then read it back. STORE_123; LOAD; // Now let's try enabling the cache and writing some new values. CACHE_ON_ESM; STORE_456; // The value in memory (cached) is 4,5,6. CACHE_OFF; // Now the cache is ignored. // The value in memory (RAM) is 1,2,3. cache_clean(); // Dirty data is copied from the cache back to RAM. // The value in memory (RAM) is 4,5,6. All is well. LOAD; // never return! while( true ); }
To test the code, step through it with a debugger and watch the value in memory switch between {1,2,3} and {4,5,6} as the value is written to cache and RAM, and as cache is enabled/disabled.
It is possible to reproduce the behavior of the SPNU562 code by commenting out cache_clean(). You will notice that when cache is re-enabled, the values {1,2,3} wind up in memory despite the more recent write of {4,5,6} to the cache.
Here is what I am wondering:
- Is the ARM documentation that I used a reliable source of information on the Cortex cores in the RM57?
- Would you agree that SPNU562 seems to be incorrect?
- ...if so, any chance of changing SPNU562 to match the code in the ARM documentation?
Thanks for any guidance,
Peter