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

SPI_B peripheral (target) mode driver – polling single-byte xfer. More...

#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 "ra8_mstp.h"
#include "ra8_spi.h"
#include "ra8_spi_regs.h"
Include dependency graph for ra8_spi_b_target.c:

Go to the source code of this file.

Enumerations

enum  ra8_spi_b_target_poll_t : uint32_t { k_spi_b_target_poll_limit = 200000U }
 Polling-loop budget for SPSR flag waits in target mode. More...

Functions

static ra8_err_t internal_target_wait_spsr (volatile r_spi_regs_t *reg, uint32_t flag_mask)
 Wait for a single SPSR flag to assert, with a bounded poll.
static uint32_t internal_target_spcr (void)
 Build the SPCR value for target (peripheral) mode.
static uint32_t internal_spcmd_target (const ra8_spi_cfg_t *cfg)
 Build SPCMD0 value from a caller-supplied configuration.
static void internal_target_program_regs (volatile r_spi_regs_t *reg, const ra8_spi_cfg_t *cfg)
 Write all control registers while SPCR.SPE=0.
ra8_err_t ra8_spi_b_target_init (uint8_t channel, const ra8_spi_cfg_t *cfg)
 Initialise an SPI_B channel in target (peripheral) mode.
ra8_err_t ra8_spi_b_target_xfer (uint8_t channel, uint8_t tx, uint8_t *rx)
 Exchange one byte as the SPI target in a polled blocking call.

Variables

static const char * s_tag = "SPI_B_TGT"
static const ra8_mstp_t s_spi_mstp_table [k_ra8_spi_b_channel_count]
 Channel-index -> MSTP id (HUM Ch 11.2.7 "MSTPCRB", p 444).

Detailed Description

SPI_B peripheral (target) mode driver – polling single-byte xfer.

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

Companion translation unit to ra8_spi_b.c for the RA8D2 SPI_B (Type-B SPI) peripheral. Where ra8_spi_b.c implements the controller role – the MCU drives RSPCK and SSL – this file implements the target (peripheral) role in which an EXTERNAL controller drives RSPCK and asserts SSL (CS), and the MCU responds on CIPO (SDO) while receiving on COPI (SDI).

Register-level design (HUM Ch 43 "Serial Peripheral Interface" p 2877):

  • ra8_spi_b_target_init mirrors ra8_spi_init from ra8_spi_b.c but omits SPBR (irrelevant: clock is external) and writes SPCR with MSTR=0 (target mode) instead of MSTR=1. SCKASE (SCK auto-stop, bit 12) is left clear – it is only meaningful in controller mode (HUM Ch 43.3.14 p 2912).
  • ra8_spi_b_target_xfer performs a polled full-duplex single-byte exchange in the target role:
    1. Waits for SPSR.SPTEF (TX buffer empty – safe to load the response byte).
    2. Writes the caller's TX byte to SPDR (CIPO data).
    3. Clears SPTEF via SPSRC.
    4. Waits for SPSR.SPRF (RX buffer full – controller has clocked a complete frame in).
    5. Reads the COPI byte from SPDR.
    6. Clears SPRF via SPSRC.

The SPSR flag waits run the same bounded polling loop on target and on the host unit-test build; on host the ra8_hw_err MMIO fault seam (ra8_fake_mmio_*) drives that real loop to succeed-after-N or to time out, so both the success and timeout legs execute on host. See internal_target_wait_spsr for the full rationale.

Note
This driver does NOT share the s_spi_state table in ra8_spi_b.c (that table is static to its TU). To tear down a channel initialised with ra8_spi_b_target_init, call ra8_spi_deinit(channel) from ra8_spi.h.

