/
vector_table.c
383 lines (340 loc) · 17.4 KB
/
vector_table.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
/*
Baremetal main program with timer interrupt.
SPDX-License-Identifier: Unlicense
https://five-embeddev.com/
Tested with sifive-hifive-revb, but should not have any
dependencies to any particular implementation.
*/
#include <stdint.h>
#include <stddef.h>
#include "riscv-abi.h"
enum {
VECTOR_TABLE_ALIGNMENT = 256,
};
// Makes use of GCC interrupt and weak reference/alias attributes
// https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
// https://gcc.gnu.org/onlinedocs/gcc/RISC-V-Function-Attributes.html#RISC-V-Function-Attributes
// Vector table - not to be called.
void riscv_mtvec_table() __attribute__((naked, section(".text.mtvec_table"), aligned(VECTOR_TABLE_ALIGNMENT)));
void riscv_stvec_table() __attribute__((naked, section(".text.stvec_table"), aligned(VECTOR_TABLE_ALIGNMENT)));
void riscv_utvec_table() __attribute__((naked, section(".text.utvec_table"), aligned(VECTOR_TABLE_ALIGNMENT)));
// Default "NOP" implementations
static exception_stack_frame_t* riscv_nop_exception(exception_stack_frame_t* stack_frame);
static void riscv_nop_machine(void) __attribute__((interrupt("machine")));
static void riscv_nop_supervisor(void) __attribute__((interrupt("supervisor")));
static void riscv_nop_user(void) __attribute__((interrupt("user")));
// Weak alias to the "NOP" implementations. If another function
exception_stack_frame_t* riscv_mtvec_exception(exception_stack_frame_t* stack_frame)
__attribute__((weak, alias("riscv_nop_exception")));
void riscv_mtvec_msi(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_mti(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_mei(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_ssi(void) __attribute__((interrupt("supervisor"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_sti(void) __attribute__((interrupt("supervisor"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_sei(void) __attribute__((interrupt("supervisor"),
weak,
alias("riscv_nop_machine")));
exception_stack_frame_t* riscv_stvec_exception(exception_stack_frame_t* stack_frame)
__attribute__((weak, alias("riscv_nop_exception")));
void riscv_stvec_ssi(void) __attribute__((interrupt("supervisor"),
weak,
alias("riscv_nop_supervisor")));
void riscv_stvec_sti(void) __attribute__((interrupt("supervisor"),
weak,
alias("riscv_nop_supervisor")));
void riscv_stvec_sei(void) __attribute__((interrupt("supervisor"),
weak,
alias("riscv_nop_supervisor")));
void riscv_utvec_usi(void) __attribute__((interrupt("user"),
weak,
alias("riscv_nop_user")));
void riscv_utvec_uti(void) __attribute__((interrupt("user"),
weak,
alias("riscv_nop_user")));
void riscv_utvec_uei(void) __attribute__((interrupt("user"),
weak,
alias("riscv_nop_user")));
#ifndef VECTOR_TABLE_MTVEC_PLATFORM_INTS
void riscv_mtvec_platform_irq0(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq1(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq2(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq3(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq4(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq5(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq6(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq7(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq8(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq9(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq10(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq11(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq12(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq13(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq14(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
void riscv_mtvec_platform_irq15(void) __attribute__((interrupt("machine"),
weak,
alias("riscv_nop_machine")));
#endif// #ifndef VECTOR_TABLE_MTVEC_PLATFORM_INTS
// Helper macros to load/save
/** @def SAVE_REG
@brief Macro wrapper around assembler to save register to the stack frame struct.
@param REG Name of the abi register
*/
#if __riscv_xlen == 64
#define SAVE_REG(REG) \
__asm__ volatile( \
"sd " #REG " , %0(sp); " \
: /* no output */ \
: /* immediate input */ "i"(offsetof(exception_stack_frame_t, REG)) \
: /* no sclobber */)
#else
#define SAVE_REG(REG) \
__asm__ volatile( \
"sw " #REG " , %0(sp); " \
: /* no output */ \
: /* immediate input */ "i"(offsetof(exception_stack_frame_t, REG)) \
: /* no clobber */)
#endif
/** @def LOAD_REG
@brief Macro wrapper around assembler to restore a register from the stack frame struct.
@param REG Name of the abi register
*/
#if __riscv_xlen == 64
#define LOAD_REG(N) \
__asm__ volatile( \
"ld " #REG " , %0(sp); " \
: /* no output */ \
: /* immediate input */ "i"(offsetof(exception_stack_frame_t, REG)) \
: /* no clobber */)
#else
#define LOAD_REG(REG) \
__asm__ volatile( \
"lw " #REG " , %0(sp); " \
: /* no output */ \
: /* immediate input */ "i"(offsetof(exception_stack_frame_t, REG)) \
: /* no clobber */)
#endif
#if defined(__riscv_32e)
// Ignore registers not implemented in rv32e
#define SAVE_REG_NOT_E(REG)
#define LOAD_REG_NOT_E(REG)
#else
// Handle all registers
#define SAVE_REG_NOT_E SAVE_REG
#define LOAD_REG_NOT_E LOAD_REG
#endif
/** @def EXCEPTION_SAVE_STACK
* @brief Save registers before calling into C
*/
#define EXCEPTION_SAVE_STACK \
/* Move stack frame */ \
__asm__ volatile( \
"addi sp, sp, -%0;" \
: /* no output */ \
: /* immediate input */ "i"(sizeof(exception_stack_frame_t)) \
: /* no clobber */); \
/* Ignore 0: zero */ \
SAVE_REG(ra); \
/* Ignore 2,3,4: Stack, global, thread pointers */ \
SAVE_REG(t0); \
SAVE_REG(t1); \
SAVE_REG_NOT_E(t2); \
SAVE_REG(a0); \
SAVE_REG(a1); \
SAVE_REG(a2); \
SAVE_REG(a3); \
SAVE_REG_NOT_E(a4); \
SAVE_REG_NOT_E(a5); \
SAVE_REG_NOT_E(a6); \
SAVE_REG_NOT_E(a7); \
SAVE_REG_NOT_E(t3); \
SAVE_REG_NOT_E(t4); \
SAVE_REG_NOT_E(t5); \
SAVE_REG_NOT_E(t6)
/** @def EXCEPTION_RESTORE_STACK
* @brief Restore registers after returning from C
*/
#define EXCEPTION_RESTORE_STACK \
/* Ignore 0: zero */ \
LOAD_REG(ra); \
/* Ignore 2,3,4: Stack, global, thread pointers */ \
LOAD_REG(t0); \
LOAD_REG(t1); \
LOAD_REG_NOT_E(t2); \
LOAD_REG(a0); \
LOAD_REG(a1); \
LOAD_REG(a2); \
LOAD_REG(a3); \
LOAD_REG_NOT_E(a4); \
LOAD_REG_NOT_E(a5); \
LOAD_REG_NOT_E(a6); \
LOAD_REG_NOT_E(a7); \
LOAD_REG_NOT_E(t3); \
LOAD_REG_NOT_E(t4); \
LOAD_REG_NOT_E(t5); \
LOAD_REG_NOT_E(t6); \
/* Restore stack frame */ \
__asm__ volatile( \
"addi sp, sp, %0;" \
: /* no output */ \
: /* immediate input */ "i"(sizeof(exception_stack_frame_t)) \
: /* no clobber */)
#pragma GCC push_options
// Ensure all ISR tables are aligned.
#pragma GCC optimize("align-functions=4")
// NOLINTBEGIN (hicpp-no-assembler)
// hicpp-no-assembler: This cannot be implemented without assembler.
// Ensure the vector table is aligned.
// The bottom 4 bits of MTVEC are ignored - so align to 16 bytes
// Vector table. Do not call!
// Possible entries defined by mcause table
// http://five-embeddev.com/riscv-isa-manual/latest/machine.html#sec:mcause
//
// When vectored interrupts are enabled, interrupt cause 0, which
// corresponds to user-mode software interrupts, are vectored to the
// same location as synchronous exceptions. This ambiguity does not
// arise in practice, since user-mode software interrupts are either
// disabled or delegated to user mode.
void riscv_mtvec_table() {
__asm__ volatile(
".org riscv_mtvec_table + 0*4;"
"jal zero,.handle_mtvec_exception;" /* 0 */
".org riscv_mtvec_table + 1*4;"
"jal zero,riscv_mtvec_ssi;" /* 1 */
".org riscv_mtvec_table + 3*4;"
"jal zero,riscv_mtvec_msi;" /* 3 */
".org riscv_mtvec_table + 5*4;"
"jal zero,riscv_mtvec_sti;" /* 5 */
".org riscv_mtvec_table + 7*4;"
"jal zero,riscv_mtvec_mti;" /* 7 */
".org riscv_mtvec_table + 9*4;"
"jal zero,riscv_mtvec_sei;" /* 9 */
".org riscv_mtvec_table + 11*4;"
"jal zero,riscv_mtvec_mei;" /* 11 */
#ifndef VECTOR_TABLE_MTVEC_PLATFORM_INTS
".org riscv_mtvec_table + 16*4;"
"jal riscv_mtvec_platform_irq0;"
"jal riscv_mtvec_platform_irq1;"
"jal riscv_mtvec_platform_irq2;"
"jal riscv_mtvec_platform_irq3;"
"jal riscv_mtvec_platform_irq4;"
"jal riscv_mtvec_platform_irq5;"
"jal riscv_mtvec_platform_irq6;"
"jal riscv_mtvec_platform_irq7;"
"jal riscv_mtvec_platform_irq8;"
"jal riscv_mtvec_platform_irq9;"
"jal riscv_mtvec_platform_irq10;"
"jal riscv_mtvec_platform_irq11;"
"jal riscv_mtvec_platform_irq12;"
"jal riscv_mtvec_platform_irq13;"
"jal riscv_mtvec_platform_irq14;"
"jal riscv_mtvec_platform_irq15;"
#endif
".handle_mtvec_exception:");
EXCEPTION_SAVE_STACK;
__asm__ volatile(
// Current stack pointer
// Save to a0
"mv a0, sp;"
// Jump to exception hander
// Pass
"jal ra,riscv_mtvec_exception;" /* 0 */
// Restore stack pointer from return value (a0)
"mv sp, a0;");
EXCEPTION_RESTORE_STACK;
// Return
__asm__ volatile("mret;");
}
// Vector table. Do not call!
// See scause table for possible entries.
// http://five-embeddev.com/riscv-isa-manual/latest/supervisor.html#sec:scause
void riscv_stvec_table() {
__asm__ volatile(
".org riscv_stvec_table + 0*4;"
"jal zero,.handle_stvec_exception;" /* 0 */
".org riscv_stvec_table + 1*4;"
"jal zero,riscv_stvec_ssi;" /* 1 */
".org riscv_stvec_table + 5*4;"
"jal zero,riscv_stvec_sti;" /* 5 */
".org riscv_stvec_table + 9*4;"
"jal zero,riscv_stvec_sei;" /* 9 */
".handle_stvec_exception:");
EXCEPTION_SAVE_STACK;
__asm__ volatile(
// Current stack pointer
// Save to a0
"mv a0, sp;"
// Jump to exception hander
// Pass
"jal ra,riscv_stvec_exception;" /* 0 */
// Restore stack pointer from return value (a0)
"mv sp, a0;");
EXCEPTION_RESTORE_STACK;
// Return
__asm__ volatile("sret;");
}
// Vector table. Do not call!
void riscv_utvec_table() {
__asm__ volatile(
".org riscv_utvec_table + 0*4;"
"jal zero,riscv_utvec_usi;" /* 0 */
".org riscv_utvec_table + 4*4;"
"jal zero,riscv_utvec_uti;" /* 4 */
".org riscv_utvec_table + 8*4;"
"jal zero,riscv_utvec_uei;" /* 8 */
);
}
// NOLINTEND (hicpp-no-assembler)
static exception_stack_frame_t* riscv_nop_exception(exception_stack_frame_t* stack_frame) {
// Nop exception handler
return stack_frame;
}
static void riscv_nop_machine(void) {
// Nop machine mode interrupt.
}
static void riscv_nop_supervisor(void) {
// Nop supervisor mode interrupt.
}
static void riscv_nop_user(void) {
// Nop user mode interrupt.
}
#pragma GCC pop_options