ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_rtt.c
Go to the documentation of this file.
1
44
45#include <stdint.h>
46#include <stdio.h>
47#include <string.h>
48
49#include "board_console.h"
50#include "board_periph_block.h"
52
54typedef enum : uint64_t {
55 k_rtt_scan_base = 0x22000000UL,
56 k_rtt_scan_span = 0x00400000UL,
57 k_rtt_scan_step = 0x00010000UL,
59
69
71typedef enum : uint32_t {
76 k_rtt_size_sane = 1048576U,
80
91static const char s_k_rtt_id[k_rtt_id_len] = {'S', 'E', 'G', 'G', 'E', 'R', ' ', 'R', 'T', 'T'};
92
104typedef struct {
105 uint64_t cb_addr;
106 uint32_t ticks;
107 uint32_t next_scan;
108 uint32_t scan_gap;
109 uint32_t drained;
110 uint32_t lines;
111 uint32_t line_len;
112 char line[k_rtt_line_max + 1U];
114
116
118static uint8_t s_rtt_stage[(uint32_t)k_rtt_scan_step + (uint32_t)k_rtt_id_len];
119
122
138RA8_INTERNAL static uint32_t internal_rtt_rd32(uc_engine* uc, uint64_t addr)
139{
140 uint32_t v = 0U;
141 if (emu_mem_read(uc, addr, &v, sizeof(v)) != UC_ERR_OK) {
142 return 0U;
143 }
144 return v;
145}
146
170RA8_INTERNAL static bool internal_rtt_cb_valid(uc_engine* uc, uint64_t cb_addr)
171{
172 const uint32_t max_up = internal_rtt_rd32(uc, cb_addr + (uint64_t)k_rtt_off_max_up);
173 if ((max_up == 0U) || (max_up > (uint32_t)k_rtt_max_up_sane)) {
174 return false;
175 }
176 const uint64_t up0 = cb_addr + (uint64_t)k_rtt_off_up0;
177 const uint32_t buf = internal_rtt_rd32(uc, up0 + (uint64_t)k_rtt_up_off_buf);
178 const uint32_t size = internal_rtt_rd32(uc, up0 + (uint64_t)k_rtt_up_off_size);
179 const uint32_t wr = internal_rtt_rd32(uc, up0 + (uint64_t)k_rtt_up_off_wr);
180 const uint32_t rd = internal_rtt_rd32(uc, up0 + (uint64_t)k_rtt_up_off_rd);
181 if ((buf == 0U) || (size == 0U) || (size > (uint32_t)k_rtt_size_sane)) {
182 return false;
183 }
184 return (wr < size) && (rd < size);
185}
186
207RA8_INTERNAL static uint64_t
208internal_rtt_match_stage(uc_engine* uc, uint64_t stage_addr, uint64_t len)
209{
210 const uint8_t* p = s_rtt_stage;
211 uint64_t left = len;
212 while (left >= (uint64_t)k_rtt_id_len) {
213 const uint8_t* hit =
214 memchr(p, (int)s_k_rtt_id[0], (size_t)(left - ((uint64_t)k_rtt_id_len - 1U)));
215 if (hit == nullptr) {
216 return 0U;
217 }
218 if (memcmp(hit, s_k_rtt_id, (size_t)k_rtt_id_len) == 0) {
219 const uint64_t cb = stage_addr + (uint64_t)(hit - s_rtt_stage);
220 if (internal_rtt_cb_valid(uc, cb)) {
221 return cb;
222 }
223 }
224 left -= (uint64_t)(hit - p) + 1U;
225 p = hit + 1U;
226 }
227 return 0U;
228}
229
249RA8_INTERNAL static void internal_rtt_scan(uc_engine* uc)
250{
251 for (uint64_t off = 0U; off < (uint64_t)k_rtt_scan_span; off += (uint64_t)k_rtt_scan_step) {
252 /* Stage one step plus the ID-length overlap into the NEXT step (clamped at
253 * the window end) so a block straddling the boundary is still found. */
254 uint64_t want = (uint64_t)k_rtt_scan_step + (uint64_t)k_rtt_id_len;
255 if ((off + want) > (uint64_t)k_rtt_scan_span) {
256 want = (uint64_t)k_rtt_scan_span - off;
257 }
258 if (emu_mem_read(uc, (uint64_t)k_rtt_scan_base + off, s_rtt_stage, (size_t)want) != UC_ERR_OK) {
259 return; /* SRAM window unreadable: give up this scan pass */
260 }
261 if (want < (uint64_t)k_rtt_id_len) {
262 return; /* tail shorter than the ID: nothing left to match */
263 }
264 const uint64_t cb = internal_rtt_match_stage(uc, (uint64_t)k_rtt_scan_base + off, want);
265 if (cb != 0U) {
266 s_rtt.cb_addr = cb;
267 return;
268 }
269 }
270}
271
289RA8_INTERNAL static void internal_rtt_line_feed(uint8_t byte)
290{
291 const char c = (char)byte;
292 if (c == '\r') {
293 return;
294 }
295 if ((c == '\n') || (s_rtt.line_len >= (uint32_t)k_rtt_line_max)) {
296 s_rtt.line[s_rtt.line_len] = '\0';
297 (void)priv_emu_io_outf("[rtt] %s\n", s_rtt.line);
299 s_rtt.lines++;
300 s_rtt.line_len = 0U;
301 if (c == '\n') {
302 return;
303 }
304 }
305 s_rtt.line[s_rtt.line_len] = c;
306 s_rtt.line_len++;
307}
308
330RA8_INTERNAL static void internal_rtt_drain(uc_engine* uc)
331{
332 uint8_t id_now[k_rtt_id_len];
333 if ((emu_mem_read(uc, s_rtt.cb_addr, id_now, sizeof(id_now)) != UC_ERR_OK) ||
334 (memcmp(id_now, s_k_rtt_id, sizeof(id_now)) != 0)) {
335 s_rtt.cb_addr = 0U; /* block gone (reboot / clobber): re-discover by scan */
336 return;
337 }
338 const uint64_t up0 = s_rtt.cb_addr + (uint64_t)k_rtt_off_up0;
339 const uint32_t buf = internal_rtt_rd32(uc, up0 + (uint64_t)k_rtt_up_off_buf);
340 const uint32_t size = internal_rtt_rd32(uc, up0 + (uint64_t)k_rtt_up_off_size);
341 const uint32_t wr = internal_rtt_rd32(uc, up0 + (uint64_t)k_rtt_up_off_wr);
342 uint32_t rd = internal_rtt_rd32(uc, up0 + (uint64_t)k_rtt_up_off_rd);
343 if ((buf == 0U) || (size == 0U) || (size > (uint32_t)k_rtt_size_sane) || (wr >= size) ||
344 (rd >= size) || (wr == rd)) {
345 return; /* invalid mid-update descriptor, or simply nothing pending */
346 }
347 const uint32_t avail = (wr > rd) ? (wr - rd) : ((size - rd) + wr);
348 const uint32_t n = (avail > (uint32_t)k_rtt_drain_max) ? (uint32_t)k_rtt_drain_max : avail;
349 uint32_t got = 0U;
350 /* At most two contiguous segments: rd..min(rd+n, size), then a wrap to 0. */
351 const uint32_t first = ((size - rd) < n) ? (size - rd) : n;
352 if (emu_mem_read(uc, (uint64_t)buf + (uint64_t)rd, s_rtt_seg, (size_t)first) == UC_ERR_OK) {
353 got = first;
354 if ((n > first) &&
355 (emu_mem_read(uc, (uint64_t)buf, &s_rtt_seg[first], (size_t)(n - first)) == UC_ERR_OK)) {
356 got = n;
357 }
358 }
359 if (got == 0U) {
360 return; /* ring storage unreadable: leave the offsets untouched */
361 }
362 for (uint32_t i = 0U; i < got; i++) {
364 }
365 rd = (rd + got) % size;
366 /* Store the advanced read offset back so the firmware sees the drain. */
367 (void)emu_mem_write(uc, up0 + (uint64_t)k_rtt_up_off_rd, &rd, sizeof(rd));
368 s_rtt.drained += got;
369}
370
390RA8_INTERNAL static void internal_rtt_tick(uc_engine* uc)
391{
392 s_rtt.ticks++;
393 if (s_rtt.cb_addr == 0U) {
394 if (s_rtt.ticks < s_rtt.next_scan) {
395 return;
396 }
398 if (s_rtt.cb_addr == 0U) {
399 /* Exponential backoff, capped: a non-RTT app pays a bounded scan cost. */
400 if (s_rtt.scan_gap < (uint32_t)k_rtt_scan_max_gap) {
401 s_rtt.scan_gap *= 2U;
402 }
403 s_rtt.next_scan = s_rtt.ticks + s_rtt.scan_gap;
404 return;
405 }
406 }
408}
409
423{
424 s_rtt = (rtt_state_t){};
425 s_rtt.next_scan = (uint32_t)k_rtt_scan_first_tick;
426 s_rtt.scan_gap = (uint32_t)k_rtt_scan_first_tick;
427}
428
442{
443 if (s_rtt.cb_addr == 0U) {
444 return; /* no RTT user in this firmware: stay quiet */
445 }
446 (void)priv_emu_io_errf(
447 " SEGGER RTT : control block @0x%08llX, drained %u byte(s), %u line(s)\n",
448 (unsigned long long)s_rtt.cb_addr,
449 s_rtt.drained,
450 s_rtt.lines);
451}
452
469RA8_INTERNAL static uint64_t internal_rtt_mmio_read(uc_engine* uc, uint64_t addr, unsigned size)
470{
471 (void)uc;
472 (void)addr;
473 (void)size;
474 return 0U;
475}
476
493RA8_INTERNAL static void
494internal_rtt_mmio_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
495{
496 (void)uc;
497 (void)addr;
498 (void)size;
499 (void)value;
500}
501
503typedef enum : uint32_t {
506
509 .base = 0U,
510 .span = 0U, /* RAM-resident protocol: dispatch never routes here. */
511 .order = (uint32_t)k_rtt_block_order,
514 .tick = internal_rtt_tick,
515 .reset = internal_rtt_reset,
516 .report = internal_rtt_report,
517 .name = "SEGGER RTT",
518};
519
521[[gnu::constructor]] RA8_INTERNAL static void internal_rtt_block_register(void)
522{
524}
Multi-channel console log store backing the board view's tabbed console.
void board_console_push(board_console_ch_t ch, const char *line)
Append one completed line to a channel ring and the ALL ring.
@ k_board_console_ch_rtt
SEGGER RTT up-buffer 0 drain ([rtt]).
Decentralized peripheral-block registry for the board emulator core.
void board_periph_register_block(const board_periph_block_t *block)
Register a peripheral block's descriptor with the core registry.
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_line_feed(uint8_t byte)
Feed one drained byte into the line assembler.
static const char s_k_rtt_id[k_rtt_id_len]
The 10-byte control-block ID a debug probe scans RAM for.
static RA8_INTERNAL void internal_rtt_block_register(void)
Self-register the RTT drain model before main (host constructor).
rtt_scan_window_t
SRAM window the control-block scan covers (mirrors main.c's map).
@ k_rtt_scan_step
Bytes staged per scan sub-read.
@ k_rtt_scan_base
On-chip SRAM base (dual-core banks).
@ k_rtt_scan_span
The full 4 MiB window main.c maps.
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).
static RA8_INTERNAL void internal_rtt_scan(uc_engine *uc)
Scan the emulated SRAM window for the RTT control-block ID.
static const board_periph_block_t s_k_rtt_block
This block's descriptor: tick/reset/report only, no MMIO window.
rtt_order_t
Per-tick order slot for the RTT drain (right after the SCI console).
@ k_rtt_block_order
After SCI (30), before the core IRQ section (35).
static rtt_state_t s_rtt
static RA8_INTERNAL void internal_rtt_report(void)
End-of-run RTT section: block address + drained totals.
rtt_tune_t
Model tuning: scan cadence, sanity caps, and line assembly.
@ k_rtt_drain_max
Bytes drained per tick (bounded).
@ k_rtt_id_len
Length of the "SEGGER RTT" ID match.
@ k_rtt_scan_first_tick
Ticks before the first RAM scan.
@ k_rtt_line_max
Chars buffered before a forced flush.
@ k_rtt_max_up_sane
Reject max_up above this (garbage).
@ k_rtt_scan_max_gap
Backoff cap between fruitless scans.
@ k_rtt_size_sane
Reject ring sizes above 1 MiB.
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.
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 void internal_rtt_drain(uc_engine *uc)
Drain up-buffer 0 of the discovered control block, host-style.
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 uint8_t s_rtt_seg[k_rtt_drain_max]
Staging buffer for the per-tick ring drain (bounded segments).
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).
rtt_cb_layout_t
Control-block field offsets (SEGGER_RTT.h, 32-bit target layout).
@ k_rtt_up_off_wr
u32 write offset (target-owned).
@ k_rtt_up_off_size
u32 ring capacity in bytes.
@ k_rtt_off_max_up
u32 count of up-buffers (after id[16]).
@ k_rtt_up_off_rd
u32 read offset (host-owned; we advance).
@ k_rtt_up_off_buf
u8* ring storage pointer within a desc.
@ k_rtt_off_up0
Up-buffer 0 descriptor (after max_down).
Bounded raw-descriptor I/O seam for the RA8 emulator.
emu_io_result_t priv_emu_io_errf(const char *format,...)
Format bounded text and write it to the injected error descriptor.
emu_io_result_t priv_emu_io_outf(const char *format,...)
Format bounded text and write it to the injected output descriptor.
uc_err emu_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t count)
Read guest memory through the central access seam.
uc_err emu_mem_write(uc_engine *uc, uint64_t address, const void *bytes, size_t count)
Write guest memory through the central access seam.
-proof
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
void * memchr(const void *s, int c, size_t n)
Locate a byte in a memory area.
int memcmp(const void *a, const void *b, size_t n)
Compare bytes in two memory areas.
A modelled peripheral block's self-description for the core registry.
RTT drain-model state (discovery, line assembly, observability).
uint32_t line_len
Chars buffered in line.
uint32_t scan_gap
Current backoff gap between scans.
uint32_t lines
Completed lines surfaced.
uint64_t cb_addr
Control-block address (0 = not found).
char line[k_rtt_line_max+1U]
In-flight (not yet newline-ended) line.
uint32_t ticks
Ticks elapsed (scan cadence clock).
uint32_t next_scan
Tick of the next discovery scan.
uint32_t drained
Total bytes drained from up-buffer 0.