ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
main.c
Go to the documentation of this file.
1
39
40#include <stddef.h>
41#include <stdint.h>
42#include <string.h>
43
44#include "book.h"
45#include "compile_on_m33.h"
46#include "parity_fixture.h"
47#include "ra8_attributes.h"
48#include "ra8_boot_entry.h"
49#include "ra8_dual_core.h"
50#include "ra8_err.h"
51#include "ra8_ipc.h"
52#include "ra8_isr.h"
53#include "ra8_log.h"
54
56extern uint32_t g_ra8_ls_cpu1_mram_start;
58extern uint32_t g_ra8_ls_cpu1_stack_top;
59
69typedef enum : uint32_t {
70 k_m85_sig_poll_budget = 10000000UL,
73
74static_assert((uint32_t)k_m33_parity_epub_len <= (uint32_t)k_com33_epub_cap,
75 "staged parity .epub must fit the shared input buffer");
76
85typedef enum : uint8_t {
87} m85_ipc_t;
88
101static volatile bool s_m33_woke;
102
120static void ipc_wake_handler(void* ctx, uint8_t channel, ra8_ipc_irq_event_id_t event_id)
121{
122 (void)ctx;
123 (void)channel;
124 (void)event_id;
125 s_m33_woke = true;
126}
127
143static void ipc0_receive_isr(void* ctx)
144{
145 (void)ctx;
147}
148
170static bool arm_ipc_wake(void)
171{
172 if (ra8_isr_init() != k_ra8_ok) {
173 return false;
174 }
175 const ra8_ipc_config_t cfg = {
176 .channel = (uint8_t)k_ipc_wake_channel,
177 .reset_fifo = true,
178 .clear_status = true,
179 .event_mask = (uint32_t)k_ra8_ipc_event_irq0,
180 };
181 if (ra8_ipc_init(&cfg) != k_ra8_ok) {
182 return false;
183 }
187 nullptr) != k_ra8_ok) {
188 return false;
189 }
192 nullptr,
193 (uint8_t)k_ra8_isr_prio_default,
194 nullptr) != k_ra8_ok) {
195 return false;
196 }
198 return true;
199}
200
223static void prep_mailbox(volatile com33_mailbox_t* mb)
224{
225 mb->magic = 0U;
226 mb->req_magic = 0U;
227 mb->m33_sig = 0U;
228 mb->status = (uint32_t)k_com33_status_running;
229 mb->blob_base = 0U;
230 mb->blob_len = 0U;
231 mb->blob_crc = 0U;
232 mb->chapter_count = 0U;
233 mb->done = 0U;
234 __asm volatile("dsb" ::: "memory");
235 mb->magic = (uint32_t)k_com33_magic;
236
237 /* Stage the source .epub into shared SRAM and POST the job: the M33 compiles
238 * the staged bytes (not a baked-in array), so the same M33 image serves any
239 * book the M85 dispatches -- this is the seam stage c reuses to offload a real
240 * dropped book. req_magic is stamped LAST, behind a dsb, so the M33 sees the
241 * job only once the staged bytes + request fields are settled in memory. */
243 mb->epub_base = (uint32_t)(uintptr_t)k_com33_epub_addr;
244 mb->epub_len = (uint32_t)k_m33_parity_epub_len;
245 mb->out_base = (uint32_t)(uintptr_t)k_com33_blob_addr;
246 mb->out_cap = (uint32_t)k_com33_blob_cap;
247 __asm volatile("dsb" ::: "memory");
248 mb->req_magic = (uint32_t)k_com33_req_magic;
249 __asm volatile("dsb" ::: "memory");
250}
251
269static bool wait_for_m33_sig(const volatile com33_mailbox_t* mb)
270{
272 for (uint32_t i = 0U; i < (uint32_t)k_m85_sig_poll_budget; i++) {
273 if (mb->m33_sig == (uint32_t)k_com33_m33_sig) {
274 return true;
275 }
276 }
277 return false;
278}
279
304static bool wait_for_done(const volatile com33_mailbox_t* mb)
305{
307 for (uint32_t i = 0U; i < (uint32_t)k_m85_done_poll_budget; i++) {
308 if (mb->done == 1U) {
309 return true;
310 }
311 /* IPC-interrupt idle: sleep until the M33 finishes the compile and pokes
312 * IPC0ISET0, which raises the armed IPC0 receive IRQ and wakes the core.
313 * The M33 orders `done` before that poke (a `dsb`), so the next iteration
314 * observes a settled `done` and returns. The bounded count is the NASA
315 * Rule 2 backstop: if the wake is ever missed the loop still terminates and
316 * the caller reports a timeout instead of hanging. WFI here also means a
317 * real import keeps the M85 off the bus while the slow core grinds. */
318 __asm volatile("wfi" ::: "memory");
319 }
320 return false;
321}
322
346static bool verify_blob(volatile com33_mailbox_t* mb)
347{
348 if (mb == nullptr) {
349 return false;
350 }
351 if (mb->status != (uint32_t)k_com33_status_ok) {
352 return false;
353 }
354 if (mb->blob_base != (uint32_t)k_com33_blob_addr) {
355 return false;
356 }
357 const uint32_t len = mb->blob_len;
358 if (len == 0U) {
359 return false;
360 }
361 if (len > (uint32_t)k_com33_blob_cap) {
362 return false;
363 }
364 const void* base = (const void*)(uintptr_t)mb->blob_base;
365 if (book_validate(base, (size_t)len) != k_ra8_ok) {
366 return false;
367 }
368 const book_header_t* hdr = book_header(base);
369 if (hdr->crc32_val != mb->blob_crc) {
370 return false;
371 }
372 if (hdr->chapter_count != mb->chapter_count) {
373 return false;
374 }
375 /* #149 a1: byte-identity on the secondary core. The M33-produced blob must
376 * equal the desktop/M85 golden (s_m33_parity_golden) byte for byte -- the same
377 * acceptance the M85 parity gate proves, now on the slow core. */
378 if (len != (uint32_t)k_m33_parity_golden_len) {
379 return false;
380 }
381 if (memcmp(base, s_m33_parity_golden, (size_t)len) != 0) {
382 return false;
383 }
384 return true;
385}
386
400[[noreturn]] static void park_forever(void)
401{
402 while (1) {
403 __asm volatile("nop");
404 }
405}
406
423void main(void)
424{
425 ra8_log_init();
426 ra8_log_info("M85", "==== RA8D2 compile_on_m33 demo (#149b emitter offload) ====");
427 ra8_log_info("M85", "Cortex-M85 primary core online");
428 ra8_log_info("M85", "shared mailbox + output blob in SRAM at 0x22100000");
429
430 volatile com33_mailbox_t* mb = com33_mailbox();
431 prep_mailbox(mb);
432
433 /* Arm the IPC0 receive IRQ before releasing the M33 so the M85 can WFI-idle
434 * until the M33's compile-done poke wakes it. A setup failure is non-fatal:
435 * wait_for_done still bounds-polls the mailbox `done` flag (its WFI then just
436 * sleeps until the next interrupt, harmless with the flag re-checked). */
437 if (!arm_ipc_wake()) {
438 ra8_log_info("M85", "IPC wake arm failed -- falling back to bounded poll");
439 } else {
440 ra8_log_info("M85", "IPC0 receive IRQ armed -- M85 will WFI-idle for the M33");
441 }
442
443 ra8_log_info("M85", "releasing Cortex-M33 to run the RABOOK1 emitter ...");
445 ra8_log_info_val("M85", "ra8_cpu1_release rc (0 = ok)", (uint32_t)err);
446 if (err != k_ra8_ok) {
447 ra8_log_info("M85", "release FAILED -- halting");
448 park_forever();
449 }
450
451 if (wait_for_m33_sig(mb)) {
452 ra8_log_info("M85", "M33 emitter is alive");
453 } else {
454 ra8_log_info("M85", "M33 signature not seen -- did it boot?");
455 }
456
457 ra8_log_info("M85", "M85 yielding -- M33 is compiling the book ...");
458 if (!wait_for_done(mb)) {
459 ra8_log_info("M85", "M33 done flag not seen -- timed out");
460 park_forever();
461 }
462
463 /* Consumer-side barrier: order the blob/field loads AFTER observing `done`,
464 * pairing with the M33's pre-`done` `dsb` so the M85 reads a settled blob. */
465 __asm volatile("dmb" ::: "memory");
466 ra8_log_info_val("M85", "M33 reported blob length (bytes)", mb->blob_len);
467
468 if (verify_blob(mb)) {
469 ra8_log_info_val("M85", "book_validate OK; chapters in blob", mb->chapter_count);
470 ra8_log_info("M85", "compile_on_m33 PASS");
471 park_forever();
472 }
473
474 /* The M33-produced blob did NOT equal the desktop/M85 golden byte for byte (or
475 * failed validation). Trap so the byte-identity is GATED: ra8_emulator's smoke
476 * detects the BKPT and fails the run. A silent park here would read as a clean
477 * boot and hide a compile regression (e.g. a future ra8_emulator long-shift seam
478 * miss on a DEFLATE'd fixture). On real silicon with no debugger the BKPT
479 * escalates to a HardFault -- a loud, visible failure for the demo. */
480 ra8_log_info_val("M85", "compile_on_m33 FAIL -- status", mb->status);
481 __asm volatile("bkpt #0");
482 park_forever();
483}
void main(void)
Secure fallback main entry point.
Definition main.c:37
Flat, execute-in-place container for a build-time "compiled" e-book.
static const book_header_t * book_header(const void *base)
View the blob base as its header.
Definition book.h:382
ra8_err_t book_validate(const void *base, size_t size)
Validate that a byte buffer is a well-formed, intact .rabook blob.
Definition book.c:231
Shared-SRAM contract for the "RABOOK1 emitter on the M33" demo (#149b).
static volatile com33_mailbox_t * com33_mailbox(void)
Typed pointer to the fixed-address shared mailbox.
@ k_com33_blob_addr
Output blob base, carved from the shared window.
@ k_com33_epub_addr
Staged input base, carved from the shared window.
@ k_com33_status_running
status: M33 is still building the blob.
@ k_com33_magic
"COM3" – M85 stamps it when ready.
@ k_com33_req_magic
"REQ0" – M85 stamps it once the job (the
@ k_com33_status_ok
status: compile finalized a valid blob.
@ k_com33_m33_sig
"M33 CODE" boot sentinel written by M33.
@ k_com33_epub_cap
Shared staged-input .epub buffer capacity (32 KiB).
@ k_com33_blob_cap
Shared output-blob buffer capacity in bytes.
static uint8_t * com33_epub(void)
Typed pointer to the fixed-address shared staged-input .epub buffer.
uint32_t g_ra8_ls_cpu1_mram_start
static volatile bool s_m33_woke
Set by the IPC0 receive callback when the M33 signals compile-done.
Definition main.c:101
m85_ipc_t
IPC channel the M85 watches for the M33's compile-done wake.
Definition main.c:85
@ k_ipc_wake_channel
IPC0 channel 0 (CPU1 -> CPU0 receive side).
Definition main.c:86
static bool wait_for_m33_sig(const volatile com33_mailbox_t *mb)
Poll the mailbox until the M33 stamps its boot signature.
Definition main.c:269
static bool arm_ipc_wake(void)
Arm the IPC0 receive interrupt so the M85 can WFI-idle for the M33.
Definition main.c:170
static void prep_mailbox(volatile com33_mailbox_t *mb)
Publish the mailbox, stage the source .epub, and post the compile job.
Definition main.c:223
static bool verify_blob(volatile com33_mailbox_t *mb)
Validate the M33-emitted blob and cross-check the mailbox report.
Definition main.c:346
static void ipc_wake_handler(void *ctx, uint8_t channel, ra8_ipc_irq_event_id_t event_id)
IPC0 receive event callback: record that the M33's wake poke arrived.
Definition main.c:120
m85_poll_t
Bounded iteration limits for the M85 polling loops.
Definition main.c:69
@ k_m85_done_poll_budget
Max iters waiting for M33 done flag.
Definition main.c:71
@ k_m85_sig_poll_budget
Max iters waiting for M33 signature.
Definition main.c:70
static void ipc0_receive_isr(void *ctx)
IPC0 receive ISR trampoline: decode the channel's pending events.
Definition main.c:143
static bool wait_for_done(const volatile com33_mailbox_t *mb)
Idle in WFI until the M33's IPC poke signals the done flag.
Definition main.c:304
uint32_t g_ra8_ls_cpu1_stack_top
Baked parity .epub + golden blob for the M33 compile (#149).
static const uint8_t s_m33_parity_epub[]
Fixture .epub bytes compiled on the M33 (text/CSS/SVG).
static const uint8_t s_m33_parity_golden[]
Golden RABOOK1 blob the M33 output must equal byte-for-byte.
@ k_m33_parity_golden_len
Length of s_m33_parity_golden in bytes.
@ k_m33_parity_epub_len
Length of s_m33_parity_epub in bytes.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_LOOP_BOUND(ceiling)
NASA Power-of-10 Rule 2: bind ONE loop to a compile-time ceiling.
Boot entry points shared between a vector table and its startup code.
Dual-core (CPU0 / CPU1) lifecycle helper – public API.
ra8_err_t ra8_cpu1_release(void *entry, void *sp)
Release CPU1 (Cortex-M33) from reset and start it executing.
ra8_elc_event_t
Partial list of ELC events (populate as drivers need them).
Error Code Definitions for ra8-firmware.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
int memcmp(const void *a, const void *b, size_t n)
Compare bytes in two memory areas.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Inter-Processor Communication (IPC) HAL driver – public API.
@ k_ra8_ipc_elc_event_irq0
Receiving-side IRQ for IPC0_*.
void ra8_ipc_dispatch(uint8_t channel)
Drive the event callback for one channel.
Definition ra8_ipc.c:733
ra8_err_t ra8_ipc_attach_event_handler(uint8_t channel, ra8_ipc_irq_event_id_t event_id, ra8_ipc_irq_fn_t fn, void *ctx)
Attach a callback for a single IRQ event line on a channel.
Definition ra8_ipc.c:716
ra8_ipc_irq_event_id_t
Index of an IRQ event line within a channel (0..7).
@ k_ra8_ipc_irq_event_0
RA8 ipc IRQ event 0.
@ k_ra8_ipc_event_irq0
Maskable IRQ event line 0.
ra8_err_t ra8_ipc_init(const ra8_ipc_config_t *cfg)
Initialise one IPC channel.
Definition ra8_ipc.c:228
NVIC + ICU IELSR allocator.
@ k_ra8_isr_prio_default
Middle priority.
Definition ra8_isr.h:107
ra8_err_t ra8_isr_init(void)
Initialise the ra8_isr table.
Definition ra8_isr.c:229
void ra8_isr_globals_enable(void)
Globally enable maskable interrupts (PRIMASK = 0).
Definition ra8_isr.c:439
ra8_err_t ra8_isr_register(ra8_elc_event_t event, ra8_isr_handler_t handler, void *ctx, uint8_t priority, uint16_t *out_slot)
Allocate an IELSR slot for an ELC event + handler.
Definition ra8_isr.c:296
Lightweight Logging Interface for ra8-firmware.
void ra8_log_init(void)
Initialise the logging backend.
Definition ra8_log.c:379
#define ra8_log_info_val(tag, message, value)
RA8 log info val.
Definition ra8_log.h:366
#define ra8_log_info(tag, message)
RA8 log info.
Definition ra8_log.h:364
Fixed 100-byte prologue describing every table and pool in the blob.
Definition book.h:246
uint32_t crc32_val
CRC-32/ISO-HDLC of the body (all bytes after this header).
Definition book.h:270
uint32_t chapter_count
Number of spine chapters.
Definition book.h:256
Cross-core handoff block backed by a fixed shared-SRAM address.
volatile uint32_t blob_base
Address of the finalized blob (= out_base).
volatile uint32_t chapter_count
Chapters the M33 emitted into the blob.
volatile uint32_t blob_len
Finalized RABOOK1 blob length, bytes.
volatile uint32_t m33_sig
M33 stamps k_com33_m33_sig on boot.
volatile uint32_t req_magic
M85 stamps k_com33_req_magic when the job is.
volatile uint32_t blob_crc
Blob header body CRC-32, echoed for cross-check.
volatile uint32_t out_cap
M85: capacity of the output buffer, bytes.
volatile uint32_t epub_len
M85: length of the staged .epub, bytes.
volatile uint32_t magic
M85 stamps k_com33_magic when ready.
volatile uint32_t out_base
M85: address the M33 writes the finalized blob.
volatile uint32_t done
Set to 1 by the M33 once the blob is published.
volatile uint32_t epub_base
M85: address of the staged source .epub bytes.
volatile uint32_t status
Compile outcome (com33_const_t status codes).
Per-channel configuration descriptor passed to ra8_ipc_init.