basic implemtation general purpose clock and tinyprintf
[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("PC:%p\n",frame->lr);
117         printf("EXCEPTION: %s\n", exception_message(nr));
118         printf("STACK TRACE:\n");
119         printf("R0:%p\n",frame->r0);
120         printf("R1:%p\n",frame->r1);
121         printf("R2:%p\n",frame->r2);
122         printf("R3:%p\n",frame->r3);
123         printf("R12:%p\n",frame->r12);
124         printf("LR:%p\n",frame->lr);
125         printf("PC:%p\n",frame->pc);
126         printf("PSR:%p\n",frame->psr);
127         
128         //for(;;);
129 }
130
131 /* Initialize interrupt vector  */
132 void ivt_init() {
133
134         /* clear entire IVT location in memory  */
135         memset(&ivt, 0, (sizeof(uint32_t) * 92));
136
137         /* The reset, NMI and hardfault handlers are originally
138          * defined in the assembly start up and can be 
139          * reused or overwritten. 
140         *  */
141         extern void * reset,  * nmi, * hardfault;
142
143         // set dummy handlers 
144         for (int i = 1; i <= 64 ; i++) {
145                 ivt_set_gate(i, dummy_isr, 0);
146         }
147
148         /* The vector table is intially at 0x0. The vector table can be
149          * relocated to other memory locations. We can do this by setting 
150          * a register in the NVIC called the vector table offset register */
151
152         regw_u32(SCB_VTOR, (uint32_t) &ivt, 0, OWRITE);
153
154 }