Can someone please show me an example of how to setup an ARM9 interrupt vector table using C or inline assembly in a bare-metal environment with no RTOS or Linux OS?
Specifically how can I use inline assembly or C to set branches to my IRQ interrupt handler ISR coded in C?
/// timer1 64-bit mode interrupt handler connected to TINT2 interrupt=#34
/// \todo I think I need to ACK it once I get working
interrupt void interruptHandlerTimer1(void) {
printf("\n [* ISR *] \n");
// ACK TINT2 interrupt #34
AINTC ->IRQ1 = 1 << (34 - 32);
}
void main(void) {
TIMER1 ->TCR = 0x00000000;
// TGCR: TIMMODE=0 64-bit GP, TIM34RS=TIM12RS=1
TIM0ER1 ->TGCR = 0x00000003;
TIMER1 ->TIM34 = 0x00000000;
TIMER1 ->TIM12 = 0x00000000;
TIMER1 ->PRD34 = 0x00000000;
TIMER1 ->PRD12 = 0x0000ffff;
// TCR: inc until period match, then reset
TIMER1 ->TCR = (2 << 6);
// This is wrong.
// I think I need to insert opcode or assembly to branch to interruptHandlerTimer1 ?
// AINTC ->EABASE located @ 0x00000000
uint32_t** ptrEabase = (uint32_t**) (AINTC ->EABASE);
ptrEabase[34] = (uint32_t*) (interruptHandlerTimer1);
// Set INT34 TINT2 to IRQ priority 2
AINTC ->INTPRI4 = 0x00000200;
// Enable INT34
AINTC ->EINT1 = (1 << (34 - 32));
// Enable IRQ in CPSR
// "TMS32DM644x ARM Subsystem", 3.3 Processor Status registers
asm(" ;Enable IRQ in CPSR");
asm(" mrs r0, cpsr");
asm(" bic r0, r0, #0x80");
asm(" msr cpsr_c, r0");
// I expected to see " [* ISR *] " print
// when TIMER1->TIM12 reaches 0x0000ffff
while (1) {
printf("%08x %08x\r\n", TIMER1 ->TIM34, TIMER1 ->TIM12);
}
}
Thanks in advance for any tips or direction.
Bare-metal development examples for ARM9 are very hard to find.
Ed
Note that this answer is only applicable to Cortex series ARM processors
I see you reached a solution for this a while back, hopefully this will be useful to anyone with a similar problem in the future.
There are many ways to set up interrupt vectors, they vary a lot between hardware and some platforms may require extra steps. The solution below is generally viable on ARM Cortex micro controllers and is compiler agnostic.
#include <string.h>
//Size of vector table, note yours is probably 16 entries
#define VECTOR_TABLE_ENTRIES 4
//Base address in MCU memory of vector table (by default 0x0 for ARM9)
#define HARDWARE_VECTOR_TABLE_ADDRESS 0x00000000
typedef void(*isr_vector)(void);
void myIsr1();
void myIsr2();
void myIsr3();
void myIsr4();
static isr_vector s_vector_table[VECTOR_TABLE_ENTRIES] =
{
myIsr1,
myIsr2,
myIsr3,
myIsr4
};
/**
* Load interrupt vector to correct area in system memory, call on startup
*/
void load_vector_table()
{
memcpy(HARDWARE_VECTOR_TABLE_ADDRESS, s_vector_table, sizeof(isr_vector));
}
void myIsr1()
{
}
...
If you just need to add a single entry to the table you could use the following:
void set_vector_table_entry(int index, isr_vector vector)
{
*(HARDWARE_VECTOR_TABLE_ADDRESS + (sizeof(isr_vector) * index)) = vector;
}
At the end of the day setting up the table is the easy part, all you are doing is loading a table of function pointers to a specific location, the hard part is setting the right bits in the right registers to enable the interrupts and clear up after them properly.
Further be aware that the interrupt functions typically need to be declared with a compiler specific keyword or pragma to make sure that the compiler generates correct code. This is because function call and return handling is often different when calling an interrupt vector to when calling a normal function.
Note that ARMv7 architecture supports remapping the vector table which can be very useful, this approach adds constraints to the alignment of the table though which in turn requires compiler/linker specific directives.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With