ABI

Application Binary Interface and implementation defined behavior of avr-gcc. Object format bits are not discussed here. See also C Implementation-defined behavior.

Type Layout

default

sizeof

Note

char

1

signed

short

2

int

2

long

4

long long

8

size_t

2

unsigned int

ptrdiff_t

2

int

void*

2

float

4

double

4

non-Standard

wchar_t

2

Deviations from the Standard

double

double is only 32 bits wide and implemented in the same way as float

8-bit int with -mint8

With -mint8 int is only 8 bits wide which does not comply to the C standard. Notice that -mint8 is not a multilib option and neither supported by AVR-Libc (except stdint.h) nor by newlib.

  • -mint8

    sizeof

    Note

    char

    1

    signed

    short

    1

    int

    1

    long

    2

    long long

    4

    size_t

    2

    long unsigned int

    prtdiff_t

    2

    long int

  • Fixed-Point Support

    avr-gcc 4.8 and up supports fixed point arithmetic according to ISO/IEC TR 18037. The support is not complete. The type layouts are as follows:

    Type

    sizeof

    unsigned

    signed

    Note

    _Fract

    short

    1

    0.8

    ±.7

    2

    0.16

    ±.15

    long

    4

    0.32

    ±.31

    long long

    8

    0.64

    ±.63

    GCC extension

    _Accum

    short

    2

       8.8

       ±8.7

    4

    16.16

    ±16.15

    long

    8

    32.32

    ±32.31

    long long

    8

    16.48

    ±16.47

    GCC extension

    Overflow behavior of the non-saturated arithmetic is unspecified.

    Please notice that some private ports found on the web implement different layouts.

    Register Layout

    Values that occupy more than one 8-bit register start in an even register.

    Fixed Registers

    Fixed Registers are registers that won't be allocated by GCC's register allocator. Registers R0 and R1 are fixed and used implicitly while printing out assembler instructions:

    R0

    is used as scratch register that need not to be restored after its usage. It must be saved and restored in interrupt service routine's (ISR) prologue and epilogue. In inline assembler you can use __tmp_reg__ for the scratch register.

    R1

    always contains zero. During an insn the content might be destroyed, e.g. by a MUL instruction that uses R0/R1 as implicit output register. If an insn destroys R1, the insn must restore R1 to zero afterwards. This register must be saved in ISR prologues and must then be set to zero because R1 might contain values other than zero. The ISR epilogue restores the value. In inline assembler you can use __zero_reg__ for the zero register.

    T
    the T flag in the status register (SREG) is used in the same way like the temporary scratch register R0.

    User-defined global registers by means of global register asm and / or -ffixed-n won't be saved or restored in function pro- and epilogue.

    Call-Used Registers

    The call-used or call-clobbered general purpose registers (GPRs) are registers that might be destroyed (clobbered) by a function call.

    R18–R27, R30, R31
    These GPRs are call clobbered. An ordinary function may use them without restoring the contents. Interrupt service routines (ISRs) must save and restore each register they use.
    R0, T-Flag
    The temporary register and the T-flag in SREG are also call-clobbered, but this knowledge is not exposed explicitly to the compiler (R0 is a fixed register).

    Call-Saved Registers

    R2–R17, R28, R29
    The remaining GPRs are call-saved, i.e. a function that uses such a registers must restore its original content. This applies even if the register is used to pass a function argument.
    R1
    The zero-register is implicity call-saved (implicit because R1 is a fixed register).

    Frame Layout

    Frame Layout after Function Prologue

    incoming arguments

    return address (2–3 bytes)

    saved registers

    stack slots, Y+1 points at the bottom

    During compilation the compiler may come up with an arbitrary number of pseudo registers which will be allocated to hard registers during register allocation.

    Calling Convention

    For example, suppose a function with the following prototype:

    then

    Exceptions to the Calling Convention

    GCC comes with libgcc, a runtime support library. This library implements functions that are too complicated to be emit inline by GCC. What functions are used when depends on the target architecture, what instructions are available, how expensive they are and on the optimization level.

    Functions in libgcc are implemented in C or hand-written assembly. In the latter case, some functions use a special ABI that allows better code generation by the compiler.

    For example, the function that computes unsigned 8-bit quotient and remainder, __udivmodqi4, just returns the quotient and the remainter and clobbers R22 and R23. The compiler knows that the function does not destroy R30, for example, and may hold a value in R30 across the function call. This reduces the register pressure in functions that call __udivmodqi4.

    Function

    Availability

    Operation

    Clobbers

    Description

    __umulhisi3

    4.7+ && MUL

    SI:22 = HI:26 * HI:18

    Rtmp

    Multiply 2 unsigned 16-bit integers to a 32-bit result

    __mulhisi3

    4.7+ && MUL

    SI:22 = HI:26 * HI:18

    Rtmp

    Multiply 2 signed 16-bit integers to a 32-bit result

    __usmulhisi3

    4.7+ && MUL

    SI:22 = HI:26 * HI:18

    Rtmp

    Multiply the signed 16-bit integer in R26 with the unsigned 16-bit integer in R18 to a 32-bit result

    __muluhisi3

    4.7+ && MUL

    SI:22 = HI:26 * SI:18

    Rtmp

    Multiply an unsigned 16-bit integer with a 32-bit integer to a 32-bit result

    __mulshisi3

    4.7+ && MUL

    SI:22 = HI:26 * SI:18

    Rtmp

    Multiply a signed 16-bit integer with a 32-bit integer to a 32-bit result

    __udivmodqi4

    QI:24 = QI:24 / QI:22
    QI:25 = QI:24 % QI:22

    R23

    Unsigned 8-bit integer quotient and remainder

    __divmodqi4

    QI:24 = QI:24 / QI:22
    QI:25 = QI:24 % QI:22

    R23, Rtmp, T

    Signed 8-bit integer quotient and remainder

    __udivmodhi4

    HI:22 = HI:24 / HI:22
    HI:24 = HI:24 % HI:22

    R21, R26...27

    Unsigned 16-bit integer quotient and remainder

    __divmodhi4

    HI:22 = HI:24 / HI:22
    HI:24 = HI:24 % HI:22

    R21, R26...27, Rtmp, T

    Signed 16-bit integer quotient and remainder

    The Operation column uses GCC's machine modes to describe how values in registers are interpreted.

    Machine Modes

    Qarter, 8 bit

    Half, 16 bit

    Single, 32 bit

    Double, 64 bit

    Partial Single, 24 bit

    Integer

    QI

    HI

    SI

    DI

    PSI

    Float

    SF

    Signed _Accum

    HA

    SA

    DA

    Signed _Fract (Q-Format)

    QQ

    HQ

    SQ

    DQ

    Unsigned _Accum

    UHA

    USA

    UDA

    Unsigned _Fract (Q-Format)

    UQQ

    UHQ

    USQ

    UDQ

    Extensions

    Types

    Attributes

    Pragmas

    Address Spaces

    Using avr-gcc

    Supporting "unsupported" Devices

    avr-gcc and avr-as support the -mmcu=device command line option to generate code for a specific device. Currently (2012), there are more than 200 known AVR devices and the hardware vendor keeps releasing new devices. If you need support for such a device and don't want to rebuild the tools, you can

    1. Sit and wait until support for your -mmcu=device is added to the tools.

    2. Use appropriate command line options to compile for your favorite device.

    Approach 1 is comfortable but slow. Lazy developers that don't care for time-to-market will use it.

    Approach 2 is preferred if you want to start development as soon as possible and don't want to wait until the tool chain with respective device support is released. This approach is only possible if the compiler and binutils already come with support for the core architecture of your device.

    When you feed code into the compiler and compile for a specific device, the compiler will only care for the respective core; it won't care for the exact device. It does not matter to the compiler how many I/O pins the device has, at what voltage it operates, how much RAM is present, how many timers or UARTs are on the silicon or in what package it is shipped. The only thing the compiler does with -mmcu=device is to build-in define a specific macro and to call the linker in a specific way, i.e. the compiler driver behaves a bit differently, but the sub-tools like compiler proper and assembler will generate exactly the same code.

    Thus, you can support your device by setting these options by hand.

    Additionally, we need the following to compile a C program:

    The Device Header avr/io.h

    This header and its subheaders contain almost all infomation about a particular device like SFR addresses, size of the interrupt table and interrupt names, etc.

    After all, it's just text and you can write it yourself. Find a device that is already supported by AVR Libc and that is similar enough to your new device to serve as a reasonable starting point for the new device description.

    If you are lucky, the device it already supported by AVR Libc but not yet by the compiler. In that case, you can use verbatim copies from AVR Libc.

    Yet another approch is to write the file from scratch or not to use avr/io.h like headers at all. I that case, you provide all needed definitions like, say, SP and size of the vector table yourself.

    If your toolchain is distributed with AVR Libc then avr/io.h is located in the installation directory at ./avr/include i.e. you find a file io.h in ./avr/include/avr. In that file you find the lines:

    #if defined (__AVR_AT94K__)
    #  include <avr/ioat94k.h>
    #elif defined (__AVR_AT43USB320__)
    #  include <avr/io43u32x.h>
    
    /* many many more entries */
    
    #else
    #  if !defined(__COMPILING_AVR_LIBC__)
    #    warning "device type not defined"
    #  endif
    #endif

    Add an entry for __AVR_mydevice__ and include your new file avr/iomydevice.h.

    If you don't want to change the existing avr/io.h then copy it to a new directory and add that directory as system search path by means of -isystem whenever you compile or preprocess a C or assembler source that shall include the extended avr/io.h. Notice that the new directory will contain a subdirectory named avr.

    Compiling the Code

    Let's start with a simple C program, source.c:

    #include <avr/io.h>
    
    int var;
    
    int main (void)
    {
        return var + SP;
    }

    Your source directory then contains the following files:

    The startup code gcrt1.S and macros.inc are verbatim copies from AVR Libc.

    sectionname.h is included by macros.inc but we don't need it: Simply provide sectionname.h as an empty file.

    For the matter of simplicity, we show how to compile for a device that is similar to ATmega8 so that we don't need to extend avr/io.h to show the work flow. In the case you copied avr/io.h to a new place, don't forget to add respective -isystem to the first two commands for source.c and gcrt1.S.

    ATmega8 is a device in core family avr4, thus we compile and assemble our source.c for that core architecture. __AVR_ATmega8__ stands for the subheader selector you added to avr/io.h.

    Similarly, we assemble the startup code for our device by means of:

    Finally, we link the stuff together to get a working source.elf (assuming that RAM starts at address 0x124):

    Voilà!

    None: avr-gcc (last edited 2013-06-23 19:06:31 by 179)