Invalidating The Dcache.


MadDog

Member
Joined
Mar 4, 2006
Messages
262
Age
54
Location
UK
Website
www.maddoggames.com
I'm trying to invalidate the cache lines for the memory area where the palette lives when I update its data with this code. The addess is 16 byte aligned. But the code locks up. :(

asm volatile
(
//Don't want to invalidate the entire cache, that would be bad.
//So we invalidate only the area that the palette is.
"mov r0,%0 \n"
"mov r1,#64 \n"//Have to do 64 cache lines. (palette size of == (256*4) / cache line size(16))
"invalidate_palette_cache: \n"
" mcr p15,0,r0,c7,c6,1 \n"
" add r0,r0,#16 \n"//Move to next cache line.
" subs r1,r1,#1 \n"//Remeber how many we done.
" bne invalidate_palette_cache \n"
:
:"r"(reg.cmd.cmd_buf->palette)
:"r0","r1"
);
 
First obvious question: you are doing this on the 940 right?

I'm pretty sure that mcr is a restricted instruction on the 920.
 
MadDog posted on Jul 5 2006 at 06:29 PM said:
I'm trying to invalidate the cache lines for the memory area where the palette lives when I update its data with this code. The addess is 16 byte aligned. But the code locks up. :(

asm volatile
(
//Don't want to invalidate the entire cache, that would be bad.
//So we invalidate only the area that the palette is.
"mov r0,%0 \n"
"mov r1,#64 \n"//Have to do 64 cache lines. (palette size of == (256*4) / cache line size(16))
"invalidate_palette_cache: \n"
" mcr p15,0,r0,c7,c6,1 \n"
" add r0,r0,#16 \n"//Move to next cache line.
" subs r1,r1,#1 \n"//Remeber how many we done.
" bne invalidate_palette_cache \n"
:
:"r"(reg.cmd.cmd_buf->palette)
:"r0","r1"
);
A couple of things here.

First, I doubt that you can do an MCR from user space. You'll probably get an invalid instruction exception if you run it from a Linux application.

If you're not doing it from user space, or you're trying it on the second core, then what's in reg.cmd.cmd_buf->palette? (If, as I presume, it's an address then it shouldn't be).

Also, I think you have the MCR command wrong. Have a look at page 59 of the ARM 940T TRM, that shows how to clean and flush an entire cache one line at a time. That uses the "MCR p15,0,rx,c7,c14,2" instruction.

HTH.
 
Last edited by a moderator:
If your doing this on the 920, the correct way would be to use SWI 0x9f0002 (__ARM_NR_cacheflush)

If your doing this on the 940, then try draining the write cache rather than flushing it first:

MOV R0, #0
MCR p15, 0, R0, c7, c10, 4

otherwise, try something like this:

_dcache_flush:
MOV R4, #0

.dcache_flush_loop2:
MOV R5, #0

.dcache_flush_loop1:
MOV R3, R5, LSL #5
ORR R3, R3, R4, LSL #26
MCR P15, 0, R3, C7, C14, 2
ADD R5, R5, #1 @ 4 sets
CMP R5, #4
BNE .dcache_flush_loop1
ADD R4, R4, #1 @ 64 indices
CMP R4, #64
BNE .dcache_flush_loop2

MOV PC, LR
 
Ahh king Squidge strikes again. I've just added a call to SWI 0x9f0002 before I set the new frame buffer and this has cured the flickering line problem in DrMD. Thanks again.
 
Sorry, forgot to say its on the 940. I just want to invalidate, I don't want to flush as the data has been placed there by the 920. If I flush it will overwirte the data in ram.

c6, 1 as far as the docs say is for invalidating a single cache line with an address. If that address hits a cache line then its invalidated, else nothing happens.

The 940 Dcache is 16bytes? Changing the code in increment the address by 32 bytes stops it from crashing although its not working.
 
MadDog posted on Jul 5 2006 at 11:19 PM said:
Sorry, forgot to say its on the 940. I just want to invalidate, I don't want to flush as the data has been placed there by the 920. If I flush it will overwirte the data in ram.

c6, 1 as far as the docs say is for invalidating a single cache line with an address. If that address hits a cache line then its invalidated, else nothing happens.

The 940 Dcache is 16bytes? Changing the code in increment the address by 32 bytes stops it from crashing although its not working.
Which docs are you looking at? They seem to disagree with the ARM 940T TRM...

If you just want to invalidate the contents and not send stuff back, then a flush is what you want. If you want it written back, then you need to clean (and flush).

In the ARM 940T TRM, the "flush DCache single entry" instruction is (page 37):
MCR p15,0,Rx,c7,c6,2

As I said before, the argument shound not be an address. You need to pass it an index/segment (page 39).

(And yeah, the cache line size is 4 words = 16 bytes).
 
Last edited by a moderator:
Ok, i've realised where I was getting confused. I was reading my book which lists all the functions on register 7 and of cause the 940 only has a subset of these. Doh! Reading the 940 doc from the Wiki the function I was using is not there on the 940. Flush it is then. :)
 
Ok, i've added the code that flushs then cleans the entire Dcache.
From the DDI0144B_940T_TRM doc on the Wiki.

