Mike said:
Normally, when I write software to access a register, I will do
something like this:
*((volatile u32*) 0x00000022) = 5; (with some wrappers to make it
cleaner)
however, I noticed that in a recent codebase that my company aquired,
volatile
is not used:
*((u32*) 0x00000022) = 5;
Is this a proper way of coding? I was under the understanding that if
volatile is not used, the code could be optimized out. Is this
correct? What does
everyone else do?
First, I hope you realize that converting integers to
and from pointers is inherently "unclean," and relies on
characteristics of the particular system you happen to be
using. It's a common technique for getting at things like
hardware registers, but it's not a portable technique.
Now, as to `volatile'. Nothing in the "register-ness"
of the target location requires `volatile' in and of itself,
but in some circumstances you may need it. For example, you
might send output to some device by writing successive bytes
to an I/O register:
u32 *ioreg = (u32*)0x00000022;
*ioreg = 'H';
*ioreg = 'e';
*ioreg = 'l';
*ioreg = 'l';
*ioreg = 'o';
This may well fail if the compiler observes that the results
of the first four assignments are unused and so eliminates
them, leaving you with only the last. Adding `volatile' to
the pointer declaration
volatile u32 *ioreg = (u32*)0x00000022;
tells the compiler that each access to `*ioreg' counts as a
necessary side-effect, and obliges it to retain all five
assignments.
It also requires the compiler to generate the side-effects
in the proper order. Let's imagine an I/O device with both a
"command register" and a "data register:"
u32 *cmnd = (u32*)0x1000;
u32 *data = (u32*)0x1004;
*data = '?';
*cmnd = CMD_STROBE;
Alas, this might not work: The compiler could decide to swap
the order of the assignments or maybe to combine them both into
one 64-bit store. By declaring both pointers `volatile' you
tell the compiler that each access is a side-effect, and
since the compiler is not allowed to rearrange the order of
side-effects the two assignments will be performed in the
order you intended.
My examples have involved assigning to the volatile
object, but reading from it may also be a side-effect. For
example, imagine a hardware timer:
hires_t *timer = (hires_t*)0x12345678;
hires_t t0, t1;
t0 = *timer;
lengthy_computation();
t1 = *timer;
printf ("Elapsed time: %f microseconds\n",
(double)(t1 - t0) / (HIRES_TICKS_PER_SEC * 1e6));
If the compiler deduces that lengthy_computation() does not
store to `*timer', it may fetch the value of `*timer' just
once, making `t0' and `t1' equal no matter how long the
computation runs. The desired side-effect of reading the
updated value of `*timer' has been lost. Again, the cure is
to use `volatile' to tell the compiler that the side-effect
exists and is required, and to force it to fetch twice.
Summary: Whether `volatile' is needed depends not on the
nature of the location being accessed, but on what you plan
to do with it.