ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_pdm.c
Go to the documentation of this file.
1
34
35#include <stdint.h>
36#include <stdio.h>
37
38#include "board_console.h"
39#include "board_periph_block.h"
41
43typedef enum : uint64_t {
44 k_pdm_base = 0x40256000UL,
45 k_pdm_span = 0x400UL,
46 k_pdm_words = 0x400UL / 4UL,
48 k_pdm_ch0_off = 0x100UL,
49 k_pdm_ch_stride = 0x100UL,
52 k_pdm_off_csr = 0x10UL,
53 k_pdm_ch_pddrcr = 0xE0UL,
54 k_pdm_ch_pddrr = 0xE8UL,
55 k_pdm_ch_pddsr = 0xECUL,
56} pdm_map_t;
57
59typedef enum : uint32_t {
61 k_pdm_pddsr_num = 0x000000FFU,
62 k_pdm_pddrr_mask20 = 0x000FFFFFU,
65
67typedef enum : uint32_t {
72
74typedef enum : int32_t {
77} pdm_amp_t;
78
80typedef enum : uint32_t {
81 k_pdm_lcg_mul = 1664525U,
82 k_pdm_lcg_add = 1013904223U,
87
89typedef enum : uint32_t {
93
95typedef struct {
96 bool running;
98 uint32_t phase;
99 uint32_t lcg;
100 uint32_t samples;
102
104static uint32_t s_pdm_reg[k_pdm_words];
105
117RA8_INTERNAL static bool internal_pdm_live(uint32_t ch)
118{
119 if (ch >= (uint32_t)k_pdm_ch_count) {
120 return false;
121 }
122 return s_pdm[ch].running && s_pdm[ch].read_enable;
123}
124
136RA8_INTERNAL static uint32_t internal_pdm_next_sample(uint32_t ch)
137{
138 pdm_channel_t* c = &s_pdm[ch];
139 const uint32_t ph = c->phase & (uint32_t)k_pdm_tone_qmask;
140 int32_t tri = 0;
141 if (ph < (uint32_t)k_pdm_tone_half) {
142 tri = -(int32_t)k_pdm_tone_amp + ((int32_t)k_pdm_tone_step * (int32_t)ph);
143 } else {
144 tri = (int32_t)k_pdm_tone_amp -
145 ((int32_t)k_pdm_tone_step * (int32_t)(ph - (uint32_t)k_pdm_tone_half));
146 }
147 c->lcg = (c->lcg * (uint32_t)k_pdm_lcg_mul) + (uint32_t)k_pdm_lcg_add;
148 const int32_t dither =
149 (int32_t)((c->lcg >> (uint32_t)k_pdm_lcg_shift) & (uint32_t)k_pdm_dither_mask) -
150 (int32_t)k_pdm_dither_half;
151 c->phase++;
152 c->samples++;
153 const int32_t sample = tri + dither;
154 return (uint32_t)sample & (uint32_t)k_pdm_pddrr_mask20;
155}
156
167{
168 const uint32_t n = s_pdm[ch].samples;
169 if ((n != 1U) && ((n % (uint32_t)k_pdm_console_every) != 0U)) {
170 return;
171 }
172 char ln[k_pdm_console_line_cap];
173 (void)snprintf(ln, sizeof(ln), "PDM%u samples=%u (synth tone)", (unsigned)ch, (unsigned)n);
175}
176
190RA8_INTERNAL static bool internal_pdm_channel_at(uint64_t off, uint32_t* ch, uint64_t* ch_off)
191{
192 if (off < (uint64_t)k_pdm_ch0_off) {
193 return false;
194 }
195 const uint64_t rel = off - (uint64_t)k_pdm_ch0_off;
196 *ch = (uint32_t)(rel / (uint64_t)k_pdm_ch_stride);
197 *ch_off = rel % (uint64_t)k_pdm_ch_stride;
198 return (*ch < (uint32_t)k_pdm_ch_count);
199}
200
212{
213 uint32_t csr = 0U;
214 for (uint32_t i = 0U; i < (uint32_t)k_pdm_ch_count; i++) {
215 if (s_pdm[i].running) {
216 csr |= (1U << i);
217 }
218 }
219 return csr;
220}
221
235RA8_INTERNAL static uint64_t internal_pdm_read(uc_engine* uc, uint64_t addr, unsigned size)
236{
237 (void)uc;
238 (void)size;
239 const uint64_t off = addr - (uint64_t)k_pdm_base;
240 if (off >= (uint64_t)k_pdm_span) {
241 return 0U;
242 }
243 if (off == (uint64_t)k_pdm_off_csr) {
244 return internal_pdm_read_csr();
245 }
246 uint32_t ch = 0U;
247 uint64_t ch_off = 0U;
248 if (internal_pdm_channel_at(off, &ch, &ch_off)) {
249 if (ch_off == (uint64_t)k_pdm_ch_pddsr) {
250 return internal_pdm_live(ch)
251 ? (uint64_t)((uint32_t)k_pdm_fifo_depth & (uint32_t)k_pdm_pddsr_num)
252 : 0U;
253 }
254 if (ch_off == (uint64_t)k_pdm_ch_pddrr) {
255 if (!internal_pdm_live(ch)) {
256 return 0U;
257 }
258 const uint32_t sample = internal_pdm_next_sample(ch);
260 return (uint64_t)sample;
261 }
262 }
263 return (uint64_t)s_pdm_reg[off >> 2U];
264}
265
278RA8_INTERNAL static void
279internal_pdm_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
280{
281 (void)uc;
282 (void)size;
283 const uint64_t off = addr - (uint64_t)k_pdm_base;
284 if (off >= (uint64_t)k_pdm_span) {
285 return;
286 }
287 s_pdm_reg[off >> 2U] = (uint32_t)value;
288
289 if (off == (uint64_t)k_pdm_off_cstrtr) {
290 for (uint32_t i = 0U; i < (uint32_t)k_pdm_ch_count; i++) {
291 if (((uint32_t)value & (1U << i)) != 0U) {
292 s_pdm[i].running = true;
293 }
294 }
295 return;
296 }
297 if (off == (uint64_t)k_pdm_off_cstptr) {
298 for (uint32_t i = 0U; i < (uint32_t)k_pdm_ch_count; i++) {
299 if (((uint32_t)value & (1U << i)) != 0U) {
300 s_pdm[i].running = false;
301 }
302 }
303 return;
304 }
305 uint32_t ch = 0U;
306 uint64_t ch_off = 0U;
307 if (internal_pdm_channel_at(off, &ch, &ch_off) && (ch_off == (uint64_t)k_pdm_ch_pddrcr)) {
308 s_pdm[ch].read_enable = (((uint32_t)value & (uint32_t)k_pdm_pddrcr_datre) != 0U);
309 }
310}
311
321{
322 for (uint32_t i = 0U; i < (uint32_t)k_pdm_ch_count; i++) {
323 s_pdm[i] = (pdm_channel_t){};
324 }
325 for (uint32_t i = 0U; i < (uint32_t)k_pdm_words; i++) {
326 s_pdm_reg[i] = 0U;
327 }
328}
329
339{
340 for (uint32_t i = 0U; i < (uint32_t)k_pdm_ch_count; i++) {
341 if (s_pdm[i].samples == 0U) {
342 continue;
343 }
344 (void)priv_emu_io_errf(" PDM ch%u : samples=%u run=%s read_en=%s (synth tone)\n",
345 i,
346 s_pdm[i].samples,
347 s_pdm[i].running ? "yes" : "no",
348 s_pdm[i].read_enable ? "yes" : "no");
349 }
350}
351
354 .base = (uint64_t)k_pdm_base,
355 .span = (uint64_t)k_pdm_span,
356 .order = (uint32_t)k_block_order_i2c, /* Pull model (read-driven); no tick. */
357 .read = internal_pdm_read,
359 .tick = nullptr,
360 .reset = internal_pdm_reset,
361 .report = internal_pdm_report,
362 .name = "PDM-IF",
363};
364
366[[gnu::constructor]] RA8_INTERNAL static void internal_board_periph_pdm_register(void)
367{
369}
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_i2s
SSIE / I2S sample-block summaries.
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.
@ k_block_order_i2c
I3C/I2C + GT911 (no tick today).
static const board_periph_block_t s_k_pdm_block
This block's descriptor (static lifetime; the core keeps the pointer).
static RA8_INTERNAL uint32_t internal_pdm_next_sample(uint32_t ch)
Next 20-bit two's-complement PCM sample for a live channel.
static RA8_INTERNAL void internal_board_periph_pdm_register(void)
Self-register the PDM-IF block before main runs (decentralized).
static RA8_INTERNAL void internal_pdm_reset(void)
Clear all PDM channel + shadow state to power-on.
pdm_field_t
PDM-IF register field masks / bits (ra8_pdm_regs.h).
@ k_pdm_pddsr_num
PDDSR[7:0] FIFO fill count field.
@ k_pdm_pddrr_mask20
PDDRR[19:0] 20-bit two's-complement.
@ k_pdm_fifo_depth
Samples reported available when live.
@ k_pdm_pddrcr_datre
PDDRCR[0] data-read enable.
static RA8_INTERNAL uint64_t internal_pdm_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read inside the PDM window.
static uint32_t s_pdm_reg[k_pdm_words]
Reflect-on-read shadow.
pdm_dither_t
Per-channel pseudo-random dither (window-to-window variety).
@ k_pdm_lcg_add
LCG increment (Numerical Recipes).
@ k_pdm_lcg_shift
Take the high bits (better spread).
@ k_pdm_lcg_mul
LCG multiplier (Numerical Recipes).
@ k_pdm_dither_mask
10-bit dither window (0..1023).
@ k_pdm_dither_half
Centre the dither at 0 (+-512).
static RA8_INTERNAL void internal_pdm_console_maybe(uint32_t ch)
Push an anti-flood "PDMn samples=.." line to the I2S/audio tab.
static RA8_INTERNAL bool internal_pdm_live(uint32_t ch)
True when channel ch is both started and read-enabled.
static RA8_INTERNAL void internal_pdm_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write inside the PDM window: latch, then update run/enable gates.
static RA8_INTERNAL bool internal_pdm_channel_at(uint64_t off, uint32_t *ch, uint64_t *ch_off)
Compute the channel index + in-bank offset for an in-window address.
static pdm_channel_t s_pdm[k_pdm_ch_count]
pdm_amp_t
Synthetic-tone amplitude / slope (20-bit signed, well below +-524288).
@ k_pdm_tone_amp
Triangle peak amplitude (+-).
@ k_pdm_tone_step
Per-sample slope = 2*amp / (period/2).
static RA8_INTERNAL uint32_t internal_pdm_read_csr(void)
PDCSR: one run bit per currently-started channel.
pdm_map_t
PDM-IF register-window geometry (ra8_pdm_regs.h).
@ k_pdm_base
PDM-IF block base (HUM Ch 49).
@ k_pdm_ch_pddrcr
CHn PDDRCR data-read control.
@ k_pdm_ch_pddsr
CHn PDDSR data status (fill count).
@ k_pdm_ch_stride
Bytes between channel banks.
@ k_pdm_off_cstptr
PDCSTPTR common stop trigger.
@ k_pdm_ch_count
PDM channels (CH0..CH2).
@ k_pdm_span
Common bank + 3 channel banks.
@ k_pdm_off_csr
PDCSR common channel status.
@ k_pdm_off_cstrtr
PDCSTRTR common start trigger.
@ k_pdm_ch_pddrr
CHn PDDRR 20-bit PCM data read.
@ k_pdm_words
Backing-store word count.
@ k_pdm_ch0_off
Offset of the first channel bank.
pdm_tone_t
Synthetic-tone shape constants (deterministic, integer-only).
@ k_pdm_tone_period
Samples per triangle cycle (power of two).
@ k_pdm_tone_half
period/2: rising-then-falling split point.
@ k_pdm_tone_qmask
period-1: phase wrap mask.
pdm_console_t
Console anti-flood cadence for the PDM (I2S/audio) tab.
@ k_pdm_console_line_cap
Max chars in a "PDM.. samples=.." line.
@ k_pdm_console_every
Push one line per N samples per channel.
static RA8_INTERNAL void internal_pdm_report(void)
Print one line per PDM channel the firmware captured from.
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.
-proof
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
A modelled peripheral block's self-description for the core registry.
One modelled PDM channel: run/enable gates + synthetic-tone state.
bool read_enable
PDDRCR.DATRE armed the data-read path.
uint32_t samples
Total PDDRR reads served (report + console).
uint32_t phase
Triangle-tone sample phase (advances on read).
bool running
A PDCSTRTR bit started this channel.
uint32_t lcg
Per-channel dither LCG state.