a6761d00c059ce8f71b5af4d9e61cc3b3e144f44
[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  * Set up of basic exceptions and interrupts. These interrupts
9  * don't do much, except for halting the system. 
10  * ivt_set_gate(interrupt nr, function, priority) can be used
11  * later to define more appropriate handling. See timer (timer.c) 
12  * or serial or (uart.c) handling for non-trivial examples.
13  *
14  * The actual code is not much, but there are a lot of details
15  * to consider. Besides that, in case more control is desired over
16  * entering and exiting interrupts (what is pushed on the stack) 
17  * A so-called naked function can be used. See below for more
18  * details.
19  * 
20  *
21  * */
22
23 #include <stdbool.h>
24 #include <stddef.h>
25 #include <stdint.h>
26
27 #include <sys/robsys.h>
28 #include <sys/mmap.h>
29
30 #include <lib/stdio.h>
31 #include <lib/string.h>
32 #include <lib/regfunc.h>
33
34 /* 
35  * These values are pushed on the stack just before
36  * entering the ISR. Normally, this would require
37  * assembly or inline assembly code. I use a so-called
38  * 'naked function' __attribute__ ((interrupt)) which
39  * gives me a little bit more control over the caller
40  *
41  * The following register are pushed to the stack
42  * in reverse order:
43  *
44  * */
45 struct interrupt_frame {
46
47         uint32_t r0; // N-32
48         uint32_t r1;
49         uint32_t r2;
50         uint32_t r3;
51         uint32_t r12;
52         uint32_t lr;
53         uint32_t pc;
54         uint32_t psr; // N-4
55 };
56
57 /* 
58  * Vector table, each entry contains an interrupt
59  * service routine:  
60  *
61  * interrupt vector 1-15: processor exceptions
62  * interrupt vector 16-92: irq0 - irq ..
63  *
64  * Vector table needs to be aligned in memory.
65  * */
66
67 uint32_t __attribute__((aligned(0x100))) ivt[92];
68
69 /* each message corresponds to each and every exception. 
70  * We get the correct message by accessing
71  *  exception_message[interrupt_number] 
72  *  exception_message[0] is not used (=MSP)*/
73
74 char * exception_message(uint8_t intnr) {
75
76         char * messages[] = {
77             "--",
78             "RESET",
79             "NMI",
80             "HARD FAULT",
81             "MEMMANAGE FAULT",
82             "BUS FAULT",
83             "USAGE FAULT",
84             "RESERVED",
85             "SVC",
86             "DEBUG MONITOR",
87             "RESERVED",
88             "RESERVED",
89             "RESERVED",
90             "RESERVED",
91             "PENDSV",
92             "SYSTICK",
93             "IRQ1",
94             "IRQ2",
95             "IRQ3",
96             "IRQ4",
97             // add more if needed
98         };
99
100         if (intnr < 20) // TODO: strlen
101                 return messages[intnr];
102
103         return NULL;
104 }
105
106 void ivt_set_gate(unsigned char num, void * isr(), short pri) {
107
108         ivt[num] = (uint32_t) isr;
109         *NVIC_ISER0 = (1 << ((uint32_t)(num) & 0x1F));
110         /* Priorities */
111 }
112
113
114 /* Dummy interrupt: comment out the comment to use a naked f
115  * function */
116
117 // __attribute__ ((interrupt)) 
118 void * dummy_isr(/* struct interrupt_frame * frame */) {
119
120         uint8_t nr = *SCB_VTOR_ST & 0xFF;
121         
122         cputs("EXCEPTION: ");
123         cputs(exception_message(nr));
124         cputs("\nSYSTEM HALTED\n");
125         
126         for(;;);
127 }
128
129 /* Initialize interrupt vector  */
130 void ivt_init() {
131
132         /* clear entire IVT, in SRAM location for SRAM + .data (in .bss section) */
133         memset(&ivt, 0, (sizeof(uint32_t) * 92));
134
135         // stack top is loaded from the first entry table on boot/reset
136         // don't need to relocate or init this here
137         extern void * reset,  * nmi, * hardfault;
138
139         for (int i = 1; i <= 64 ; i++) {
140                 ivt_set_gate(i, dummy_isr, 0);
141         }
142
143         /* The vector table is intially at 0x0. The vector table can be
144          * relocated to other memory locations. We can do this by setting 
145          * a register in the NVIC called the vector table offset register */
146
147         regw_u32(SCB_VTOR, (uint32_t) &ivt, 0, OWRITE);
148
149 }