5.4. Interrupt Vectors and Handlers
Prev | Chapter 5. CS3™: The CodeSourcery Common Startup Code Sequence | Next

## 5.4. Interrupt Vectors and Handlers

CS3 provides standard handlers for interrupts, exceptions and traps, but also allows you to easily define your own handlers as needed. In this section, we use the term interrupt as a generic term for this entire class of events.

Different processors handle interrupts in various ways, but there are two general approaches:

- Some processors fetch an address from an array indexed by the interrupt number, and jump to that address. We call these address vector processors; ColdFire systems are a typical example.

- Others multiply the interrupt number by some constant factor, add a base address, and jump directly to that address. Here, the interrupt vector consists of blocks of code, so we call these code vector processors; PowerPC systems are a typical example.

On address vector processors, the CS3 library provides an array of pointers to interrupt handlers named `__cs3_interrupt_vector_`form``, occupying a section named `.cs3.interrupt_vector`, where `form` identifies the particular processor variant the vector is appropriate for. If the processor supports more than one variety of interrupt vector (for example, a full-length form and a shortened form), then `form` identifies the variety as well. Each entry in the vector holds a reference to a symbol named `__cs3_isr_`int``, where `int` is the customary name of that interrupt on the processor, or a number if there is no consistently used name. The library further provides a reasonable default definition for each `__cs3_isr_`int`` handler routine.

To override an individual handler, provide your own definition for the appropriate `__cs3_isr_`int`` symbol. The definition need not be placed in any particular object file section.

Interrupt handlers typically require special call/return and register usage conventions that are target-specific and beyond the scope of this document. As an alternative to writing interrupt handlers in assembly language, on some targets they may be written in C using the `interrupt` attribute. For example, to override the `__cs3_isr_access_error` handler, use the following definition:

    void __attribute__ ((interrupt)) __cs3_isr_access_error (void)
    {
      ... custom handler code ...
    }

To override the entire interrupt vector, you can define `__cs3_interrupt_vector_`form``, placing the definition in a section named `.cs3.interrupt_vector`. The linker script reports an error if the `.cs3.interrupt_vector` section is empty, to ensure that the definition of `__cs3_interrupt_vector_`form`` occupies the proper section.

You may define the vector in C with an array of pointers using the `section` attribute to place it in the appropriate section. For example, to override the interrupt vector on M5208EVB boards, make the following definition:

    typedef void handler(void);
    handler *__attribute__((section (".cs3.interrupt_vector")))
      __cs3_interrupt_vector_coldfire[] =
    { ... };

On code vector processors, we follow the same conventions, with the following exceptions:

- In addition to being named `__cs3_isr_`int``, each interrupt handler must also occupy a section named `.cs3.interrupt_`int``. Naturally, each handler must fit within a single interrupt vector entry.

- Instead of providing a default definition for `__cs3_interrupt_vector_`form`` in the library, the linker script gathers the `.cs3.interrupt_`int`` sections together, in the proper order and on the necessary address boundaries, and defines the `__cs3_interrupt_vector_`form`` symbol to refer to its start.

To override an individual handler on a code vector processor, you provide your own definition for `__cs3_isr_`int``, placed in an appropriate section. The linker script ensures that each `.cs3.interrupt_`int`` section is non-empty, so that placing a handler in the wrong section elicits an error at link time.

CS3 does not allow you to override the entire interrupt vector on code vector processors, because the code vector must be constructed by the linker script, and thus cannot come from a library or object file. However, the portion of the linker script that constructs the interrupt vector occupies its own file, which other linker scripts can incorporate using the INCLUDE linker script command, making it easier to replace the linker script entirely and still take advantage of CS3's other features.

Some processors, like the Innovasic fido, use more than one interrupt vector: the processor provides several interrupt vector pointer registers, each used in different circumstances. Each register may point to a different vector, or some or all may share vectors.

On these processors, CS3 provides only a single pre-constructed interrupt vector, but defines a separate symbol for each interrupt vector pointer register; all the symbols point to the pre-constructed vector by default. The CS3 startup code initializes each register from the corresponding symbol. You can provide your own vectors by defining the appropriate symbols.

For example, the fido processor has five contexts, each of which can use its own interrupt vector; on this architecture, CS3 defines the standard `__cs3_interrupt_vector_fido` symbol referring to the pre-constructed vector, and then goes on to define per-context symbols `__cs3_interrupt_vector_fido_ctx0`, `__cs3_interrupt_vector_fido_ctx1`, and so on, all referring to `__cs3_interrupt_vector_fido`. The CS3 startup code sets each context's vector register to the value of the corresponding symbol. By default, all the contexts share an interrupt vector, but if your code provides its own definition for `__cs3_interrupt_vector_fido_ctx1`, then the startup code initializes context one's register to point to that vector instead.

This arrangement requires you to use a different approach to specify a handler for a secondary context that differs from the corresponding handler in the primary context. For example, to handle division-by-zero exceptions in context 1 with the function `ctx1_divide_by_zero`, you should write the following:

    typedef void (*handler_type) (void);
    handler_type __cs3_interrupt_vector_fido_ctx1[256];
    extern handler_type __cs3_interrupt_vector_fido[256];

    __attribute__((interrupt))
    void
    ctx1_divide_by_zero (void)
    {
      /* Your code here.  */
    }

    __attribute__((constructor))
    void
    initialize_vector_ctx1 (void)
    {
      /* Initialize our custom vector from the
         pre-constructed CS3 vector.  */
      memcpy (__cs3_interrupt_vector_fido_ctx1,
              __cs3_interrupt_vector_fido,
              sizeof (__cs3_interrupt_vector_fido));

      /* Initialize custom interrupt handlers.  */
      __cs3_interrupt_vector_fido_ctx1[5] = ctx1_divide_by_zero;
    }

With this code in place, when a division-by-zero exception occurs in context 1, the processor calls `ctx1_divide_by_zero` to handle it. Defining `initialize_vector_ctx1` with the `constructor` attribute arranges for CS3 to call it before calling your program's `main` function.

Prev | Up | Next
5.3. Memory Layout | Home | 5.5. Linker Scripts