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#include "ra8_exception.h"
31#include "ra8_isr.h"
32
33/* =============================================================================
34 * Linker-provided symbols
35 * =============================================================================
36 *
37 * Project convention: linker symbols carry the `g_ra8_ls_` prefix so they
38 * are not in the reserved leading-underscore namespace that ISO C (and
39 * cert-dcl37-c / bugprone-reserved-identifier) reject. The linker script
40 * in usb_cdc_echo/linker_script.ld defines them with this exact spelling.
41 */
42
43extern uint32_t g_ra8_ls_stack_top;
44extern uint32_t g_ra8_ls_sdata;
45extern uint32_t g_ra8_ls_edata;
46extern uint32_t g_ra8_ls_sidata;
47extern uint32_t g_ra8_ls_sbss;
48extern uint32_t g_ra8_ls_ebss;
49
50/* =============================================================================
51 * Handler declarations
52 * =============================================================================
53 */
54
55typedef void (*exc_handler_t)(void);
56
57void Reset_Handler(void);
58
59void HardFault_Handler(void);
60
61void MemManage_Handler(void);
62
63void BusFault_Handler(void);
64
65void UsageFault_Handler(void);
66
67/* Core exceptions (Cortex-M85). HardFault / MemManage / BusFault /
68 * UsageFault each get a dedicated naked trampoline below which
69 * forwards to ra8_exception_report(). The rest are weak-aliased to
70 * Default_Handler and may be overridden by application code.
71 *
72 * Mach-O (macOS host) does not support `__attribute__((alias(...)))`.
73 * On the host syntax-check / unit-test build we drop the alias and
74 * keep only `weak`; the host build never branches through these
75 * symbols (the firmware vector table is not exercised on the test
76 * host), so we just need them to exist for the `&` references in
77 * `g_ra8_vector_table_start` to be well-formed. */
78#ifndef __APPLE__
79[[gnu::weak, gnu::alias("Default_Handler")]] void NMI_Handler(void);
80[[gnu::weak, gnu::alias("Default_Handler")]] void SecureFault_Handler(void);
81[[gnu::weak, gnu::alias("Default_Handler")]] void SVC_Handler(void);
82[[gnu::weak, gnu::alias("Default_Handler")]] void DebugMon_Handler(void);
83[[gnu::weak, gnu::alias("Default_Handler")]] void PendSV_Handler(void);
84#else
85[[gnu::weak]] void NMI_Handler(void);
86[[gnu::weak]] void SecureFault_Handler(void);
87[[gnu::weak]] void SVC_Handler(void);
88[[gnu::weak]] void DebugMon_Handler(void);
89[[gnu::weak]] void PendSV_Handler(void);
90#endif
91
104extern void SysTick_Handler(void);
105
106/* =============================================================================
107 * Peripheral IRQ handlers
108 * =============================================================================
109 *
110 * The RA Interrupt Control Unit exposes 112 routable interrupt lines
111 * to the NVIC (per HUM table 13.x).
112 *
113 * Each peripheral IRQ vector forwards to ra8_isr_dispatch(slot) so any
114 * (handler, ctx) registered through the substrate (ra8_isr_register)
115 * runs in NVIC handler-mode. Slots that were never registered land in
116 * the dispatcher's bounds-checked no-op path. The trampolines are
117 * weak so a driver can still install a fully-custom IRQ handler by
118 * defining a non-weak ``IRQ<n>_Handler``. HUM Ch 13 NVIC + Ch 14 ICU
119 * IELSR.
120 *
121 * NB: a prior revision aliased every IRQn_Handler directly to
122 * Default_Handler (which contains ``bkpt #0``). On debug-disabled
123 * silicon a stray bkpt escalates to HardFault, so the first USB IRQ
124 * after ra8_isr_register armed the NVIC line halted the chip -- this NSC
125 * demo routes USBFS_INT to NVIC slot 0 (ra8_usb_device_attach), so it hit
126 * exactly that. The working tz_secure_only_usb_fs / threadx_usbx_cdc_demo
127 * apps use this dispatching form; this app now matches.
128 */
129
130enum : uint16_t {
132};
133
134/* On Apple host syntax-check we drop ra8_isr_dispatch (host build
135 * never branches through these) and keep only a weak empty body. */
136#ifndef __APPLE__
138#define RA8_IRQ_STUB(n) \
139 void IRQ##n##_Handler(void); \
140 [[gnu::weak]] void IRQ##n##_Handler(void) \
141 { \
142 ra8_isr_dispatch((uint16_t)(n)); \
143 } \
144 static_assert(1, "ra8_irq_stub_" #n)
145#else
147#define RA8_IRQ_STUB(n) \
148 void IRQ##n##_Handler(void); \
149 [[gnu::weak]] void IRQ##n##_Handler(void) {} \
150 static_assert(1, "ra8_irq_stub_" #n)
151#endif
152
265
266#undef RA8_IRQ_STUB
267
268/* =============================================================================
269 * Vector table
270 * =============================================================================
271 *
272 * Placed in its own section so the linker script can pin it to the
273 * start of MRAM. The Cortex-M85 reads the initial MSP from offset 0
274 * and the reset vector from offset 4.
275 */
276
277/* Mach-O `section()` requires a `"segment,section"` form, but on the
278 * host syntax-check / unit-test build we only need this array to
279 * exist as a normal global -- the host never reads it from a fixed
280 * vector address. So drop the section pinning on Apple hosts. */
281#ifndef __APPLE__
282[[gnu::section(".vectors"), gnu::used]]
283#else
284[[gnu::used]]
285#endif
287 /* Core exceptions (slots 0..15). */
288 (exc_handler_t)&g_ra8_ls_stack_top, /* 0 Initial main stack pointer. */
289 Reset_Handler, /* 1 Reset vector. */
290 NMI_Handler, /* 2 NMI. */
291 HardFault_Handler, /* 3 HardFault. */
292 MemManage_Handler, /* 4 MemManage. */
293 BusFault_Handler, /* 5 BusFault. */
294 UsageFault_Handler, /* 6 UsageFault. */
295 SecureFault_Handler, /* 7 SecureFault. */
296 0, /* 8 Reserved. */
297 0, /* 9 Reserved. */
298 0, /* 10 Reserved. */
299 SVC_Handler, /* 11 SVC. */
300 DebugMon_Handler, /* 12 DebugMon. */
301 0, /* 13 Reserved. */
302 PendSV_Handler, /* 14 PendSV. */
303 SysTick_Handler, /* 15 SysTick. */
304
305/* Peripheral IRQs 0..111. */
307#define E(n) IRQ##n##_Handler
308 E(0),
309 E(1),
310 E(2),
311 E(3),
312 E(4),
313 E(5),
314 E(6),
315 E(7),
316 E(8),
317 E(9),
318 E(10),
319 E(11),
320 E(12),
321 E(13),
322 E(14),
323 E(15),
324 E(16),
325 E(17),
326 E(18),
327 E(19),
328 E(20),
329 E(21),
330 E(22),
331 E(23),
332 E(24),
333 E(25),
334 E(26),
335 E(27),
336 E(28),
337 E(29),
338 E(30),
339 E(31),
340 E(32),
341 E(33),
342 E(34),
343 E(35),
344 E(36),
345 E(37),
346 E(38),
347 E(39),
348 E(40),
349 E(41),
350 E(42),
351 E(43),
352 E(44),
353 E(45),
354 E(46),
355 E(47),
356 E(48),
357 E(49),
358 E(50),
359 E(51),
360 E(52),
361 E(53),
362 E(54),
363 E(55),
364 E(56),
365 E(57),
366 E(58),
367 E(59),
368 E(60),
369 E(61),
370 E(62),
371 E(63),
372 E(64),
373 E(65),
374 E(66),
375 E(67),
376 E(68),
377 E(69),
378 E(70),
379 E(71),
380 E(72),
381 E(73),
382 E(74),
383 E(75),
384 E(76),
385 E(77),
386 E(78),
387 E(79),
388 E(80),
389 E(81),
390 E(82),
391 E(83),
392 E(84),
393 E(85),
394 E(86),
395 E(87),
396 E(88),
397 E(89),
398 E(90),
399 E(91),
400 E(92),
401 E(93),
402 E(94),
403 E(95),
404 E(96),
405 E(97),
406 E(98),
407 E(99),
408 E(100),
409 E(101),
410 E(102),
411 E(103),
412 E(104),
413 E(105),
414 E(106),
415 E(107),
416 E(108),
417 E(109),
418 E(110),
419 E(111),
420#undef E
421};
422
423/* =============================================================================
424 * Reset handler: copy .data from MRAM to SRAM, zero .bss, call main()
425 * =============================================================================
426 */
427
441{
442 /* Step 1: initialise the C runtime FIRST. SystemInit() below performs the
443 * full Secure bring-up (clock tree + SAU + `ra8_trustzone_init`), and
444 * ra8_trustzone_init() BLXNS-es into the Non-Secure world and never returns on
445 * hardware. So the .data copy MUST run before SystemInit() -- if it ran after
446 * (the classic CMSIS order) it would be dead code here, and every Secure
447 * driver would execute against an uninitialised .data image (e.g. log-tag
448 * pointers read as NULL, which the pin validator rejects as a null owner). */
449
450 /* Copy initialized data from flash/MRAM to SRAM. */
451 // NOLINTBEGIN(clang-analyzer-security.ArrayBound) -- Linker symbols define ranges, not one-element arrays.
452 uint32_t* src = &g_ra8_ls_sidata;
453 uint32_t* dst = &g_ra8_ls_sdata;
454 while (dst < &g_ra8_ls_edata) {
455 *dst++ = *src++;
456 }
457
458 /* Zero BSS. */
459 dst = &g_ra8_ls_sbss;
460 while (dst < &g_ra8_ls_ebss) {
461 *dst++ = 0U;
462 }
463
464 // NOLINTEND(clang-analyzer-security.ArrayBound)
465
466 /* Step 2: core-level init (VTOR, FPU, priority grouping, interrupts masked)
467 * followed by the Secure bring-up and the BLXNS into the NS image. Safe to run
468 * after the .data copy: the core steps touch no .data globals, and the Secure
469 * drivers now see a fully initialised .data image. Does not return on hardware. */
470 SystemInit();
471
472 /* Step 3: SystemInit() does not return once it BLXNS-es into NS. Reaching here
473 * means the NS transition was bypassed (e.g. a host / off-target build): fall through to
474 * the Secure fallback main(), which parks the CPU. */
475 main();
476
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.