ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_dual_core.c File Reference

CPU1 (Cortex-M33) lifecycle helper – implementation. More...

#include "ra8_dual_core.h"
#include <stdint.h>
#include "ra8_attributes.h"
#include "ra8_check.h"
#include "ra8_err.h"
#include "ra8_hw_err.h"
#include "ra8_log.h"
Include dependency graph for ra8_dual_core.c:

Go to the source code of this file.

Enumerations

enum  dc_mask_t : uint16_t { k_dc_byte_mask = 0xFFU }
 Low-byte mask for ACTCSR field extraction. More...
enum  ra8_dual_core_align_t : uint32_t {
  k_ra8_dual_core_align_vtor = 128U ,
  k_ra8_dual_core_align_sp = 8U
}

Functions

static volatile uint16_t * internal_actcsr_ptr (void)
 Address of CPU1ACTCSR on the live chip.
static volatile uint8_t * internal_waitcr_ptr (void)
 Address of CPU1WAITCR on the live chip.
static volatile uint32_t * internal_initvtor_ptr (void)
 Address of CPU1INITVTOR on the live chip.
static uint16_t internal_actcsr_read (void)
 Read the live CPU1ACTCSR register on the chip.
static void internal_actcsr_write (uint16_t value)
 Write the live CPU1ACTCSR register on the chip.
static void internal_waitcr_write (uint8_t value)
 Write the live CPU1WAITCR register on the chip.
static uint8_t internal_waitcr_read (void)
 Read 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 ra8_err_t internal_wait_act_set (void)
 Bounded poll for CPU1ACTCSR.ACT to assert after an ACTREQ.
ra8_err_t ra8_cpu1_release (void *entry, void *sp)
 Release CPU1 (Cortex-M33) from reset and start it executing.
ra8_err_t ra8_cpu1_halt (void)
 Park CPU1 by asserting CPU1WAITCR.CPUWAIT.
bool ra8_cpu1_is_running (void)
 Return whether CPU1 is currently fetching instructions.

Variables

static const char *const s_tag = "ra8_dual_core"
 Log tag for ra8_dual_core diagnostics.

Detailed Description

CPU1 (Cortex-M33) lifecycle helper – implementation.

Tag
[Ring 3 / HAL] {World: NS}

See ra8_dual_core.h for the public contract. This file implements the three lifecycle entry points (release / halt / is_running) on top of the CPU_CTRL register window's CPU1INITVTOR / CPU1WAITCR / CPU1ACTCSR registers (HUM Ch 2.9.1 p 128-130). Earlier drafts used SYSC.LPCSR / VTORC1 / MSPC1 register addresses that belong to a different RA family and do not exist on RA8D2; those silently dropped writes, which is what made cpu1_pingpong's CPU1 stay in reset forever.

Host (RA8_OFF_TARGET) builds back the registers with a small static state struct so unit tests can drive the API without a chip.

Since
0.1.0

Definition in file ra8_dual_core.c.

Enumeration Type Documentation

◆ dc_mask_t

enum dc_mask_t : uint16_t

Low-byte mask for ACTCSR field extraction.

Enumerator
k_dc_byte_mask 

Dc byte mask.

Definition at line 39 of file ra8_dual_core.c.

◆ ra8_dual_core_align_t

enum ra8_dual_core_align_t : uint32_t
Enumerator
k_ra8_dual_core_align_vtor 

Armv8-M VTOR alignment requirement.

k_ra8_dual_core_align_sp 

AAPCS main-stack alignment.

Definition at line 43 of file ra8_dual_core.c.

Function Documentation

◆ internal_actcsr_ptr()

volatile uint16_t * internal_actcsr_ptr ( void )
inlinestatic

Address of CPU1ACTCSR on the live chip.

Returns
Volatile 16-bit pointer to CPU1ACTCSR.
Precondition
CPU control window powered.
Caller is on CPU0.
Postcondition
No state modified.
Pointer is non-NULL and aligned for 16-bit access.
Note
Pure address arithmetic.
Since
0.1.0

Definition at line 223 of file ra8_dual_core.c.

References k_ra8_dual_core_ctrl_base_addr, k_ra8_dual_core_off_cpu1_actcsr, and RA8_INTERNAL.

Referenced by internal_actcsr_read(), and internal_actcsr_write().

◆ internal_actcsr_read()

uint16_t internal_actcsr_read ( void )
inlinestatic

