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

DMAC mem-to-mem coherency HIL proof with the M85 D-cache ENABLED. More...

#include <stdint.h>
#include "ra8_attributes.h"
#include "ra8_board_ek_ra8d2.h"
#include "ra8_boot_entry.h"
#include "ra8_cache.h"
#include "ra8_cgc.h"
#include "ra8_dmac.h"
#include "ra8_dmac_regs.h"
#include "ra8_err.h"
#include "ra8_isr.h"
#include "ra8_log.h"
#include "ra8_mstp.h"
Include dependency graph for main.c:

Go to the source code of this file.

Enumerations

enum  dma_coh_config_t : uint32_t {
  k_dma_coh_baud = 115200U ,
  k_dma_coh_buf_words = 256U ,
  k_dma_coh_poll_limit = 100000U ,
  k_dma_coh_sentinel = 0xA5A5A5A5U
}
 Compile-time settings. More...
enum  dma_coh_byte_t : uint8_t {
  k_dma_coh_channel = 0U ,
  k_dma_coh_byte_sh = 8U ,
  k_dma_coh_align = 32U
}
 Single-byte constants. More...

Functions

static void internal_park (void)
 Halt forever in WFI – panic stop on a fatal init failure.
static void internal_setup_or_halt (void)
 Bring CGC + MSTP + SCI8 console + LEDs up.
static void internal_fill_buffers (void)
 Fill s_src with a deterministic pattern; prime s_dst.
static uint8_t internal_verify (void)
 Verify s_dst matches s_src element-by-element.
static ra8_err_t internal_run_copy (void)
 Clean src, run the DMAC block copy, then invalidate dst.
static void internal_report (uint8_t ok)
 Emit the PASS/FAIL result over VCOM and ITM, toggle an LED.
void main (void)
 Application entry: one-shot DMAC coherency proof, then WFI.

Variables

static const uint8_t s_dma_coh_pass_banner [] = "dma_coherency_hil: dma coherent PASS\r\n"
 VCOM success banner.
static const uint8_t s_dma_coh_fail_banner [] = "dma_coherency_hil: dma coherent FAIL\r\n"
 VCOM failure banner (caught by hil.conf HIL_EXPECT_NEGATIVE).
static uint32_t s_src [k_dma_coh_buf_words]
 Source buffer – M85 writes leave it dirty in the D-cache.
static uint32_t s_dst [k_dma_coh_buf_words]
 Destination buffer the DMAC writes; M85 must invalidate to read.

Detailed Description

DMAC mem-to-mem coherency HIL proof with the M85 D-cache ENABLED.

Tag
[Ring 6 / APP] {World: S}

Single-core (Cortex-M85) hardware-in-the-loop test that proves the DMA cache-maintenance pattern – clean-before / invalidate-after – keeps a DMAC0 mem-to-mem copy coherent with the M85 L1 D-cache while the cache is turned ON.

This app is the first in the tree to build with RA8_BOOT_ENABLE_CACHE_MPU (set in its CMakeLists.txt). With that flag the shared boot (libs/ra8_board_ek_ra8d2/src/boot/system_init.c) brings up the MPU (5 regions) + I-cache + D-cache + branch predictor. Both buffers live in MPU region 1 (0x22000000, M85-private cacheable SRAM), so the cache hazard is real on silicon.

Sequence (one-shot, then WFI):

  1. ra8_cgc_init() + ra8_mstp_init() + SCI8 J-Link OB console.
  2. Fill s_src with a deterministic pattern. These are M85 stores, so the fresh bytes sit dirty in the D-cache, not yet in SRAM. s_dst is filled with a sentinel so a no-op copy fails the verify.
  3. ra8_cache_dcache_clean_by_addr(s_src, sizeof s_src) – write the dirty source lines back so the DMAC reads the real pattern from memory, not stale SRAM (clean-before).
  4. Programme DMAC0 channel 0 for a 32-bit-wide, increment-both, single-block transfer of 256 words and software-trigger it (DMREQ.SWREQ); poll DMSTS.ACT to completion.
  5. ra8_cache_dcache_invalidate_by_addr(s_dst, sizeof s_dst) – drop the stale destination lines so the verify reads what the DMAC wrote to memory, not a cached copy (invalidate-after).
  6. Verify s_dst == s_src word-for-word. PASS -> emit "dma_coherency_hil: dma coherent PASS\r\n" over VCOM and the same result over ITM (ra8_log_info), toggle LED1. Mismatch -> FAIL banner + LED2. Either way the function parks in WFI so the emulator's idle-stop terminates the run after the one-shot.