Inclusive terminology (this driver's names -> RA8D2 register effect):

  • Controller role = SPCR.MSTR=1 (the MCU drives RSPCK / SSL)
  • Peripheral / target role = SPCR.MSTR=0 (an external controller drives the bus)
  • CS (Chip Select) = SSL pin, asserted by the external controller
  • COPI (Controller Out Peripheral In) = SDI data received from the controller
  • CIPO (Controller In Peripheral Out) = SDO data this peripheral drives out

Definition in file ra8_spi_b_target.c.

Enumeration Type Documentation

◆ ra8_spi_b_target_poll_t

enum ra8_spi_b_target_poll_t : uint32_t

Polling-loop budget for SPSR flag waits in target mode.

Matches k_ra8_spi_b_poll_limit in ra8_spi_b.c so that both controller and target paths share the same worst-case timeout budget per flag wait. The value is large enough to cover the maximum SPI frame period at the minimum supported bit-rate (about 10 kHz) across the SPI_B clock divider range.

Enumerator
k_spi_b_target_poll_limit 

Max iterations per flag wait.

Definition at line 100 of file ra8_spi_b_target.c.

Function Documentation

◆ internal_spcmd_target()

uint32_t internal_spcmd_target ( const ra8_spi_cfg_t * cfg)
static

Build SPCMD0 value from a caller-supplied configuration.

Encodes CPHA (bit 0), CPOL (bit 1), LSBF (bit 12), and SPB[20:16] from the public ra8_spi_cfg_t. The bit-rate divider and SSL-select fields are left at zero (clock is external; SSL is an input in target mode). See HUM Ch 43.2.7 "SPCMDm : SPI Command Register" p 2893.

A switch statement is used in place of || chains so that compiler MC/DC instrumentation tracks each mode as a distinct branch rather than a compound boolean.

Parameters
[in]cfgCaller-supplied configuration; must be non-NULL (validated by ra8_spi_b_target_init before this call).
Returns
SPCMD0 value ready to write to reg->SPCMD[0].
Return values
SPCMD0CPHA/CPOL bits encode cfg->mode; LSBF set when cfg->lsb_first is true; SPB[4:0] set to k_ra8_spcmd_spb_8bit; all other fields (bit-rate divider, SSL-select) left zero.
Precondition
cfg is non-NULL.
cfg->mode is one of k_ra8_spi_mode_0 through k_ra8_spi_mode_3.
Postcondition
Returned CPHA/CPOL bits match the requested SPI mode.
Returned SPB[4:0] encodes an 8-bit frame (k_ra8_spcmd_spb_8bit).
Note
Pure; no side effects; thread-safe.
Since
0.1.0

Definition at line 215 of file ra8_spi_b_target.c.

References k_ra8_spcmd_bit_spb_lo, k_ra8_spcmd_mask_cpha, k_ra8_spcmd_mask_cpol, k_ra8_spcmd_mask_lsbf, k_ra8_spcmd_mask_spb, k_ra8_spcmd_spb_8bit, k_ra8_spi_mode_0, k_ra8_spi_mode_1, k_ra8_spi_mode_2, k_ra8_spi_mode_3, ra8_spi_cfg_t::lsb_first, ra8_spi_cfg_t::mode, and RA8_INTERNAL.

Referenced by internal_target_program_regs().

◆ internal_target_program_regs()

void internal_target_program_regs ( volatile r_spi_regs_t * reg,
const ra8_spi_cfg_t * cfg )
static

Write all control registers while SPCR.SPE=0.

Called from ra8_spi_b_target_init after SPCR has been cleared to zero (SPE=0). All registers that the HUM restricts to writes while SPE=0 (SPCR2, SPCMD0) are programmed here. SPCR3 is written with zero because SPBR is irrelevant in target mode (the external controller drives RSPCK). The FIFO is reset via SPFCR and SPSR flags are cleared twice (before and after the FIFO reset) to ensure the first call to ra8_spi_b_target_xfer sees a clean status.

Parameters
[in]regChannel register block; validated non-NULL by caller.
[in]cfgCaller-supplied config; validated non-NULL by caller.
Precondition
SPCR.SPE has been cleared (reg->SPCR == 0).
MSTP gate for the channel is already open.
Postcondition
All control registers are programmed for target mode.
SPSR flags are cleared; FIFO contents are flushed.
Note
Not thread-safe; caller must serialise channel access.
Since
0.1.0

Definition at line 264 of file ra8_spi_b_target.c.

References internal_spcmd_target(), k_ra8_spfcr_mask_spfrst, k_ra8_spsrc_mask_all, RA8_INTERNAL, r_spi_regs_t::SPCMD, r_spi_regs_t::SPCR2, r_spi_regs_t::SPCR3, r_spi_regs_t::SPDCR, r_spi_regs_t::SPDCR2, r_spi_regs_t::SPDECR, r_spi_regs_t::SPFCR, and r_spi_regs_t::SPSRC.

Referenced by ra8_spi_b_target_init().

◆ internal_target_spcr()

uint32_t internal_target_spcr ( void )
static

Build the SPCR value for target (peripheral) mode.

Sets SPE (enable) and MODFEN (mode fault detection) while leaving MSTR=0 (peripheral/target role), SCKASE=0 (controller-only feature), and all interrupt-enable bits clear (polling driver). See HUM Ch 43.2.4 "SPCR : SPI Control Register" p 2884 and HUM Ch 43.3.14 "SPI peripheral-mode operation" p 2912 for the register specification.

Returns
SPCR value suitable for writing to the register.
Return values
(k_ra8_spcr_mask_spe| k_ra8_spcr_mask_modfen) The only value ever returned: SPE and MODFEN set; MSTR and all interrupt-enable bits left clear.
Precondition
Called only from ra8_spi_b_target_init after SPE has been cleared.
The channel's MSTP gate is already open.
Postcondition
Bit MSTR (bit 30) is clear in the returned value (target mode).
Bit SPE (bit 0) is set in the returned value (function enable).
Note
Pure; no side effects; thread-safe.
Since
0.1.0

Definition at line 175 of file ra8_spi_b_target.c.

References k_ra8_spcr_mask_modfen, k_ra8_spcr_mask_spe, and RA8_INTERNAL.

Referenced by ra8_spi_b_target_init().

◆ internal_target_wait_spsr()

ra8_err_t internal_target_wait_spsr ( volatile r_spi_regs_t * reg,
uint32_t flag_mask )
static

Wait for a single SPSR flag to assert, with a bounded poll.

Delegates to ra8_hw_wait_flag_set32, a bounded polling loop (NASA P10 Rule 2) that spins up to k_spi_b_target_poll_limit iterations before returning k_ra8_err_hw_timeout. That loop is consulted by the host-test MMIO fault seam (ra8_fake_mmio_*): a test pre-staging SPSR with the awaited flag succeeds on the first poll (seam transparent), fail_wait drives the timeout leg, and satisfy_after(n) steps the loop's continuation branch for MC/DC. Both the success and timeout legs therefore run on host, rather than being compiled out behind an RA8_OFF_TARGET single-shot short-circuit (T1-01).

Parameters
[in]regPointer to the channel's SPI_B register block.
[in]flag_maskSingle SPSR flag mask (e.g. k_ra8_spsr_mask_sprf).
Returns
ra8_err_t
Return values
k_ra8_okFlag asserted within the poll budget.
k_ra8_err_hw_timeoutFlag did not assert within the budget.
Precondition
reg is a valid, mapped SPI_B register block pointer.
flag_mask is one of the single-bit k_ra8_spsr_mask_* constants.
Postcondition
On k_ra8_ok the indicated SPSR flag is still asserted (caller must clear via SPSRC).
On k_ra8_err_hw_timeout no SPSR flag has been cleared.
Note
Not thread-safe; designed for single-caller polling paths.
Since
0.1.0

Definition at line 140 of file ra8_spi_b_target.c.

References k_spi_b_target_poll_limit, ra8_hw_wait_flag_set32(), RA8_INTERNAL, and r_spi_regs_t::SPSR.

Referenced by ra8_spi_b_target_xfer().

◆ ra8_spi_b_target_init()

ra8_err_t ra8_spi_b_target_init ( uint8_t channel,
const ra8_spi_cfg_t * cfg )
nodiscard

Initialise an SPI_B channel in target (peripheral) mode.

Configures the SPI_B peripheral to act as a target device: the external controller drives RSPCK and asserts SSL (CS); the MCU responds on CIPO and receives on COPI. Implementation lives in libs/ra8_hal/src/ra8_spi_b_target.c.

Register-level effect (HUM Ch 43.2.4 "SPCR" p 2884):

  • SPCR is written with MSTR=0 (peripheral/target), MODFEN=1, SPE=1.
  • SCKASE is NOT set – it is valid only in controller mode.
  • SPBR (SPCR3) is set to zero because the clock is external.
  • SPCMD0 is programmed from cfg (CPHA, CPOL, LSBF) for 8-bit frames.

The baud_hz and pclka_hz fields of cfg are accepted but ignored – the bit-rate is determined by the external controller.

Parameters
[in]channelSPI channel (0 or 1).
[in]cfgConfiguration descriptor (non-NULL).
Returns
ra8_err_t error code.
Return values
k_ra8_okChannel configured in target mode; awaiting external controller frames.
k_ra8_err_null_ptrcfg is NULL.
k_ra8_err_invalid_argchannel is out of range.
k_ra8_err_hw_errorra8_mstp_enable failed.
Precondition
IRQs masked or single-threaded init context.
ra8_mstp_init has been called.
Postcondition
On success, SPCR.SPE=1 and SPCR.MSTR=0 (target mode is active).
SPDCR, SPDCR2, SPCR2, and SPCR3 are cleared; SPSR flags are clear.
Note
Thread safety: not thread-safe.
To deinitialise a channel opened in target mode, call ra8_spi_deinit(channel) from ra8_spi.h.
See also
ra8_spi_b_target_xfer Exchange one byte after init.
ra8_spi_init Initialise the same channel in controller mode.
Since
0.1.0

Definition at line 301 of file ra8_spi_b_target.c.

References internal_target_program_regs(), internal_target_spcr(), k_ra8_err_invalid_arg, k_ra8_ok, k_ra8_spi_b_channel_count, RA8_CHECK_NULL_PTR, ra8_log_info_val, ra8_mstp_enable(), RA8_RETURN_ON_ERROR, ra8_spi(), s_spi_mstp_table, s_tag, and r_spi_regs_t::SPCR.

◆ ra8_spi_b_target_xfer()

ra8_err_t ra8_spi_b_target_xfer ( uint8_t channel,
uint8_t tx,
uint8_t * rx )
nodiscard

Exchange one byte as the SPI target in a polled blocking call.

Performs a full-duplex single-byte exchange in target (peripheral) role:

  1. Waits for SPSR.SPTEF (TX buffer empty) – confirms it is safe to pre-load tx without overwriting a pending outgoing byte.
  2. Writes tx to SPDR (CIPO data – what this peripheral sends to the external controller on the next controller-initiated frame).
  3. Waits for SPSR.SPRF (RX buffer full) – asserted when the external controller has completed clocking one SPI frame.
  4. Reads SPDR into *rx (the COPI byte received from the controller).

Under RA8_OFF_TARGET the SPSR wait reduces to a single-shot register read: pre-seed the register as shown below.

Example:
// Off-target (or pre-staged hardware test):
uint8_t rx = 0U;
ra8_spi_b_target_xfer(0, 0xA5U, &rx);
ra8_err_t ra8_spi_b_target_xfer(uint8_t channel, uint8_t tx, uint8_t *rx)
Exchange one byte as the SPI target in a polled blocking call.
static volatile r_spi_regs_t * ra8_spi(uint8_t channel)
Get pointer to SPI_B channel channel (0..1).
@ k_ra8_spsr_mask_sprf
RA8 spsr mask sprf.
@ k_ra8_spsr_mask_sptef
RA8 spsr mask sptef.
volatile uint32_t SPSR
+0x50 SPI Status Register.
Parameters
[in]channelSPI channel (0 or 1).
[in]txByte to send on CIPO (Controller In Peripheral Out).
[out]rxPointer to receive the COPI byte; may be NULL if the received byte is not needed.
Returns
ra8_err_t error code.
Return values
k_ra8_okFrame exchange completed.
k_ra8_err_null_ptrchannel is out of range (ra8_spi() returned NULL).
k_ra8_err_hw_timeoutSPSR.SPTEF or SPSR.SPRF never asserted within k_spi_b_target_poll_limit iterations.
Precondition
Channel previously initialised via ra8_spi_b_target_init.
SPCR.SPE is set.
Postcondition
On k_ra8_ok, tx was loaded into SPDR and (if rx != NULL) *rx holds the byte received on COPI from the controller.
SPSR.SPTEF and SPSR.SPRF have been cleared via SPSRC.
Note
Thread safety: not thread-safe.
The SPI_B SPDR register backs TX and RX through separate FIFO queues on hardware; they share a single address in the fake's plain-RAM model, so the fake echoes tx back as *rx.
See also
ra8_spi_b_target_init Initialise the channel first.
ra8_spi_xfer8 Controller-mode equivalent.
Since
0.1.0

Definition at line 332 of file ra8_spi_b_target.c.

References internal_target_wait_spsr(), k_ra8_ok, k_ra8_spsr_mask_sprf, k_ra8_spsr_mask_sptef, k_ra8_spsrc_mask_sprfc, k_ra8_spsrc_mask_sptefc, RA8_CHECK_NULL_PTR, ra8_spi(), s_tag, r_spi_regs_t::SPDR, and r_spi_regs_t::SPSRC.

Variable Documentation

◆ s_spi_mstp_table

const ra8_mstp_t s_spi_mstp_table[k_ra8_spi_b_channel_count]
static
Initial value:
= {
}
@ k_ra8_mstp_spi0
MSTPB19 SPI0.
@ k_ra8_mstp_spi1
MSTPB18 SPI1.

Channel-index -> MSTP id (HUM Ch 11.2.7 "MSTPCRB", p 444).

Matches the table in ra8_spi_b.c; this TU manages its own MSTP state rather than calling into the controller driver.

Note
Read-only after initialisation; thread-safe for concurrent reads.
Warning
Do not modify directly.
Since
0.1.0

Definition at line 85 of file ra8_spi_b_target.c.

◆ s_tag

const char* s_tag = "SPI_B_TGT"
static

Definition at line 68 of file ra8_spi_b_target.c.