Embedded Freaks..

August 7, 2009

Cortex-M3 Interrupt Vector Table

Filed under: Cortex-M — Tags: — kunilkuda @ 12:00 am

In Cortex-M3, there are 255 interrupt vectors, and it’s relocateable. During the initial boot, the interrupt vector table are located at 0x00, but, then, if you want to, you can move it to somewhere else.

The first ten interrupts are fixed by ARM (means you will always find them eventhough you’re using NXP, Atmel, STMicro, Luminary Micro, etc). The rest is up to the silicon vendors implementation.

The interrupt handler is a bit different from any other MCUs. You just need to write the interrupt handler address, without any additional code (such as: B <interrupt_handler_addr> to jump to specific address). Also, the first thing on your interrupt table is the stack address. The Cortex-M3 interrupt controller (NVIC) will need stack address before it can jump to the handler. Hence, it’s put as the first thing on the interrupt table.

Here’s how to implement it

/* Simple startup file for Cortex-M3 */

 /* Thumb-2 instructions are only supported in unified syntax mode */
 .syntax unified

/* Vector table definition */
 .section ".cs3.interrupt_vector"
 .long  __cs3_stack                 /* Top of Stack                 */
 .long  Reset_Handler               /* Reset Handler                */
 .long  NMI_Handler                 /* NMI Handler                  */
 .long  HardFault_Handler           /* Hard Fault Handler           */
 .long  MemManage_Handler           /* MPU Fault Handler            */
 .long  BusFault_Handler            /* Bus Fault Handler            */
 .long  UsageFault_Handler          /* Usage Fault Handler          */
 .long  0                           /* Reserved                     */
 .long  0                           /* Reserved                     */
 .long  0                           /* Reserved                     */
 .long  0                           /* Reserved                     */
 .long  SVC_Handler                 /* SVCall Handler               */
 .long  DebugMon_Handler            /* Debug Monitor Handler        */
 .long  0                           /* Reserved                     */
 .long  PendSV_Handler              /* PendSV Handler               */
 .long  SysTick_Handler             /* SysTick Handler              */

/* Vendor specific interrupts
 *  ---- Not implemented
 */

 /* Program section */
 .section ".text"

 /* Declare as thumb function. Otherwise it will not be linked
 * correctly
 */
 .thumb_func
 /* Export the symbol so linker can see this */
 .global Reset_Handler
Reset_Handler:
 /* Jump to main(), a thumb function */
 LDR     R0, =main
 BX      R0
 /* If main() ever exit, this should hold MCU from running wild */
 B       .

/* This is how the lazy guy doing it: by aliasing all the
 * interrupts into single address
 */
.thumb_func
NMI_Handler:
.thumb_func
HardFault_Handler:
.thumb_func
MemManage_Handler:
.thumb_func
BusFault_Handler:
.thumb_func
UsageFault_Handler:
.thumb_func
SVC_Handler:
.thumb_func
DebugMon_Handler:
.thumb_func
PendSV_Handler:
.thumb_func
SysTick_Handler:
 B    . /* while(1); */

You don’t need to write your interrupt handler in assembly. Here’s simple way to write your Reset_Handler() in C:

/*
 * Don't forget to take out the Reset_Handler() on the startup file first
 */
extern int main(void);
void Reset_Handler(void) {
 main();

 // If main() ever exit, this should hold MCU from running wild
 while(1);
}

But some people has found a way to code everything in C (instead of assembly and C) using callback function mechanism

/*
 * startup_LPC1766.c - Startup file for LPC1766 Cortex-M3
 *
 * @author: kunilkuda_at_gmail_dot_com
 */
extern void __cs3_stack; ///< This is global variable from linker script

/* We gave 'weak' attribute, so these functions can be aliased into a single
 * function
 */
void __attribute__((weak)) Reset_Handler(void);     /* Reset Handler */
void __attribute__((weak)) NMI_Handler(void);       /* NMI Handler */
void __attribute__((weak)) HardFault_Handler(void); /* Hard Fault Handler */
void __attribute__((weak)) MemManage_Handler(void); /* MPU Fault Handler */
void __attribute__((weak)) BusFault_Handler(void);  /* Bus Fault Handler */
void __attribute__((weak)) UsageFault_Handler(void);/* Usage Fault Handler */
void __attribute__((weak)) SVC_Handler(void);       /* SVCall Handler */
void __attribute__((weak)) DebugMon_Handler(void);  /* Debug Monitor Handler */
void __attribute__((weak)) PendSV_Handler(void);    /* PendSV Handler */
void __attribute__((weak)) SysTick_Handler(void);   /* SysTick Handler */

/*
 * Ask GCC to put this array into .cs3.interrupt_vector section
 */
