Hey. Get your hardware developer to make hardware that can read the software's mind as it should. :-)
:^)
Oh, and I left the "volatile" keyword out of all of the above, but you absolutely, positively need it as well when you're touching hardware.
Linus Torvalds said somewhere he didn't like volatile because it never did what people expected. Linux people usually use a macro as follows:
#define barrier() __asm__ __volatile__("": : :"memory")
I don't see why Linus (or anyone else, for that matter) would have a problem understanding how "volatile" works.
His approach is clearly one solution, albeit one that requires code like this:
int *reg = ADDR;
*reg = val1; barrier(); *reg = val2; barrier(); *reg = val3; barrier();
Forget one of those barrier()'s, and you've given the optimizer a free-for-all (and yourself a real headache). The more correct way:
volatile int *reg = ADDR;
*reg = val1; *reg = val2; *reg = val3;
The behavior of Linus's barrier() is implied by the "volatile" keyword. One less line of code to deal with, which doesn't sound like a big deal until you're an ADHD trying to maintain mutliple, 400K+ line code sets yourself. :^)
Or, even better:
typedef volatile unsigned int *hwio32u;
hwio32u reg = (unsigned int*)0xfe; *reg = 1; *reg = 2;
Want proof? :^) With the volatile keyword in the typedef:
$ arm-elf-gcc -g -O3 -S test.c && less test.s foo: /* load register address */ mov r0, #254 /* 0xfe */ mov r1, #1 mov r2, #2 /* load write a '1' to it */ str r1, [r0, #0] /* load write a '2' to it */ str r2, [r0, #0] mov pc, lr
Without:
foo: mov r1, #2 mov r0, #254 str r1, [r0, #0] mov pc, lr
The compiler optimizes away the first write to the register, which is exactly what you would want it to do if it weren't pointing to real hardware...
Oh, and Linus's approach ties you inextricably to gcc. Mine works for any ANSI-compliant compiler.
b.g.
-- Bill Gatliff Embedded Linux development and training services. bgat@xxxxxxxxxxxxxxx