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

RA8P1 Ethos-U55 inference runtime smoke – quantize + IRQ + TFLite-micro. More...

#include <stdint.h>
#include "ra8_attributes.h"
#include "ra8_board_ek_ra8d2.h"
#include "ra8_boot_entry.h"
#include "ra8_cgc.h"
#include "ra8_device.h"
#include "ra8_err.h"
#include "ra8_ethosu_kernel.h"
#include "ra8_isr.h"
#include "ra8_npu.h"
#include "ra8_npu_fake_cmd.h"
#include "ra8_npu_quant.h"
#include "ra8_npu_regs.h"
Include dependency graph for main.c:

Go to the source code of this file.

Enumerations

enum  npu_infer_size_t : uint32_t {
  k_npu_infer_baud = 115200U ,
  k_npu_infer_arena_bytes = 32U ,
  k_npu_infer_cmd_words = k_ra8_npu_fake_word_num ,
  k_npu_infer_hex_digits = 8U ,
  k_npu_infer_hex_shift = 4U ,
  k_npu_infer_hex_mask = 0x0FU
}
 Command-stream / tensor-arena sizes and the console baud. More...
enum  npu_infer_region_t : uint8_t {
  k_npu_infer_region_weights = 0U ,
  k_npu_infer_region_input = 1U ,
  k_npu_infer_region_output = 2U ,
  k_npu_infer_region_count = 3U
}
 Tensor-region indices the job programs into BASEPn. More...
enum  npu_infer_quant_t : int32_t {
  k_npu_infer_zero_point = 4 ,
  k_npu_infer_addk = 0x10 ,
  k_npu_infer_seed_mask = 15 ,
  k_npu_infer_byte_mask = 0xFF
}
 Deterministic quantization parameters + the add-constant the op applies. More...
enum  npu_infer_fnv_t : uint32_t {
  k_npu_infer_fnv_offset = 0x811C9DC5U ,
  k_npu_infer_fnv_prime = 0x01000193U
}
 FNV-1a 32-bit constants for the displayed output checkword. More...

Functions

static void internal_npu_infer_panic_halt (void)
 Park the CPU forever in WFI after a fatal init error (a real panic).
static void internal_npu_infer_park (void)
 Park the CPU forever in WFI after the verdict banner (a clean stop).
static void internal_npu_infer_setup_or_halt (void)
 Bring up CGC, the SCI8 console, and the ra8_isr substrate.
static void internal_npu_infer_build_stream (void)
 Fill the command stream (add-constant op) per the stand-in convention.
static ra8_err_t internal_npu_infer_prepare_input (void)
 Seed the float input, quantize it to UINT8, and zero the output arenas.
static ra8_err_t internal_npu_infer_run_job_irq (void)
 Submit the job, arm the IRQ latch, kick it, and await the NPU interrupt.
static bool internal_npu_infer_verify (uint32_t *out_check)
 Verify quantize + NPU op + dequantize, folding the output to a checkword.
static void internal_npu_infer_write (const uint8_t *data, size_t len)
static void internal_npu_infer_write_hex32 (uint32_t value)
static void internal_npu_infer_write_status (bool ok)
static void internal_npu_infer_write_verdict (bool pass)
static void internal_npu_infer_emit (uint32_t id, bool tflm_ok, bool run_ok, uint32_t check, bool pass)
 Print the one-line verdict banner over the SCI8 console.
static bool internal_npu_infer_execute (uint32_t *out_id, bool *out_tflm, uint32_t *out_check)
 Bring up the NPU, register its completion IRQ, and run one inference.
void main (void)
 Application entry: quantize + NPU op (IRQ) + dequantize, print verdict.

Variables

static const float s_infer_scale = 0.5F
 Quantization scale (const float; enums cannot carry a float).
static const float s_infer_deq_tol = 0.001F
 Absolute tolerance for the dequantized float comparison.
static uint32_t s_npu_cmd_stream [k_npu_infer_cmd_words]
 Stand-in command stream in SRAM (ra8_npu_fake_cmd.h layout; add-constant op).
static uint8_t s_npu_weights [k_npu_infer_arena_bytes]
 Region 0 weight arena (present by Vela convention; unused by this op).
