ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_dual_core.c
Go to the documentation of this file.
1
24
25#include "ra8_dual_core.h"
26
27#include <stdint.h>
28
29#include "ra8_attributes.h"
30#include "ra8_check.h"
31#include "ra8_err.h"
32#include "ra8_hw_err.h"
33#include "ra8_log.h"
34
36static const char* const s_tag = "ra8_dual_core";
37
39typedef enum : uint16_t {
41} dc_mask_t;
42
47
48/* ----------------------------------------------------------------------------
49 * Host shim
50 * --------------------------------------------------------------------------*/
51#ifdef RA8_OFF_TARGET
52
57typedef struct {
58 uint32_t initvtor;
59 uint16_t actcsr;
60 uint8_t waitcr;
61} ra8_dual_core_fake_state_t;
62
69static ra8_dual_core_fake_state_t s_fake = {
70 .initvtor = 0U,
71 .actcsr = 0U,
72 .waitcr = 0U,
73};
74
94RA8_INTERNAL static inline uint16_t internal_actcsr_read(void)
95{
96 return s_fake.actcsr;
97}
98
117RA8_INTERNAL static inline void internal_actcsr_write(uint16_t value)
118{
119 const uint16_t key =
120 (uint16_t)((value >> (uint16_t)k_ra8_dual_core_actcsr_key_shift) & k_dc_byte_mask);
121 if (key != (uint16_t)k_ra8_dual_core_actcsr_key_value) {
122 /* Only caller ra8_cpu1_release always presents KEY=0xA5; wrong-key drop is host-unreachable. */
123 /* Silent drop -- matches HUM Ch 2.9.1.9 KEY gate. */
124 return; /* GCOVR_EXCL_LINE -- build-fixed role */
125 }
126 uint16_t actcsr = s_fake.actcsr;
127 if ((value & (uint16_t)k_ra8_dual_core_actcsr_actreq_mask) != 0U) {
128 actcsr |= (uint16_t)k_ra8_dual_core_actcsr_act_mask;
129 }
130 s_fake.actcsr = actcsr;
131}
132
151RA8_INTERNAL static inline void internal_waitcr_write(uint8_t value)
152{
153 s_fake.waitcr = (uint8_t)(value & (uint8_t)k_ra8_dual_core_waitcr_cpuwait_mask);
154}
155
176RA8_INTERNAL static inline uint8_t internal_waitcr_read(void)
177{
178 return s_fake.waitcr;
179}
180
199RA8_INTERNAL static inline void internal_initvtor_write(uint32_t value)
200{
201 s_fake.initvtor = value;
202}
203
204#ifdef UNIT_TEST
205volatile const void* ra8_dual_core_test_actcsr_key(void)
206{
207 return (volatile const void*)&s_fake.actcsr;
208}
209#endif /* UNIT_TEST */
210
211#else /* !RA8_OFF_TARGET */
212
223RA8_INTERNAL static inline volatile uint16_t* internal_actcsr_ptr(void)
224{
225 return (volatile uint16_t*)((uintptr_t)k_ra8_dual_core_ctrl_base_addr +
227}
228
239RA8_INTERNAL static inline volatile uint8_t* internal_waitcr_ptr(void)
240{
241 return (volatile uint8_t*)((uintptr_t)k_ra8_dual_core_ctrl_base_addr +
243}
244
255RA8_INTERNAL static inline volatile uint32_t* internal_initvtor_ptr(void)
256{
257 return (volatile uint32_t*)((uintptr_t)k_ra8_dual_core_ctrl_base_addr +
259}
260
281RA8_INTERNAL static inline uint16_t internal_actcsr_read(void)
282{
283 return *internal_actcsr_ptr();
284}
285
302RA8_INTERNAL static inline void internal_actcsr_write(uint16_t value)
303{
304 *internal_actcsr_ptr() = value;
305}
306
325RA8_INTERNAL static inline void internal_waitcr_write(uint8_t value)
326{
327 *internal_waitcr_ptr() = value;
328}
329
349RA8_INTERNAL static inline uint8_t internal_waitcr_read(void)
350{
351 return *internal_waitcr_ptr();
352}
353
371RA8_INTERNAL static inline void internal_initvtor_write(uint32_t value)
372{
373 *internal_initvtor_ptr() = value;
374}
375
376#endif /* RA8_OFF_TARGET */
377
399RA8_INTERNAL static inline bool internal_is_cpu0(void)
400{
401#ifdef RA8_BUILD_FOR_CPU1
402 return false;
403#else
404 return true;
405#endif
406}
407
432{
433#if defined(RA8_OFF_TARGET) && defined(UNIT_TEST)
434 volatile const void* act_key = ra8_dual_core_test_actcsr_key();
435#endif
436 for (uint32_t i = 0U; i < (uint32_t)k_ra8_dual_core_release_poll_max; ++i) {
437 const bool act_set = (internal_actcsr_read() & (uint16_t)k_ra8_dual_core_actcsr_act_mask) != 0U;
438#if defined(RA8_OFF_TARGET) && defined(UNIT_TEST)
439 if (ra8_fake_mmio_wait_eval(act_key, i, act_set)) {
440 return k_ra8_ok;
441 }
442#else
443 if (act_set) {
444 return k_ra8_ok;
445 }
446#endif
447 }
448 ra8_log_error(s_tag, "release: ACTCSR.ACT did not assert");
449 return k_ra8_err_timeout;
450}
451
452/* ----------------------------------------------------------------------------
453 * Public API
454 * --------------------------------------------------------------------------*/
455
494ra8_err_t ra8_cpu1_release(void* entry, void* sp)
495{
496 if (entry == nullptr || sp == nullptr) {
497 ra8_log_error(s_tag, "release: NULL entry or sp");
498 return k_ra8_err_null_ptr;
499 }
500 if (((uintptr_t)entry & ((uintptr_t)k_ra8_dual_core_align_vtor - 1U)) != 0U) {
501 ra8_log_error(s_tag, "release: entry not 128-byte aligned");
503 }
504 if (((uintptr_t)sp & ((uintptr_t)k_ra8_dual_core_align_sp - 1U)) != 0U) {
505 ra8_log_error(s_tag, "release: sp not 8-byte aligned");
507 }
508 if (!internal_is_cpu0()) {
509 /* RA8_BUILD_FOR_CPU1 is never defined in host builds; this branch is compile-time dead. */
510 ra8_log_error(s_tag, "release: caller is not CPU0"); /* GCOVR_EXCL_LINE -- build-fixed role */
511 return k_ra8_err_not_supported; /* GCOVR_EXCL_LINE -- build-fixed role */
512 }
513
514 /* HUM Ch 2.9.1.7 "CPU1INITVTOR" p 128-129: program CPU1's initial
515 * vector-table base. CPU1 will latch this into VTOR on the next
516 * reset-release edge driven by ACTCSR.ACTREQ below. The M33 also
517 * fetches its initial MSP from the first word of this table. */
518 internal_initvtor_write((uint32_t)(uintptr_t)entry);
519
520 /* HUM Ch 2.9.1.8 "CPU1WAITCR" p 129: make sure CPUWAIT is cleared so
521 * CPU1 begins executing immediately on activation. Reset value is
522 * already 0 but writing it defensively keeps repeated halt/release
523 * cycles deterministic. */
525
526 /* HUM Ch 2.9.1.9 "CPU1ACTCSR" p 129-130: writes require KEY=0xA5 in
527 * bits 15:8 alongside the ACTREQ bit. Anything else is silently
528 * dropped by the hardware. */
529 const uint16_t actreq = (uint16_t)(((uint16_t)k_ra8_dual_core_actcsr_key_value
532 internal_actcsr_write(actreq);
533
534 /* Bounded poll for ACT to assert (HUM Ch 2.9.1.9 ACT bit p 130). */
535 return internal_wait_act_set();
536}
537
562{
563 if (!internal_is_cpu0()) {
564 /* RA8_BUILD_FOR_CPU1 is never defined in host builds; this branch is compile-time dead. */
565 ra8_log_error(s_tag, "halt: caller is not CPU0"); /* GCOVR_EXCL_LINE -- build-fixed CPU role */
566 return k_ra8_err_not_supported; /* GCOVR_EXCL_LINE -- build-fixed CPU role */
567 }
568
570 return k_ra8_ok;
571}
572
597{
598 /* Sequential ifs so the function has zero compound boolean
599 * decisions -- keeps MC/DC test obligations linear per condition
600 * rather than N+1 combinatorial, and matches the safety-checklist
601 * preference for split conditions in libs/ra8_hal/. */
602 const bool active = (internal_actcsr_read() & (uint16_t)k_ra8_dual_core_actcsr_act_mask) != 0U;
603 if (!active) {
604 return false;
605 }
606 const bool stalled =
608 return !stalled;
609}
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
bool ra8_cpu1_is_running(void)
Return whether CPU1 is currently fetching instructions.
static uint16_t internal_actcsr_read(void)
Read the live CPU1ACTCSR register on the chip.
static volatile uint32_t * internal_initvtor_ptr(void)
Address of CPU1INITVTOR on the live chip.
static void internal_waitcr_write(uint8_t value)
Write the live CPU1WAITCR register on the chip.
static void internal_initvtor_write(uint32_t value)
Write the live CPU1INITVTOR register on the chip.
static bool internal_is_cpu0(void)
Compile-time check that the caller is CPU0.
static void internal_actcsr_write(uint16_t value)
Write the live CPU1ACTCSR register on the chip.
static volatile uint8_t * internal_waitcr_ptr(void)
Address of CPU1WAITCR on the live chip.
ra8_err_t ra8_cpu1_release(void *entry, void *sp)
Release CPU1 (Cortex-M33) from reset and start it executing.
dc_mask_t
Low-byte mask for ACTCSR field extraction.
@ k_dc_byte_mask
Dc byte mask.
ra8_err_t ra8_cpu1_halt(void)
Park CPU1 by asserting CPU1WAITCR.CPUWAIT.
ra8_dual_core_align_t
@ k_ra8_dual_core_align_vtor
Armv8-M VTOR alignment requirement.
@ k_ra8_dual_core_align_sp
AAPCS main-stack alignment.
static ra8_err_t internal_wait_act_set(void)
Bounded poll for CPU1ACTCSR.ACT to assert after an ACTREQ.
static uint8_t internal_waitcr_read(void)
Read the live CPU1WAITCR register on the chip.
static volatile uint16_t * internal_actcsr_ptr(void)
Address of CPU1ACTCSR on the live chip.
Dual-core (CPU0 / CPU1) lifecycle helper – public API.
@ k_ra8_dual_core_release_poll_max
Bounded poll iterations.
@ k_ra8_dual_core_actcsr_act_mask
CPU1ACTCSR.ACT.
@ k_ra8_dual_core_actcsr_actreq_mask
CPU1ACTCSR.ACTREQ.
@ k_ra8_dual_core_actcsr_key_shift
Key byte position.
@ k_ra8_dual_core_waitcr_cpuwait_mask
CPU1WAITCR.CPUWAIT.
@ k_ra8_dual_core_actcsr_key_value
Required key code.
@ k_ra8_dual_core_off_cpu1_actcsr
CPU1ACTCSR (16-bit).
@ k_ra8_dual_core_off_cpu1_waitcr
CPU1WAITCR (8-bit).
@ k_ra8_dual_core_off_cpu1_initvtor
CPU1INITVTOR (32-bit).
@ k_ra8_dual_core_ctrl_base_addr
CPU_CTRL Secure base.
Error Code Definitions for ra8-firmware.
@ k_ra8_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
Definition ra8_err.h:180
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_timeout
Operation exceeded its time budget.
Definition ra8_err.h:188
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Bounded wait-flag primitives for RA8D2 HAL drivers.
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335