ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
vector_table.c
Go to the documentation of this file.
1
26
27#include <stdint.h>
28
29#include "ra8_boot_entry.h"
30
31/* =============================================================================
32 * Linker-provided symbols
33 * =============================================================================
34 *
35 * Project convention: linker symbols carry the `g_ra8_ls_` prefix so they
36 * are not in the reserved leading-underscore namespace that ISO C (and
37 * cert-dcl37-c / bugprone-reserved-identifier) reject. The linker script
38 * in blink/linker_script.ld defines them with this exact spelling.
39 */
40
41extern uint32_t g_ra8_ls_stack_top;
42extern uint32_t g_ra8_ls_sdata;
43extern uint32_t g_ra8_ls_edata;
44extern uint32_t g_ra8_ls_sidata;
45extern uint32_t g_ra8_ls_sbss;
46extern uint32_t g_ra8_ls_ebss;
47
48/* =============================================================================
49 * Handler declarations
50 * =============================================================================
51 */
52
53typedef void (*exc_handler_t)(void);
54
55void Reset_Handler(void);
56void HardFault_Handler(void);
57void MemManage_Handler(void);
58void BusFault_Handler(void);
59void UsageFault_Handler(void);
60
61/* Core exceptions (Cortex-M85). HardFault / MemManage / BusFault /
62 * UsageFault each get a dedicated naked trampoline below which
63 * forwards to ra8_exception_report(). The rest are weak-aliased to
64 * Default_Handler and may be overridden by application code.
65 *
66 * Mach-O (macOS host) does not support `__attribute__((alias(...)))`.
67 * On the host syntax-check / unit-test build we drop the alias and
68 * keep only `weak`; the host build never branches through these
69 * symbols (the firmware vector table is not exercised on the test
70 * host), so we just need them to exist for the `&` references in
71 * `g_ra8_vector_table_start` to be well-formed. */
72#ifndef __APPLE__
73[[gnu::weak, gnu::alias("Default_Handler")]] void NMI_Handler(void);
74[[gnu::weak, gnu::alias("Default_Handler")]] void SecureFault_Handler(void);
75[[gnu::weak, gnu::alias("Default_Handler")]] void SVC_Handler(void);
76[[gnu::weak, gnu::alias("Default_Handler")]] void DebugMon_Handler(void);
77[[gnu::weak, gnu::alias("Default_Handler")]] void PendSV_Handler(void);
78#else
79[[gnu::weak]] void NMI_Handler(void);
80[[gnu::weak]] void SecureFault_Handler(void);
81[[gnu::weak]] void SVC_Handler(void);
82[[gnu::weak]] void DebugMon_Handler(void);
83[[gnu::weak]] void PendSV_Handler(void);
84#endif
85
86/* SysTick provided by libs/ra8_core/src/ra8_time.c (weak) so RTOS apps may override. */
87extern void SysTick_Handler(void);
88
89/* Each peripheral IRQ trampoline forwards to the ra8_isr substrate dispatcher so
90 * a driver that called ra8_isr_register (here: the NPU completion IRQ ->
91 * ra8_npu_irq_handler) runs in NVIC handler-mode. */
92extern void ra8_isr_dispatch(uint16_t slot);
93
94/* =============================================================================
95 * Peripheral IRQ handlers
96 * =============================================================================
97 *
98 * The RA Interrupt Control Unit exposes 112 routable interrupt lines
99 * to the NVIC (per HUM table 13.x).
100 *
101 * Each peripheral IRQ vector forwards to ra8_isr_dispatch(slot) so any
102 * (handler, ctx) registered through the substrate (ra8_isr_register) runs in
103 * NVIC handler-mode. Slots that were never registered land in the dispatcher's
104 * bounds-checked no-op path. The trampolines are weak so a driver can still
105 * install a fully-custom IRQ handler by defining a non-weak ``IRQ<n>_Handler``.
106 * HUM Ch 13 NVIC + Ch 14 ICU IELSR.
107 */
108
109enum : uint16_t {
111};
112
113/* On Apple host syntax-check we drop ra8_isr_dispatch (host build never branches
114 * through these) and keep only a weak empty body. */
115#ifndef __APPLE__
117#define RA8_IRQ_STUB(n) \
118 void IRQ##n##_Handler(void); \
119 [[gnu::weak]] void IRQ##n##_Handler(void) \
120 { \
121 ra8_isr_dispatch((uint16_t)(n)); \
122 } \
123 static_assert(1, "ra8_irq_stub_" #n)
124#else
126#define RA8_IRQ_STUB(n) \
127 void IRQ##n##_Handler(void); \
128 [[gnu::weak]] void IRQ##n##_Handler(void) {} \
129 static_assert(1, "ra8_irq_stub_" #n)
130#endif
131
244
245#undef RA8_IRQ_STUB
246
247/* =============================================================================
248 * Vector table
249 * =============================================================================
250 *
251 * Placed in its own section so the linker script can pin it to the
252 * start of MRAM. The Cortex-M85 reads the initial MSP from offset 0
253 * and the reset vector from offset 4.
254 */
255
256/* Mach-O `section()` requires a `"segment,section"` form, but on the
257 * host syntax-check / unit-test build we only need this array to
258 * exist as a normal global -- the host never reads it from a fixed
259 * vector address. So drop the section pinning on Apple hosts. */
260#ifndef __APPLE__
261[[gnu::section(".vectors"), gnu::used]]
262#else
263[[gnu::used]]
264#endif
266 /* Core exceptions (slots 0..15). */
267 (exc_handler_t)&g_ra8_ls_stack_top, /* 0 Initial main stack pointer. */
268 Reset_Handler, /* 1 Reset vector. */
269 NMI_Handler, /* 2 NMI. */
270 HardFault_Handler, /* 3 HardFault. */
271 MemManage_Handler, /* 4 MemManage. */
272 BusFault_Handler, /* 5 BusFault. */
273 UsageFault_Handler, /* 6 UsageFault. */
274 SecureFault_Handler, /* 7 SecureFault. */
275 0, /* 8 Reserved. */
276 0, /* 9 Reserved. */
277 0, /* 10 Reserved. */
278 SVC_Handler, /* 11 SVC. */
279 DebugMon_Handler, /* 12 DebugMon. */
280 0, /* 13 Reserved. */
281 PendSV_Handler, /* 14 PendSV. */
282 SysTick_Handler, /* 15 SysTick. */
283
284/* Peripheral IRQs 0..111. */
286#define E(n) IRQ##n##_Handler
287 E(0),
288 E(1),
289 E(2),
290 E(3),
291 E(4),
292 E(5),
293 E(6),
294 E(7),
295 E(8),
296 E(9),
297 E(10),
298 E(11),
299 E(12),
300 E(13),
301 E(14),
302 E(15),
303 E(16),
304 E(17),
305 E(18),
306 E(19),
307 E(20),
308 E(21),
309 E(22),
310 E(23),
311 E(24),
312 E(25),
313 E(26),
314 E(27),
315 E(28),
316 E(29),
317 E(30),
318 E(31),
319 E(32),
320 E(33),
321 E(34),
322 E(35),
323 E(36),
324 E(37),
325 E(38),
326 E(39),
327 E(40),
328 E(41),
329 E(42),
330 E(43),
331 E(44),
332 E(45),
333 E(46),
334 E(47),
335 E(48),
336 E(49),
337 E(50),
338 E(51),
339 E(52),
340 E(53),
341 E(54),
342 E(55),
343 E(56),
344 E(57),
345 E(58),
346 E(59),
347 E(60),
348 E(61),
349 E(62),
350 E(63),
351 E(64),
352 E(65),
353 E(66),
354 E(67),
355 E(68),
356 E(69),
357 E(70),
358 E(71),
359 E(72),
360 E(73),
361 E(74),
362 E(75),
363 E(76),
364 E(77),
365 E(78),
366 E(79),
367 E(80),
368 E(81),
369 E(82),
370 E(83),
371 E(84),
372 E(85),
373 E(86),
374 E(87),
375 E(88),
376 E(89),
377 E(90),
378 E(91),
379 E(92),
380 E(93),
381 E(94),
382 E(95),
383 E(96),
384 E(97),
385 E(98),
386 E(99),
387 E(100),
388 E(101),
389 E(102),
390 E(103),
391 E(104),
392 E(105),
393 E(106),
394 E(107),
395 E(108),
396 E(109),
397 E(110),
398 E(111),
399#undef E
400};
401
402/* =============================================================================
403 * Reset handler: copy .data from MRAM to SRAM, zero .bss, call main()
404 * =============================================================================
405 */
406
408{
409 /* Step 1: core-level init (VTOR, FPU, caches, priority grouping,
410 * interrupts masked). Runs before we touch .data or .bss so it
411 * must not read or write any global variables. */
412 SystemInit();
413
414 /* Step 2: copy initialized data from flash/MRAM to SRAM. */
415 // NOLINTBEGIN(clang-analyzer-security.ArrayBound) -- Linker symbols define ranges, not one-element arrays.
416 const uint32_t* src = &g_ra8_ls_sidata;
417 uint32_t* dst = &g_ra8_ls_sdata;
418 while (dst < &g_ra8_ls_edata) {
419 *dst++ = *src++;
420 }
421
422 /* Step 3: zero BSS. */
423 dst = &g_ra8_ls_sbss;
424 while (dst < &g_ra8_ls_ebss) {
425 *dst++ = 0U;
426 }
427
428 // NOLINTEND(clang-analyzer-security.ArrayBound)
429
430 /* Step 4: enter C. `main()` is responsible for enabling interrupts
431 * via `__enable_irq()` once all drivers are ready. */
432 main();
433
434 /* main() should never return; if it does, halt. */
435 while (1) {
436 __asm__ volatile("wfi");
437 }
438}
439
440/* =============================================================================
441 * HardFault / MemManage / BusFault / UsageFault naked trampolines
442 * =============================================================================
443 *
444 * Each trampoline picks the stack pointer the fault was taken on
445 * (MSP if `EXC_RETURN[2]=0`, PSP otherwise), packs an `exception
446 * number` in `r1`, and tail-calls `ra8_exception_report()`. See
447 * `ra8_exception.h` for the frame layout and HUM reference.
448 */
449
450extern void ra8_exception_report(const void* frame, uint32_t exc_number);
451
462
463#ifndef RA8_OFF_TARGET
464[[gnu::naked, noreturn]] void HardFault_Handler(void)
465{
466 __asm__ volatile("tst lr, #4 \n"
467 "ite eq \n"
468 "mrseq r0, msp \n"
469 "mrsne r0, psp \n"
470 "mov r1, #3 \n"
471 "b ra8_exception_report\n");
472}
473
474[[gnu::naked, noreturn]] void MemManage_Handler(void)
475{
476 __asm__ volatile("tst lr, #4 \n"
477 "ite eq \n"
478 "mrseq r0, msp \n"
479 "mrsne r0, psp \n"
480 "mov r1, #4 \n"
481 "b ra8_exception_report\n");
482}
483
484[[gnu::naked, noreturn]] void BusFault_Handler(void)
485{
486 __asm__ volatile("tst lr, #4 \n"
487 "ite eq \n"
488 "mrseq r0, msp \n"
489 "mrsne r0, psp \n"
490 "mov r1, #5 \n"
491 "b ra8_exception_report\n");
492}
493
494[[gnu::naked, noreturn]] void UsageFault_Handler(void)
495{
496 __asm__ volatile("tst lr, #4 \n"
497 "ite eq \n"
498 "mrseq r0, msp \n"
499 "mrsne r0, psp \n"
500 "mov r1, #6 \n"
501 "b ra8_exception_report\n");
502}
503#else
504/* Host build: fault handlers trap directly to the reporter. The host
505 * compiler does not know `ra8_exception_report` is noreturn through
506 * the extern declaration, so we add `__builtin_unreachable()` after
507 * each call to keep the `noreturn` attribute on the handler valid. */
508[[noreturn]] void HardFault_Handler(void)
509{
511 __builtin_unreachable();
512}
513[[noreturn]] void MemManage_Handler(void)
514{
516 __builtin_unreachable();
517}
518[[noreturn]] void BusFault_Handler(void)
519{
521 __builtin_unreachable();
522}
523[[noreturn]] void UsageFault_Handler(void)
524{
526 __builtin_unreachable();
527}
528#endif
529
530/* =============================================================================
531 * Default trap
532 * =============================================================================
533 */
534
536{
537 /* Stop here so an attached debugger can inspect stack / faults. */
538#ifndef RA8_OFF_TARGET
539 __asm__ volatile("bkpt #0");
540 while (1) {
541 __asm__ volatile("wfi");
542 }
543#endif
544}
void UsageFault_Handler(void)
Capture a UsageFault and transfer to the common exception reporter.
void HardFault_Handler(void)
Capture a HardFault and transfer to the common exception reporter.
void MemManage_Handler(void)
Capture a MemManage fault and transfer to the exception reporter.
void SVC_Handler(void)
void Default_Handler(void)
Catch-all handler for every exception slot nothing else claims.
void PendSV_Handler(void)
void NMI_Handler(void)
Cortex-M85 NMI handler (vector table slot 2).
void BusFault_Handler(void)
Capture a BusFault and transfer to the common exception reporter.
#define E(n)
void SysTick_Handler(void)
Service the SysTick exception selected by the linked application.
Definition ra8_time.c:244
void SecureFault_Handler(void)
Cortex-M85 SecureFault handler (vector table slot 7).
void DebugMon_Handler(void)
void Reset_Handler(void)
Enter the Secure e-reader image after processor reset.
#define RA8_IRQ_STUB(n)
RA8 IRQ STUB.
void ra8_isr_dispatch(uint16_t slot)
Invoke the handler stored in dispatch-table slot slot.
Definition ra8_isr.c:362
void SecureFault_Handler(void)
Cortex-M85 SecureFault handler (vector table slot 7).
uint32_t g_ra8_ls_stack_top
void SystemInit(void)
Earliest C code the image runs: bring the core up before RAM init.
const uint32_t g_ra8_vector_table_start[]
void UsageFault_Handler(void)
Capture a UsageFault and transfer to the common exception reporter.
void HardFault_Handler(void)
Capture a HardFault and transfer to the common exception reporter.
void MemManage_Handler(void)
Capture a MemManage fault and transfer to the exception reporter.
void SVC_Handler(void)
uint32_t g_ra8_ls_sidata
Source of .data in MRAM.
ra8_vector_exc_t
Exception numbers matching the Cortex-M vector table slots.
@ k_ra8_vector_hardfault
RA8 vector hardfault.
@ k_ra8_vector_usagefault
RA8 vector usagefault.
@ k_ra8_vector_busfault
RA8 vector busfault.
@ k_ra8_vector_memmanage
RA8 vector memmanage.
uint32_t g_ra8_ls_edata
End of .data in SRAM.
void PendSV_Handler(void)
uint32_t g_ra8_ls_sdata
Start of .data in SRAM.
uint32_t g_ra8_ls_sbss
Start of .bss in SRAM.
void BusFault_Handler(void)
Capture a BusFault and transfer to the common exception reporter.
void SysTick_Handler(void)
Service the SysTick exception selected by the linked application.
Definition ra8_time.c:244
void(* exc_handler_t)(void)
@ k_ra8_irq_count
RA8 IRQ count.
void DebugMon_Handler(void)
void Reset_Handler(void)
Enter an EK-RA8D2 firmware image after processor reset.
uint32_t g_ra8_ls_ebss
End of .bss in SRAM.
void NMI_Handler(void)
Cortex-M85 NMI handler (vector table slot 2).
Boot entry points shared between a vector table and its startup code.
void main(void)
The application entry point Reset_Handler hands control to.
Definition main.c:298
void ra8_exception_report(const ra8_exception_frame_t *frame, uint32_t exc_number)
Emit a full fault dump over the log backend.