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
32
33#include <stdint.h>
34
35#include "ra8_boot_entry.h"
36#include "ra8_exception.h"
37#include "ra8_isr.h"
38#include "ra8_lpm_safe_boot.h"
39
40/* =============================================================================
41 * Linker-provided symbols
42 * =============================================================================
43 *
44 * Project convention: linker symbols carry the `g_ra8_ls_` prefix so they
45 * are not in the reserved leading-underscore namespace that ISO C (and
46 * cert-dcl37-c / bugprone-reserved-identifier) reject. The linker script
47 * (the app-local linker_script.ld or the shared
48 * libs/ra8_board_ek_ra8d2/ld/linker_script.ld) defines them with this
49 * exact spelling.
50 */
51
52extern uint32_t g_ra8_ls_stack_top;
53extern uint32_t g_ra8_ls_sdata;
54extern uint32_t g_ra8_ls_edata;
55extern uint32_t g_ra8_ls_sidata;
56extern uint32_t g_ra8_ls_sbss;
57extern uint32_t g_ra8_ls_ebss;
58
59/* =============================================================================
60 * Handler declarations
61 * =============================================================================
62 */
63
64typedef void (*exc_handler_t)(void);
65
66void Reset_Handler(void);
67
68void HardFault_Handler(void);
69
70void MemManage_Handler(void);
71
72void BusFault_Handler(void);
73
74void UsageFault_Handler(void);
75
76/* Core exceptions (Cortex-M85). HardFault / MemManage / BusFault /
77 * UsageFault each get a dedicated naked trampoline below which
78 * forwards to ra8_exception_report(). The rest are weak-aliased to
79 * Default_Handler and may be overridden by application code.
80 *
81 * Mach-O (macOS host) does not support `__attribute__((alias(...)))`.
82 * On the host syntax-check / unit-test build we drop the alias and
83 * keep only `weak`; the host build never branches through these
84 * symbols (the firmware vector table is not exercised on the test
85 * host), so we just need them to exist for the `&` references in
86 * `g_ra8_vector_table_start` to be well-formed. */
87#ifndef __APPLE__
88[[gnu::weak, gnu::alias("Default_Handler")]] void NMI_Handler(void);
89[[gnu::weak, gnu::alias("Default_Handler")]] void SecureFault_Handler(void);
90[[gnu::weak, gnu::alias("Default_Handler")]] void SVC_Handler(void);
91[[gnu::weak, gnu::alias("Default_Handler")]] void DebugMon_Handler(void);
92[[gnu::weak, gnu::alias("Default_Handler")]] void PendSV_Handler(void);
93#else
94[[gnu::weak]] void NMI_Handler(void);
95[[gnu::weak]] void SecureFault_Handler(void);
96[[gnu::weak]] void SVC_Handler(void);
97[[gnu::weak]] void DebugMon_Handler(void);
98[[gnu::weak]] void PendSV_Handler(void);
99#endif
100
113extern void SysTick_Handler(void);
114
115/* =============================================================================
116 * Peripheral IRQ handlers
117 * =============================================================================
118 *
119 * The RA Interrupt Control Unit exposes 112 routable interrupt lines
120 * to the NVIC (per HUM table 13.x).
121 *
122 * Each peripheral IRQ vector forwards to ra8_isr_dispatch(slot) so any
123 * (handler, ctx) registered through the substrate (ra8_isr_register)
124 * runs in NVIC handler-mode. Slots that were never registered land in
125 * the dispatcher's bounds-checked no-op path. The trampolines are
126 * weak so a driver can still install a fully-custom IRQ handler by
127 * defining a non-weak ``IRQ<n>_Handler``. HUM Ch 13 NVIC + Ch 14 ICU
128 * IELSR.
129 *
130 * NB: a prior revision aliased every IRQn_Handler directly to
131 * Default_Handler (which contains ``bkpt #0``). On debug-disabled
132 * silicon a stray bkpt escalates to HardFault, so the first SCI/SPI/USB
133 * IRQ a driver armed via ra8_isr_register would halt the chip. The
134 * dispatching form below avoids that and matches the working apps.
135 */
136
137enum : uint16_t {
139};
140
141/* On Apple host syntax-check we drop ra8_isr_dispatch (the host build
142 * never branches through these vectors) and keep only a weak empty
143 * body so the `&IRQn_Handler` references in the table are well-formed. */
144#ifndef __APPLE__
146#define RA8_IRQ_STUB(n) \
147 void IRQ##n##_Handler(void); \
148 [[gnu::weak]] void IRQ##n##_Handler(void) \
149 { \
150 ra8_isr_dispatch((uint16_t)(n)); \
151 } \
152 static_assert(1, "ra8_irq_stub_" #n)
153#else
155#define RA8_IRQ_STUB(n) \
156 void IRQ##n##_Handler(void); \
157 [[gnu::weak]] void IRQ##n##_Handler(void) {} \
158 static_assert(1, "ra8_irq_stub_" #n)
159#endif
160
273
274#undef RA8_IRQ_STUB
275
276/* =============================================================================
277 * Vector table
278 * =============================================================================
279 *
280 * Placed in its own section so the linker script can pin it to the
281 * start of MRAM. The Cortex-M85 reads the initial MSP from offset 0
282 * and the reset vector from offset 4.
283 */
284
285/* Mach-O `section()` requires a `"segment,section"` form, but on the
286 * host syntax-check / unit-test build we only need this array to
287 * exist as a normal global -- the host never reads it from a fixed
288 * vector address. So drop the section pinning on Apple hosts. */
289#ifndef __APPLE__
290[[gnu::section(".vectors"), gnu::used]]
291#else
292[[gnu::used]]
293#endif
295 /* Core exceptions (slots 0..15). */
296 (exc_handler_t)&g_ra8_ls_stack_top, /* 0 Initial main stack pointer. */
297 Reset_Handler, /* 1 Reset vector. */
298 NMI_Handler, /* 2 NMI. */
299 HardFault_Handler, /* 3 HardFault. */
300 MemManage_Handler, /* 4 MemManage. */
301 BusFault_Handler, /* 5 BusFault. */
302 UsageFault_Handler, /* 6 UsageFault. */
303 SecureFault_Handler, /* 7 SecureFault. */
304 0, /* 8 Reserved. */
305 0, /* 9 Reserved. */
306 0, /* 10 Reserved. */
307 SVC_Handler, /* 11 SVC. */
308 DebugMon_Handler, /* 12 DebugMon. */
309 0, /* 13 Reserved. */
310 PendSV_Handler, /* 14 PendSV. */
311 SysTick_Handler, /* 15 SysTick. */
312
313/* Peripheral IRQs 0..111. */
315#define E(n) IRQ##n##_Handler
316 E(0),
317 E(1),
318 E(2),
319 E(3),
320 E(4),
321 E(5),
322 E(6),
323 E(7),
324 E(8),
325 E(9),
326 E(10),
327 E(11),
328 E(12),
329 E(13),
330 E(14),
331 E(15),
332 E(16),
333 E(17),
334 E(18),
335 E(19),
336 E(20),
337 E(21),
338 E(22),
339 E(23),
340 E(24),
341 E(25),
342 E(26),
343 E(27),
344 E(28),
345 E(29),
346 E(30),
347 E(31),
348 E(32),
349 E(33),
350 E(34),
351 E(35),
352 E(36),
353 E(37),
354 E(38),
355 E(39),
356 E(40),
357 E(41),
358 E(42),
359 E(43),
360 E(44),
361 E(45),
362 E(46),
363 E(47),
364 E(48),
365 E(49),
366 E(50),
367 E(51),
368 E(52),
369 E(53),
370 E(54),
371 E(55),
372 E(56),
373 E(57),
374 E(58),
375 E(59),
376 E(60),
377 E(61),
378 E(62),
379 E(63),
380 E(64),
381 E(65),
382 E(66),
383 E(67),
384 E(68),
385 E(69),
386 E(70),
387 E(71),
388 E(72),
389 E(73),
390 E(74),
391 E(75),
392 E(76),
393 E(77),
394 E(78),
395 E(79),
396 E(80),
397 E(81),
398 E(82),
399 E(83),
400 E(84),
401 E(85),
402 E(86),
403 E(87),
404 E(88),
405 E(89),
406 E(90),
407 E(91),
408 E(92),
409 E(93),
410 E(94),
411 E(95),
412 E(96),
413 E(97),
414 E(98),
415 E(99),
416 E(100),
417 E(101),
418 E(102),
419 E(103),
420 E(104),
421 E(105),
422 E(106),
423 E(107),
424 E(108),
425 E(109),
426 E(110),
427 E(111),
428#undef E
429};
430
431/* =============================================================================
432 * Reset handler: copy .data from MRAM to SRAM, zero .bss, call main()
433 * =============================================================================
434 */
435
449{
450 /* Must be first: clear LPSCR / SBYCR / DPSBYCR before any code
451 * can execute WFI. Survives a previous app leaving LPSCR set to
452 * software-standby across SYSRESETREQ (the SYSC LPM block is in
453 * a separate reset domain). See libs/ra8_hal/inc/ra8_lpm_safe_boot.h. */
455
456 /* Step 1: core-level init (VTOR, FPU, caches, priority grouping,
457 * interrupts masked). Runs before we touch .data or .bss so it
458 * must not read or write any global variables. */
459 SystemInit();
460
461 /* Step 2: copy initialized data from flash/MRAM to SRAM. */
462 const uint32_t* src = &g_ra8_ls_sidata;
463 uint32_t* dst = &g_ra8_ls_sdata;
464 while (dst < &g_ra8_ls_edata) {
465 *dst++ = *src++;
466 }
467
468 /* Step 3: zero BSS. */
469 dst = &g_ra8_ls_sbss;
470 while (dst < &g_ra8_ls_ebss) {
471 *dst++ = 0U;
472 }
473
474 /* Step 4: enter C. `main()` is responsible for enabling interrupts
475 * via `__enable_irq()` once all drivers are ready. */
476 main();
477 /* main() should never return; if it does, halt. */
478 while (1) {
479 __asm__ volatile("wfi");
480 }
481}
482
483/* =============================================================================
484 * HardFault / MemManage / BusFault / UsageFault naked trampolines
485 * =============================================================================
486 *
487 * Each trampoline picks the stack pointer the fault was taken on
488 * (MSP if `EXC_RETURN[2]=0`, PSP otherwise), packs an `exception
489 * number` in `r1`, and tail-calls `ra8_exception_report()`. See
490 * `ra8_exception.h` for the frame layout and HUM reference.
491 */
492
503
504#ifndef RA8_OFF_TARGET
517[[gnu::naked, noreturn]] void HardFault_Handler(void)
518{
519 __asm__ volatile("tst lr, #4 \n"
520 "ite eq \n"
521 "mrseq r0, msp \n"
522 "mrsne r0, psp \n"
523 "mov r1, #3 \n"
524 "b ra8_exception_report\n");
525}
526
539[[gnu::naked, noreturn]] void MemManage_Handler(void)
540{
541 __asm__ volatile("tst lr, #4 \n"
542 "ite eq \n"
543 "mrseq r0, msp \n"
544 "mrsne r0, psp \n"
545 "mov r1, #4 \n"
546 "b ra8_exception_report\n");
547}
548
561[[gnu::naked, noreturn]] void BusFault_Handler(void)
562{
563 __asm__ volatile("tst lr, #4 \n"
564 "ite eq \n"
565 "mrseq r0, msp \n"
566 "mrsne r0, psp \n"
567 "mov r1, #5 \n"
568 "b ra8_exception_report\n");
569}
570
583[[gnu::naked, noreturn]] void UsageFault_Handler(void)
584{
585 __asm__ volatile("tst lr, #4 \n"
586 "ite eq \n"
587 "mrseq r0, msp \n"
588 "mrsne r0, psp \n"
589 "mov r1, #6 \n"
590 "b ra8_exception_report\n");
591}
592#else
593/* Host build: fault handlers trap directly to the reporter. The host
594 * compiler does not know `ra8_exception_report` is noreturn through
595 * the extern declaration, so we add `__builtin_unreachable()` after
596 * each call to keep the `noreturn` attribute on the handler valid. */
597[[noreturn]] void HardFault_Handler(void)
598{
600 __builtin_unreachable();
601}
602[[noreturn]] void MemManage_Handler(void)
603{
605 __builtin_unreachable();
606}
607[[noreturn]] void BusFault_Handler(void)
608{
610 __builtin_unreachable();
611}
612[[noreturn]] void UsageFault_Handler(void)
613{
615 __builtin_unreachable();
616}
617#endif
618
619/* =============================================================================
620 * Default trap
621 * =============================================================================
622 */
623
625{
626 /* Stop here so an attached debugger can inspect stack / faults. */
627#ifndef RA8_OFF_TARGET
628 __asm__ volatile("bkpt #0");
629 while (1) {
630 __asm__ volatile("wfi");
631 }
632#endif
633}
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 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
Cortex-M85 CPU exception diagnostic helpers.
void ra8_exception_report(const ra8_exception_frame_t *frame, uint32_t exc_number)
Emit a full fault dump over the log backend.
NVIC + ICU IELSR allocator.
Self-healing LPM register reset for very early boot.
static void ra8_lpm_safe_boot(void)
Restore LPSCR / SBYCR / DPSBYCR to cold-reset values.