mk450: code clean up and documentation
[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 #include <lib/tinyprintf.h>
34
35 /* 
36  * These values are pushed on the stack just before
37  * entering the ISR. Normally, this would require
38  * assembly or inline assembly code. I use a so-called
39  * 'naked function' __attribute__ ((interrupt)) which
40  * gives me a little bit more control over the caller
41  *
42  * The following register are pushed to the stack
43  * in reverse order:
44  *
45  * */
46 struct interrupt_frame {
47
48         uint32_t r0; // N-32
49         uint32_t r1;
50         uint32_t r2;
51         uint32_t r3;
52         uint32_t r12;
53         uint32_t lr;
54         uint32_t pc;
55         uint32_t psr; // N-4
56 };
57
58 /* 
59  * Vector table, each entry contains an interrupt
60  * service routine: 
61  * interrupt vector 1-15: processor exceptions
62  * interrupt vector 16-92: irq0 - irq ..
63  * Vector table needs to be aligned in memory.
64  * */
65 uint32_t __attribute__((aligned(0x100))) ivt[92];
66
67 /* Each message corresponds to each and every exception. */
68 char * exception_message(uint8_t intnr) {
69
70 char * messages[] = {
71     "--",
72     "RESET",
73     "NMI",
74     "HARD FAULT",
75     "MEMMANAGE FAULT",
76     "BUS FAULT",
77     "USAGE FAULT",
78     "RESERVED",
79     "SVC",
80     "DEBUG MONITOR",
81     "RESERVED",
82     "RESERVED",
83     "RESERVED",
84     "RESERVED",
85     "PENDSV",
86     "SYSTICK",
87     "IRQ1",
88     "IRQ2",
89     "IRQ3",
90     "IRQ4",
91     // add more if needed
92 };
93
94 if (intnr < 20) // TODO: strlen
95         return messages[intnr];
96
97 return "UNKNOWN";
98 }
99
100 void ivt_set_gate(unsigned char num, void * isr(), short pri) {
101
102         ivt[num] = (uint32_t) isr;
103 //      if (num <= 32)
104 //              *NVIC_ISER0 = (1 << ((uint32_t)(num) & 0x1F));
105         /* TODO: Priorities */
106 }
107
108
109 /* Dummy interrupt: comment out the comment to use a naked
110  * function */
111
112 __attribute__ ((interrupt)) 
113 void * dummy_isr( struct interrupt_frame * frame ) {
114
115         uint8_t nr = *SCB_VTOR_ST & 0xFF;
116         printf("EXCEPTION: %s\n", exception_message(nr));
117         printf("STACK TRACE:\n");
118         printf("R0:%p\n",frame->r0);
119         printf("R1:%p\n",frame->r1);
120         printf("R2:%p\n",frame->r2);
121         printf("R3:%p\n",frame->r3);
122         printf("R12:%p\n",frame->r12);
123         printf("LR:%p\n",frame->lr);
124         printf("PC:%p\n",frame->pc);
125         printf("PSR:%p\n",frame->psr);
126         
127         for(;;);
128 }
129
130 /* Initialize interrupt vector  */
131 void ivt_init() {
132
133         /* clear entire IVT location in memory  */
134         memset(&ivt, 0, (sizeof(uint32_t) * 92));
135
136         /* The reset, NMI and hardfault handlers are originally
137          * defined in the assembly start up and can be 
138          * reused or overwritten. 
139         *  */
140         extern void * reset,  * nmi, * hardfault;
141
142         // set dummy handlers 
143         for (int i = 1; i <= 64 ; i++) {
144                 ivt_set_gate(i, dummy_isr, 0);
145         }
146
147         /* The vector table is intially at 0x0. The vector table can be
148          * relocated to other memory locations. We can do this by setting 
149          * a register in the NVIC called the vector table offset register */
150
151         rwrite(SCB_VTOR, (uint32_t) &ivt);
152
153 }