183713f51250652fc67a3fea43a3b93cffcfed66
[cortex-from-scratch] / ivt.c
1 #include <stdbool.h>
2 #include <stddef.h>
3 #include <stdint.h>
4 #include <stm32.h>
5 #include <mmap.h>
6
7 /* 
8  * These values are pushed on the stack just before
9  * entering the ISR. Normally, this would require
10  * assembly or inline assembly code. I use a so-called
11  * 'naked function' __attribute__ ((interrupt)) which
12  * gives me a little bit more control over the caller
13  *
14  * The following register are pushed to the stack
15  * in reverse order
16  *
17  * */
18 struct interrupt_frame {
19
20         uint32_t r0; // N-32
21         uint32_t r1;
22         uint32_t r2;
23         uint32_t r3;
24         uint32_t r12;
25         uint32_t lr;
26         uint32_t pc;
27         uint32_t psr; // N-4
28 };
29
30 /* 
31  * Vector table, each entry contains an interrupt
32  * service routine:  
33  *
34  * interrupt vector 1-15: processor exceptions
35  * interrupt vector 16-92: irq0 - irq ..
36  * */
37
38 uint32_t ivt[92];
39
40 /* each message corresponds to each and every exception. 
41  * We get the correct message by accessing
42  *  exception_message[interrupt_number] 
43  *  exception_message[0] is not used (=MSP)*/
44
45 char * exception_message(uint8_t intnr) {
46
47         char * messages[] = {
48             "--",
49             "RESET",
50             "NMI",
51             "HARD FAULT",
52             "MEMMANAGE FAULT",
53             "BUS FAULT",
54             "USAGE FAULT",
55             "RESERVED",
56             "SVC",
57             "DEBUG MONITOR",
58             "RESERVED",
59             "RESERVED",
60             "RESERVED",
61             "RESERVED",
62             "PENDSV",
63             "SYSTICK",
64             "IRQ1",
65             "IRQ2",
66             "IRQ3",
67             "IRQ4",
68             // add more if needed
69         };
70
71         if (intnr < 20) // TODO: strlen
72                 return messages[intnr];
73
74         return NULL;
75 }
76
77 void ivt_set_gate(unsigned char num, void * isr(), short pri) {
78
79         ivt[num] = (uint32_t) isr;
80         *NVIC_ISER0 = (1 << ((uint32_t)(num) & 0x1F));
81         /* Priorities */
82 }
83
84
85 /* Dummy interrupt */
86 __attribute__ ((interrupt)) 
87 void * dummy_isr(struct interrupt_frame * frame) {
88
89         uint8_t nr = *SCB_VTOR_ST & 0xFF;
90         
91         uart_puts("EXCEPTION: ");
92         uart_puts(exception_message(nr));
93         uart_puts("\nSYSTEM HALTED\n");
94         
95         for(;;);
96 }
97
98 /* Initialize interrupt vector  */
99 void ivt_init() {
100
101         /* clear entiry IVT, in SRAM location for SRAM + .data (in .bss section) */
102         memset(&ivt, 0, (sizeof(uint32_t) * 92));
103
104         // stack top is loaded from the first entry table on boot/reset
105         // don't need to relocate or init this here
106         extern void * reset,  * nmi, * hardfault;
107
108         for (int i = 1; i <= 6 ; i++) {
109                 ivt_set_gate(i, dummy_isr, 0);
110         }
111
112         /* the vector table starts at 0x0. Since the address 0x0 point to 
113          * bootcode, it is on ROM or FLASH. The vector table can be
114          * relocated to other memory locations. We can do this by setting 
115          * a register in the NVIC called the vector table offset register */
116
117         //*SCB_VTOR = (volatile uint32_t) &ivt; 
118         regw_u32(SCB_VTOR, (uint32_t) &ivt, 0, 0x01);
119
120 }