Does AVR-GCC properly work with 16-bit AVR I/O registers?

做~自己de王妃 提交于 2020-01-05 19:25:35

问题


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.


回答1:


I don't know how it knows, but I've used TCNT1 directly in dozens of programs without issue. The FAQ you linked suggests you do it that way, as does every tutorial I've read.

The point the FAQ is making is to assure an interrupt can't happen between the writing of the two registers. Though avr-gcc will generate code that accesses them in the correct order, it can't guarantee an interrupt won't happen between them: you must take care of that.




回答2:


If you read the source of e.g. /usr/lib/avr/include/avr/iom32u4.h you'll see this:

#define TCNT1 _SFR_MEM16(0x84)

expanding the _SFR_MEM16 macro in /usr/lib/avr/include/avr/sfr_defs.h you see this:

#define _SFR_MEM16(mem_addr) _MMIO_WORD(mem_addr)

expanding _MMIO_WORD:

#define _MMIO_WORD(mem_addr) (*(volatile uint16_t *)(mem_addr))

Then looking at stdint.h: typedef unsigned int uint16_t;

Here is where the compiler steps in. The int type on the AVR platform is 16 bits, and the registers on the AVR are 8, so the compiler must make 2 memory accesses (there are no 16-bit memory access instructions defined for it to use). As per the documentation here:

The compiler will choose the correct instruction sequence to generate based on the address of the register being accessed.


As an aside, I first thought it was variable attributes, but it turns out it's baked in to the AVR architecture by the memory map of the chip.



来源:https://stackoverflow.com/questions/14161918/does-avr-gcc-properly-work-with-16-bit-avr-i-o-registers

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!