Preamble
It's known that for atomic and simultaneous reading/writing high and low part of 16-bit I/O registers (timer-counters, ICR/OCR, ADC...) AVR uses a shadow temporary register. E.g. reading TCNT1
on ATmega8:
uint8_t tl, th;
tl = TCNT1L; // tl <- TCNT1L, avr_temp <- TCNT1H (atomic)
th = TCNT1H; // th <- avr_temp
(Here avr_temp
is the AVR temporary shadow register). So, it's wrong to read TCNT1H
first, for example.
Question
Is it safe to use AVR-GCC with the code like the following?
uint16_t ticks;
ticks = TCNT1;
TCNT1 = 0x1234;
Will AVR-GCC always generate proper code for these operations?
(It seems to be "no" (how GCC knows that accessing to memory pointed by TCNT1
uses AVR shadow register?), but avr-libc define macro TCNT1 as well as TCNT1H, TCNT1L and avr-libc' FAQ recommend to directly use TCNT1
. I'm confused.)
I tested AVR-GCC v4.7.2, and it seems to generate correct code always. Even if I write 'TCNT1 |= 1' it produce proper code with -O3
:
$ avr-gcc -std=c99 -mmcu=atmega8 -S -O3 -o - 1.c
...
in r24,0x2c // TCNT1L
in r25,0x2c+1 // TCNT1H
ori r24,1
out 0x2c+1,r25
out 0x2c,r24
...
The code is the same even if I change TCNT1
with an ordinary 16-bit variable. So, "how does GCC know that accessing memory pointed by TCNT1
uses the AVR shadow register?" -- it seems by default to suppose the shadow register always when accessing any 16-bit variable.