Okay, that was partially horse siht (I am talking about my post)...Shouldnt ramble that late at night perhaps...
The 920 has 8 words per cache line (32 bytes per line).
dcache and icache are separate.
address bits 31 downto 8 are used in the tag, bits 7 downto 5 are used for the segment, then bits 4 downto 2 select where in the cache line. So if bits 7 downto 5 dont match for two addresses then they cant affect each other in the cache (sequential execution, small loops). So you are looking for 31 downto 8 to not match and 7 downto 5 to match to have an effect 0x1000000 vs 0x2000000 or 0x1000000 and 0x1000100
but not 0x1000000 and 0x1000080.
a[ra+ic]=b[rb+ic];
a[ra+ic] and b[rb+ic] should not affect each other, the code needs to be longer than 256 bytes or 64 instructions (8 cache lines).
This:
int one ( void )
{
for(ic=0;ic

;ic++)
{
a[ra+ic]=b[rb+ic];
}
return(0);
}
gives this:
.L2:
ldr r3, [r5, #0]
ldr r2, [r6, #0]
add r3, r0, r3
ldr r1, [lr, r3, asl #2]
add r2, r0, r2
str r1, [r4, r2, asl #2]
ldr r3, [ip, #0]
add r0, r3, #1
cmp r0, #2
str r0, [ip, #0]
bls .L2
for both -O2 and -O3 (gcc 4.1.1)
12 words plus branch shadow (this is assuming you were accessing 32 bit quantities)
two or three cache lines depending on how they line up, it wont kick itself out of the cache. I would assume the first time through the cache lines are most likely going to be misses, the next two times through they will hit (so long as you dont have any interrupts going on).
If a[] and b[] are 16 bit quantities it actually unrolled the loop in my simple test:
ldr r3, .L3+12
ldr lr, [r2, #0]
mov ip, r1, asl #1
ldr r4, [r3, #0]
ldrh ip, [ip, lr]
mov r5, r0, asl #1
strh ip, [r5, r4] @ movhi
add r3, r1, #1
mov r3, r3, asl #1
ldrh r3, [r3, lr]
add r2, r0, #1
mov r2, r2, asl #1
strh r3, [r2, r4] @ movhi
add r1, r1, #2
mov r1, r1, asl #1
ldrh r1, [r1, lr]
add r0, r0, #2
mov r0, r0, asl #1
ldr r3, .L3+16
strh r1, [r0, r4] @ movhi
mov r2, #3
mov r0, #0
str r2, [r3, #0]
Much more likely to have all of it miss in the cache, 23 instructions so 4 or 5 cache lines no branches though, no dumping of the pipe and losing two clocks (or more, how deep is the pipe?).
But you are really wanting to do bytes,
.L2:
ldr r3, [r4, #0]
ldr r1, [r5, #0]
ldr r2, [r7, #0]
add r1, r1, r3
ldr r3, [r6, #0]
ldrb r0, [r1, ip] @ zero_extendqisi2
add r2, r2, r3
strb r0, [r2, ip]
ldr r3, [lr, #0]
add ip, r3, #1
cmp ip, #2
str ip, [lr, #0]
bls .L2
gcc rolled it up again. 14 instructions, two or three cache lines, second and third time through no cache misses.
Does this loop iterate three times because of red, green and blue? (I am heading off on a wild tangent based on this assumption) Why do you have the three separate, why not work with 16 bit pixel colors instead of the component colors? You should see some performance gains by going to 4 bytes per pixel (red, green, blue, dontcare), assuming you dont want to work with 16 bit colors but the red, green blue components instead. This is because you can do things like this rotate on word boundaries (no need for the loop) a[ra]=b[rb]. the loop goes away, it becomes a single word transfer:
ldr r3, [lr, #0]
ldr r2, [r4, #0]
ldr r1, [r0, r3, asl #2]
str r1, [ip, r2, asl #2]
Much simpler, faster, still one or two cache lines, and there are odds that they will hit but even with that its faster than either of the two above.
This only works of course if you take the time to align your pixels on word boundaries:
unsigned char myscreendata[320*240*4];
wont work, you either need to align it:
unsigned char rawscreen[(320*240*4)+8];
unsigned char *myscreendata;
unsigned long ra;
...
(one time initialization code)
ra=rawscreen;
if(ra&3) ra=(ra+4)&0xFFFFFFFC;
*myscreendata=ra;
Or:
unsigned long rawscreen[320x240];
unsigned char *myscreendata;
...
(one time init)
myscreendata=rawscreen;
Then you can deal with the component colors using myscreendata[].
I think with the arm you are likely to find that using unsigned longs for all pixels, doing all pixel based work at the unsigned long level then if you need to get into componet colors use a shift:
red=(unsigned char)(mypixeldata[index]>>16);
green=(unsigned char)(mypixeldata[index]>>8);
blue=(unsigned char)(mypixeldata[index]>>0);
As the ARM has a good barrel shifter.
red=(unsigned char)(a[ra]>>16);
green=(unsigned char)(a[ra]>>8);
blue=(unsigned char)(a[ra]>>0);
ldr r3, [r1, #0]
mov r3, r3, lsr #16
strb r3, [lr, #0]
ldr r2, [r1, #0]
mov r2, r2, lsr #8
strb r2, [ip, #0]
ldr r3, [r1, #0]
strb r3, [r0, #0]
And if you like you can even help the compiler:
rb=a[ra];
red=(unsigned char)(rb>>16);
green=(unsigned char)(rb>>8);
blue=(unsigned char)(rb>>0);
ldr r3, [r0, #0]
mov r2, r3, lsr #16
mov r1, r3, lsr #8
strb r2, [r4, #0]
strb r1, [lr, #0]
strb r3, [ip, #0]
str r3, [r5, #0]
Rebuilding a pixel, perhaps something like this:
rb=red; rb<<=8;
rb|=green; rb<<=8;
rb|=blue;
a[ra]=rb;
ldrb r1, [r7, #0] @ zero_extendqisi2
ldrb r3, [r6, #0] @ zero_extendqisi2
ldrb r2, [r5, #0] @ zero_extendqisi2
orr r3, r3, r1, asl #8
ldr r0, [lr, #0]
orr r2, r2, r3, asl #8
str r2, [r4, #0]
str r2, [ip, r0, asl #2]
Or
a[ra]= (((unsigned long)red)<<16) | (((unsigned long)green)<<16) | blue;
ldrb r1, [r4, #0] @ zero_extendqisi2
ldrb r3, [lr, #0] @ zero_extendqisi2
ldrb r0, [r5, #0] @ zero_extendqisi2
orr r3, r3, r1, asl #16
ldr r2, [r6, #0]
orr r3, r3, r0, asl #16
str r3, [ip, r2, asl #2]
probably would have been the same had I not had the intermediate rb on the one above this one.
Maybe:
ucptr=(unsigned char *)&a[ra];
*ucptr++;
*ucptr++=red;
*ucptr++=green;
*ucptr++=blue;
OUCH, that was a step in the wrong direction:
ldr r1, [r6, #0]
ldr r3, [r5, #0]
ldrb r2, [lr, #0] @ zero_extendqisi2
add r3, r3, r1, asl #2
strb r2, [r3, #1]
ldrb r1, [ip, #0] @ zero_extendqisi2
add r3, r3, #1
strb r1, [r3, #1]
add r3, r3, #1
ldrb r2, [r0, #0] @ zero_extendqisi2
add r1, r3, #2
strb r2, [r3, #1]
str r1, [r4, #0]
Anyway, I was just guessing that your loop from zero to three in a pixel rotate might be related to red, green and blue bytes, I think you definitely want to burn the extra 25% more memory to word align those pixels and do all pixel work at the word level. Its all relative though you know how much pixel work there is vs color work (I would assume all the artwork is done pre-compile time which is why I dont understand why there would be any color work anyway, and thus use pre-compile 16 bit colors ready for video memory).
Using this:
((uint32_t *)o)[index_o>>2] = ((uint32_t *)i)[index_i>>2];
Instead of this:
for (ic=0; ic

; ic++)
o[iyx_o + ic] = i[i_index + ic];
Only helps if your triplets are word aligned. It should still result in three byte reads and writes with an index or pointer...Lets see, just disassemble it...
int one ( unsigned char *a, unsigned char *b )
{
while(1)
{
((unsigned long *)a)[ra>>2]=((unsigned long *)B)[rb>>2];
}
return(0);
}
one:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
stmfd sp!, {r4, lr}
ldr r4, .L6
ldr lr, .L6+4
mov ip, r1
.L3:
ldr r3, [lr, #0]
ldr r2, [r4, #0]
mov r3, r3, lsr #2
ldr r1, [ip, r3, asl #2]
mov r2, r2, lsr #2
str r1, [r0, r2, asl #2]
b .L3
Does the non-loop code even work? You are forcing unaligned word accesses, which most non-ARM gurus, and even many ARM gurus jump up and down about the ARM ARM saying you cant do, in reality what is supposed to happen is something like:
Before:
[0x1000] 0x11
[0x1001] 0x22
[0x1002] 0x33
[0x1003] 0x44
[0x1004] 0x55
[0x1005] 0x66
[0x2000] 0xAA
[0x2001] 0xBB
[0x2002] 0xCC
[0x2003] 0

D
[0x2004] 0xEE
[0x2005] 0xFF
*i = 0x1000
*o = 0x2000
Byte based (for(ic=0;ic

;ic++) o[base+ic]=i[base+ic]
[0x2000] 0x11
[0x2001] 0x22
[0x2002] 0x33
[0x2003] 0

D
[0x2004] 0xEE
[0x2005] 0xFF
Word based o[index>>2]=i[index>>2];
[0x2000] 0x11
[0x2001] 0x22
[0x2002] 0x33
[0x2003] 0x44
[0x2004] 0xEE
[0x2005] 0xFF
An innocent bystander was taken out
but if you are not word aligned
*i=0x1001
*o=0x2000
byte based:
[0x2000] 0xAA
[0x2001] 0x11
[0x2002] 0x22
[0x2003] 0x33
[0x2004] 0xEE
[0x2005] 0xFF
Word based should do something like:
[0x2000] 0x44
[0x2001] 0x11
[0x2002] 0x22
[0x2003] 0x33
[0x2004] 0xEE
[0x2005] 0xFF
So you wipe out a byte.
Or for the case
*i = 0x1000
*o = 0x2002
byte based:
[0x2000] 0xAA
[0x2001] 0xBB
[0x2002] 0x11
[0x2003] 0x22
[0x2004] 0x33
[0x2005] 0xFF
word based:
[0x2000] 0x33
[0x2001] 0x44
[0x2002] 0x11
[0x2003] 0x22
[0x2004] 0xEE
[0x2005] 0xFF
Not the same at all and you hose two innocent bystanders.
At least this is the way I remember it working on the ARM7, I cannot find my reference that explains
unaligned addressing in the ARM, I am pretty sure it is how I have described above. I have not tested this on the gp2x (yet).
The newer ARM ARM mentions "if alignment faults are enabled", that ones new to me.
I guess we can go complain to gcc about this bug in the compiler:
int one ( unsigned char *a, unsigned char *b )
{
while(1)
{
((unsigned long *)a)[ra>>2]=((unsigned long *)B)[rb>>2];
}
return(0);
}
Producing this:
one:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
stmfd sp!, {r4, lr}
ldr r4, .L6
ldr lr, .L6+4
mov ip, r1
.L3:
ldr r3, [lr, #0]
ldr r2, [r4, #0]
mov r3, r3, lsr #2
ldr r1, [ip, r3, asl #2]
mov r2, r2, lsr #2
str r1, [r0, r2, asl #2]
b .L3
And see what they say about it, most likely explain that it is a bug in the C code for this architecture, not a bug in the backend.