code wiki / _hdl_build / nx_rv64_qemu_oracle.nx
nx_rv64_qemu_oracle.nx source
↩ module page · 79 lines · 5763 B
1// nx_rv64_qemu_oracle.nx -- EXTERNAL-ORACLE EXECUTION VALIDATION: assemble an M-extension test program with the
2// sovereign assembler, run it on the golden behavioral sim (rv64im_min_sim, models the UART), emit the UART bytes it
3// produced as hex, and WRITE THE FLAT BINARY to disk so the SAME bytes can be run in real qemu-system-riscv64. If the
4// UART output matches QEMU's, my emulator's EXECUTION semantics (esp. the tricky M-ext edge cases: high-mul, signed
5// div rounding, unsigned div) are validated against the world's REFERENCE RISC-V implementation -- not just against my
6// own other engines (which could share a misconception). The program emits low bytes of: sum(i^2,1..10)=385(0x81),
7// 385/7=55(0x37), 385%7=0(0x00), mulhu(-1,-1)=..fe(0xFE), (-20)/3=-6(0xFA), (2^64-1)/2(0xFF), sentinel '*'(0x2A).
8// expect UART = 81 37 00 fe fa ff 2a. expect_exit: 0
9import "nx_syscalls.nx"
10import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
11import "nishi_hdl_primitives.nx"
12import "rv64im_min_decoder.nx"
13import "rv64im_min_alu.nx"
14import "rv64im_min_regfile.nx"
15import "rv64im_min_csr.nx"
16import "rv64im_min_clint.nx"
17import "rv64im_min_uart.nx"
18import "rv64im_min_virtio.nx"
19import "rv64im_min_sim.nx"
20import "nx_rv64_asm.nx"
21import "nx_rv64_fast.nx"
22
23func g_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
24// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
25// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
26// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
27// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
28func g_pn(v: i64) -> i64 { nxi_out(v); return 0 }
29func g_hx(v: i64) -> i64 { let b: *u8=sys_mmap(4); let n0: i64=(v>>4)&15; let n1: i64=v&15; if n0<10 { b[0]=(48+n0) as u8 } else { b[0]=(87+n0) as u8 } if n1<10 { b[1]=(48+n1) as u8 } else { b[1]=(87+n1) as u8 } b[2]=32 as u8; sys_write(1,b,3); return 0 }
30func ck(name: *u8, c: i64) -> i64 { if c==1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) } g_puts(name); g_puts("\n" as *u8); return c }
31
32const DMEM_BASE: i64 = 0x80000000
33const DMEM_SIZE: i64 = 65536
34
35func main() -> i64 {
36 g_puts("nx_rv64_qemu_oracle (assemble M-ext program, run on golden sim, emit UART hex + write binary for QEMU cross-check)\n" as *u8)
37 var pass: i64=0; var total: i64=0
38 let code: *u8 = sys_mmap(4096)
39 let src: *u8 = " li t0, 0\n li t1, 1\n li t2, 11\nsq:\n mul s0, t1, t1\n add t0, t0, s0\n addi t1, t1, 1\n blt t1, t2, sq\n li s1, 7\n div a0, t0, s1\n rem a1, t0, s1\n li s2, -1\n mulhu a2, s2, s2\n li s3, -20\n li s4, 3\n div a3, s3, s4\n li s5, 2\n divu a4, s2, s5\n lui t3, 0x10000\n sb t0, 0(t3)\n sb a0, 0(t3)\n sb a1, 0(t3)\n sb a2, 0(t3)\n sb a3, 0(t3)\n sb a4, 0(t3)\n li t4, 42\n sb t4, 0(t3)\n lui t5, 0x100\n lui t6, 0x5\n addi t6, t6, 0x555\n sw t6, 0(t5)\n" as *u8
40 let nb: i64 = rvasm_assemble_str(src, code, 4096)
41 if nb < 0 { g_puts(" ASSEMBLE FAILED\n" as *u8); sys_exit(1); return 1 }
42 g_puts(" assembled "); g_pn(nb/4); g_puts(" instructions ("); g_pn(nb); g_puts(" bytes)\n" as *u8)
43
44 // set up the golden behavioral sim with a UART (tx capture buffer).
45 let rf_storage: *i64 = sys_mmap(8 * NX_RV64IM_RF_N_REGS) as *i64
46 let csr_storage: *i64 = sys_mmap(8 * NX_CSR_SLOT_N) as *i64
47 let clint_storage: *i64 = sys_mmap(8 * NX_CLINT_SLOT_N) as *i64
48 let uart_storage: *i64 = sys_mmap(8 * NX_UART_SLOT_N) as *i64
49 let mem: *u8 = sys_mmap(DMEM_SIZE); let tx_buf: *u8 = sys_mmap(256)
50 let rf: *NxRv64imRegfile = sys_mmap(64) as *NxRv64imRegfile
51 let csr: *NxRv64imCsrFile = sys_mmap(64) as *NxRv64imCsrFile
52 let clint: *NxClint = sys_mmap(64) as *NxClint
53 let uart: *NxUart = sys_mmap(64) as *NxUart
54 let sim: *NxRv64imSim = sys_mmap(128) as *NxRv64imSim
55 nx_rv64im_rf_init(rf, rf_storage); nx_rv64im_csr_init(csr, csr_storage, 0); nx_clint_init(clint, clint_storage); nx_uart_init(uart, uart_storage, tx_buf, 256)
56 nx_rv64im_sim_init(sim, rf, csr, clint, uart, DMEM_BASE, mem, DMEM_SIZE, 0)
57 var i: i64=0; while i<nb { mem[i]=code[i]; i=i+1 }
58 nx_rv64im_sim_run(sim, 10000000)
59
60 let n: i64 = nx_uart_tx_count(uart)
61 g_puts(" golden sim UART output ("); g_pn(n); g_puts(" bytes): "); i=0; while i<n { g_hx(tx_buf[i] as i64); i=i+1 } g_puts("\n" as *u8)
62 g_puts(" expected : 81 37 00 fe fa ff 2a\n" as *u8)
63
64 // check the sim produced the expected bytes.
65 var t1: i64=0
66 if n==7 { if tx_buf[0]==(0x81 as u8) { if tx_buf[1]==(0x37 as u8) { if tx_buf[2]==(0x00 as u8) { if tx_buf[3]==(0xfe as u8) { if tx_buf[4]==(0xfa as u8) { if tx_buf[5]==(0xff as u8) { if tx_buf[6]==(0x2a as u8) { t1=1 } } } } } } } }
67 pass=pass+ck("T1: golden sim executes the M-ext program -> UART = 81 37 00 fe fa ff 2a (385/55/0/mulhu/-6/divu/sentinel)" as *u8, t1); total=total+1
68
69 // write the flat binary so QEMU can run the SAME bytes.
70 let fd: i64 = sys_openat_wr("knowledge/hw/mext_qemu.bin" as *u8, 420)
71 var t2: i64=0
72 if fd>=0 { let wn: i64=sys_write(fd, code, nb); sys_close(fd); if wn==nb { t2=1 } }
73 pass=pass+ck("T2: flat binary written to knowledge/hw/mext_qemu.bin for the QEMU cross-check" as *u8, t2); total=total+1
74
75 var okall: i64=0; if pass==total { okall=1 }
76 g_puts("---- nx_rv64_qemu_oracle: passed "); g_pn(pass); g_puts(" / "); g_pn(total); g_puts(" ----\n" as *u8)
77 if okall==1 { g_puts("verdict=GREEN (golden sim UART bytes emitted + binary written; run the same binary in qemu-system-riscv64 and diff)\n" as *u8); sys_exit(0); return 0 }
78 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1
79}