asm volatile //not sure if I need the volatile bit here.
(
"mov r1,#0 \n"
"outer_loop: \n"
" mov r0,#0 \n"
"inner_loop: \n"
" orr r2,r1,r0 \n"
" mcr p15,0,r2,c7,c14,2 \n"
" add r0,r0,#0x10 \n"
" cmp r0,#0x40 \n"
" bne inner_loop \n"
" add r1,r1,#0x04000000 \n"
" cmp r1,#0 \n"
" bne outer_loop \n"
:
:
:"r0","r1","r2"
);

This works but will have the posible bug where if the palette has been updated by the 920 and some of the old one is still in the cache then the old one will overwrite the new one.

Note: My palette is more than just colours, it has two 64k look up tables for blending and alpha ops.

One observation is that c6,0 is a bit daft, it invalidates the entire dcache (no clean), so now your stack that is correct in the cache and incorrect in the memory will now be corrupt when you next read a var.

Am I correct in saying Clean means make the mem reflect whats in the cache and flush means mark a cache line as unused and so the value in the ram is the value read and the value in the cache is lost?

How do I workout, if posible, which cache lines are caching my palette data?

I want this data cached for obvious reasons but i'm now thinking that when I update the HW palette at the end of the frame I should read the new palette lookups from uncached memory to cached memory instead of just invalidating the cache lines. Means an extra copy of the data but may be more reliable.
 
MadDog posted on Jul 6 2006 at 06:06 PM said:
Am I correct in saying Clean means make the mem reflect whats in the cache and flush means mark a cache line as unused and so the value in the ram is the value read and the value in the cache is lost?
Yeah. Of course if the data in the cache has never changed (i.e. marked dirty) then it never gets rewritten out to memory. You should probably also drain the write buffer.

What functionality are you trying to achieve here? It seems to me that you're perhaps "optimising" something that a) probably doesn't need it, and b ) will bite you later if you don't get all of this exactly right.

I just can't see what you'd gain from this. :S
 
Last edited by a moderator:
refractor posted on Jul 6 2006 at 06:28 PM said:
MadDog posted on Jul 6 2006 at 06:06 PM said:
Am I correct in saying Clean means make the mem reflect whats in the cache and flush means mark a cache line as unused and so the value in the ram is the value read and the value in the cache is lost?
Yeah. Of course if the data in the cache has never changed (i.e. marked dirty) then it never gets rewritten out to memory. You should probably also drain the write buffer.

What functionality are you trying to achieve here? It seems to me that you're perhaps "optimising" something that a) probably doesn't need it, and b ) will bite you later if you don't get all of this exactly right.

I just can't see what you'd gain from this. :S

Yes, thats posible. What I have is the upper 32megs for the 940. Uncached for the 920 as its only ever passing stuff to the 940. The 940 runs all the memory cached except for 512k block at 16.5megs. The 940 memory layout is 0 -> 16megs cached, 16 -> 16.5megs protected so the video stuff still works ok when I exit (just incase my code goes bonkers). 16.5megs -> 17megs uncached and used to init stuff for the 940. 17 megs -> 32megs cached. (I have the 940 memory offset to the upper 32megs)

This block of memory is managed by the 920 as its the only thing except for the frame buffers that writes to it. The 940 for the best part only reads from this memory so it works well for the 920 to manage the memory and the 940 to be told where stuff is, via a coms struct mapped to the uncached 512k block.

Using the palette as an example, although it could just as easily be vertex/texture data. The 920 allocs a block of (i'll call it vram) vram for the palette. 256*4 for the colours and 2x64k blocks for look up tables. It loads the palette file to this memory and sends a command to the 940 to tell it its palette has changed. On the next vblank the 940 updates the HW palette values and changes its internal pointer that points to the lookup tables used when rendering. Now these look up tables could do with being cached so what I was hoping to do was when I changed the pointer the 940 has pointing to the look up tables I wanted to reset the
any cache lines that maybe mirroring this memory area.

So yes in short its an optimisation but its one thats needed.

My only other option is to load the palette into some memory the 920 owns (not vram) and copy this data into the command buffer or into some other buffer for the 940 to then copy to its internal cached lookup tables. As you can see not going the route I wanted and causes two memory copies.

P.s. Setting the frame buffers to be cached then draining the write buffers on the vblank gives a significant speed up.
 
Last edited by a moderator:
Ok, yes, in that case you're looking at the right thing. You're sharing cached data so you do have to invalidate it on the consumer (the 940) when the producer (the 920) modifies it.

Can you move the "responsibility" for the palette stuff entirely to the 940? That way you bypass any synchronisation problems (though in your other examples like the vertex buffer this would likely still be necessary).
 
refractor posted on Jul 6 2006 at 08:26 PM said:
Ok at yes at in that case you're looking at the right thing. You're sharing cached data so you do have to invalidate it on the consumer (the 940) when the producer (the 920) modifies it.

Can you move the "responsibility" for the palette stuff entirely to the 940? That way you bypass any synchronisation problems (though in your other examples like the vertex buffer this would likely still be necessary).

The 920 will always have some work to do, its the one that loads it. But as i've reserved 512k of uncached memory for the two to chat to each other with i'm thinking i'll just have a buffer that the 940 will copy from to its cached lookup tables. Seems the less bug prone way to do it, and i've got so much ram to play with. They have given us so much ram!!! :)
 
Last edited by a moderator:
Back
Top