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