Read the live CPU1ACTCSR register on the chip.

Single-volatile load via the typed accessor. Used by the ra8_cpu1_release ACT-poll and by ra8_cpu1_is_running; never mutates the register so reentrant reads are safe.

Returns
uint16_t Current ACTCSR value (ACT in bit 7, ACTREQ in bit 0).
Return values
0..UINT16_MAXWhatever the controller exposes; bit 7 latches once ACTREQ has been honored.
Precondition
CPU control window is mapped and powered.
Caller is running on CPU0.
Postcondition
No registers are modified.
Returned value reflects ACTCSR at the read instant.
Note
Re-entrant (read-only volatile access).
Since
0.1.0

Definition at line 281 of file ra8_dual_core.c.

References internal_actcsr_ptr(), and RA8_INTERNAL.

Referenced by internal_wait_act_set(), and ra8_cpu1_is_running().

◆ internal_actcsr_write()

void internal_actcsr_write ( uint16_t value)
inlinestatic

Write the live CPU1ACTCSR register on the chip.

The hardware silently drops writes that do not present KEY[7:0] == 0xA5 in the upper byte (HUM Ch 2.9.1.9 p 130).

Parameters
[in]valueNew 16-bit ACTCSR value (KEY | ACTREQ).
Precondition
CPU control window is mapped and powered.
Caller is running on CPU0.
Postcondition
ACTCSR has been written with value; key gating applies.
No other CPU_CTRL registers are modified.
Note
Not thread-safe; serialise CPU1 lifecycle ops at a higher layer.
Since
0.1.0

Definition at line 302 of file ra8_dual_core.c.

References internal_actcsr_ptr(), and RA8_INTERNAL.

Referenced by ra8_cpu1_release().

◆ internal_initvtor_ptr()

volatile uint32_t * internal_initvtor_ptr ( void )
inlinestatic

Address of CPU1INITVTOR on the live chip.

Returns
Volatile 32-bit pointer to CPU1INITVTOR.
Precondition
CPU control window powered.
Caller is on CPU0.
Postcondition
No state modified.
Pointer is non-NULL and aligned for 32-bit access.
Note
Pure address arithmetic.
Since
0.1.0

Definition at line 255 of file ra8_dual_core.c.

References k_ra8_dual_core_ctrl_base_addr, k_ra8_dual_core_off_cpu1_initvtor, and RA8_INTERNAL.

Referenced by internal_initvtor_write().

◆ internal_initvtor_write()

void internal_initvtor_write ( uint32_t value)
inlinestatic

Write the live CPU1INITVTOR register on the chip.

Programs the CPU1 reset vector base. The Cortex-M33 latches INITVTOR into its VTOR on the next reset-release edge and fetches the initial MSP from offset 0 of the table.

Parameters
[in]valueNew 32-bit INITVTOR value (CPU1 vector table base).
Precondition
CPU control window is mapped and powered.
Caller is running on CPU0.
Postcondition
INITVTOR has been written with value.
No other CPU_CTRL registers are modified.
Note
Not thread-safe; serialise CPU1 lifecycle ops.
Since
0.1.0

Definition at line 371 of file ra8_dual_core.c.

References internal_initvtor_ptr(), and RA8_INTERNAL.

Referenced by ra8_cpu1_release().

◆ internal_is_cpu0()

bool internal_is_cpu0 ( void )
inlinestatic

Compile-time check that the caller is CPU0.

On the M33 build (RA8_BUILD_FOR_CPU1) we refuse to touch the CPU1 lifecycle registers; CPU1 cannot release or stall itself through this API. On the M85 / host build there is no "other core" so we always return true.

Returns
bool True if running on CPU0, false otherwise.
Return values
trueCaller is the M85 primary (or host build).
falseCaller is the M33 secondary (RA8_BUILD_FOR_CPU1).
Precondition
Build flags are set consistently with the actual target core.
None at runtime; result is fully resolved at compile time.
Postcondition
No state is modified.
No side effects.
Note
Re-entrant; pure function.
Since
0.1.0

Definition at line 399 of file ra8_dual_core.c.

References RA8_INTERNAL.

Referenced by ra8_cpu1_halt(), and ra8_cpu1_release().

◆ internal_wait_act_set()

ra8_err_t internal_wait_act_set ( void )
static

Bounded poll for CPU1ACTCSR.ACT to assert after an ACTREQ.

