[En-Nut-Discussion] Hardware Register Access Using Volatile Pointer

Bernd Walter enut at cicely.de
Tue Apr 26 13:00:50 CEST 2011


On Tue, Apr 26, 2011 at 09:27:50AM +0200, Harald Kipp wrote:
> Hi Bernd,
> 
> On 4/25/2011 10:45 PM, Bernd Walter wrote:
> > On Mon, Apr 25, 2011 at 07:51:25PM +0200, Harald Kipp wrote:
> >> #define inr(_reg)   (*((volatile unsigned int *)(_reg)))
> > The Atmel ARM include has something like this:
> > typedef volatile unsigned int AT91_REG;// Hardware register definition typedef struct
> > _AT91S_SYS {
> > AT91_REG         AIC_SMR[32];   // Source Mode Register
> > AT91_REG         AIC_SVR[32];   // Source Vector Register
> 
> IMHO, that's even worse. inr() and outr() offer much more flexibility.

With the structure design you can have pointer to USART instances
in a type save way.
In the end I think it's just a different style with different pros and
cons.
I personally use the Atmel style in my own projects, which sometimes
is tricky since there are name clashings with ethernut includes.

> >> The problem is, that the compiler will not guarantee, that the
> >> sequence of statements is kept. To force this, a memory barrier
> >> can be used
> > Which statements can be reordered?
> 
> Any, as long as this doesn't break the logic of statements.
> 
> The order of accesses to volatile memory is kept. But when mixing
> volatile and non-volatile accesses, the optimizer may re-order the
> statements, which may introduce unexpected side effects.
> 
> I vaguely remember such a case, but cannot remember where that happened 
> exactly. Let me try to construct a lousy example. Let's assume, we have 
> an interrupt routine running in two modes, controlled by a global variable.
> 
>    /* Set mode used by IRQ function. */
>    imode = 0;
>    /* Enable interrupts. */
>    outr(EIR, 1);
> 
> The compiler may decide to execute
> 
>    outr(EIR, 1);
>    imode = 0;
> 
> Of course we could declare imode as volatile too to force the compiler 
> to execute the statements in right order. BUT:
> 
> 1. imode is not volatile per definition, because its content will not 
> change out of the scope of the compiler.

Ok - but in that case it hasn't anything to do with outr.
It is that the compiler doesn't know that there is any dependency on
imode, so it's is allowed to cache (write cacheing) in a register until
end of the function.
If the function is done inline it might even hold back the value even
beyound end of function.

> 2. Each time imode is used at other places, the compiler will generate 
> less optimal code.

Yes - it is that you need to declare imode as volatile only in those
cases where you depend on direct writes.
A (compiler) memory barrier isn't just writing imode - it also writes
other registers, which are unrelated.

> > A memory barrier usually is more than just a compiler thing, since
> > it pushed things out to the coherence point for other CPUs or DMA.
> 
> I know, but when implemented as
> 
>   asm volatile("": : :"memory");
> 
> it is just a compiler thing. It simply tells GCC to write out all values 
> which are temporarily kept in registers. This will have the effect, that 
> outr() and inr() are working correctly even if the pointers are not 
> declared as pointers to volatiles.

Even then it is more than required.
I don't think it is a good idea to always invalidate every register
cached variable used inside a function, which hasn't anything to do
with the register.
It's only the programmer who knows wether a barrier or volatile variable
is the better option.

> > - IMHO the compiler shouldn't reorder things around volatile
> > variables.
> 
> Is this documented (guaranteed) anywhere?

I'm not a C specification guy, but what's the sense of volatile, when
it doesn't guaranti it's written in time?
But I think you are right to assume that non volatile variables can be
moved - in that point my sentence is wrong.

> > With the original volatile it must read, since it is volatile. In
> > your cae with the barrier I'm not sure, since it's not reading from
> > a volatile memory anymore it might be optimized away.
> 
> That's the point. I assume too, that the suggested approach will remove 
> inr(). Further, I'm not sure, that an access to volatile memory will be 
> kept, if the result is not used in the code. I wasn't able to find any 
> document on this either.

Yes - but with defining it volatile the compiler can't know that reading
won't change anything - as it surely does for reading hardware registers.
It is free to remove the dummy variable itself, which I think is a good
thing.

-- 
B.Walter <bernd at bwct.de> http://www.bwct.de
Modbus/TCP Ethernet I/O Baugruppen, ARM basierte FreeBSD Rechner uvm.


More information about the En-Nut-Discussion mailing list