code wiki / _hdl_build / nx_drvgen.nx
nx_drvgen.nx
buildroot/runtime/_hdl_build/nx_drvgen.nx
about
SUPERSEDED 2026-06-13 (no-sprawl consolidation): the CANONICAL driver-from-spec emitter is the
op-list-driven nx_drv_proto_emit.nx -- there a NEW device class is a NEW op-list SPEC with ZERO
emitter change (the protocol step-sequence is DATA). nx_drvgen hardcodes the NVMe stage ORDERING
in code (a per-class emitter), which the X-DRV-W1 council flagged as a generality dilution. NVMe
is now carried as an op-list spec: knowledge/specs/nvme_oplist_virt.spec, gate-proven by
_nvme_oplist_gate (knowledge/status/driver_spec.log::NVMEOPGATE verdict=GREEN). nx_drvgen +
nx_driver_shapes are KEPT as the regression reference (NVMEGATE stage=B), not extended. New work
-> add an op-list spec to nx_drv_proto_emit, NOT a new bespoke emitter.
nx_drvgen.nx -- the GENERIC driver-image author for the 2nd device class (X-DRV-W1 stage B).
A NEW device class IS A SPEC FILE: nx_drvgen reads an NVMe-class controller spec
(knowledge/specs/nvme_admin_enable_virt.spec) and AUTHORS a bare-metal rv64 flat image whose
driver performs the REAL NVMe controller bring-up by routing EVERY byte through the SAME
device-agnostic shape library (nx_driver_shapes: WIRE_TLV / COMMAND_QUEUE / STATE_MACHINE /
STRUCT_WALK) that re-emits the virtio image byte-identically in PROOF-1. The shape library
knows ZERO NVMe -- the controller identity, offsets, queue bases, SQE/CQE layout, and the
phase-tag KAT all arrive from the spec. So the NVMe driver's distinctness comes from spec
DATA, not from any virtio code path.
The driver the image runs (NVMe 1.4 base spec bring-up subset):
verify VS == 0x00010400 (1.4.0) -> emit "VIO-NVME "
write CC.EN=1, POLL CSTS until RDY set (STATE_MACHINE poll loop) -> emit "EN " <-- a REAL backward branch (the structural fingerprint virtio's straight-line handshake lacks)
bind AQA + ASQ/ACQ base-address regs, read ASQ_LO back + verify -> emit "RDY "
lay the 64-byte SQE (opcode/CID/CDW10) into ASQ_base (STRUCT_WALK) -> emit "ASQ "
ring the SQ-Tail doorbell (the device posts the phase-tagged CQE) -> emit "SQE "
POLL the CQE phase bit in ACQ RAM, read NvmeCqPeek back + verify -> emit "CQ\n" <-- a SECOND backward branch (the CQE phase-tag poll); the verify is the tamper canary
write the SiFive finisher -> clean halt.
Any verify that fails BRANCHES PAST the rest of the bring-up straight to the finisher, so the
transcript loses its tail (the " CQ" canary, etc.) -- the gate's tamper test exploits this.
nx_drvgen <specpath> -> writes the flat image to the spec's `out` + the golden to <out>.gold
VERDICT log -> knowledge/status/virtio_blk.log (DRVGEN rows). Sovereign: syscalls only,
no gcc/.sh. NVMe/PCI is a last-mile interop PROBE + measuring stick to EXCEED, never a
substrate -- the emitted driver + shapes are Nishi-native. license_tier: ORIGINAL
dependencies 3 imports · 0 importers
imports: nx_syscalls.nxnx_itoa_lib.nxnx_driver_shapes.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 36 | const NV_MAGIC_8192: i64 = 8192 |
| 39 | const NV_X0: i64 = 0 |
| 40 | const NV_T2: i64 = 7 // device base |
| 41 | const NV_T3: i64 = 28 // loaded register value (actual) |
| 42 | const NV_T5: i64 = 30 // struct-base scratch (ASQ / ACQ guest addr) |
| 45 | const NC_BASE: i64 = 0 |
| 46 | const NC_OFF_CAP: i64 = 1 |
| 47 | const NC_OFF_VS: i64 = 2 |
| 48 | const NC_OFF_CC: i64 = 3 |
| 49 | const NC_OFF_CSTS: i64 = 4 |
| 50 | const NC_OFF_AQA: i64 = 5 |
| 51 | const NC_OFF_ASQ_LO: i64 = 6 |
| 52 | const NC_OFF_ASQ_HI: i64 = 7 |
| 53 | const NC_OFF_ACQ_LO: i64 = 8 |
| 54 | const NC_OFF_ACQ_HI: i64 = 9 |
| 55 | const NC_OFF_SQ0TDBL: i64 = 10 |
| 56 | const NC_OFF_CQ0HDBL: i64 = 11 |
| 57 | const NC_OFF_CQPEEK: i64 = 12 |
| 58 | const NC_VS_EXPECTED: i64 = 13 |
| 59 | const NC_CC_EN: i64 = 14 |
| 60 | const NC_CSTS_RDY_MASK: i64 = 15 |
| 61 | const NC_ASQ_ADDR: i64 = 16 |
| 62 | const NC_ACQ_ADDR: i64 = 17 |
| 63 | const NC_AQA_VAL: i64 = 18 |
| 64 | const NC_SQE_OPCODE: i64 = 19 |
| 65 | const NC_SQE_CID: i64 = 20 |
| 66 | const NC_SQE_OFF_OPCODE:i64 = 21 |
| 67 | const NC_SQE_OFF_CID: i64 = 22 |
| 68 | const NC_SQE_OFF_CDW10: i64 = 23 |
| 69 | const NC_SQE_CDW10: i64 = 24 |
| 70 | const NC_CQE_OFF_CID: i64 = 25 |
| 71 | const NC_CQE_OFF_STATUS:i64 = 26 |
| 72 | const NC_CQE_PHASE_BIT: i64 = 27 |
| 73 | const NC_CQE_CID_EXP: i64 = 28 |
| 74 | const NC_CQE_STATUS_EXP:i64 = 29 |
| 75 | const NC_CQPEEK_EXP: i64 = 30 |
| 76 | const NC_N: i64 = 31 |
functions
| 78 | func nv_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } |
| 79 | func nv_fp(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 } |
| 84 | func nv_fn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 } |
| 85 | func nv_hex(fd: i64, v: i64) -> i64 { let d: *u8=sys_mmap(16); var i: i64=0; while i<8{let nib: i64=(v >> ((7-i)*4)) & 0xf; if nib<10{d[i]=(48+nib) as u8} else {d[i]=(87+nib) as u8} i=i+1}; sys_write(fd,d,8); return 0 } |
| 92 | func nv_emit_image(buf: *u8, cfg: *i64, tail: *u8, segs: *i64, fail_off: i64) -> i64 called by 1: main calls 13: ds_w32ds_luids_emit_verifyds_emit_strds_emit_poll_until_setds_emit_regwrite+7 |
| 141 | func nv_parse_num(buf: *u8, p: i64, le: i64, endp: *i64) -> i64 called by 1: nv_num_field |
| 171 | func nv_num_field(buf: *u8, ls: i64, le: i64, key: *u8, out: *i64) -> i64 |
| 183 | func nv_str_field(buf: *u8, ls: i64, le: i64, key: *u8, out: *u8) -> i64 called by 1: main |
| 197 | func nv_log(name: *u8, bytes: i64, golden: *u8, verdict: *u8) -> i64 |
| 208 | func main(argc: i64, argv: *i64) -> i64 |