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
51
52#include <stdint.h>
53
54#include "ereader_m33.h"
55#include "ra8_attributes.h"
56#include "ra8_boot_entry.h"
57#include "ra8_dual_core.h"
58#include "ra8_err.h"
59#include "ra8_ipc.h"
60#include "ra8_isr.h"
61#include "ra8_log.h"
62#include "ra8_lpm.h"
63
65extern uint32_t g_ra8_ls_cpu1_mram_start;
67extern uint32_t g_ra8_ls_cpu1_stack_top;
68
79typedef enum : uint32_t {
80 k_m85_sig_poll_budget = 10000000UL,
81 k_m85_done_poll_budget = 200000000UL,
83
89typedef enum : uint32_t {
92
98typedef enum : uint8_t {
102} m85_hex_t;
103
109typedef enum : uint8_t {
112} m85_dec_t;
113
122typedef enum : uint32_t {
124} m85_work_t;
125
144static void banner_append(char* dst, uint32_t* off, uint32_t cap, const char* src)
145{
146 if (dst == nullptr) {
147 return;
148 }
149 if (off == nullptr) {
150 return;
151 }
152 if (src == nullptr) {
153 return;
154 }
156 for (uint32_t i = 0U; i < cap; i++) {
157 const char c = src[i];
158 if (c == '\0') {
159 break;
160 }
161 if (*off >= (cap - 1U)) {
162 break;
163 }
164 dst[*off] = c;
165 *off += 1U;
166 }
167 dst[*off] = '\0';
168}
169
188static void banner_append_hex(char* dst, uint32_t* off, uint32_t cap, uint32_t value)
189{
190 if (dst == nullptr) {
191 return;
192 }
193 if (off == nullptr) {
194 return;
195 }
196 static const char digits[] = "0123456789ABCDEF";
198 for (uint32_t i = 0U; i < (uint32_t)k_hex_nibbles; i++) {
199 if (*off >= (cap - 1U)) {
200 break;
201 }
202 const uint32_t shift = ((uint32_t)k_hex_nibbles - 1U - i) * (uint32_t)k_nibble_bits;
203 const uint32_t nibble = (value >> shift) & (uint32_t)k_nibble_mask;
204 dst[*off] = digits[nibble];
205 *off += 1U;
206 }
207}
208
231static void banner_append_u32(char* dst, uint32_t* off, uint32_t cap, uint32_t value)
232{
233 if (dst == nullptr) {
234 return;
235 }
236 if (off == nullptr) {
237 return;
238 }
239 char tmp[k_dec_digits_max];
240 uint32_t count = 0U;
241 uint32_t v = value;
243 for (uint32_t i = 0U; i < (uint32_t)k_dec_digits_max; i++) {
244 tmp[count] = (char)('0' + (char)(v % (uint32_t)k_dec_radix));
245 count += 1U;
246 v /= (uint32_t)k_dec_radix;
247 if (v == 0U) {
248 break;
249 }
250 }
252 for (uint32_t i = 0U; i < count; i++) {
253 if (*off >= (cap - 1U)) {
254 break;
255 }
256 dst[*off] = tmp[count - 1U - i];
257 *off += 1U;
258 }
259}
260
276static void prep_mailbox(volatile erm33_mailbox_t* mb)
277{
278 mb->magic = 0U;
279 mb->m33_sig = 0U;
280 mb->status = (uint32_t)k_erm33_status_running;
281 mb->fb_base = 0U;
282 mb->fb_width = 0U;
283 mb->fb_height = 0U;
284 mb->fb_stride = 0U;
285 mb->fb_format = 0U;
286 mb->fb_crc = 0U;
287 mb->glyph_count = 0U;
288 mb->done = 0U;
289 mb->turn_req = 0U;
290 mb->turn_ack = 0U;
291 mb->turn_done = 0U;
292 __asm volatile("dsb" ::: "memory");
293 mb->magic = (uint32_t)k_erm33_magic;
294 __asm volatile("dsb" ::: "memory");
295}
296
314static bool wait_for_m33_sig(const volatile erm33_mailbox_t* mb)
315{
317 for (uint32_t i = 0U; i < (uint32_t)k_m85_sig_poll_budget; i++) {
318 if (mb->m33_sig == (uint32_t)k_erm33_m33_sig) {
319 return true;
320 }
321 }
322 return false;
323}
324
342static bool wait_for_done(const volatile erm33_mailbox_t* mb)
343{
345 for (uint32_t i = 0U; i < (uint32_t)k_m85_done_poll_budget; i++) {
346 if (mb->done == 1U) {
347 return true;
348 }
349 }
350 return false;
351}
352
375static bool verify_page(const volatile erm33_mailbox_t* mb)
376{
377 if (mb->status != (uint32_t)k_erm33_status_ok) {
378 return false;
379 }
380 if ((mb->fb_base < (uint32_t)k_erm33_sdram_base) ||
381 (mb->fb_base >= (uint32_t)k_erm33_sdram_end)) {
382 return false;
383 }
384 if ((mb->fb_width != (uint32_t)k_erm33_fb_width) ||
385 (mb->fb_height != (uint32_t)k_erm33_fb_height)) {
386 return false;
387 }
388 if (mb->fb_stride != (uint32_t)k_erm33_fb_stride) {
389 return false;
390 }
391 if (mb->fb_format != (uint32_t)k_erm33_fb_format_rgb565) {
392 return false;
393 }
394 if ((mb->glyph_count == 0U) || (mb->fb_crc == 0U)) {
395 return false;
396 }
397 return true;
398}
399
416static void emit_verdict(uint32_t crc, bool pass)
417{
418 char buf[k_banner_cap];
419 uint32_t off = 0U;
420 banner_append(buf, &off, (uint32_t)k_banner_cap, "ereader_m33: rgb565 256x64 sdram crc=");
421 banner_append_hex(buf, &off, (uint32_t)k_banner_cap, crc);
422 banner_append(buf, &off, (uint32_t)k_banner_cap, pass ? " PASS" : " FAIL");
423 ra8_log_info("M85", buf);
424}
425
435typedef enum : uint8_t {
437} m85_ipc_t;
438
450static volatile bool s_m33_woke;
451
469static void ipc_wake_handler(void* ctx, uint8_t channel, ra8_ipc_irq_event_id_t event_id)
470{
471 (void)ctx;
472 (void)channel;
473 (void)event_id;
474 s_m33_woke = true;
475}
476
492static void ipc0_receive_isr(void* ctx)
493{
494 (void)ctx;
496}
497
519static bool arm_ipc_wake(void)
520{
521 if (ra8_isr_init() != k_ra8_ok) {
522 return false;
523 }
524 const ra8_ipc_config_t cfg = {
525 .channel = (uint8_t)k_ipc_wake_channel,
526 .reset_fifo = true,
527 .clear_status = true,
528 .event_mask = (uint32_t)k_ra8_ipc_event_irq0,
529 };
530 if (ra8_ipc_init(&cfg) != k_ra8_ok) {
531 return false;
532 }
536 nullptr) != k_ra8_ok) {
537 return false;
538 }
541 nullptr,
542 (uint8_t)k_ra8_isr_prio_default,
543 nullptr) != k_ra8_ok) {
544 return false;
545 }
547 return true;
548}
549
570static void m85_lpm_configure(void)
571{
572 if (ra8_lpm_prcr_unlock() != k_ra8_ok) {
573 return;
574 }
575 const ra8_lpm_config_t cfg = {
576 .opa_bus_keep = true,
577 };
578 (void)ra8_lpm_init(&cfg);
579 (void)ra8_lpm_prcr_relock();
580}
581
604static void m85_gate_hoco(bool stop)
605{
606 if (ra8_lpm_prcr_unlock() != k_ra8_ok) {
607 return;
608 }
610 (void)ra8_lpm_prcr_relock();
611}
612
637static bool m85_wait_turn(const volatile erm33_mailbox_t* mb, uint32_t turn)
638{
640 for (uint32_t i = 0U; i < (uint32_t)k_m85_done_poll_budget; i++) {
641 if (mb->turn_req >= turn) {
642 return true;
643 }
644 __asm volatile("wfi" ::: "memory");
645 }
646 return false;
647}
648
672static uint32_t m85_heavy_work(uint32_t turn)
673{
674 uint32_t acc = turn;
676 for (uint32_t i = 0U; i < (uint32_t)k_heavy_work_iters; i++) {
677 acc += i;
678 }
679 return acc;
680}
681
705static bool m85_wait_turn_done(const volatile erm33_mailbox_t* mb, uint32_t turn)
706{
708 for (uint32_t i = 0U; i < (uint32_t)k_m85_done_poll_budget; i++) {
709 if (mb->turn_done >= turn) {
710 return true;
711 }
712 }
713 return false;
714}
715
740static bool run_handoff_cycle(volatile erm33_mailbox_t* mb)
741{
742 if (mb == nullptr) {
743 return false;
744 }
746 for (uint32_t turn = 1U; turn <= (uint32_t)k_erm33_max_turns; turn++) {
747 m85_gate_hoco(true);
748 const bool woke = m85_wait_turn(mb, turn);
749 m85_gate_hoco(false);
750 if (!woke) {
751 ra8_log_info_val("M85", "page-turn wake timed out at turn", turn);
752 return false;
753 }
754 [[maybe_unused]] const uint32_t work = m85_heavy_work(turn);
755 mb->turn_ack = turn;
756 __asm volatile("dsb" ::: "memory");
757 ra8_log_info_val("M85", "woke from low-power for page turn", turn);
758 ra8_log_info_val("M85", "M85 heavy next-page work result", work);
759 }
760 return true;
761}
762
784static void emit_cycle_verdict(const volatile erm33_mailbox_t* mb, bool pass)
785{
786 if (mb == nullptr) {
787 return;
788 }
789 char buf[k_banner_cap];
790 uint32_t off = 0U;
791 banner_append(buf, &off, (uint32_t)k_banner_cap, "ereader_m33: handoff turns=");
792 banner_append_u32(buf, &off, (uint32_t)k_banner_cap, mb->turn_done);
793 banner_append(buf, &off, (uint32_t)k_banner_cap, " crc=");
794 banner_append_hex(buf, &off, (uint32_t)k_banner_cap, mb->fb_crc);
795 banner_append(buf, &off, (uint32_t)k_banner_cap, pass ? " PARKED" : " FAIL");
796 ra8_log_info("M85", buf);
797}
798
820static void run_mode_switch(volatile erm33_mailbox_t* mb)
821{
822 if (mb == nullptr) {
823 return;
824 }
825 ra8_log_info("M85", "entering #150 mode-switch cycle; M33 holds the page + polls touch");
826 const bool cycled = run_handoff_cycle(mb);
827 bool done_ok = false;
828 if (cycled) {
829 done_ok = m85_wait_turn_done(mb, (uint32_t)k_erm33_max_turns);
830 }
831 bool verdict = false;
832 if (done_ok) {
833 if (mb->fb_crc != 0U) {
834 verdict = true;
835 }
836 }
837 emit_cycle_verdict(mb, verdict);
838}
839
855[[noreturn]] static void park_low_power(void)
856{
857 ra8_log_info("M85", "M85 parked in low-power WFI; M33 holds the page");
858 m85_gate_hoco(true);
859 while (1) {
860 __asm volatile("wfi");
861 }
862}
863
877[[noreturn]] static void park_forever(void)
878{
879 while (1) {
880 __asm volatile("nop");
881 }
882}
883
901void main(void)
902{
903 ra8_log_init();
904 ra8_log_info("M85", "==== RA8D2 ereader_m33 demo (#150 M85-park / M33-hold) ====");
905 ra8_log_info("M85", "Cortex-M85 primary core online");
906 ra8_log_info("M85", "M33 renders a held page into external SDRAM (0x68000000)");
907
908 volatile erm33_mailbox_t* mb = erm33_mailbox();
909 prep_mailbox(mb);
910
911 /* Arm the IPC0 receive IRQ + configure the LPM block BEFORE releasing the M33,
912 * so the page-turn wake path and the Sleep-mode posture are ready the moment
913 * the M33 starts running. A wake-arm failure is non-fatal: m85_wait_turn still
914 * bounds-polls the mailbox (its WFI then just sleeps to the next interrupt). */
915 if (arm_ipc_wake()) {
916 ra8_log_info("M85", "IPC0 receive IRQ armed -- M85 can WFI-wake on a page turn");
917 } else {
918 ra8_log_info("M85", "IPC wake arm failed -- page-turn WFI falls back to bounded poll");
919 }
921
922 ra8_log_info("M85", "releasing Cortex-M33 secondary core ...");
924 ra8_log_info_val("M85", "ra8_cpu1_release rc (0 = ok)", (uint32_t)err);
925 if (err != k_ra8_ok) {
926 ra8_log_info("M85", "release FAILED -- halting");
927 park_forever();
928 }
929
930 if (wait_for_m33_sig(mb)) {
931 ra8_log_info("M85", "M33 reader is alive");
932 } else {
933 ra8_log_info("M85", "M33 signature not seen -- did it boot?");
934 }
935
936 ra8_log_info("M85", "M85 idle; waiting for the M33 to render the held page ...");
937 if (!wait_for_done(mb)) {
938 ra8_log_info("M85", "M33 done flag not seen -- timed out");
939 park_forever();
940 }
941 __asm volatile("dsb" ::: "memory");
942
943 ra8_log_info_val("M85", "M33 published fb_base", mb->fb_base);
944 ra8_log_info_val("M85", "M33 held-page glyphs", mb->glyph_count);
945 ra8_log_info_val("M85", "M33 held-page pixels CRC32 (decimal)", mb->fb_crc);
947
948 /* #150 mode-switch: hand the live core to the slow M33, park, and wake on each
949 * scripted page turn to do the heavy next-page work, then park for good. */
950 run_mode_switch(mb);
952}
void main(void)
Secure fallback main entry point.
Definition main.c:37
Shared contract for the "render a held e-reader page on the M33" demo.
@ k_erm33_status_running
status: M33 is rendering the page.
@ k_erm33_status_ok
status: page rendered + published.
@ k_erm33_fb_format_rgb565
Published fb_format (RGB565).
@ k_erm33_magic
"ERM3" – M85 stamps it when ready.
@ k_erm33_m33_sig
"RDR3" boot sentinel written by M33.
@ k_erm33_sdram_base
External SDRAM window base.
@ k_erm33_sdram_end
External SDRAM window end (+64 MiB).
@ k_erm33_fb_stride
Bytes per pixel row (width * bpp).
@ k_erm33_fb_height
Plane height in pixels (4 glyph rows of 16).
@ k_erm33_fb_width
Plane width in pixels.
@ k_erm33_max_turns
Page-turn handoffs the cycle exercises.
static volatile erm33_mailbox_t * erm33_mailbox(void)
Typed pointer to the fixed-address shared progress mailbox.
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 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
static void m85_lpm_configure(void)
Configure the LPM block once so a plain WFI is a CPU Sleep.
Definition main.c:570
static void emit_cycle_verdict(const volatile erm33_mailbox_t *mb, bool pass)
Assemble and log the single deterministic handoff-cycle verdict banner.
Definition main.c:784
static void banner_append_hex(char *dst, uint32_t *off, uint32_t cap, uint32_t value)
Append value as 8 uppercase hex digits into the banner buffer.
Definition main.c:188
static bool m85_wait_turn_done(const volatile erm33_mailbox_t *mb, uint32_t turn)
Bounded-poll the mailbox until the M33 republishes through turn.
Definition main.c:705
static bool m85_wait_turn(const volatile erm33_mailbox_t *mb, uint32_t turn)
Park in Sleep-mode WFI until the M33 requests page turn turn.
Definition main.c:637
static void emit_verdict(uint32_t crc, bool pass)
Assemble and log the single deterministic verdict banner.
Definition main.c:416
static void banner_append_u32(char *dst, uint32_t *off, uint32_t cap, uint32_t value)
Append value as decimal digits (no leading zeros) into the banner.
Definition main.c:231
static void run_mode_switch(volatile erm33_mailbox_t *mb)
Run the #150 mode-switch and log its verdict.
Definition main.c:820
m85_hex_t
Constants for formatting a 32-bit value as 8 uppercase hex digits.
Definition main.c:98
@ k_nibble_bits
Bits per hex nibble.
Definition main.c:100
@ k_hex_nibbles
Hex digits in a 32-bit value.
Definition main.c:99
static void park_low_power(void)
Park the M85 in low-power WFI after the M33 owns the page.
Definition main.c:855
static uint32_t m85_heavy_work(uint32_t turn)
Deterministic stand-in for the M85's heavy next-page work.
Definition main.c:672
static bool run_handoff_cycle(volatile erm33_mailbox_t *mb)
Run the #150 mode-switch cycle: park, wake on a page turn, repeat.
Definition main.c:740
static bool verify_page(const volatile erm33_mailbox_t *mb)
Validate the M33's published held-page descriptor.
Definition main.c:375
m85_dec_t
Constants for formatting a 32-bit value as decimal digits.
Definition main.c:109
@ k_dec_digits_max
Decimal digits in a 32-bit value (4294967295).
Definition main.c:110
@ k_dec_radix
Base-10 radix for the digit extraction.
Definition main.c:111
m85_work_t
Bound for the M85's deterministic "heavy next-page work" stand-in.
Definition main.c:122
@ k_heavy_work_iters
Iterations of the placeholder next-page fold.
Definition main.c:123
m85_banner_t
Capacity of the verdict banner the M85 assembles in a stack buffer.
Definition main.c:89
@ k_banner_cap
Bytes reserved for the assembled verdict line.
Definition main.c:90
static void banner_append(char *dst, uint32_t *off, uint32_t cap, const char *src)
Append a NUL-terminated string into a bounded banner buffer.
Definition main.c:144
static void m85_gate_hoco(bool stop)
Gate or restore the HOCO via the LPM clock-stop matrix (CGC OCR write).
Definition main.c:604
uint32_t g_ra8_ls_cpu1_stack_top
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
@ k_nibble_mask
Nibble mask.
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
Low Power Mode (LPM) HAL driver – public API.
ra8_err_t ra8_lpm_init(const ra8_lpm_config_t *cfg)
Initialise the LPM block from a config descriptor.
Definition ra8_lpm.c:358
@ k_ra8_lpm_clock_hoco
High-Speed On-Chip Oscillator.
Definition ra8_lpm.h:154
ra8_err_t ra8_lpm_prcr_relock(void)
Re-lock PRCR.PRC1 after protected writes.
Definition ra8_lpm.c:441
ra8_err_t ra8_lpm_prcr_unlock(void)
Unlock PRCR.PRC1 to permit writes to LPM control registers.
Definition ra8_lpm.c:430
ra8_err_t ra8_lpm_set_clock_stop(ra8_lpm_clock_t clock, bool stop)
Set or clear the per-oscillator stop bit before sleep entry.
Definition ra8_lpm.c:661
Cross-core progress block backed by a fixed shared-SRAM address.
volatile uint32_t magic
M85 stamps k_erm33_magic when ready.
volatile uint32_t turn_done
M33->M85: re-render published for that turn.
volatile uint32_t fb_base
M33-published SDRAM framebuffer base address.
volatile uint32_t done
Set to 1 by the M33 once the page is published.
volatile uint32_t fb_crc
M33-folded CRC-32 over the rendered pixels.
volatile uint32_t turn_ack
M85->M33: heavy-work ack for the matching turn.
volatile uint32_t fb_format
M33-published ra8_gfx_format_t (RGB565).
volatile uint32_t turn_req
M33->M85: page-turn request, bumped per touch.
volatile uint32_t glyph_count
Characters the M33 laid onto the held page.
volatile uint32_t fb_stride
M33-published framebuffer stride in bytes.
volatile uint32_t m33_sig
M33 stamps k_erm33_m33_sig on boot.
volatile uint32_t status
Render status (erm33_const_t status codes).
volatile uint32_t fb_height
M33-published framebuffer height in pixels.
volatile uint32_t fb_width
M33-published framebuffer width in pixels.
Per-channel configuration descriptor passed to ra8_ipc_init.
Configuration descriptor for ra8_lpm_init.
Definition ra8_lpm.h:96