ra8_emulator note (off-target path)
tools/ra8_emulator DOES model the DMAC mem-to-mem transfer: board_periph_dmac.c (dmac_copy_units) actually moves the bytes in emulated memory on the DMREQ.SWREQ software trigger, so the real ra8_dmac path runs off-target – no CPU-memcpy fallback is needed. ra8_emulator's memory is byte-exact and it does not model the L1 D-cache, so the clean/invalidate calls are exercised (the line-size and barrier logic run) but have no caching effect, and the copy verifies trivially. The cache hazard this app guards against is only observable on real silicon. RA8_OFF_TARGET is a host-unit-test define and is NOT set for the ARM cross-build that ra8_emulator executes, so there is nothing to #ifdef here.

Bare EK-RA8D2 only – no shields or external transceivers.

Author
Brighton Sikarskie
Date
2026-06-29
Since
0.1.0

Definition in file main.c.

Enumeration Type Documentation

◆ dma_coh_byte_t

enum dma_coh_byte_t : uint8_t

Single-byte constants.

Enumerator
k_dma_coh_channel 

DMAC0 channel index.

k_dma_coh_byte_sh 

Pattern xor shift.

k_dma_coh_align 

D-cache line size (bytes).

Definition at line 87 of file main.c.

◆ dma_coh_config_t

enum dma_coh_config_t : uint32_t

Compile-time settings.

Enumerator
k_dma_coh_baud 

J-Link OB VCOM baud rate.

k_dma_coh_buf_words 

Words per buffer (256 x 4 = 1 KiB).

k_dma_coh_poll_limit 

DMSTS.ACT poll bound.

k_dma_coh_sentinel 

Pre-fill so a no-op copy fails.

Definition at line 79 of file main.c.

Function Documentation

◆ internal_fill_buffers()

void internal_fill_buffers ( void )
static

Fill s_src with a deterministic pattern; prime s_dst.

s_src[i] = i ^ (i >> 8) are M85 stores that sit dirty in the D-cache. s_dst is primed with a sentinel distinct from every source word so a copy that never lands fails the verify.

MC/DC:
Single counting loop, no compound decision; only the implicit loop bound. No N+1 vectors required.
Precondition
Buffers are statically allocated and 32-byte aligned.
Called single-threaded before the DMAC is armed.
Postcondition
Every word of s_src holds the pattern.
Every word of s_dst holds k_dma_coh_sentinel.
Note
Not thread-safe.
Since
0.1.0

Definition at line 201 of file main.c.

References k_dma_coh_buf_words, k_dma_coh_byte_sh, k_dma_coh_sentinel, RA8_INTERNAL, s_dst, and s_src.

Referenced by main().

◆ internal_park()

void internal_park ( void )
static

Halt forever in WFI – panic stop on a fatal init failure.

Parks the core; only a debugger or external reset wakes it. Under ra8_emulator a WFI with no pending work triggers RA8_EMU_IDLE_STOP, terminating the run cleanly.

Precondition
Called only after a fatal error in boot or after the one-shot test.
IRQs are in whatever state the caller left them.
Postcondition
The core never returns from this function.
No further bus activity is generated.
Note
Not thread-safe; terminal sink.
Since
0.1.0

Definition at line 140 of file main.c.

References RA8_INTERNAL.

Referenced by internal_setup_or_halt(), and main().

◆ internal_report()

void internal_report ( uint8_t ok)
static