static float s_infer_input_f [k_npu_infer_arena_bytes]
 Float input arena, seeded with a deterministic pattern before quantize.
static uint8_t s_npu_input [k_npu_infer_arena_bytes]
 Region 1 UINT8 input tensor (quantized s_infer_input_f).
static uint8_t s_npu_output [k_npu_infer_arena_bytes]
 Region 2 UINT8 output tensor: zeroed pre-run, holds the NPU result.
static float s_infer_output_f [k_npu_infer_arena_bytes]
 Float output arena, dequantized from s_npu_output after the run.
volatile uint32_t g_npu_infer_pass = 0U
 Final verdict (1 = PASS, 0 = FAIL), for external (J-Link) inspection.

Detailed Description

RA8P1 Ethos-U55 inference runtime smoke – quantize + IRQ + TFLite-micro.

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

The inference-runtime companion to examples/ra8p1_foundation/npu_smoke (which exercises the bare ra8_npu command/queue driver). This app drives the pieces a real TensorFlow Lite for Microcontrollers Ethos-U runtime needs on top of the driver, and CHECKS each end-to-end on the RA8P1 profile:

  1. Input/output quantization (ra8_npu_quant): a float input arena is quantized to the UINT8 tensor the NPU reads, and the UINT8 result is dequantized back to float.
  2. IRQ-driven completion (ra8_npu_irq_arm / ra8_npu_wait_irq + ra8_npu_irq_handler routed through ra8_isr): the job is awaited on the NPU interrupt (ra8_npu_clear_irq from the ISR), not a busy-wait.
  3. TFLite-micro Ethos-U operator wiring (ra8_ethosu_kernel_available): asserts the real first-party tflite::Register_ETHOSU() is linked and registered (the vendored stub returned nullptr). This app links the vendored TFLite-micro runtime (USES tflite_micro).

The NPU "operator" run under ra8_emulator is the tiny deterministic add-constant of the documented ra8_npu_fake_cmd.h convention (NOT a real Vela command stream, which needs the offline Vela compiler + silicon – a follow-up). So the quantize -> NPU op -> dequantize pipeline is checkable byte-for-byte in the emulator, while the TFLite-micro MicroInterpreter model-driven path is proven to LINK + REGISTER here and exercised end-to-end on silicon later. The verdict is printed over the SCI8 console:

npu-infer: id=0x10060000 tflm=OK quant=OK irq=OK out=0x........ verdict=PASS
Architectural ring
See docs/RING_AND_WORLD.md for what [Ring 6 / APP] {World: S} means – application-layer code that runs in the Secure world.
Author
Brighton Sikarskie
Date
2026-07-15
Since
0.1.0

Definition in file main.c.

Enumeration Type Documentation

◆ npu_infer_fnv_t

enum npu_infer_fnv_t : uint32_t

FNV-1a 32-bit constants for the displayed output checkword.

Enumerator
k_npu_infer_fnv_offset 

FNV-1a 32-bit offset basis.

k_npu_infer_fnv_prime 

FNV-1a 32-bit prime.

Definition at line 111 of file main.c.

◆ npu_infer_quant_t

enum npu_infer_quant_t : int32_t

Deterministic quantization parameters + the add-constant the op applies.

Enumerator
k_npu_infer_zero_point 

UINT8 zero-point (non-zero to exercise it).

k_npu_infer_addk 

Constant the NPU add-const op adds (16).

k_npu_infer_seed_mask 

Input pattern wrap: in_f[i] uses (i & 15).

k_npu_infer_byte_mask 

8-bit element wrap (matches the op).

Definition at line 100 of file main.c.

◆ npu_infer_region_t

enum npu_infer_region_t : uint8_t

Tensor-region indices the job programs into BASEPn.

Enumerator
k_npu_infer_region_weights 

Region 0: Vela weight arena (unused by op).

k_npu_infer_region_input 

Region 1: op source (quantized input).

k_npu_infer_region_output 

Region 2: op destination (quantized output).

k_npu_infer_region_count 

Regions programmed (BASEP0..2).

Definition at line 89 of file main.c.

◆ npu_infer_size_t

enum npu_infer_size_t : uint32_t

