how to guarantee write/read order in gcc?

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am currently using a define like #define BARRIER(x) {do {X} while (0);} to surround all operations that need to take place in that exact order, I use it like: BARRIER(PORTA.OUT = 4);

If it is possible to guarantee the write order to IO-ports and registers without using this kind of construct, I would be much happier since it uglies my code.

Does gcc guarantee the exact order of these operations, or do I still need this kind of constructs?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avr-gcc will read or write a 16 bit register correctly IF you refer to it by its 16 bit name. If the compiler does not recognise the name, do it by hand.

e.g. TCNT1 is 16 bit name for TCNT1L & TCNT1H

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
avr-gcc will read or write a 16 bit register correctly IF you refer to it by its 16 bit name. If the compiler does not recognise the name, do it by hand.

e.g. TCNT1 is 16 bit name for TCNT1L & TCNT1H

David.

I'm sorry, but I dont quite get what you mean. What does this have to do with the temporal order of register-writes? Say for example:

PORTA.OUTSET = 1; PORTA.OUTSET = 2; where I am dependent on these two writes happening in that order, and not being funneled together into one PORTA.OUTSET = 0x3; write.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

PORTA.OUTSET = 1; PORTA.OUTSET = 2; where I am dependent on these two writes happening in that order, and not being funneled together into one PORTA.OUTSET = 0x3; write

If you use:

PORTA |= (1<<PA0);
PORTA |= (1<<PA1);

then this is bound to be compiled as two separate writes because PORTA is 'volatile' so the optimiser cannot conglomerate this into a single write of ((1<<PA1) | (1<<PA0))

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, thanks. Then I won't need the bulky barrier-stuff anymore.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

> Then I won't need the bulky barrier-stuff anymore.

It didn't do anything anyway. Just for a simple test:

#include 

//#define PORTA (*(volatile uint8_t *)((0x1B) + 0x20))
#define PORTA (*(uint8_t *)((0x1B) + 0x20))

void
configure_porta(void)
{
        do { PORTA |= 2; } while(0);
        do { PORTA |= 4; } while(0);
}

That compiles into:

        in r24,59-0x20
        ori r24,lo8(6)
        out 59-0x20,r24

As you can see, both operations are merged into one. If you switch
the definitions on the top to the one which uses volatile (which
matches the original PORTA definition for an ATmega16), you get
instead:

        sbi 59-0x20,1
        sbi 59-0x20,2

So the do...while construct, while being "syntactical sugar" in some way,
doesn't help in the slightest way to "fight" against optimization not working
the way you intended.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
asm volatile("" : : : "memory");

seems to be the best way to guarantee that gcc doesn't move things around, but it takes a heavy toll on optimization.

Did you know that the classic

uint8_t temp;
temp = SREG;
cli();

/* critical section code */

SREG = temp;

does not protect the critical section code like you thought it would? GCC will, at times, reorder the code to be

uint8_t temp;
temp = SREG;
cli();
SREG = temp;
/* critical section code */

You can use the blocks to make sure that your code is really protected (AFAIK).

Math is cool.
jevinskie.com