Spins up to k_ra8_dual_core_release_poll_max reads of CPU1ACTCSR waiting for the ACT bit, extracted from ra8_cpu1_release to keep that function within the NASA Power of 10 Rule 4 line budget. On the host build nothing self-sets ACT, so the ra8_fake_mmio fault seam owns the loop-exit decision – first-poll success unless a test arms a fault on the ACTCSR key to drive the retry / timeout legs.

Returns
ra8_err_t Error code.
Return values
k_ra8_okACT observed set within the budget.
k_ra8_err_timeoutACT stayed clear for the full budget.
Precondition
ra8_cpu1_release has just issued the keyed ACTREQ write.
Caller runs on CPU0.
Postcondition
No CPU_CTRL register is modified.
On timeout the failure is logged via ra8_log_error.
Note
Not thread-safe; part of the serialised CPU1 lifecycle path.
Since
0.1.0

Definition at line 431 of file ra8_dual_core.c.

References internal_actcsr_read(), k_ra8_dual_core_actcsr_act_mask, k_ra8_dual_core_release_poll_max, k_ra8_err_timeout, k_ra8_ok, RA8_INTERNAL, ra8_log_error, and s_tag.

Referenced by ra8_cpu1_release().

◆ internal_waitcr_ptr()

volatile uint8_t * internal_waitcr_ptr ( void )
inlinestatic

Address of CPU1WAITCR on the live chip.

Returns
Volatile 8-bit pointer to CPU1WAITCR.
Precondition
CPU control window powered.
Caller is on CPU0.
Postcondition
No state modified.
Pointer is non-NULL and aligned for 8-bit access.
Note
Pure address arithmetic.
Since
0.1.0

Definition at line 239 of file ra8_dual_core.c.

References k_ra8_dual_core_ctrl_base_addr, k_ra8_dual_core_off_cpu1_waitcr, and RA8_INTERNAL.

Referenced by internal_waitcr_read(), and internal_waitcr_write().

◆ internal_waitcr_read()

uint8_t internal_waitcr_read ( void )
inlinestatic

Read the live CPU1WAITCR register on the chip.

Single-volatile load. Drives the stalled-vs-running query in ra8_cpu1_is_running.

Returns
uint8_t Current WAITCR value.
Return values
0..1Bit 0 reflects the CPUWAIT gate; other bits read as zero per HUM Ch 2.9.1.10 p 130.
Precondition
CPU control window is mapped and powered.
Caller is running on CPU0.
Postcondition
No registers are modified.
Returned value reflects WAITCR at the read instant.
Note
Re-entrant (read-only volatile access).
Since
0.1.0

Definition at line 349 of file ra8_dual_core.c.

References internal_waitcr_ptr(), and RA8_INTERNAL.

Referenced by ra8_cpu1_is_running().

◆ internal_waitcr_write()

void internal_waitcr_write ( uint8_t value)
inlinestatic

Write the live CPU1WAITCR register on the chip.

Single-volatile store via the typed accessor. CPU1 stays stalled while WAITCR.CPUWAIT == 1; clearing the bit lets the released core start fetching at CPU1INITVTOR.

Parameters
[in]valueNew 8-bit WAITCR value (bit 0 = CPUWAIT).
Precondition
CPU control window is mapped and powered.
Caller is running on CPU0.
Postcondition
WAITCR has been written with value.
No other CPU_CTRL registers are modified.
Note
Not thread-safe; serialise CPU1 lifecycle ops.
Since
0.1.0

Definition at line 325 of file ra8_dual_core.c.

References internal_waitcr_ptr(), and RA8_INTERNAL.

Referenced by ra8_cpu1_halt(), and ra8_cpu1_release().

◆ ra8_cpu1_halt()

ra8_err_t ra8_cpu1_halt ( void )
nodiscard

Park CPU1 by asserting CPU1WAITCR.CPUWAIT.

See ra8_dual_core.h for the public contract. The RA8D2 HUM does not document a clean "go back to inactive" transition for the already-activated secondary core, so this driver stalls CPU1 by writing CPUWAIT=1. The chip remains powered/clocked but no instructions retire on the M33. ra8_cpu1_is_running() returns false while CPUWAIT is asserted.

