ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_npu.c
Go to the documentation of this file.
1
55
56#include <stdint.h>
57#include <stdio.h>
58
59#include "board_console.h"
60#include "board_periph_block.h"
62#include "ra8_npu_fake_cmd.h"
63
71typedef enum : uint64_t {
72 k_npu_base = 0x40140000UL,
73 k_npu_span = 0x1000UL,
74 k_npu_words = 0x1000UL / 4UL,
75 k_npu_off_id = 0x0000UL,
76 k_npu_off_status = 0x0004UL,
77 k_npu_off_cmd = 0x0008UL,
78 k_npu_off_reset = 0x000CUL,
79 k_npu_off_qbase = 0x0010UL,
80 k_npu_off_qsize = 0x0020UL,
81 k_npu_off_basep0 = 0x0080UL,
82} npu_map_t;
83
87typedef enum : uint32_t {
88 k_npu_reg_hi_off = 0x0004U,
93
111
126
141
148typedef enum : uint32_t {
151} npu_misc_t;
152
160typedef enum : uint32_t {
163 k_npu_fnv_offset = 0x811C9DC5U,
164 k_npu_fnv_prime = 0x01000193U,
167
171typedef enum : uint8_t {
175} npu_dec_t;
176
180typedef struct {
181 uint32_t op;
182 uint32_t src;
183 uint32_t dst;
184 uint32_t count;
185 uint32_t konst;
186} npu_cmd_t;
187
191typedef struct {
192 uint32_t reg[k_npu_words];
193 uint32_t status;
194 uint32_t jobs;
195 uint32_t faults;
196 uint32_t last_op;
197 uint32_t last_bytes;
198 uint32_t last_check;
199 uint32_t reads;
200 uint32_t writes;
202
204
206static const uint32_t s_npu_id =
207 ((uint32_t)k_npu_id_arch_major << (uint32_t)k_npu_id_arch_major_pos) |
208 ((uint32_t)k_npu_id_arch_minor << (uint32_t)k_npu_id_arch_minor_pos) |
209 ((uint32_t)k_npu_id_arch_patch << (uint32_t)k_npu_id_arch_patch_pos) |
210 ((uint32_t)k_npu_id_product_u55 << (uint32_t)k_npu_id_product_pos);
211
223RA8_INTERNAL static uint32_t internal_npu_word(uint64_t off)
224{
225 return (uint32_t)(off >> 2U);
226}
227
239RA8_INTERNAL static uint64_t internal_npu_reg64(uint64_t lo_off)
240{
241 const uint32_t lo = s_npu.reg[internal_npu_word(lo_off)];
242 const uint32_t hi = s_npu.reg[internal_npu_word(lo_off + (uint64_t)k_npu_reg_hi_off)];
243 return (uint64_t)lo | ((uint64_t)hi << (uint32_t)k_npu_addr_hi_pos);
244}
245
257RA8_INTERNAL static uint64_t internal_npu_region_base(uint32_t idx)
258{
259 const uint64_t lo_off =
260 (uint64_t)k_npu_off_basep0 + ((uint64_t)idx * (uint64_t)k_npu_basep_stride);
261 return internal_npu_reg64(lo_off);
262}
263
277RA8_INTERNAL static bool internal_npu_guest_word(uc_engine* uc, uint64_t addr, uint32_t* out)
278{
279 uint8_t b[4] = {};
280 if (emu_mem_read(uc, addr, b, sizeof(b)) != UC_ERR_OK) {
281 return false;
282 }
283 /* Little-endian assembly over the fixed 4-byte buffer -- the loop keeps the
284 * byte shift a named multiple (i * 8U) instead of a bare 24U high-byte
285 * literal, and is statically bounded by sizeof(b) (NASA P10 Rule 2). */
286 *out = 0U;
287 for (uint32_t i = 0U; i < sizeof(b); ++i) {
288 *out |= (uint32_t)b[i] << (i * 8U);
289 }
290 return true;
291}
292
302RA8_INTERNAL static void internal_npu_fault(uint32_t status_bit)
303{
304 s_npu.status = ((uint32_t)1U << status_bit) | ((uint32_t)1U << (uint32_t)k_npu_status_irq_bit);
305 s_npu.faults++;
306}
307
321{
322 const uint64_t qbase = internal_npu_reg64((uint64_t)k_npu_off_qbase);
323 const uint32_t qsize = s_npu.reg[internal_npu_word((uint64_t)k_npu_off_qsize)];
324 if (qsize < (uint32_t)k_ra8_npu_fake_header_bytes) {
325 return k_npu_dec_parse;
326 }
327 uint32_t w[k_ra8_npu_fake_word_num] = {};
328 for (uint32_t i = 0U; i < (uint32_t)k_ra8_npu_fake_word_num; i++) {
329 if (!internal_npu_guest_word(uc, qbase + ((uint64_t)i * 4UL), &w[i])) {
330 return k_npu_dec_bus;
331 }
332 }
334 (uint32_t)k_ra8_npu_fake_magic) {
335 return k_npu_dec_parse; /* Real Vela stream / not ours: cannot interpret. */
336 }
342 const bool bad_region =
343 (out->src >= (uint32_t)k_npu_region_count) || (out->dst >= (uint32_t)k_npu_region_count);
344 const bool bad_count = (out->count == 0U) || (out->count > (uint32_t)k_ra8_npu_fake_max_bytes);
345 if (bad_region || bad_count) {
346 return k_npu_dec_parse;
347 }
348 return k_npu_dec_ok;
349}
350
366RA8_INTERNAL static bool internal_npu_apply(uc_engine* uc,
367 const npu_cmd_t* c,
368 uint64_t src,
369 uint64_t dst,
370 uint32_t* out_check)
371{
372 uint8_t buf[k_npu_chunk_bytes] = {};
373 uint32_t check = (uint32_t)k_npu_fnv_offset;
374 uint32_t done = 0U;
375 for (uint32_t chunk = 0U; chunk < (uint32_t)k_npu_max_chunks; chunk++) {
376 if (done >= c->count) {
377 break;
378 }
379 uint32_t n = c->count - done;
380 if (n > (uint32_t)k_npu_chunk_bytes) {
381 n = (uint32_t)k_npu_chunk_bytes;
382 }
383 if (emu_mem_read(uc, src + done, buf, n) != UC_ERR_OK) {
384 return false;
385 }
386 if (c->op == (uint32_t)k_ra8_npu_fake_op_addk) {
387 for (uint32_t i = 0U; i < n; i++) {
388 buf[i] = (uint8_t)((buf[i] + c->konst) & (uint32_t)k_ra8_npu_fake_byte_mask);
389 }
390 }
391 if (emu_mem_write(uc, dst + done, buf, n) != UC_ERR_OK) {
392 return false;
393 }
394 for (uint32_t i = 0U; i < n; i++) {
395 check = (check ^ (uint32_t)buf[i]) * (uint32_t)k_npu_fnv_prime;
396 }
397 done += n;
398 }
399 *out_check = check;
400 return true;
401}
402
404RA8_INTERNAL static const char* internal_npu_op_name(uint32_t op)
405{
406 if (op == (uint32_t)k_ra8_npu_fake_op_copy) {
407 return "copy";
408 }
409 if (op == (uint32_t)k_ra8_npu_fake_op_addk) {
410 return "add-const";
411 }
412 return "unknown";
413}
414
426{
427 char ln[k_npu_line_cap];
428 (void)snprintf(ln,
429 sizeof(ln),
430 "NPU %s r%u->r%u %uB check=0x%08X",
432 (unsigned)c->src,
433 (unsigned)c->dst,
434 (unsigned)c->count,
435 (unsigned)check);
437}
438
448RA8_INTERNAL static void internal_npu_execute(uc_engine* uc)
449{
450 npu_cmd_t c = {};
451 const npu_dec_t dec = internal_npu_decode(uc, &c);
452 if (dec == k_npu_dec_bus) {
454 return;
455 }
456 if (dec != k_npu_dec_ok) {
458 return;
459 }
460 const uint64_t src = internal_npu_region_base(c.src);
461 const uint64_t dst = internal_npu_region_base(c.dst);
462 if ((src == 0U) || (dst == 0U)) {
464 return;
465 }
466 uint32_t check = 0U;
467 if (!internal_npu_apply(uc, &c, src, dst, &check)) {
469 return;
470 }
471 s_npu.status = ((uint32_t)1U << (uint32_t)k_npu_status_cmdend_bit) |
472 ((uint32_t)1U << (uint32_t)k_npu_status_irq_bit);
473 s_npu.jobs++;
474 s_npu.last_op = c.op;
475 s_npu.last_bytes = c.count;
476 s_npu.last_check = check;
479}
480
491RA8_INTERNAL static void internal_npu_on_cmd(uc_engine* uc, uint32_t value)
492{
493 if ((value & ((uint32_t)1U << (uint32_t)k_npu_cmd_clear_irq_bit)) != 0U) {
494 s_npu.status &= ~((uint32_t)1U << (uint32_t)k_npu_status_irq_bit);
495 }
496 if ((value & ((uint32_t)1U << (uint32_t)k_npu_cmd_run_bit)) != 0U) {
498 }
499}
500
514RA8_INTERNAL static uint64_t internal_npu_read(uc_engine* uc, uint64_t addr, unsigned size)
515{
516 (void)uc;
517 (void)size;
518 const uint64_t off = addr - (uint64_t)k_npu_base;
519 if (off >= (uint64_t)k_npu_span) {
520 return 0U;
521 }
522 s_npu.reads++;
523 if (off == (uint64_t)k_npu_off_id) {
524 return (uint64_t)s_npu_id;
525 }
526 if (off == (uint64_t)k_npu_off_status) {
527 return (uint64_t)s_npu.status; /* STATUS.reset stays 0: reset is instant. */
528 }
529 return (uint64_t)s_npu.reg[internal_npu_word(off)];
530}
531
544RA8_INTERNAL static void
545internal_npu_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
546{
547 (void)size;
548 const uint64_t off = addr - (uint64_t)k_npu_base;
549 if (off >= (uint64_t)k_npu_span) {
550 return;
551 }
552 s_npu.writes++;
553 s_npu.reg[internal_npu_word(off)] = (uint32_t)value;
554 if (off == (uint64_t)k_npu_off_cmd) {
555 internal_npu_on_cmd(uc, (uint32_t)value);
556 return;
557 }
558 if (off == (uint64_t)k_npu_off_reset) {
559 s_npu.status = 0U; /* Reset discards job state; STATUS.reset reads 0. */
560 }
561}
562
572{
573 s_npu = (npu_state_t){};
574}
575
585{
586 if ((s_npu.jobs == 0U) && (s_npu.faults == 0U)) {
587 if ((s_npu.reads != 0U) || (s_npu.writes != 0U)) {
588 (void)priv_emu_io_errf(" NPU(Ethos-U55): window touched r=%u w=%u -- no job kicked\n",
589 s_npu.reads,
590 s_npu.writes);
591 }
592 return;
593 }
594 (void)priv_emu_io_errf(" NPU(Ethos-U55): jobs=%u faults=%u last=%s bytes=%u check=0x%08X"
595 " (ra8_emulator stand-in, not Vela)\n",
596 s_npu.jobs,
597 s_npu.faults,
599 s_npu.last_bytes,
600 s_npu.last_check);
601}
602
605 .base = (uint64_t)k_npu_base,
606 .span = (uint64_t)k_npu_span,
607 .order = (uint32_t)k_npu_block_order,
608 .read = internal_npu_read,
610 .tick = nullptr,
611 .reset = internal_npu_reset,
612 .report = internal_npu_report,
613 .name = "NPU",
614 .device = k_board_block_dev_ra8p1,
615};
616
618[[gnu::constructor]] RA8_INTERNAL static void internal_npu_block_register(void)
619{
621}
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_dma
DMAC + DTC transfer-complete summaries.
Decentralized peripheral-block registry for the board emulator core.
void board_periph_icu_raise_event(uc_engine *uc, uint16_t event)
Raise a peripheral ELC event through the core's ICU -> NVIC path.
void board_periph_register_block(const board_periph_block_t *block)
Register a peripheral block's descriptor with the core registry.
@ k_board_block_dev_ra8p1
RA8P1-only (Ethos-U55 NPU).
static RA8_INTERNAL uint64_t internal_npu_reg64(uint64_t lo_off)
Assemble the 64-bit value of a lo/hi shadow-register pair.
npu_misc_t
NPU interrupt event number and this block's report/tick order slot.
@ k_npu_event_irq
NPU_IRQ ELC/ICU event (RA8P1 elc_event_t).
@ k_npu_block_order
Report/tick order slot (relative only).
static RA8_INTERNAL bool internal_npu_apply(uc_engine *uc, const npu_cmd_t *c, uint64_t src, uint64_t dst, uint32_t *out_check)
Apply the op to the arenas; fold the output into a checkword (bool ok).
npu_bit_t
NPU_CMD / NPU_STATUS bit positions (mirror ra8_npu_regs.h).
@ k_npu_status_buserr_bit
STATUS.bus_error (AXI fault).
@ k_npu_cmd_run_bit
CMD.transition_to_running_state (start).
@ k_npu_status_reset_bit
STATUS.reset (reset in progress).
@ k_npu_status_irq_bit
STATUS.irq_raised.
@ k_npu_cmd_clear_irq_bit
CMD.clear_irq (write 1 to acknowledge).
@ k_npu_status_state_bit
STATUS.state (1 = running).
@ k_npu_status_cmdend_bit
STATUS.cmd_end (stream fully consumed).
@ k_npu_status_parse_bit
STATUS.cmd_parse (parse error).
static RA8_INTERNAL void internal_npu_reset(void)
Clear the NPU model to power-on state.
static RA8_INTERNAL bool internal_npu_guest_word(uc_engine *uc, uint64_t addr, uint32_t *out)
Read one little-endian 32-bit word from guest memory (bool ok).
static RA8_INTERNAL void internal_npu_fault(uint32_t status_bit)
Latch a STATUS fault bit and stop the engine (honest: no fake done).
static const uint32_t s_npu_id
Composited Ethos-U55 NPU_ID (arch 1.0.6, product 0 = U55).
static RA8_INTERNAL const char * internal_npu_op_name(uint32_t op)
Human label for a fake opcode (report / console).
npu_dec_t
Result of decoding one submitted command stream.
@ k_npu_dec_parse
Bad magic / field: real-Vela or malformed.
@ k_npu_dec_ok
Recognised fake program; fields valid.
@ k_npu_dec_bus
Guest-memory read of the stream failed.
npu_map_t
Ethos-U55 NPU register-window geometry (mirrors ra8_npu_regs.h).
@ k_npu_off_basep0
BASEP0[31:0] (region 0 AXI base).
@ k_npu_base
R_NPU register-window base (Ethos-U55).
@ k_npu_words
Backing-store word count.
@ k_npu_off_qbase
QBASE[31:0] (cmd-stream address).
@ k_npu_off_status
NPU_STATUS (state + fault flags).
@ k_npu_off_reset
NPU_RESET (software reset).
@ k_npu_off_qsize
QSIZE (cmd-stream length bytes).
@ k_npu_off_cmd
NPU_CMD (run / clear-irq).
@ k_npu_span
4 KiB register window.
@ k_npu_off_id
NPU_ID (Arm arch id/revision).
npu_geom_t
Ethos-U55 64-bit-register geometry + region count (mirrors ra8_npu_regs.h).
@ k_npu_basep_stride
Bytes between BASEPn pairs (lo + hi).
@ k_npu_region_count
BASEP0..7 region base-pointer pairs.
@ k_npu_addr_hi_pos
uint64 address -> high 32 bits.
@ k_npu_reg_hi_off
Low->high word gap in a 64-bit register.
npu_id_pos_t
NPU_ID field positions in the Ethos-U55 id register.
@ k_npu_id_arch_major_pos
id[31:28] arch_major_rev.
@ k_npu_id_product_pos
id[15:12] product_major (0 = U55).
@ k_npu_id_arch_patch_pos
id[19:16] arch_patch_rev.
@ k_npu_id_arch_minor_pos
id[27:20] arch_minor_rev.
static RA8_INTERNAL void internal_npu_execute(uc_engine *uc)
Run one submitted job: decode -> execute -> latch STATUS + IRQ.
npu_id_val_t
NPU_ID field values for the Ethos-U55.
@ k_npu_id_arch_minor
arch minor revision.
@ k_npu_id_product_u55
product_major: 0 = Ethos-U55.
@ k_npu_id_arch_patch
arch patch revision.
@ k_npu_id_arch_major
arch major revision.
static RA8_INTERNAL uint64_t internal_npu_region_base(uint32_t idx)
AXI base programmed into BASEPidx (0 if unprogrammed).
npu_exec_const_t
Stand-in "execution" bounds + checkword constants (deterministic).
@ k_npu_max_chunks
k_ra8_npu_fake_max_bytes / chunk (bound).
@ k_npu_fnv_prime
FNV-1a 32-bit prime.
@ k_npu_line_cap
Console line buffer cap.
@ k_npu_fnv_offset
FNV-1a 32-bit offset basis.
@ k_npu_chunk_bytes
Bytes moved per transfer-loop chunk.
static RA8_INTERNAL void internal_npu_report(void)
End-of-run NPU section: jobs run + last-result checkword, or touch notice.
static RA8_INTERNAL void internal_npu_block_register(void)
Register the NPU block before main (host constructor).
static RA8_INTERNAL uint32_t internal_npu_word(uint64_t off)
Word index of a byte offset inside the shadow store.
static RA8_INTERNAL uint64_t internal_npu_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read inside the NPU window: ID + STATUS are computed, rest shadow.
static RA8_INTERNAL void internal_npu_console_job(const npu_cmd_t *c, uint32_t check)
Push one completed-job summary line to the DMA/compute console lane.
static RA8_INTERNAL void internal_npu_on_cmd(uc_engine *uc, uint32_t value)
Act on an NPU_CMD write: run kicks a job, clear_irq de-asserts the IRQ.
static const board_periph_block_t s_k_npu_block
NPU block descriptor (RA8P1-only; self-registered with the core).
static npu_state_t s_npu
static RA8_INTERNAL npu_dec_t internal_npu_decode(uc_engine *uc, npu_cmd_t *out)
Decode the submitted command stream at QBASE per ra8_npu_fake_cmd.h.
static RA8_INTERNAL void internal_npu_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write inside the NPU window: latch, then act on CMD / RESET.
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.
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.
-copyright
-proof
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
ra8_emulator / host-test command-stream convention for the Ethos-U55 model
@ k_ra8_npu_fake_op_copy
dst[i] = src[i].
@ k_ra8_npu_fake_op_addk
dst[i] = (src[i] + constant) & 0xFF.
@ k_ra8_npu_fake_word_dst
Destination region index (0 .
@ k_ra8_npu_fake_word_count
Byte count to process.
@ k_ra8_npu_fake_word_num
Header word count (populated words).
@ k_ra8_npu_fake_word_src
Source region index (0 .
@ k_ra8_npu_fake_word_op
magic | opcode.
@ k_ra8_npu_fake_word_const
Constant addend (add-constant op).
@ k_ra8_npu_fake_byte_mask
8-bit element wrap (add-constant op).
@ k_ra8_npu_fake_max_bytes
Max processed bytes (bounds the loop).
@ k_ra8_npu_fake_header_bytes
Fixed header length in bytes (5 words).
@ k_ra8_npu_fake_magic_mask
Bits [31:16] select the magic marker.
@ k_ra8_npu_fake_magic
Emu-Ethos-U55 marker in bits [31:16].
@ k_ra8_npu_fake_op_mask
Bits [15:0] select the opcode.
A modelled peripheral block's self-description for the core registry.
One decoded fake command (see ra8_npu_fake_cmd.h).
uint32_t dst
Destination region index.
uint32_t count
Byte count to process.
uint32_t op
Opcode (COPY / add-constant).
uint32_t src
Source region index.
uint32_t konst
Constant addend (add-constant op).
NPU model state: register shadow + job tally for the end-of-run report.
uint32_t faults
Faulted kicks (report).
uint32_t last_check
Output checkword of the last completed job.
uint32_t reads
Reads observed inside the window.
uint32_t jobs
Jobs completed (report).
uint32_t status
Computed NPU_STATUS value.
uint32_t last_op
Opcode of the last completed job (report).
uint32_t writes
Writes observed inside the window.
uint32_t reg[k_npu_words]
Reflect-on-read shadow of raw register writes.
uint32_t last_bytes
Bytes moved by the last completed job.