Emit the PASS/FAIL result over VCOM and ITM, toggle an LED.

Chooses one result banner, flushes it to VCOM, mirrors the result through the logging backend, and toggles the matching board LED so both automated and visual probes see one outcome.

Parameters
[in]okNon-zero if the coherency check passed.
Precondition
The console has been initialised by dma_coh_setup_or_halt.
Both LEDs have been initialised.
Postcondition
Exactly one terminal banner has been written and flushed to VCOM.
The matching result line has been emitted over ITM.
Note
Not thread-safe; single-shot reporting.
Since
0.1.0

Definition at line 350 of file main.c.

References k_ra8_board_led1, k_ra8_board_led2, ra8_board_led_toggle(), ra8_board_uart_console_flush(), ra8_board_uart_console_write(), RA8_INTERNAL, ra8_log_info, s_dma_coh_fail_banner, and s_dma_coh_pass_banner.

Referenced by main().

◆ internal_run_copy()

ra8_err_t internal_run_copy ( void )
staticnodiscard

Clean src, run the DMAC block copy, then invalidate dst.

Implements the clean-before / invalidate-after pattern:

  1. ra8_cache_dcache_clean_by_addr(s_src) flushes the dirty source lines so the DMAC reads the real pattern from SRAM (HUM-independent Arm v8-M cache maintenance).
  2. ra8_dmac_start_block programmes DMAC0 channel 0 in block mode (DMTMD.MD = 10b, HUM Ch 17.2.10 p 738): one software trigger moves the whole k_dma_coh_buf_words-word block, 32-bit units, increment both sides.
  3. DMREQ.SWREQ software-triggers the block (HUM Ch 17.2.15 p 744).
  4. Poll DMSTS.ACT (HUM Ch 17.2.16 p 745) until the controller goes idle – the completion gate for a synchronous block copy.
  5. ra8_cache_dcache_invalidate_by_addr(s_dst) drops the stale destination lines so the subsequent verify reads from SRAM.
Returns
ra8_err_t error code.
Return values
k_ra8_okCopy completed and both maintenance ops ran.
k_ra8_err_hw_errorA cache op failed or the channel accessor returned NULL.
k_ra8_err_hw_timeoutDMSTS.ACT never cleared within the poll bound.
(other)Propagated ra8_dmac_start_block / ra8_dmac_stop error.
Precondition
ra8_mstp_init() has run and the buffers are filled.
IRQs are enabled but the channel uses no completion ISR here.
Postcondition
On k_ra8_ok the DMAC has copied s_src into SRAM-backed s_dst and s_dst's cache lines are invalid.
On any error the channel is stopped (DMCNT.DTE = 0).
Note
Not thread-safe; single-shot init-context use.
Since
0.1.0

Definition at line 277 of file main.c.

References r_dmac_channel_regs_t::DMREQ, r_dmac_channel_regs_t::DMSTS, k_dma_coh_buf_words, k_dma_coh_channel, k_dma_coh_poll_limit, k_ra8_dmac_width_word, k_ra8_dmreq_swreq_mask, k_ra8_dmsts_act_mask, k_ra8_err_hw_error, k_ra8_err_hw_timeout, k_ra8_ok, ra8_cache_dcache_clean_by_addr(), ra8_cache_dcache_invalidate_by_addr(), ra8_dmac(), ra8_dmac_start_block(), ra8_dmac_stop(), RA8_INTERNAL, s_dst, and s_src.

Referenced by main().

◆ internal_setup_or_halt()

void internal_setup_or_halt ( void )
static

Bring CGC + MSTP + SCI8 console + LEDs up.

Panic-halts on fail.

The DMAC0 module clock is ungated by ra8_mstp_init() (ra8_dmac_start references the same MSTP gate). The SCI8 J-Link OB console (TXD8 = PD_02 / RXD8 = PD_03 @ 115200 8N1) comes up through the BSP console API.

