Skip to main content

IAR Embedded Workbench for Arm 9.70.x

Exception functions for 64-bit mode

In this section:

The compiler provides the following primitives related to writing exception functions for the 64-bit mode:

  • The extended keywords __exception, __nested, and __svc

  • The intrinsic functions __enable_interrupt and __disable_interrupt

  • The special function names Synchronous_Handler_A64, Error_Handler_A64, IRQ_Handler_A64, and FIQ_Handler_A64

Exception functions

An exception function is used for handling external interrupt events or internal exceptions. When an exception occurs, the code executed in the core is stopped and code in an exception starts executing instead. It is important that the environment of the excepted code is restored after the exception has been handled—this includes the values of the processor registers, status registers, etc. The execution can then continue as if no exception took place.

__exception is a function type attribute that defines an exception function. It must have void as a return value, and cannot have parameters. All used registers are saved at entrance and restored at exit. It returns with an ERET instruction.

__exception void func(void)
{
    /* Do something */
}

Exceptions and C++ member functions

Only static member functions can be exception functions. When a non-static member function is called, it must be applied to an object. When an exception occurs and the exception function is called, there is no object available to apply the member function to.

Exception vector table

The IAR C/C++ Compiler uses the same exception vector table for the three exception levels EL1, EL2, and EL3. The exception vector table starts at the linker-defined symbol __eevector. It has 16 vectors, each 128 bytes large.

The IAR C/C++ Compiler only defines the vectors for exceptions that do not change exception level and that use the SP for the current exception level (offsets 0x200, 0x280, 0x300, and 0x380). Those four defined vectors have the names Synchronous_Handler_A64, Error_Handler_A64, IRQ_Handler_A64, and FIQ_Handler_A64. They have a default implementation that can be overridden by defining an __exception function with one of those names. If the function is too large to fit in a vector, the compiler will issue an error. The function cannot then be used directly as an exception function. Instead, you must:

  1. Write an assembler module that starts with a global symbol, for example ee. The symbol should jump to the exception function.

  2. Edit the linker configuration file. Replace the place at directive for the relevant exception function (for example Synchronous_Handler_A64) with place at address synchronous_evector { symbol ee };.

By default, the exception vector table is placed at address 2048. To place it at another address, use one of these methods:

  • Use the linker option ‑‑config_def to set the linker configuration symbol __Exception_table_address, like this: ‑‑config_def __Exception_table_address=4096

  • Edit the linker configuration file that the project uses

The exception table must be 2Kbyte-aligned.

Nested exception functions

An exception function can be nested. This also saves the ELR_EL1 system register at entrance. When the function exits, interrupts are disabled and all saved registers are restored.

Note that the SPSR_EL1 system register is not saved automatically. It must be saved explicitly before interrupts are enabled, for the status flags to be preserved after the exception. Doing this explicitly allows other bits of SPSR_EL1 to be manipulated.

An example:

#include <intrinsics.h>
__exception __nested void func(void)
{
    // All used registers + ELR_EL1 have been saved. SPSR_EL1
    // and ESR_EL1 can be saved/used.

    __enable_interrupt();

    // Do stuff

    __disable_interrupt();

    // The possibly changed SPSR_EL1 and ESR_EL1 can be restored.
    // At exit, interrupts will be disabled and then all used
    // registers are restored. Then ERET is executed.
}

Supervisor-defined functions

A function defined using the function type attribute __svc can have return values and can take parameters. It preserves the same registers as a normal function call, and returns with an ERET instruction. An SVC-defined function handles synchronous exceptions.

__svc void func(void)
{
    /* Do something */
}

See below for an example.

Supervisor call

SVC is an A64 instruction that makes a supervisor call, that is, an exception. It is handled by the synchronous exception vector. The IAR C/C++ Compiler supports exchanging the normal call instruction used to call a function with the SVC instruction, by using the pragma directive svc_number in front of any function declaration or definition. The supplied number will be stored in the ESR_EL1 system register.

#pragma svc_number = 23
__svc int Synchronous_Handler_A64(int i)
{
    return i;
}
    
void f()
{
    int i = Synchronous_Handler_A64(5); // Will use an SVC
}

The intended use for SVC functions is to let code executing in a lower exception level call code in a higher exception level.

// User code
#pragma svc_number = 1
int svc1(int);
#pragma svc_number = 2
int svc2(int);

int main(void)
{
    svc1(1);
}

// Supervisor code
__svc int Synchronous_Handler_A64(int a)
{
    // Get syndrome: AARCH64 SVC
    long long nr = 0;
    __asm("MRS %x0, ESR_EL1\n" : "=r"(nr));
    int ec = (nr >> 26) & 0x3F;
    if (ec != 0x15)
        return -1;

    // Get SVC number.
    nr &= 0xFF'FFFF;

    return nr + a;
}

Functions declared with #pragma svc_number do not have to use the same function signature. If different signatures are used, Synchronous_Handler_A64 must be written in assembler language as a trampoline to the various calling handlers, in order to pass parameters and handle return values correctly.

Reset address

By default, the reset address is assumed to be at address 0. To place it at another address, use one of these methods:

  • Use the linker option ‑‑config_def to set the linker configuration symbol __Reset_address, like this: ‑‑config_def __Reset_address=4096

  • Edit the linker configuration file that the project uses