code wiki / _hdl_build / nx_emu_bench.nx
nx_emu_bench.nx source
↩ module page · 95 lines · 6046 B
1// nx_emu_bench.nx -- MEASURE the sovereign rv64 emulator's throughput (instructions/sec) so the "behind QEMU on perf"
2// claim is a NUMBER, not a hand-wave (measured-not-asserted). Assembles a compute loop with the sovereign assembler
3// (nx_rv64_asm), runs it IN-PROCESS on rv64im_min_sim with a high step cap, times the run with sys_now_us, and reports
4// the measured instr/sec + a bounded honest comparison to QEMU-TCG's ballpark. expect_exit:0
5import "nx_syscalls.nx"
6import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
7import "nishi_hdl_primitives.nx"
8import "rv64im_min_decoder.nx"
9import "rv64im_min_alu.nx"
10import "rv64im_min_regfile.nx"
11import "rv64im_min_csr.nx"
12import "rv64im_min_clint.nx"
13import "rv64im_min_uart.nx"
14import "rv64im_min_sim.nx"
15import "nx_rv64_asm.nx"
16const BENCH_MAGIC_4096: i64 = 4096
17const BENCH_MAGIC_200000000: i64 = 200000000
18const BENCH_MAGIC_1000000: i64 = 1000000
19const BENCH_MAGIC_16000000: i64 = 16000000
20
21func b_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
22// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
23// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
24// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
25// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
26func b_pn(v: i64) -> i64 { nxi_out(v); return 0 }
27func ck(name: *u8, c: i64) -> i64 { if c==1 { b_puts(" PASS " as *u8) } else { b_puts(" FAIL " as *u8) } b_puts(name); b_puts("\n" as *u8); return c }
28
29const BENCH_MEM_BASE: i64 = 0x80000000
30const BENCH_MEM_SIZE: i64 = 65536
31const BENCH_TX_CAP: i64 = 256
32
33func main() -> i64 {
34 b_puts("nx_emu_bench (MEASURE the sovereign rv64 emulator throughput: instr/sec on a compute loop)\n" as *u8)
35
36 // ---- assemble a tight compute loop: t0 counts to 0x800000 (8.4M), 2 instr/iter, then finisher-halt ----
37 let code: *u8 = sys_mmap(BENCH_MAGIC_4096)
38 let nb: i64 = rvasm_assemble_str(" li t0, 0\n lui t1, 0x800\nloop:\n addi t0, t0, 1\n blt t0, t1, loop\n lui a0, 0x100\n lui a1, 0x5\n addi a1, a1, 0x555\n sw a1, 0(a0)\nspin:\n j spin\n" as *u8, code, BENCH_MAGIC_4096)
39 if nb < 0 { b_puts(" ASSEMBLE FAILED\n" as *u8); sys_exit(1); return 1 }
40
41 // ---- build the sovereign sim ----
42 let rf_storage: *i64 = sys_mmap(8 * NX_RV64IM_RF_N_REGS) as *i64
43 let csr_storage: *i64 = sys_mmap(8 * NX_CSR_SLOT_N) as *i64
44 let clint_storage: *i64 = sys_mmap(8 * NX_CLINT_SLOT_N) as *i64
45 let uart_storage: *i64 = sys_mmap(8 * NX_UART_SLOT_N) as *i64
46 let mem: *u8 = sys_mmap(BENCH_MEM_SIZE)
47 let tx_buf: *u8 = sys_mmap(BENCH_TX_CAP)
48 let rf: *NxRv64imRegfile = sys_mmap(64) as *NxRv64imRegfile
49 let csr: *NxRv64imCsrFile = sys_mmap(64) as *NxRv64imCsrFile
50 let clint: *NxClint = sys_mmap(64) as *NxClint
51 let uart: *NxUart = sys_mmap(64) as *NxUart
52 let sim: *NxRv64imSim = sys_mmap(128) as *NxRv64imSim
53 nx_rv64im_rf_init(rf, rf_storage)
54 nx_rv64im_csr_init(csr, csr_storage, 0)
55 nx_clint_init(clint, clint_storage)
56 nx_uart_init(uart, uart_storage, tx_buf, BENCH_TX_CAP)
57 nx_rv64im_sim_init(sim, rf, csr, clint, uart, BENCH_MEM_BASE, mem, BENCH_MEM_SIZE, 0)
58 var i: i64 = 0; while i < nb { mem[i] = code[i]; i = i + 1 }
59
60 // ---- time the run ----
61 let t_start: i64 = sys_now_us()
62 nx_rv64im_sim_run(sim, BENCH_MAGIC_200000000)
63 let t_end: i64 = sys_now_us()
64 let elapsed_us: i64 = t_end - t_start
65 let steps: i64 = sim.steps
66
67 b_puts(" workload: count-to-0x800000 loop; instructions executed="); b_pn(steps)
68 b_puts(" halted="); b_pn(sim.halted); b_puts("\n" as *u8)
69 b_puts(" elapsed="); b_pn(elapsed_us); b_puts(" us ("); b_pn(elapsed_us/1000); b_puts(" ms)\n" as *u8)
70 var ips: i64 = 0
71 if elapsed_us > 0 { ips = (steps / elapsed_us) * BENCH_MAGIC_1000000 + ((steps % elapsed_us) * BENCH_MAGIC_1000000) / elapsed_us }
72 b_puts(" MEASURED THROUGHPUT ~= "); b_pn(ips); b_puts(" instructions/sec ("); b_pn(ips/BENCH_MAGIC_1000000); b_puts(" MIPS)\n" as *u8)
73
74 b_puts(" -- honest placement vs QEMU (grounded em_qemu/em_iss research) --\n" as *u8)
75 b_puts(" QEMU uses TCG (dynamic binary translation / JIT) -> ~hundreds of MIPS to multi-GIPS guest throughput.\n" as *u8)
76 b_puts(" Ours is a BEHAVIORAL step-interpreter (decode-per-step, struct device models) -> BEHIND on raw speed by\n" as *u8)
77 b_puts(" ~1-3 orders of magnitude; EXCEED axis = DETERMINISM (bit-exact/replayable), SOVEREIGNTY (own everything),\n" as *u8)
78 b_puts(" auditability. For driver bring-up + boot + gated tests this throughput is sufficient; a JIT/decode-cache\n" as *u8)
79 b_puts(" is the perf climb (measured baseline established here).\n" as *u8)
80
81 var pass: i64=0; var total: i64=0
82 var t1: i64=0; if sim.halted==1 { if steps > BENCH_MAGIC_16000000 { t1=1 } }
83 pass=pass+ck("T1: the loop ran to a clean finisher halt with >16M instructions executed" as *u8, t1); total=total+1
84 var t2: i64=0; if elapsed_us > 0 { if ips > 0 { t2=1 } }
85 pass=pass+ck("T2: throughput MEASURED (elapsed>0, instr/sec computed) -- the perf claim is now a number" as *u8, t2); total=total+1
86
87 var okall: i64=0; if pass==total { okall=1 }
88 b_puts("---- nx_emu_bench: passed "); b_pn(pass); b_puts(" / "); b_pn(total); b_puts(" ----\n" as *u8)
89 if okall==1 {
90 let logf: i64=sys_openat_append("knowledge/status/emu_bench.log" as *u8, 420)
91 if logf>=0 { b_puts("" as *u8); let z: i64=sys_write(logf,"NXEMUBENCH GREEN: sovereign rv64 emu throughput MEASURED (behavioral step-interpreter); honest vs QEMU-TCG JIT -- behind on raw MIPS, exceed on determinism/sovereignty\n" as *u8,157); sys_close(logf) }
92 b_puts("verdict=GREEN (sovereign emulator throughput MEASURED -- the 'behind QEMU on perf' claim is now a real MIPS number, honest gap stated)\n" as *u8); sys_exit(0); return 0
93 }
94 b_puts("verdict=RED\n" as *u8); sys_exit(1); return 1
95}