Precondition
Reset_Handler has copied .data and zeroed .bss.
SystemInit has enabled the MPU + caches (RA8_BOOT_ENABLE_CACHE_MPU).
Postcondition
On success CGC, MSTP, the console and both LEDs are initialised.
On any failure the function does not return (parks in WFI).
Note
Single-threaded init context; not thread-safe.
Since
0.1.0

Definition at line 163 of file main.c.

References internal_park(), k_dma_coh_baud, k_ra8_board_led1, k_ra8_board_led2, k_ra8_ok, ra8_board_led_init(), ra8_board_uart_console_init(), ra8_cgc_init(), RA8_INTERNAL, and ra8_mstp_init().

◆ internal_verify()

uint8_t internal_verify ( void )
staticnodiscard

Verify s_dst matches s_src element-by-element.

Must run AFTER ra8_cache_dcache_invalidate_by_addr(s_dst) so the reads miss the cache and fetch the DMAC-written bytes from SRAM.

MC/DC:
One atomic condition s_dst[i] != s_src[i] x 2 vectors: all-match (steady state, this run) and one mismatch (the cache-hazard regression exercised on silicon when the invalidate is omitted).
Returns
1 if all words equal, 0 otherwise.
Return values
1Buffers identical – copy + maintenance coherent.
0At least one word differs.
Precondition
Both buffers are filled and the DMAC copy has completed.
s_dst lines have been invalidated.
Postcondition
The return value is exactly 0 or 1.
Neither buffer is modified.
Note
Not thread-safe.
Since
0.1.0

Definition at line 233 of file main.c.

References k_dma_coh_buf_words, RA8_INTERNAL, s_dst, and s_src.

Referenced by main().

◆ main()

void main ( void )

Application entry: one-shot DMAC coherency proof, then WFI.

The application entry point Reset_Handler hands control to.

Precondition
Reset_Handler has copied .data and zeroed .bss.
SystemInit enabled the MPU + caches via RA8_BOOT_ENABLE_CACHE_MPU.
Postcondition
Exactly one PASS/FAIL banner is emitted, then the core parks.
The core ends in WFI so ra8_emulator's idle-stop terminates the run.
Since
0.1.0

Definition at line 377 of file main.c.

References internal_fill_buffers(), internal_park(), internal_report(), internal_run_copy(), internal_setup_or_halt(), internal_verify(), k_ra8_ok, and ra8_isr_globals_enable().

Variable Documentation

◆ s_dma_coh_fail_banner

const uint8_t s_dma_coh_fail_banner[] = "dma_coherency_hil: dma coherent FAIL\r\n"
static

VCOM failure banner (caught by hil.conf HIL_EXPECT_NEGATIVE).

Definition at line 97 of file main.c.

Referenced by internal_report().

◆ s_dma_coh_pass_banner

const uint8_t s_dma_coh_pass_banner[] = "dma_coherency_hil: dma coherent PASS\r\n"
static

VCOM success banner.

Unique; not a substring of the FAIL line.

Definition at line 94 of file main.c.

Referenced by internal_report().

◆ s_dst

uint32_t s_dst[k_dma_coh_buf_words]
static

Destination buffer the DMAC writes; M85 must invalidate to read.

1 KiB, 32-byte aligned and a whole number of cache lines so ra8_cache_dcache_invalidate_by_addr drops exactly the buffer's lines.

Note
Lives in MPU region 1 (cacheable SRAM) when RA8_BOOT_ENABLE_CACHE_MPU.
Warning
Stale cached lines here are the exact hazard this app proves.
Since
0.1.0

Definition at line 123 of file main.c.

◆ s_src

uint32_t s_src[k_dma_coh_buf_words]
static

Source buffer – M85 writes leave it dirty in the D-cache.

1 KiB, aligned to a 32-byte D-cache line and an exact whole number of lines, so ra8_cache_dcache_clean_by_addr cleans exactly the buffer with no partial-line spill.

Note
Lives in MPU region 1 (cacheable SRAM) when RA8_BOOT_ENABLE_CACHE_MPU.
Warning
Do not read after a DMAC write without invalidating first.
Since
0.1.0

Definition at line 110 of file main.c.