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

SEGGER RTT up-buffer drain model for ra8_emulator. More...

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "board_console.h"
#include "board_periph_block.h"
#include "emu_host_io_internal.h"
Include dependency graph for board_periph_rtt.c:

Go to the source code of this file.

Data Structures

struct  rtt_state_t
 RTT drain-model state (discovery, line assembly, observability). More...

Enumerations

enum  rtt_scan_window_t : uint64_t {
  k_rtt_scan_base = 0x22000000UL ,
  k_rtt_scan_span = 0x00400000UL ,
  k_rtt_scan_step = 0x00010000UL
}
 SRAM window the control-block scan covers (mirrors main.c's map). More...
enum  rtt_cb_layout_t : uint32_t {
  k_rtt_off_max_up = 16U ,
  k_rtt_off_up0 = 24U ,
  k_rtt_up_off_buf = 4U ,
  k_rtt_up_off_size = 8U ,
  k_rtt_up_off_wr = 12U ,
  k_rtt_up_off_rd = 16U
}
 Control-block field offsets (SEGGER_RTT.h, 32-bit target layout). More...
enum  rtt_tune_t : uint32_t {
  k_rtt_id_len = 10U ,
  k_rtt_scan_first_tick = 64U ,
  k_rtt_scan_max_gap = 1024U ,
  k_rtt_max_up_sane = 16U ,
  k_rtt_size_sane = 1048576U ,
  k_rtt_drain_max = 4096U ,
  k_rtt_line_max = 240U
}
 Model tuning: scan cadence, sanity caps, and line assembly. More...
enum  rtt_order_t : uint32_t { k_rtt_block_order = 32U }
 Per-tick order slot for the RTT drain (right after the SCI console). More...

Functions

static RA8_INTERNAL uint32_t internal_rtt_rd32 (uc_engine *uc, uint64_t addr)
 Read a 32-bit little-endian word from emulated memory.
static RA8_INTERNAL bool internal_rtt_cb_valid (uc_engine *uc, uint64_t cb_addr)
 Validate a candidate control block beyond its ID match.
static RA8_INTERNAL uint64_t internal_rtt_match_stage (uc_engine *uc, uint64_t stage_addr, uint64_t len)
 Match the staged bytes for a validated control-block ID.
static RA8_INTERNAL void internal_rtt_scan (uc_engine *uc)
 Scan the emulated SRAM window for the RTT control-block ID.
static RA8_INTERNAL void internal_rtt_line_feed (uint8_t byte)
 Feed one drained byte into the line assembler.
static RA8_INTERNAL void internal_rtt_drain (uc_engine *uc)
 Drain up-buffer 0 of the discovered control block, host-style.
static RA8_INTERNAL void internal_rtt_tick (uc_engine *uc)
 Per-chunk advance: discover the control block, then keep it drained.
static RA8_INTERNAL void internal_rtt_reset (void)
 Reset the RTT drain model to power-on state (warm reboot).
static RA8_INTERNAL void internal_rtt_report (void)
 End-of-run RTT section: block address + drained totals.
static RA8_INTERNAL uint64_t internal_rtt_mmio_read (uc_engine *uc, uint64_t addr, unsigned size)
 MMIO read stub – never dispatched (this block owns no window).
static RA8_INTERNAL void internal_rtt_mmio_write (uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
 MMIO write stub – never dispatched (this block owns no window).
static RA8_INTERNAL void internal_rtt_block_register (void)
 Self-register the RTT drain model before main (host constructor).

Variables

static const char s_k_rtt_id [k_rtt_id_len] = {'S', 'E', 'G', 'G', 'E', 'R', ' ', 'R', 'T', 'T'}
 The 10-byte control-block ID a debug probe scans RAM for.
static rtt_state_t s_rtt
static uint8_t s_rtt_stage [(uint32_t) k_rtt_scan_step+(uint32_t) k_rtt_id_len]
 Staging buffer for scan sub-reads (step + ID overlap carry).
static uint8_t s_rtt_seg [k_rtt_drain_max]
 Staging buffer for the per-tick ring drain (bounded segments).
static const board_periph_block_t s_k_rtt_block
 This block's descriptor: tick/reset/report only, no MMIO window.

Detailed Description

SEGGER RTT up-buffer drain model for ra8_emulator.

Models the HOST side of the SEGGER Real-Time Transfer channel: the debug probe that finds the in-RAM _SEGGER_RTT-compatible control block and drains its terminal up-buffer. On real hardware the J-Link OB scans target RAM for the 10-byte "SEGGER RTT" ID string the control block begins with, then reads bytes out of up-buffer 0's ring (advancing the read offset so the firmware sees the buffer emptying). This block does exactly that against the emulated SRAM, so an RTT-logging app (rtt_log_demo) surfaces its banner in ra8_emulator with no UART pin – the same text JLinkRTTViewer would show on the bench.

Control-block layout drained here (SEGGER_RTT.h, 32-bit target): char id[16] ("SEGGER RTT" + NUL padding), u32 max_up, u32 max_down, then max_up up-buffer descriptors of {const char* name; u8* buf; u32 size; u32 wr_off; u32 rd_off; u32 flags}. Only up-buffer 0 (the standard "Terminal" channel) is drained; down-buffers (host -> target input) are not modelled.

Discovery is by RAM scan, not ELF symbol: the firmware deliberately writes the ID string byte-by-byte at runtime so an image scan cannot match a stale literal-pool copy, and a scan needs no per-app symbol name (the demo's block is a file-static, not _SEGGER_RTT). The scan walks the 4 MiB SRAM window on a backoff cadence until the block appears, then re-verifies the ID once per drain so a clobbered block is forgotten and re-discovered.

Drained bytes are assembled into lines (CR dropped, latched on LF) and surfaced exactly like the SCI console: each completed line prints to injected output sink as [rtt] <line> and lands in the board_console RTT channel, whose newest line the run loop's RA8_EMU_STOP_ON banner guard also checks – so the EIL gate scrapes an RTT banner the same way it scrapes a UART one.

This block owns NO MMIO window (base = span = 0): RTT is a plain-RAM protocol, so only the tick / reset / report hooks do work and the required read / write handlers are never-dispatched stubs.

Since
0.1.0

Definition in file board_periph_rtt.c.

Enumeration Type Documentation

◆ rtt_cb_layout_t

enum rtt_cb_layout_t : uint32_t

Control-block field offsets (SEGGER_RTT.h, 32-bit target layout).

Enumerator
k_rtt_off_max_up 

u32 count of up-buffers (after id[16]).

k_rtt_off_up0 

Up-buffer 0 descriptor (after max_down).

k_rtt_up_off_buf 

u8* ring storage pointer within a desc.

k_rtt_up_off_size 

u32 ring capacity in bytes.

k_rtt_up_off_wr 

u32 write offset (target-owned).

k_rtt_up_off_rd 

u32 read offset (host-owned; we advance).

Definition at line 61 of file board_periph_rtt.c.

◆ rtt_order_t

enum rtt_order_t : uint32_t

Per-tick order slot for the RTT drain (right after the SCI console).

Enumerator
k_rtt_block_order 

After SCI (30), before the core IRQ section (35).

Definition at line 503 of file board_periph_rtt.c.

◆ rtt_scan_window_t

enum rtt_scan_window_t : uint64_t

SRAM window the control-block scan covers (mirrors main.c's map).

Enumerator
k_rtt_scan_base 

On-chip SRAM base (dual-core banks).

k_rtt_scan_span 

The full 4 MiB window main.c maps.

k_rtt_scan_step 

Bytes staged per scan sub-read.

Definition at line 54 of file board_periph_rtt.c.

◆ rtt_tune_t

enum rtt_tune_t : uint32_t

Model tuning: scan cadence, sanity caps, and line assembly.

Enumerator
k_rtt_id_len 

Length of the "SEGGER RTT" ID match.

k_rtt_scan_first_tick 

Ticks before the first RAM scan.

k_rtt_scan_max_gap 

Backoff cap between fruitless scans.

k_rtt_max_up_sane 

Reject max_up above this (garbage).

k_rtt_size_sane 

Reject ring sizes above 1 MiB.

k_rtt_drain_max 

Bytes drained per tick (bounded).

k_rtt_line_max 

Chars buffered before a forced flush.

Definition at line 71 of file board_periph_rtt.c.

Function Documentation

◆ internal_rtt_block_register()

RA8_INTERNAL void internal_rtt_block_register ( void )
static

Self-register the RTT drain model before main (host constructor).

Definition at line 521 of file board_periph_rtt.c.

References board_periph_register_block(), RA8_INTERNAL, and s_k_rtt_block.

◆ internal_rtt_cb_valid()

RA8_INTERNAL bool internal_rtt_cb_valid ( uc_engine * uc,
uint64_t cb_addr )
static

Validate a candidate control block beyond its ID match.

A 10-byte ID hit alone could be a coincidental byte pattern (or a half-initialised block), so the descriptor fields are sanity-checked the way a real probe does before trusting the ring: at least one up-buffer must be declared (and not absurdly many), the ring storage pointer must be non-NULL, the ring capacity must be non-zero and sane, and both offsets must lie inside the ring. A block that is mid-rtt_init simply fails here and is re-tried on the next scan.

Parameters
[in,out]ucUnicorn engine (descriptor fields are read from RAM).
[in]cb_addrCandidate control-block base (the ID match address).
Returns
true when every descriptor sanity check passes.
Return values
trueThe block is a live, fully-initialised RTT control block.
falseA field is out of range; the candidate is rejected.
Precondition
uc is a live engine with the SRAM window mapped.
cb_addr begins with the 10-byte RTT ID (the caller matched it).
Postcondition
No guest state is modified (pure validation).
A true result means up-buffer 0 is safe to drain this tick.
Note
Not thread-safe; ra8_emulator is single-threaded.
Since
0.1.0

Definition at line 170 of file board_periph_rtt.c.

References internal_rtt_rd32(), k_rtt_max_up_sane, k_rtt_off_max_up, k_rtt_off_up0, k_rtt_size_sane, k_rtt_up_off_buf, k_rtt_up_off_rd, k_rtt_up_off_size, k_rtt_up_off_wr, and RA8_INTERNAL.

Referenced by internal_rtt_match_stage().

◆ internal_rtt_drain()

RA8_INTERNAL void internal_rtt_drain ( uc_engine * uc)
static

Drain up-buffer 0 of the discovered control block, host-style.

Re-verifies the block ID (a warm-clobbered block is forgotten and re-discovered by the scan), reads the live descriptor, then consumes up to k_rtt_drain_max pending bytes this tick – at most two contiguous segments around the ring wrap – and stores the advanced read offset back, exactly as JLinkRTTViewer does so the firmware's writer sees space free up. A descriptor that fails validation this tick is skipped (drained next tick once the firmware finishes touching it).

Parameters
[in,out]ucUnicorn engine (ring bytes + offsets live in guest RAM).
Returns
Nothing.
Precondition
s_rtt.cb_addr points at a previously validated control block.
uc is a live engine with the SRAM window mapped.
Postcondition
The guest read offset equals the pre-drain write offset (or advanced by k_rtt_drain_max on an oversized backlog).
Every drained byte passed through internal_rtt_line_feed in ring order.
Note
Not thread-safe; ra8_emulator is single-threaded.
Since
0.1.0

Definition at line 330 of file board_periph_rtt.c.

References emu_mem_read(), emu_mem_write(), internal_rtt_line_feed(), internal_rtt_rd32(), k_rtt_drain_max, k_rtt_id_len, k_rtt_off_up0, k_rtt_size_sane, k_rtt_up_off_buf, k_rtt_up_off_rd, k_rtt_up_off_size, k_rtt_up_off_wr, memcmp(), RA8_INTERNAL, s_k_rtt_id, s_rtt, and s_rtt_seg.

Referenced by internal_rtt_tick().

◆ internal_rtt_line_feed()

RA8_INTERNAL void internal_rtt_line_feed ( uint8_t byte)
static

Feed one drained byte into the line assembler.

Mirrors the ITM / UART console formatting: CR is dropped, and a LF (or a full buffer) latches the pending text – printed to injected output sink as [rtt] <line> and pushed into the board_console RTT channel, where the run loop's RA8_EMU_STOP_ON banner guard reads the newest line back.

Parameters
[in]byteThe drained up-buffer byte.
Returns
Nothing.
Precondition
s_rtt.line_len < k_rtt_line_max (the flush below maintains it).
board_console is initialised (static storage guarantees it).
Postcondition
On LF the completed line is surfaced and the buffer reset.
Any other non-CR byte is appended to the pending line.
Note
Not thread-safe; ra8_emulator is single-threaded.
Since
0.1.0

Definition at line 289 of file board_periph_rtt.c.

References board_console_push(), k_board_console_ch_rtt, k_rtt_line_max, priv_emu_io_outf(), RA8_INTERNAL, and s_rtt.

Referenced by internal_rtt_drain().

◆ internal_rtt_match_stage()

RA8_INTERNAL uint64_t internal_rtt_match_stage ( uc_engine * uc,
uint64_t stage_addr,
uint64_t len )
static

Match the staged bytes for a validated control-block ID.

Walks s_rtt_stage memchr-first (candidates share the ID's first byte) and memcmp-confirms each candidate, then internal_rtt_cb_valid-checks the matching guest address. Bounded: every iteration advances at least one byte through the fixed-size stage.

Parameters
[in,out]ucUnicorn engine (descriptor validation reads RAM).
[in]stage_addrGuest address of s_rtt_stage byte 0.
[in]lenStaged byte count (<= sizeof s_rtt_stage).
Returns
Guest address of the first validated control block, or 0.
Return values
0No validated ID match in this stage.
Precondition
len is at least k_rtt_id_len (the caller guarantees it).
s_rtt_stage holds len bytes read from stage_addr.
Postcondition
No guest state is modified (pure match).
A non-zero result begins with the 10-byte RTT ID.
Note
Not thread-safe; ra8_emulator is single-threaded.
Since
0.1.0

Definition at line 208 of file board_periph_rtt.c.

References internal_rtt_cb_valid(), k_rtt_id_len, memchr(), memcmp(), s_k_rtt_id, and s_rtt_stage.

Referenced by internal_rtt_scan().

◆ internal_rtt_mmio_read()

RA8_INTERNAL uint64_t internal_rtt_mmio_read ( uc_engine * uc,
uint64_t addr,
unsigned size )
static

MMIO read stub – never dispatched (this block owns no window).

Parameters
[in,out]ucUnicorn engine; unused.
[in]addrAccess address; unused.
[in]sizeAccess width; unused.
Returns
Always 0.
Return values
0The only value (unreachable in practice: span is 0).
Precondition
None (the zero-length window means the core never routes here).
The descriptor's span stays 0 (see s_k_rtt_block).
Postcondition
No state is touched.
The return value is 0.
Note
Present only because the registry requires a read handler.
Since
0.1.0

MMIO read stub – never dispatched (this block owns no window); this step is contained within the board periph rtt model and uses bounded caller or module-owned storage.

Definition at line 469 of file board_periph_rtt.c.

References RA8_INTERNAL.

◆ internal_rtt_mmio_write()

RA8_INTERNAL void internal_rtt_mmio_write ( uc_engine * uc,
uint64_t addr,
unsigned size,
uint64_t value )
static

MMIO write stub – never dispatched (this block owns no window).

Parameters
[in,out]ucUnicorn engine; unused.
[in]addrAccess address; unused.
[in]sizeAccess width; unused.
[in]valueWritten value; unused.
Returns
Nothing.
Precondition
None (the zero-length window means the core never routes here).
The descriptor's span stays 0 (see s_k_rtt_block).
Postcondition
No state is touched.
The write is discarded.
Note
Present only because the registry requires a write handler.
Since
0.1.0

MMIO write stub – never dispatched (this block owns no window); this step is contained within the board periph rtt model and uses bounded caller or module-owned storage.

Definition at line 494 of file board_periph_rtt.c.

◆ internal_rtt_rd32()

RA8_INTERNAL uint32_t internal_rtt_rd32 ( uc_engine * uc,
uint64_t addr )
static

Read a 32-bit little-endian word from emulated memory.

Parameters
[in,out]ucUnicorn engine to read through.
[in]addrGuest address of the word.
Returns
The word value, or 0 if the read failed (unmapped address).
Precondition
uc is a live engine with the SRAM window mapped.
addr points into mapped guest memory for a meaningful result.
Postcondition
No guest state is modified (pure read).
The returned value is 0 on any read failure.
Note
Not thread-safe; ra8_emulator is single-threaded.
Since
0.1.0

Read a 32-bit little-endian word from emulated memory; this step is contained within the board periph rtt model and uses bounded caller or module-owned storage.

Return values
valueThe operation-specific rtt rd32 value.

Definition at line 138 of file board_periph_rtt.c.

References emu_mem_read(), and RA8_INTERNAL.

Referenced by internal_rtt_cb_valid(), and internal_rtt_drain().

◆ internal_rtt_report()

RA8_INTERNAL void internal_rtt_report ( void )
static

End-of-run RTT section: block address + drained totals.

Returns
Nothing.
Precondition
The run loop has finished (report cadence owned by the registry).
injected error sink is writable (the summary sink).
Postcondition
One summary line is printed iff a control block was discovered.
Model state is unchanged (pure report).
Note
Not thread-safe; ra8_emulator is single-threaded.
Since
0.1.0

End-of-run rtt section: block address + drained totals; this step is contained within the board periph rtt model and uses bounded caller or module-owned storage.

Definition at line 441 of file board_periph_rtt.c.

References priv_emu_io_errf(), RA8_INTERNAL, and s_rtt.

◆ internal_rtt_reset()

RA8_INTERNAL void internal_rtt_reset ( void )
static

Reset the RTT drain model to power-on state (warm reboot).

Returns
Nothing.
Precondition
Called from the single-threaded reset path (board_periph_init).
The block registry owns the call cadence (never re-entered).
Postcondition
The control block is forgotten and the scan cadence re-armed.
All counters and the in-flight line buffer read zero/empty.
Note
Not thread-safe; ra8_emulator is single-threaded.
Since
0.1.0

Reset the rtt drain model to power-on state (warm reboot); this step is contained within the board periph rtt model and uses bounded caller or module-owned storage.

Definition at line 422 of file board_periph_rtt.c.

References k_rtt_scan_first_tick, RA8_INTERNAL, and s_rtt.

◆ internal_rtt_scan()

RA8_INTERNAL void internal_rtt_scan ( uc_engine * uc)
static

Scan the emulated SRAM window for the RTT control-block ID.

Stages the window through s_rtt_stage in k_rtt_scan_step sub-reads, carrying an ID-length overlap so a control block straddling a stage boundary still matches, and hands each stage to internal_rtt_match_stage – exactly how the J-Link OB discovers the block on silicon. The first validated match is latched into s_rtt.cb_addr. The walk is bounded by the fixed window span (64 sub-reads).

Parameters
[in,out]ucUnicorn engine (the SRAM bytes are read through it).
Returns
Nothing.
Precondition
uc is a live engine with the SRAM window mapped.
s_rtt.cb_addr is 0 (the caller only scans while undiscovered).
Postcondition
On a validated match s_rtt.cb_addr holds the block address.
No guest state is modified (pure scan).
Note
Not thread-safe; ra8_emulator is single-threaded.
Since
0.1.0

Definition at line 249 of file board_periph_rtt.c.

References emu_mem_read(), internal_rtt_match_stage(), k_rtt_id_len, k_rtt_scan_base, k_rtt_scan_span, k_rtt_scan_step, RA8_INTERNAL, s_rtt, and s_rtt_stage.

Referenced by internal_rtt_tick().

◆ internal_rtt_tick()

RA8_INTERNAL void internal_rtt_tick ( uc_engine * uc)
static

Per-chunk advance: discover the control block, then keep it drained.

While undiscovered, runs internal_rtt_scan on a backoff cadence (first at tick k_rtt_scan_first_tick, then doubling the gap up to k_rtt_scan_max_gap) so an app that never initialises RTT costs a bounded handful of scans per run instead of one per tick. Once found, drains up-buffer 0 every tick – far faster than any firmware writer, so the ring never back-pressures the target.

Parameters
[in,out]ucUnicorn engine (all block state lives in guest RAM).
Returns
Nothing.
Precondition
The block registry called internal_rtt_reset at init (state is coherent).
uc is a live engine with the SRAM window mapped.
Postcondition
s_rtt.ticks advanced by one.
Any pending up-buffer bytes of a discovered block were surfaced.
Note
Not thread-safe; ra8_emulator is single-threaded.
Since
0.1.0

Definition at line 390 of file board_periph_rtt.c.

References internal_rtt_drain(), internal_rtt_scan(), k_rtt_scan_max_gap, RA8_INTERNAL, and s_rtt.

Variable Documentation

◆ s_k_rtt_block

const board_periph_block_t s_k_rtt_block
static
Initial value:
= {
.base = 0U,
.span = 0U,
.order = (uint32_t)k_rtt_block_order,
.name = "SEGGER RTT",
}
static RA8_INTERNAL void internal_rtt_reset(void)
Reset the RTT drain model to power-on state (warm reboot).
static RA8_INTERNAL void internal_rtt_mmio_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write stub – never dispatched (this block owns no window).
@ k_rtt_block_order
After SCI (30), before the core IRQ section (35).
static RA8_INTERNAL void internal_rtt_report(void)
End-of-run RTT section: block address + drained totals.
static RA8_INTERNAL uint64_t internal_rtt_mmio_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read stub – never dispatched (this block owns no window).
static RA8_INTERNAL void internal_rtt_tick(uc_engine *uc)
Per-chunk advance: discover the control block, then keep it drained.
-proof

This block's descriptor: tick/reset/report only, no MMIO window.

Definition at line 508 of file board_periph_rtt.c.

Referenced by internal_rtt_block_register().

◆ s_k_rtt_id

const char s_k_rtt_id[k_rtt_id_len] = {'S', 'E', 'G', 'G', 'E', 'R', ' ', 'R', 'T', 'T'}
static

The 10-byte control-block ID a debug probe scans RAM for.

Spelled as a char array (not a string literal) for the same reason the firmware writes it byte-by-byte: this host tool's own rodata must never be mistaken for a control block if it is ever staged through a scan buffer.

Note
Read-only; compared with memcmp against staged SRAM bytes.
Since
0.1.0

Definition at line 91 of file board_periph_rtt.c.

Referenced by internal_rtt_drain(), and internal_rtt_match_stage().

◆ s_rtt

◆ s_rtt_seg

uint8_t s_rtt_seg[k_rtt_drain_max]
static

Staging buffer for the per-tick ring drain (bounded segments).

Definition at line 121 of file board_periph_rtt.c.

Referenced by internal_rtt_drain().

◆ s_rtt_stage

uint8_t s_rtt_stage[(uint32_t) k_rtt_scan_step+(uint32_t) k_rtt_id_len]
static

Staging buffer for scan sub-reads (step + ID overlap carry).

Definition at line 118 of file board_periph_rtt.c.

Referenced by internal_rtt_match_stage(), and internal_rtt_scan().