ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_npu_loader.c
Go to the documentation of this file.
1
22
23#include "ra8_device.h"
24
25#ifdef RA8_HAS_NPU
26
27#include <stdint.h>
28
29#include "ra8_check.h"
30#include "ra8_err.h"
31#include "ra8_log.h"
32#include "ra8_npu.h"
33#include "ra8_npu_blob.h"
34#include "ra8_npu_loader.h"
35
43static const char* s_tag = "NPU-LOAD";
44
56typedef enum : uint32_t {
57 k_ra8_npu_loader_align_mask =
58 (uint32_t)k_ra8_npu_blob_arena_align - 1U,
59} ra8_npu_loader_const_t;
60
84static bool internal_npu_span_ok(uint32_t offset, uint32_t size, uint32_t limit)
85{
86 if (size > limit) {
87 return false;
88 }
89 if (offset > (limit - size)) {
90 return false;
91 }
92 return true;
93}
94
123static ra8_err_t internal_npu_check_header(const uint8_t* p,
124 uint32_t blob_bytes,
125 uint32_t* out_total,
126 uint32_t* out_rcount,
127 uint32_t* out_coff,
128 uint32_t* out_cbytes)
129{
130 if (blob_bytes < (uint32_t)k_ra8_npu_blob_header_bytes) {
131 ra8_log_error(s_tag, "load: buffer smaller than header");
133 }
135 (uint32_t)k_ra8_npu_blob_word_magic *
136 (uint32_t)k_ra8_npu_blob_word_bytes) !=
137 (uint32_t)k_ra8_npu_blob_magic) {
138 ra8_log_error(s_tag, "load: bad magic");
140 }
143 (uint32_t)k_ra8_npu_blob_word_bytes) !=
144 (uint32_t)k_ra8_npu_blob_version) {
145 ra8_log_error(s_tag, "load: unsupported version");
147 }
148 const uint32_t total = ra8_npu_blob_read_word(p,
150 (uint32_t)k_ra8_npu_blob_word_bytes);
151 if (!internal_npu_span_ok(0U, total, blob_bytes)) {
152 ra8_log_error(s_tag, "load: total_bytes exceeds buffer");
154 }
155 const uint32_t rcount = ra8_npu_blob_read_word(p,
157 (uint32_t)k_ra8_npu_blob_word_bytes);
158 if (rcount > (uint32_t)k_ra8_npu_region_count) {
159 ra8_log_error(s_tag, "load: too many regions");
161 }
162 const uint32_t coff = ra8_npu_blob_read_word(p,
164 (uint32_t)k_ra8_npu_blob_word_bytes);
165 const uint32_t cbytes = ra8_npu_blob_read_word(p,
167 (uint32_t)k_ra8_npu_blob_word_bytes);
168 if (cbytes == 0U) {
169 ra8_log_error(s_tag, "load: empty command stream");
171 }
172 if (!internal_npu_span_ok(coff, cbytes, total)) {
173 ra8_log_error(s_tag, "load: command stream outside blob");
175 }
176 *out_total = total;
177 *out_rcount = rcount;
178 *out_coff = coff;
179 *out_cbytes = cbytes;
180 return k_ra8_ok;
181}
182
205static ra8_err_t internal_npu_verify_checksum(const uint8_t* p, uint32_t total)
206{
207 uint32_t digest = (uint32_t)k_ra8_npu_blob_fnv_offset;
208 for (uint32_t i = (uint32_t)k_ra8_npu_blob_header_bytes; i < total; i++) {
209 digest = (digest ^ (uint32_t)p[i]) * (uint32_t)k_ra8_npu_blob_fnv_prime;
210 }
211 const uint32_t stored = ra8_npu_blob_read_word(p,
213 (uint32_t)k_ra8_npu_blob_word_bytes);
214 if (digest != stored) {
215 ra8_log_error(s_tag, "load: checksum mismatch");
217 }
218 return k_ra8_ok;
219}
220
249static ra8_err_t internal_npu_place_region(const uint8_t* p,
250 uint32_t total,
251 uint32_t desc_off,
252 const ra8_npu_arena_t* arena,
253 uint32_t* arena_used,
254 uint64_t* out_base)
255{
256 const uint32_t flags = ra8_npu_blob_read_word(
257 p,
258 desc_off + ((uint32_t)k_ra8_npu_blob_rdesc_flags * (uint32_t)k_ra8_npu_blob_word_bytes));
259 const uint32_t size = ra8_npu_blob_read_word(
260 p,
261 desc_off + ((uint32_t)k_ra8_npu_blob_rdesc_size * (uint32_t)k_ra8_npu_blob_word_bytes));
262 if ((flags & (uint32_t)k_ra8_npu_blob_rflag_baked) != 0U) {
263 const uint32_t data_off =
265 desc_off + ((uint32_t)k_ra8_npu_blob_rdesc_data_offset *
266 (uint32_t)k_ra8_npu_blob_word_bytes));
267 if (!internal_npu_span_ok(data_off, size, total)) {
268 ra8_log_error(s_tag, "load: baked region outside blob");
270 }
271 *out_base = (uint64_t)(uintptr_t)(p + data_off);
272 return k_ra8_ok;
273 }
274 const uint32_t aligned =
275 (*arena_used + (uint32_t)k_ra8_npu_loader_align_mask) & ~(uint32_t)k_ra8_npu_loader_align_mask;
276 if (aligned > arena->bytes) {
277 ra8_log_error(s_tag, "load: runtime arena exhausted");
278 return k_ra8_err_no_mem;
279 }
280 if (size > (arena->bytes - aligned)) {
281 ra8_log_error(s_tag, "load: runtime region does not fit");
282 return k_ra8_err_no_mem;
283 }
284 *out_base = (uint64_t)(uintptr_t)(arena->base + aligned);
285 *arena_used = aligned + size;
286 return k_ra8_ok;
287}
288
316static ra8_err_t internal_npu_place_all_regions(const uint8_t* p,
317 uint32_t total,
318 uint32_t rcount,
319 const ra8_npu_arena_t* arena,
320 ra8_npu_job_t* job)
321{
322 uint32_t used = 0U;
323 for (uint32_t r = 0U; r < rcount; r++) {
324 const uint32_t desc_off =
326 const ra8_err_t rgn =
327 internal_npu_place_region(p, total, desc_off, arena, &used, &job->region_base[r]);
328 RA8_RETURN_ON_ERROR(rgn, s_tag, "load: region");
329 }
330 return k_ra8_ok;
331}
332
364static ra8_err_t internal_npu_build_job(const uint8_t* p,
365 uint32_t blob_bytes,
366 const ra8_npu_arena_t* arena,
367 ra8_npu_job_t* out_job)
368{
369 uint32_t total = 0U;
370 uint32_t rcount = 0U;
371 uint32_t coff = 0U;
372 uint32_t cbytes = 0U;
373 /* Each callee logs its own specific failure, so a plain early return here
374 * propagates the code without a redundant second log line. */
375 const ra8_err_t hdr = internal_npu_check_header(p, blob_bytes, &total, &rcount, &coff, &cbytes);
376 if (hdr != k_ra8_ok) {
377 return hdr;
378 }
379 if (!internal_npu_span_ok((uint32_t)k_ra8_npu_blob_header_bytes,
380 rcount * (uint32_t)k_ra8_npu_blob_region_desc_bytes,
381 total)) {
382 ra8_log_error(s_tag, "load: region table outside blob");
384 }
385 const ra8_err_t sum = internal_npu_verify_checksum(p, total);
386 if (sum != k_ra8_ok) {
387 return sum;
388 }
389 ra8_npu_job_t job = {};
390 const ra8_err_t rgn = internal_npu_place_all_regions(p, total, rcount, arena, &job);
391 if (rgn != k_ra8_ok) {
392 return rgn;
393 }
394 job.cmd_stream = p + coff;
395 job.cmd_stream_bytes = cbytes;
396 job.region_count = (uint8_t)rcount;
397 *out_job = job;
398 return k_ra8_ok;
399}
400
401ra8_err_t ra8_npu_load(const void* blob,
402 uint32_t blob_bytes,
403 const ra8_npu_arena_t* arena,
404 ra8_npu_job_t* out_job)
405{
406 RA8_CHECK_NULL_PTR(blob, s_tag, "blob must not be nullptr");
407 RA8_CHECK_NULL_PTR(arena, s_tag, "arena must not be nullptr");
408 RA8_CHECK_NULL_PTR(out_job, s_tag, "out_job must not be nullptr");
409 return internal_npu_build_job((const uint8_t*)blob, blob_bytes, arena, out_job);
410}
411
412#else
413/* RA8D2 (no NPU): this translation unit is intentionally empty. */
415#endif /* RA8_HAS_NPU */
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_RETURN_ON_ERROR(err, tag, message)
Early return on error, propagating the code upward.
Definition ra8_check.h:184
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
Compile-time device selection for the RA8 multi-chip build (RA8D2 / RA8P1).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_err_checksum_mismatch
Stored / transmitted checksum does not match computed value.
Definition ra8_err.h:465
@ k_ra8_err_out_of_range
Sensor or peripheral output out of valid range.
Definition ra8_err.h:337
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
Arm Ethos-U55 NPU command/queue driver foundation (RA8P1-only).
.npub linkable-blob container for a Vela-compiled Ethos-U55 model
@ k_ra8_npu_blob_word_version
format version.
@ k_ra8_npu_blob_word_cmd_bytes
command-stream length in bytes.
@ k_ra8_npu_blob_word_region_count
number of region descriptors.
@ k_ra8_npu_blob_word_checksum
FNV-1a digest over the payload.
@ k_ra8_npu_blob_word_magic
magic == k_ra8_npu_blob_magic.
@ k_ra8_npu_blob_word_cmd_offset
byte offset to the command stream.
@ k_ra8_npu_blob_word_total_bytes
whole blob length in bytes.
@ k_ra8_npu_blob_magic
First word: "NPU1" little-endian.
@ k_ra8_npu_blob_version
Container format version.
@ k_ra8_npu_blob_rflag_baked
Region bytes are baked into the blob.
static uint32_t ra8_npu_blob_read_word(const uint8_t *buf, uint32_t byte_off)
Read one little-endian 32-bit word from a .npub byte buffer.
@ k_ra8_npu_blob_fnv_offset
FNV-1a 32-bit offset basis.
@ k_ra8_npu_blob_fnv_prime
FNV-1a 32-bit prime.
@ k_ra8_npu_blob_rdesc_size
region size in bytes.
@ k_ra8_npu_blob_rdesc_flags
region flags (ra8_npu_blob_rflag_t).
@ k_ra8_npu_blob_rdesc_data_offset
baked-data byte offset (if baked).
@ k_ra8_npu_blob_arena_align
Runtime-region alignment (bytes).
@ k_ra8_npu_blob_word_bytes
Bytes in one little-endian word.
@ k_ra8_npu_blob_header_bytes
Header length (8 words * 4).
@ k_ra8_npu_blob_region_desc_bytes
One region descriptor (4 words * 4).
int ra8_npu_loader_not_on_this_device_t
On-target loader: .npub Vela blob -> ra8_npu_job_t (RA8P1-only).
ra8_err_t ra8_npu_load(const void *blob, uint32_t blob_bytes, const ra8_npu_arena_t *arena, ra8_npu_job_t *out_job)
Validate a .npub blob and map it into an ra8_npu_job_t.
@ k_ra8_npu_region_count
Number of BASEPn region base-pointer pairs.
Caller-provided runtime arena for a model's RUNTIME regions.
uint32_t bytes
Runtime arena capacity in bytes (0 if no runtime rgn).
uint8_t * base
Runtime arena base in NPU-visible SRAM (writable).
One Ethos-U55 inference job: a command stream plus its tensor arenas.
Definition ra8_npu.h:87
uint8_t region_count
Number of region_base[] entries in use.
Definition ra8_npu.h:90
uint32_t cmd_stream_bytes
Command stream length in bytes (> 0).
Definition ra8_npu.h:89
uint64_t region_base[k_ra8_npu_region_count]
AXI base of each BASEPn arena.
Definition ra8_npu.h:91
const void * cmd_stream
Vela command stream base in SRAM.
Definition ra8_npu.h:88