code wiki / (root) / nx_mcu_q4.nx

nx_mcu_q4.nx

buildroot/runtime/nx_mcu_q4.nx

4502 B104 linesdepth 2pulls 2 transitivereach 1 importersview sourcekind librarytopic mcu
docsdependenciesstructsconstsfunctions

about

nx_mcu_q4.nx -- the int4-weight x int8-activation group dot product: the kernel MCU-class LLM inference actually spends its time in. Pure integer, no float lib, no libc. WHY INTEGER AND WHY THIS KERNEL. On an ESP32-S3 the weights are group-wise symmetric int4 and the activations are quantized to int8 per token, so every dot is int4 x int8 accumulated into int32. That int32 group sum is EXACTLY what the S3 SIMD int8 dot instruction produces, which is why a scalar reference and a vectorised device kernel are numerically identical rather than merely close. Being integer, it is also bit-exact -- so its gate asserts EQUALITY, never a tolerance. A float kernel would need an epsilon, and an epsilon is where a wrong answer hides. LAYOUT (mirrors the exported PLE1 format byte-for-byte): - a row is ceil(cols/2) bytes of packed nibbles; rows are byte-aligned, so odd cols leave a dead high nibble in the last byte. Ragged, no padding. - nibble = value + 8, giving the signed range -8..+7 from an unsigned 0..15 code. - EVEN column index -> LOW nibble; ODD column index -> HIGH nibble. Getting this backwards still produces plausible-looking numbers, so the gate pins it with a discriminating control. - activations are int8 two's complement in a byte, so they need sign extension, not a cast. Group scales are fp16 and are applied OUTSIDE this kernel (acc * scale), deliberately: this file stays integer-exact and single-responsibility, and the float scaling lives with the code that owns floats. Semantics derived from the int8-activation path of esp32-ai firmware/common/llm.h (MIT, (c) 2026 Viacheslav Sierbov). Independent NishiLang implementation; behaviour is pinned by nx_mcu_q4_gate. Shifts are written as / and % so this stays portable to the no-shift lowering path. license_tier: DERIVED-MIT No hw writes (Rule 26).

dependencies 1 imports · 1 importers

nx_syscalls.nx nx_mcu_q4.nx nx_mcu_q4_gate.nx

imports: nx_syscalls.nx

imported by: nx_mcu_q4_gate.nx

structs

none

consts

28const Q4_BIAS: i64 = 8
29const Q4_CODES: i64 = 16
30const Q4_I8_WRAP: i64 = 256
31const Q4_I8_MAX: i64 = 127

functions

34func q4_row_bytes(cols: i64) -> i64
called by 2: q4_packmain
40func q4_i8(b: i64) -> i64
called by 2: q4_group_dotmain
46func q4_code_at(row: *u8, j: i64) -> i64
called by 1: q4_val_at
53func q4_val_at(row: *u8, j: i64) -> i64
called by 2: q4_group_dotmain calls 1: q4_code_at
59func q4_group_dot(row: *u8, xq: *u8, begin: i64, end: i64) -> i64
called by 2: q4_row_dotmain calls 2: q4_val_atq4_i8
71func q4_row_dot(row: *u8, xq: *u8, cols: i64) -> i64
called by 1: main calls 1: q4_group_dot
76func q4_n_groups(cols: i64, group: i64) -> i64
called by 1: main
83func q4_group_end(cols: i64, group: i64, gi: i64) -> i64
called by 1: main
90func q4_pack(dst: *u8, vals: *i64, cols: i64) -> i64
called by 1: main calls 1: q4_row_bytes