Command-stream / tensor-arena sizes and the console baud.

Enumerator
k_npu_infer_baud 

SCI8 J-Link OB console baud.

k_npu_infer_arena_bytes 

Tensor-arena length (bytes).

k_npu_infer_cmd_words 

Command-stream word count.

k_npu_infer_hex_digits 

Fixed-width hex word digits.

k_npu_infer_hex_shift 

Bits per hex nibble.

k_npu_infer_hex_mask 

Mask for one hex nibble.

Definition at line 76 of file main.c.

Function Documentation

◆ internal_npu_infer_build_stream()

void internal_npu_infer_build_stream ( void )
static

Fill the command stream (add-constant op) per the stand-in convention.

Writes the fixed opcode, region indexes, byte count, and addend used by the deterministic inference-path exercise.

Precondition
s_npu_cmd_stream has k_ra8_npu_fake_word_num words.
No NPU job is concurrently reading the command stream.
Postcondition
s_npu_cmd_stream describes an add-constant of region 1 -> region 2.
Words outside the defined fake command header remain untouched.
Note
This stand-in stream validates orchestration rather than a trained model.
Since
0.1.0

Definition at line 278 of file main.c.

References k_npu_infer_addk, k_npu_infer_arena_bytes, k_npu_infer_region_input, k_npu_infer_region_output, k_ra8_npu_fake_magic, k_ra8_npu_fake_op_addk, k_ra8_npu_fake_word_const, k_ra8_npu_fake_word_count, k_ra8_npu_fake_word_dst, k_ra8_npu_fake_word_op, k_ra8_npu_fake_word_src, RA8_INTERNAL, and s_npu_cmd_stream.

Referenced by internal_npu_infer_execute().

◆ internal_npu_infer_emit()

void internal_npu_infer_emit ( uint32_t id,
bool tflm_ok,
bool run_ok,
uint32_t check,
bool pass )
static

Print the one-line verdict banner over the SCI8 console.

Emits fixed text fragments and fixed-width hexadecimal fields directly, avoiding the libc snprintf implementation unavailable to the freestanding target.

Parameters
[in]idNPU_ID read after init.
[in]tflm_okWhether the real TFLite-micro Ethos-U op is registered.
[in]run_okWhether the submit/arm/run/wait-irq sequence returned ok.
[in]checkOutput checkword from internal_npu_infer_verify().
[in]passFinal verdict (all stages matched).
Precondition
The SCI8 console is initialised.
The supplied flags and checkword are final snapshots for this run.
Postcondition
One banner line has been written and flushed to the console.
File-scope verdict globals and NPU arenas remain unchanged.
Note
Individual diagnostic writes are best-effort; the final flush keeps the complete line observable by the HIL scraper.
Since
0.1.0

Definition at line 452 of file main.c.

References internal_npu_infer_write(), internal_npu_infer_write_hex32(), internal_npu_infer_write_status(), internal_npu_infer_write_verdict(), and ra8_board_uart_console_flush().

Referenced by main().

◆ internal_npu_infer_execute()

bool internal_npu_infer_execute ( uint32_t * out_id,
bool * out_tflm,
uint32_t * out_check )
static

Bring up the NPU, register its completion IRQ, and run one inference.

Initializes the device, publishes its identity, registers the IRQ, prepares static regions, executes the job, dequantizes, and verifies it.

Parameters
[out]out_idNPU_ID captured after init.
[out]out_tflmWhether the real TFLite-micro Ethos-U op is registered.
[out]out_checkOutput checkword (valid only when the return is true).
Returns
true when init + IRQ registration + the quantize/op/dequantize pipeline all succeeded and every stage matched its expectation.
Return values
trueAll initialization, execution, and verification stages passed.
falseThe first failing stage stopped the remaining pipeline.
Precondition
The console + ra8_isr substrate are up (internal_npu_infer_setup_or_halt ran).
out_id / out_tflm / out_check are non-NULL.
Postcondition
*out_id / *out_tflm / *out_check reflect the run.
No second NPU job remains queued after this synchronous execution.
Note
Output values are initialized before any fallible operation for diagnostics.
Since
0.1.0

Definition at line 489 of file main.c.