__attribute__ ((section(".cs3.interrupt_vector")))
void (* const g_pfnVectors[])(void) = {
 &__cs3_stack,       // The initial stack pointer
 Reset_Handler,      // The reset handler
 NMI_Handler,        // The NMI handler
 HardFault_Handler,  // The hard fault handler
 MemManage_Handler,  // The MPU fault handler
 BusFault_Handler,   // The bus fault handler
 UsageFault_Handler, // The usage fault handler
 0,                  // Reserved
 0,                  // Reserved
 0,                  // Reserved
 0,                  // Reserved
 SVC_Handler,        // SVCall handler
 DebugMon_Handler,   // Debug monitor handler
 0,                  // Reserved
 PendSV_Handler,     // The PendSV handler
 SysTick_Handler,    // The SysTick handler
};

/* Here's how to alias multiple functions into single function
 */
void Default_Handler(void);
#pragma weak NMI_Handler        = Default_Handler /* NMI handler */
#pragma weak HardFault_Handler  = Default_Handler /* Hard Fault handler */
#pragma weak MemManage_Handler  = Default_Handler /* MPU Fault Handler */
#pragma weak BusFault_Handler   = Default_Handler /* Bus Fault Handler */
#pragma weak UsageFault_Handler = Default_Handler /* Usage Fault Handler */
#pragma weak SVC_Handler        = Default_Handler /* SVCall Handler */
#pragma weak DebugMon_Handler   = Default_Handler /* Debug Monitor Handler */
#pragma weak PendSV_Handler     = Default_Handler /* PendSV Handler */
#pragma weak SysTick_Handler    = Default_Handler /* SysTick Handler */

void Default_Handler(void) {
 while (1);
}

extern int main(void);
void Reset_Handler(void) {
 main();

 // If main() ever exit, this should hold MCU from running wild
 while(1);
}

For any method that you use, the most important part is defining your linker script to layout the code into the correct place.

/*
 * Define the supported output formats - elf32-littlearm is the
 *  default
 */
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")

/* Define the target architecture */
OUTPUT_ARCH(arm)

/* Define the system entry point */
ENTRY(Reset_Handler)

/* Define the memory layout for the board */
SECTIONS
{
 /* LPC1766 flash is at 0x0000_0000 */
 .flash 0x00000000 :
 {
 /* Tell the linker to collect any .cs3.interrupt_vector section to this
 address */
 KEEP(*(.cs3.interrupt_vector));

 /* Collect the executable parts here */
 *(.text .text.*);
 /* Collect the const variables here */
 *(.rodata .rodata.*);

 __sidata = .;
 }

 /*
 * This section refers to variables with init value. The init values are
 * stored on ROM, but addressed on RAM (otherwise the variables cannot be
 * manipulated).
 *
 * This section will be loaded at 0x10000000, the start of LPC1766 RAM
 */
 .data_at_ram 0x10000000: AT (LOADADDR(.flash) + SIZEOF(.flash))
 {
 __sdata = .;
 *(.data .data.*);
 __edata = .;
 }

 .ram :
 {
 __sbss = .;
 /* Collect the global / static variables, without init value here */
 *(.bss .bss.*);
 __ebss = .;
 }

 /* Set __cs3_stack to point the end of memory
 * This one depends on your MCU. I'm using LPC1766 with 256kB flash and
 * 32kB RAM
 */

 __cs3_stack = 0x10000000 + (32 * 1024);
}

This is just an example that does nothing. For more complete demo, you can download the original source code from CMSIS website (http://www.onarm.com/download/download389.asp).

Advertisements

4 Comments »

  1. Hi,

    Thanks for this very useful reference. Could you tell me what “__sidata = .” in the linker file is doing?

    Comment by Shahzeb — March 8, 2011 @ 11:52 pm

  2. Hi, isn’t the stack pointer meant to be &__cs3_stack – 1 ?

    Comment by alex — March 27, 2011 @ 8:07 pm

  3. No. Cortex M3 will store new value first before moving to the next empty space

    Comment by kunilkuda — April 1, 2011 @ 7:02 pm

  4. […] Cortex-M3 Interrupt Vector Table (via Embedded Freaks..) 2011/04/13 bygreencn Leave a comment Go to comments In Cortex-M3, there are 255 interrupt vectors, and it's relocateable. During the initial boot, the interrupt vector table are located at 0×00, but, then, if you want to, you can move it to somewhere else. The first ten interrupts are fixed by ARM (means you will always find them eventhough you're using NXP, Atmel, STMicro, Luminary Micro, etc). The rest is up to the silicon vendors implementation. The interrupt handler is a bit different from any … Read More […]

    Pingback by Cortex-M3 Interrupt Vector Table (via Embedded Freaks..) « Green World — April 13, 2011 @ 12:25 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: