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

Bernd Walter enut at cicely.de
Mon Apr 25 22:45:53 CEST 2011

On Mon, Apr 25, 2011 at 07:51:25PM +0200, Harald Kipp wrote:
> Hi all,
> today (with GCC) we are using
>  #define inr(_reg)   (*((volatile unsigned int *)(_reg)))
> and friend to directly access I/O registers. It seems to work, but that's deceptive.

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
In other words: all registers are declared volatile on their own.

> 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?
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.
On alpha, sparc and other CPUs there are real instructions to do
this and those are typically expensive ones.
e.g. you say that the a buffer needs to be writen out of the CPU
cache before writing the start_dma to the io register.
The IO registers are usually part of uncacheable memory, so there
is no reason to think about hardware based reordering for registers,
but probably about DMA buffers written to cached RAM.
But you are talking about compiler reordering - IMHO the compiler
shouldn't reorder things around volatile variables.

>  asm volatile("": : :"memory");
> If used, the volatile attribute in inr() is dispensable, because the memory barrier will enforce the write.
> Now, where I stumbled is the case, where I simply want to read an I/O status register to clear a flag. In this case I'm not interested in the value, I simply want to read from the given address and throw away the contents. The question is: Will
>  #define inr(_reg)   (*((unsigned int *)(_reg)))
>  #define mem_barrier() asm volatile("": : :"memory")
>  inr(TC0_SR);
>  mem_barrier();
> ensure, that a value is read to clear the AT91 timer status register TC0_SR? Or might it be possible that the compiler optimizes out the register access?

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.

PS: reading into a dummy variable won't help, since the compiler is
free to optimze dummy (and even real) variable away.

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