References internal_npu_infer_build_stream(), internal_npu_infer_prepare_input(), internal_npu_infer_run_job_irq(), internal_npu_infer_verify(), k_npu_infer_arena_bytes, k_npu_infer_zero_point, k_ra8_isr_prio_default, k_ra8_npu_event_irq, k_ra8_ok, ra8_isr_globals_enable(), ra8_isr_register(), ra8_npu_dequantize_u8(), ra8_npu_init(), ra8_npu_irq_handler(), ra8_npu_read_id(), s_infer_output_f, s_infer_scale, and s_npu_output.

Referenced by main().

◆ internal_npu_infer_panic_halt()

void internal_npu_infer_panic_halt ( void )
static

Park the CPU forever in WFI after a fatal init error (a real panic).

Reserved for failures BEFORE a verdict can be emitted (CGC or SCI8 did not come up), so a ra8_emulator gate scanning for a *panic_halt terminal PC correctly reads this as a failed run.

Precondition
Called only after a fatal init error, before the verdict banner.
No initialized diagnostic path can report a normal verdict.
Postcondition
CPU is parked; only a debugger or reset wakes it.
No NPU job or console operation is attempted after entry.
Note
The distinct panic symbol lets emulator automation classify boot failure.
Since
0.1.0

Definition at line 212 of file main.c.

References RA8_INTERNAL.

Referenced by internal_npu_infer_setup_or_halt().

◆ internal_npu_infer_park()

void internal_npu_infer_park ( void )
static

Park the CPU forever in WFI after the verdict banner (a clean stop).

Distinct from internal_npu_infer_panic_halt: a run that reached the verdict parks here, whose name deliberately does NOT match the *panic_halt pattern a ra8_emulator gate treats as a give-up.

Precondition
The verdict banner has been emitted over the SCI8 console.
All run-result globals contain their final values.
Postcondition
CPU is parked; only a debugger or reset wakes it.
The published verdict and checkword remain stable for inspection.
Note
This clean terminal is intentionally distinct from the panic path.
Since
0.1.0

Definition at line 233 of file main.c.

References RA8_INTERNAL.

Referenced by main().

◆ internal_npu_infer_prepare_input()

ra8_err_t internal_npu_infer_prepare_input ( void )
static

Seed the float input, quantize it to UINT8, and zero the output arenas.

Builds a deterministic float pattern, clears stale output bytes, and invokes the production quantizer with the configured scale and zero point.

Returns
ra8_err_t from ra8_npu_quantize_u8(), else k_ra8_ok.
Return values
k_ra8_okThe input arena holds the quantized pattern.
Precondition
The arenas are k_npu_infer_arena_bytes long.
The input and output arenas are exclusively owned by this test run.
Postcondition
s_npu_input holds quantize_u8(s_infer_input_f); s_npu_output is zero.
On quantizer error no NPU submission occurs in the caller.
Note
The fixed pattern makes host and hardware checkwords reproducible.
Since
0.1.0

Definition at line 303 of file main.c.

References k_npu_infer_arena_bytes, k_npu_infer_seed_mask, k_npu_infer_zero_point, RA8_INTERNAL, ra8_npu_quantize_u8(), s_infer_input_f, s_infer_scale, s_npu_input, and s_npu_output.

Referenced by internal_npu_infer_execute().

◆ internal_npu_infer_run_job_irq()

ra8_err_t internal_npu_infer_run_job_irq ( void )
static

Submit the job, arm the IRQ latch, kick it, and await the NPU interrupt.

Constructs a job over the static regions, submits it, arms completion, starts execution, and returns the first driver error or IRQ-wait result.

Returns
ra8_err_t from the first failing driver call, else k_ra8_ok.
Return values
k_ra8_okJob completed via the interrupt path (STATUS.cmd_end latched).
Precondition
The command stream + input arena are populated and the NPU IRQ is routed.
The NPU has been initialized and no earlier job is active.
Postcondition
On k_ra8_ok the output arena holds the NPU result.
On failure no later submission stage is attempted by this helper.
Note
Region storage remains caller-owned throughout the synchronous wait.
Since
0.1.0

Definition at line 331 of file main.c.

