Embedded Freaks..

October 19, 2010

Tail-Chaining ARM Cortex-M0 Interrupts

Filed under: Cortex-M — Tags: — kunilkuda @ 6:08 pm

Building ARM Cortex-M0 bootloader seems like a bit more difficult that what I’ve imagined.

Here’s the first challenge: the ARM Cortex-M0 interrupt vector table is not relocate-able. Hence, if we put our bootloader into ARM Cortex-M0, we need to find some way to chain the bootloader’s interrupt vector to user’s interrupt vector.

Here’s how I did it:

First of all, point all the interrupt vectors into single default handler, except for the Reset handler and MSP, which will always point to bootloader’s Reset handler and MSP. The reason is that all of the interrupts in bootloader, except for the Reset Handler, only needs to do one thing: fetch the user’s interrupt handler address and jump to that address.

#define ALIAS(f) __attribute__ ((alias (#f)))

extern void _vStackTop(void);
extern void ResetISR(void);
static void NMI_Handler(void) ALIAS(NormalIntDefaultHandler);
static void HardFault_Handler(void) ALIAS(NormalIntDefaultHandler);
static void SVCall_Handler(void) ALIAS(NormalIntDefaultHandler);
static void PendSV_Handler(void) ALIAS(NormalIntDefaultHandler);
static void SysTick_Handler(void) ALIAS(NormalIntDefaultHandler);
static void CAN_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void SSP1_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void I2C_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void TIMER16_0_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void TIMER16_1_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void TIMER32_0_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void TIMER32_1_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void SSP0_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void UART_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void ADC_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void WDT_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void BOD_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void PIOINT3_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void PIOINT2_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void PIOINT1_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void PIOINT0_IRQHandler(void) ALIAS(NormalIntDefaultHandler);
static void WAKEUP_IRQHandler(void) ALIAS(NormalIntDefaultHandler);

Then, define the ‘default interrupt handler’ as ‘naked’ (without function prolog), to save the stack and cpu cycle when handling interrupt.

static void NormalIntDefaultHandler(void) __attribute__((naked));

Inside the ‘default interrupt handler’, get the interrupt vector no, and its address offset in interrupt vector table. Then jump to user’s interrupt vector table offset. Some warning here is that, we need to make sure that the compiler will only use: r0-r3 and r12. The reason is that those registers are used for scratch registers (and it’s saved into stack automatically during start of interrupt by NVIC). Hence, it’s save for us to operate using the registers.

void NormalIntDefaultHandler(void) {
	/* Note: Only use scratch registers: r0, r1, r2, r3
	 */
	register void *user_program_offset asm("r2") = (void *) USER_PROGRAM_OFFSET;

	asm (
			".syntax unified    \n\t"
			/* Get current IRQ no from PSR */
			"MRS    r0, PSR     \n\t"
			/* Mask the interrupt number only */
			"MOVS   r1, #0x3F   \n\t"
			"ANDS   r0, r1      \n\t"
			/* Irq address position = IRQ No * 4 */
			"LSLS   r0, r0, #2  \n\t"
			/* Fetch the user vector offset */
			"LDR    r0, [r0, r2]\n\t"
			/* Jump to user interrupt vector */
			"BX     r0          \n\t"
			: /* output */
			: /* input */
			: /* clobbered reg */"r0","r1","r2"
	);

}

The “.syntax unified” instruction is needed to enable Thumb-2 specific instruction set.

Ok..I guess we can tail chain the ARM Cortex-M0 interrupts now..

1 Comment »

  1. How does this separate the bootloader’s Interrupt handler from the user application? it seens like the bootloader’s interrupt handler is disabled when tail chaining is enable.

    Comment by Charlie — July 14, 2012 @ 9:11 am


RSS feed for comments on this post. TrackBack URI

Leave a comment