Returns
ra8_err_t Error code.
Return values
k_ra8_okCPU1 stalled.
k_ra8_err_not_supportedCaller is not CPU0.
Precondition
Caller is CPU0.
CPU1 was previously released via ra8_cpu1_release().
Postcondition
On success, CPU1WAITCR.CPUWAIT reads 1.
On success, ra8_cpu1_is_running() returns false.
Note
Not thread-safe; caller must serialise CPU1 lifecycle ops.
See also
ra8_cpu1_release()
Since
0.1.0

Definition at line 561 of file ra8_dual_core.c.

References internal_is_cpu0(), internal_waitcr_write(), k_ra8_dual_core_waitcr_cpuwait_mask, k_ra8_err_not_supported, k_ra8_ok, ra8_log_error, and s_tag.

◆ ra8_cpu1_is_running()

bool ra8_cpu1_is_running ( void )

Return whether CPU1 is currently fetching instructions.

Combines two reads: CPU1ACTCSR.ACT tells us the core has been activated (out of power-gating), and CPU1WAITCR.CPUWAIT tells us whether the application has subsequently stalled it. CPU1 is "running" only when ACT=1 and CPUWAIT=0.

Returns
bool True if CPU1 is fetching, false otherwise.
Return values
trueCPU1 active and not stalled.
falseCPU1 inactive, or active but CPUWAIT=1.
Precondition
CPU control window is powered.
Caller is CPU0.
Postcondition
No registers are modified.
Two registers are sampled once each and the boolean result returned.
Note
Re-entrant (read-only volatile access).
See also
ra8_cpu1_release()
ra8_cpu1_halt()
Since
0.1.0

Definition at line 596 of file ra8_dual_core.c.

References internal_actcsr_read(), internal_waitcr_read(), k_ra8_dual_core_actcsr_act_mask, and k_ra8_dual_core_waitcr_cpuwait_mask.

◆ ra8_cpu1_release()

ra8_err_t ra8_cpu1_release ( void * entry,
void * sp )
nodiscard

Release CPU1 (Cortex-M33) from reset and start it executing.

See ra8_dual_core.h for the full public contract. This implementation validates entry and sp (non-NULL, alignment), confirms the caller is CPU0, programs CPU1INITVTOR, clears CPU1WAITCR.CPUWAIT so CPU1 runs immediately on activation, then issues a key-protected ACTREQ via CPU1ACTCSR and bounded-polls ACTCSR.ACT for the active transition. The Cortex-M33 reads its initial SP from the first word of the vector table at entry (Armv8-M reset behaviour), so the sp argument is validated for alignment only – ensuring the caller has actually populated entry[0] with the intended stack top is the application's responsibility.

Parameters
[in]entryCPU1 reset vector base, 128-byte aligned.
[in]spCPU1 initial main stack pointer, 8-byte aligned.
Returns
ra8_err_t Error code.
Return values
k_ra8_okCPU1 released and observed active.
k_ra8_err_null_ptrentry or sp was NULL.
k_ra8_err_invalid_argAlignment check failed.
k_ra8_err_not_supportedCaller is not CPU0.
k_ra8_err_timeoutACTCSR.ACT did not assert within bound.
Precondition
Caller is CPU0 (the M85 primary).
CPU control window is powered (it is at boot on this MCU).
Postcondition
On success, CPU1 is fetching from entry with MSP loaded from entry[0].
On error, no further CPU_CTRL registers are touched after the initial INITVTOR/WAITCR pair (which are harmless until ACTREQ fires).
Note
Not thread-safe; caller must serialise CPU1 lifecycle ops.
See also
ra8_cpu1_halt()
ra8_cpu1_is_running()
Since
0.1.0

Definition at line 494 of file ra8_dual_core.c.

References internal_actcsr_write(), internal_initvtor_write(), internal_is_cpu0(), internal_wait_act_set(), internal_waitcr_write(), k_ra8_dual_core_actcsr_actreq_mask, k_ra8_dual_core_actcsr_key_shift, k_ra8_dual_core_actcsr_key_value, k_ra8_dual_core_align_sp, k_ra8_dual_core_align_vtor, k_ra8_err_invalid_arg, k_ra8_err_not_supported, k_ra8_err_null_ptr, ra8_log_error, and s_tag.

Referenced by main().

Variable Documentation

◆ s_tag

const char* const s_tag = "ra8_dual_core"
static

Log tag for ra8_dual_core diagnostics.

Definition at line 36 of file ra8_dual_core.c.