References ra8_npu_job_t::cmd_stream, ra8_npu_job_t::cmd_stream_bytes, k_npu_infer_region_count, k_npu_infer_region_input, k_npu_infer_region_output, k_npu_infer_region_weights, k_ra8_ok, RA8_INTERNAL, ra8_npu_irq_arm(), ra8_npu_run(), ra8_npu_submit(), ra8_npu_wait_irq(), ra8_npu_job_t::region_base, ra8_npu_job_t::region_count, s_npu_cmd_stream, s_npu_input, s_npu_output, and s_npu_weights.

Referenced by internal_npu_infer_execute().

◆ internal_npu_infer_setup_or_halt()

void internal_npu_infer_setup_or_halt ( void )
static

Bring up CGC, the SCI8 console, and the ra8_isr substrate.

Halts on error.

The NPU needs no clock beyond the CGC default NPUCLK; the console (PCLKA/SCICLK) prints the verdict and ra8_isr routes the NPU IRQ.

Precondition
Reset_Handler has initialised .data / .bss.
The application is still in single-threaded boot context.
Postcondition
On return CGC + SCI8 console + ra8_isr table are ready.
Any mandatory initialization error transfers to the panic halt.
Note
NPU initialization is deferred until the executable test routine.
Since
0.1.0

Definition at line 253 of file main.c.

References internal_npu_infer_panic_halt(), k_npu_infer_baud, k_ra8_ok, ra8_board_uart_console_init(), ra8_cgc_init(), RA8_INTERNAL, and ra8_isr_init().

Referenced by main().

◆ internal_npu_infer_verify()

bool internal_npu_infer_verify ( uint32_t * out_check)
static

Verify quantize + NPU op + dequantize, folding the output to a checkword.

Checks each pipeline stage against its deterministic expectation and accumulates an FNV-1a digest even when one or more samples mismatch.

Parameters
[out]out_checkFNV-1a digest of the output bytes (for the banner).
Returns
true when every stage matches its deterministic expectation.
Return values
trueQuantized input, NPU output, and dequantized values all match.
falseAt least one stage differs from its expected value.
Precondition
out_check is non-NULL; the job has completed and been dequantized.
All input, output, and float arenas contain the current run's data.
Postcondition
*out_check holds the output digest regardless of the verdict.
Verification does not modify any inference arena.
Note
Float comparison uses the configured absolute tolerance.
Since
0.1.0

Definition at line 373 of file main.c.

References k_npu_infer_addk, k_npu_infer_arena_bytes, k_npu_infer_byte_mask, k_npu_infer_fnv_offset, k_npu_infer_fnv_prime, k_npu_infer_seed_mask, k_npu_infer_zero_point, RA8_INTERNAL, s_infer_deq_tol, s_infer_output_f, s_infer_scale, s_npu_input, and s_npu_output.

Referenced by internal_npu_infer_execute().

◆ internal_npu_infer_write()

void internal_npu_infer_write ( const uint8_t * data,
size_t len )
static

◆ internal_npu_infer_write_hex32()

void internal_npu_infer_write_hex32 ( uint32_t value)
static

◆ internal_npu_infer_write_status()

void internal_npu_infer_write_status ( bool ok)
static

Definition at line 418 of file main.c.

References internal_npu_infer_write(), and RA8_INTERNAL.

Referenced by internal_npu_infer_emit().

◆ internal_npu_infer_write_verdict()

void internal_npu_infer_write_verdict ( bool pass)
static

Definition at line 425 of file main.c.

References internal_npu_infer_write(), and RA8_INTERNAL.

Referenced by internal_npu_infer_emit().

◆ main()

void main ( void )

Application entry: quantize + NPU op (IRQ) + dequantize, print verdict.

The application entry point Reset_Handler hands control to.

Precondition
Reset_Handler has copied .data and zeroed .bss.
Postcondition
The verdict banner is on the console; the CPU is parked in WFI.
Since
0.1.0

Definition at line 534 of file main.c.

References g_npu_infer_pass, internal_npu_infer_emit(), internal_npu_infer_execute(), internal_npu_infer_park(), and internal_npu_infer_setup_or_halt().

Variable Documentation

