2
\$\begingroup\$

Most (all?) of the AVR family controllers provide some special GPIO registers.

4.8.1 General Purpose I/O Registers
The lowest 16 I/O Memory addresses is reserved for General Purpose I/O Registers. These registers can be used for storing information, and they are particularly useful for storing global variables and flags, as they are directly bit-accessible using the SBI, CBI, SBIS, and SBIC instructions.

Q: Are those registers actually used by compilers, especially AVRGCC? Or would one be free to use those registers in some inline ASM for example without messing things up? After some digging, I haven't found anything conclusive.


Background:
Controller: XMEGA128A1
In a recent project, I discovered that an uninitialized pointer to a struct (size = 9 bytes) was dereferenced (and "written" to), potentially corrupting data at address 0x0000. This address is located in the I/O memory section instead of the SRAM area.

enter image description here

The first 16 bytes are used by the 16 x 8-bit GPIO registers available for this controller.

enter image description here

As I discovered this bug (that has actually been present for some time) the question came up, why there were no noticeable ramifications. So it would be interesting to know, if those registers are used at all or if we just got lucky so far.

\$\endgroup\$
  • \$\begingroup\$ If you talk about ASM compiler, they probably aren't. For C compiler, they might be used for code optimization, perhaps a good dive into the compiler manual would answer the question. \$\endgroup\$ – Damien Mar 5 at 12:06
  • \$\begingroup\$ Question is related to the C compiler but a more generic answer is welcome as well. \$\endgroup\$ – Rev1.0 Mar 5 at 12:23
  • \$\begingroup\$ These registers have no useful value whatsoever to the compiler, they are not part of the processor itself nor can they be used to store anything useful like ram might, although any ram used by the compiler is defined by the programmer directly or indirectly through the linker script or equivalent. these are not equivalent to general purpose registers in a processor. \$\endgroup\$ – old_timer Mar 6 at 1:35
  • \$\begingroup\$ these are the equivalent of a peripheral in the same way that a usb controller or hard disk controller, are peripherals and mapped through the address space. Like any other peripheral you the programmer writes code to make the peripheral do what you want it to do, the compiler simply generates code for the processor itself. \$\endgroup\$ – old_timer Mar 6 at 1:37
1
\$\begingroup\$

Peripheral registers are not accessed by GCC unless you specifically write code to access them.

There is a difference between CPU registers and peripheral registers. The CPU registers are in the CPU core. Peripheral registers reside in the peripheral blocks.

Peripheral registers are not used by GCC to compute any logic or math expressions or to manage the stack. Peripheral registers are only accessed when you specifically write code to access them.

//EXAMPLE PORTA = 0xFF ;//set all bits on port A to 1, writes to peripheral register PORTA

//EXAMPLE X = ((Y+Z)*76+P);//Calculate something, doesnt use peripheral registers.

If there are registers are implemented in your device and they don't connect to any used IO pins then you can probably use them as scratch registers without any problems.

\$\endgroup\$
1
\$\begingroup\$

No. The register macros are all defined in the io.h headers to point to some location in memory. If you don't expressly use the memory locations, they will remain unused.


I have compiled code for numerous AVR chips at every optimization level, and the compiler has never automatically put any of my variable in the GPIO registers. That said, you should manually use those registers in place of any global variables. I've also found it to significantly increase efficiency in terms of program performance and flash memory use, especially when using volatile variables with an ISR.

// Do this
#define my_global_var     GPIO1     // Or equivalent register definition

// Instead of this
static volatile uint8_t my_global_var = 0

You can actually do this with any of the available registers for chip peripherals. For example, if you aren't going to use any of the hardware pin change interrupts, the pin mask registers are free to use for global variables.

// Not using the pin change interrupts, so this mask register is free to use
#define my_global_var     PCMSK0

Just note the width of these registers, as some of them will only have a portion of the bits available for storage. The datasheet will say something like

bits 7:4 will always read 0

If you only need to count to 15, 4 usable bits of a register will work fine. Also note the power-on-reset value of the register; some of them are not 0!


Also, this is not the same as using the register C keyword, which is a hint for the compiler to put a variable in a processor register instead of RAM for quicker access. The compiler does this automatically as part of its optimization, using the "working registers" mentioned in the datasheet.

\$\endgroup\$
0
\$\begingroup\$

In C, an uninitialized pointer is called a NULL pointer. The value of a null pointer must be a constant with the value zero. What you are seeing is not a bug, it is the standard behavior for a pointer in C. Dereferencing a null pointer leads to undefined behavior, so you should be very sure that a pointer is not null before using it.

\$\endgroup\$
  • \$\begingroup\$ I improved the question. Sorry if it wasn't clear before, that the issue was exactly that - dereferencing a NULL-pointer. \$\endgroup\$ – Rev1.0 Mar 5 at 15:17
0
\$\begingroup\$

Unlikely that compilers would use these, because these are connected to I/O ports. You could use those registers that are not used for I/O on that particular CPU or board, but you'd need compiler options to specify which ones they are, they aren't really useful for passing values between functions (because then the function call ABI would become dependent on the list of usable low-address registers), and only a tiny fraction of programs would benefit in a measurable way.

So, not worth it for compilers to support.

\$\endgroup\$
  • \$\begingroup\$ I don't think the registers I refer to are used for actual (physical) I/O ports at all. At first I thought that as well, but from looking at the IO address space and the data sheet it doesn't seem so. This seems to support that. \$\endgroup\$ – Rev1.0 Mar 5 at 15:35
0
\$\begingroup\$

Those registers are like PORTA, PORTB etc. and not like r0 ... r31 CPU registers. So no, C compiler will not use them. However, you can still make use of those registers if you treat them like global variables and map them to a struct like this:

#include <avr/io.h>

typedef struct
{
    unsigned bit0: 1;
    unsigned bit1: 1;
    unsigned bit2: 1;
    unsigned bit3: 1;
    unsigned bit4: 1;
    unsigned bit5: 1;
    unsigned bit6: 1;
    unsigned bit7: 1;
}Bits;

int main(void)
{
    volatile Bits *bits = ((volatile Bits*)_SFR_MEM_ADDR(GPIOR0));
    bits->bit3 = 1;
    return 0;
}

This code will result in only one SBI instruction:

sbi 0x00, 3

While doing the same (setting bit 3) in normal global variable will result in three instructions:

uint8_t global;
int main(void)
{
    global |= 8;
 228:   80 91 00 20     lds r24, 0x2000 ; 0x802000 <_edata>
 22c:   88 60           ori r24, 0x08   ; 8
 22e:   80 93 00 20     sts 0x2000, r24 ; 0x802000 <_edata>
    return 0;
}
\$\endgroup\$
  • \$\begingroup\$ So to summarize: In essence, those registers are just meant to be used as "fast global variables". They do not relate to any hardware function. However, a compiler wouldn't make use of them because availability of those registers it is too specific. Is that about right? \$\endgroup\$ – Rev1.0 Mar 5 at 20:23
  • 1
    \$\begingroup\$ Compiler wouldn't make use of them because that would not be a wise thing to do. What if user wants to access those registers in C code? She then whould need to somehow tell the compiler not to use those registers. When inlining assembly and modifying r0...r31 registers, there's a convention that the user must temporally store register values and then restore them. But there's no such convention for non-CPU registers. \$\endgroup\$ – user930473 Mar 5 at 21:06
  • \$\begingroup\$ never use bitfields and never point structures across compile domains. \$\endgroup\$ – old_timer Mar 6 at 1:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.