◆ g_npu_infer_pass

volatile uint32_t g_npu_infer_pass = 0U

Final verdict (1 = PASS, 0 = FAIL), for external (J-Link) inspection.

volatile + non-static so a memprobe can read the verdict.

Note
Read externally only.
Since
0.1.0

Definition at line 196 of file main.c.

Referenced by main().

◆ s_infer_deq_tol

const float s_infer_deq_tol = 0.001F
static

Absolute tolerance for the dequantized float comparison.

The fixture is integer-exact, but a small epsilon guards the float equality against any FPU rounding.

Note
Read-only; never modified.
Since
0.1.0

Definition at line 133 of file main.c.

Referenced by internal_npu_infer_verify().

◆ s_infer_input_f

float s_infer_input_f[k_npu_infer_arena_bytes]
static

Float input arena, seeded with a deterministic pattern before quantize.

Quantized into s_npu_input by ra8_npu_quantize_u8().

Note
Seeded by npu_infer_seed().
Since
0.1.0

Definition at line 160 of file main.c.

Referenced by internal_npu_infer_prepare_input().

◆ s_infer_output_f

float s_infer_output_f[k_npu_infer_arena_bytes]
static

Float output arena, dequantized from s_npu_output after the run.

Checked against scale*(byte - zero_point) for the dequantize path.

Note
Written by ra8_npu_dequantize_u8().
Since
0.1.0

Definition at line 187 of file main.c.

Referenced by internal_npu_infer_execute(), and internal_npu_infer_verify().

◆ s_infer_scale

const float s_infer_scale = 0.5F
static

Quantization scale (const float; enums cannot carry a float).

in_f[i] = scale * (i & 15), so quantize is exact for this fixture.

Note
Read-only; never modified.
Since
0.1.0

Definition at line 123 of file main.c.

Referenced by internal_npu_infer_execute(), internal_npu_infer_prepare_input(), and internal_npu_infer_verify().

◆ s_npu_cmd_stream

uint32_t s_npu_cmd_stream[k_npu_infer_cmd_words]
static

Stand-in command stream in SRAM (ra8_npu_fake_cmd.h layout; add-constant op).

QBASE/QSIZE point the NPU at this; the ra8_emulator model decodes it.

Note
Not a real Vela program – see the file header.
Since
0.1.0

Definition at line 142 of file main.c.

Referenced by internal_npu_infer_build_stream(), internal_npu_infer_run_job_irq(), internal_npu_smoke_build_stream(), and internal_npu_smoke_run_job().

◆ s_npu_input

uint8_t s_npu_input[k_npu_infer_arena_bytes]
static

Region 1 UINT8 input tensor (quantized s_infer_input_f).

The NPU (fake) reads this and writes input+K to the output arena.

Note
Written by ra8_npu_quantize_u8().
Since
0.1.0

Definition at line 169 of file main.c.

Referenced by internal_npu_infer_prepare_input(), internal_npu_infer_run_job_irq(), internal_npu_infer_verify(), internal_npu_smoke_run_job(), internal_npu_smoke_seed_arenas(), and internal_npu_smoke_verify().

◆ s_npu_output

uint8_t s_npu_output[k_npu_infer_arena_bytes]
static

Region 2 UINT8 output tensor: zeroed pre-run, holds the NPU result.

Dequantized into s_infer_output_f and verified byte-for-byte.

Note
Written by the NPU (fake), read back by the app.
Since
0.1.0

Definition at line 178 of file main.c.

Referenced by internal_npu_infer_execute(), internal_npu_infer_prepare_input(), internal_npu_infer_run_job_irq(), internal_npu_infer_verify(), internal_npu_smoke_run_job(), internal_npu_smoke_seed_arenas(), and internal_npu_smoke_verify().

◆ s_npu_weights

uint8_t s_npu_weights[k_npu_infer_arena_bytes]
static

Region 0 weight arena (present by Vela convention; unused by this op).

Programmed into BASEP0 so the region layout matches a real job.

Note
Contents irrelevant to the add-constant op.
Since
0.1.0

Definition at line 151 of file main.c.

Referenced by internal_npu_infer_run_job_irq(), and internal_npu_smoke_run_job().