code wiki / _hdl_build / nx_rv64_fast_gate.nx

nx_rv64_fast_gate.nx source

↩ module page · 119 lines · 9073 B

1// nx_rv64_fast_gate.nx -- GATE: prove the predecoded interpreter (nx_rv64_fast) is (a) EQUIVALENT to the golden 2// behavioral sim (rv64im_min_sim) on assembled programs, and (b) FASTER (measured speedup). Both run the SAME 3// sovereign-assembled binaries; each program stores its result to a scratch word the gate reads back from each 4// engine's memory. Equivalence = same result; then the big compute loop is timed on both -> measured MIPS + speedup. 5// This is the honest first perf-engineering rung: decode-once beats decode-per-step, validated against the golden model. 6// expect_exit:0 7import "nx_syscalls.nx" 8import "nishi_hdl_primitives.nx" 9import "rv64im_min_decoder.nx" 10import "rv64im_min_alu.nx" 11import "rv64im_min_regfile.nx" 12import "rv64im_min_csr.nx" 13import "rv64im_min_clint.nx" 14import "rv64im_min_uart.nx" 15import "rv64im_min_sim.nx" 16import "nx_rv64_asm.nx" 17import "nx_rv64_fast.nx" 18 19func 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 } 20func g_pn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var x: i64=v; if x<0{b[0]=45;sys_write(1,b,1);x=0-x} if x==0{b[0]=48;sys_write(1,b,1);return 0} var d: i64=0; var y: i64=x; while y>0{d=d+1;y=y/10} var i: i64=d-1; y=x; while i>=0{b[i]=(48+(y%10)) as u8;y=y/10;i=i-1} sys_write(1,b,d); return 0 } 21func 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 } 22 23const GMEM_BASE: i64 = 0x80000000 24const GMEM_SIZE: i64 = 65536 25const GSCRATCH_OFF: i64 = 0x8000 // 0x80008000 - base 26 27// run `code` (nb bytes) on the golden behavioral sim; write steps+us; return the word stored at the scratch offset. 28func run_on_sim(code: *u8, nb: i64, steps_out: *i64, us_out: *i64) -> i64 { 29 let rf_storage: *i64 = sys_mmap(8 * NX_RV64IM_RF_N_REGS) as *i64 30 let csr_storage: *i64 = sys_mmap(8 * NX_CSR_SLOT_N) as *i64 31 let clint_storage: *i64 = sys_mmap(8 * NX_CLINT_SLOT_N) as *i64 32 let uart_storage: *i64 = sys_mmap(8 * NX_UART_SLOT_N) as *i64 33 let mem: *u8 = sys_mmap(GMEM_SIZE) 34 let tx_buf: *u8 = sys_mmap(256) 35 let rf: *NxRv64imRegfile = sys_mmap(64) as *NxRv64imRegfile 36 let csr: *NxRv64imCsrFile = sys_mmap(64) as *NxRv64imCsrFile 37 let clint: *NxClint = sys_mmap(64) as *NxClint 38 let uart: *NxUart = sys_mmap(64) as *NxUart 39 let sim: *NxRv64imSim = sys_mmap(128) as *NxRv64imSim 40 nx_rv64im_rf_init(rf, rf_storage) 41 nx_rv64im_csr_init(csr, csr_storage, 0) 42 nx_clint_init(clint, clint_storage) 43 nx_uart_init(uart, uart_storage, tx_buf, 256) 44 nx_rv64im_sim_init(sim, rf, csr, clint, uart, GMEM_BASE, mem, GMEM_SIZE, 0) 45 var i: i64=0; while i<nb { mem[i]=code[i]; i=i+1 } 46 let t0: i64=sys_now_us() 47 nx_rv64im_sim_run(sim, 200000000) 48 let t1: i64=sys_now_us() 49 steps_out[0]=sim.steps; us_out[0]=t1-t0 50 return fk_ld(mem, GSCRATCH_OFF, 4) 51} 52// run `code` on the predecoded fast interpreter; write steps+us; return the scratch word. 53func run_on_fast(code: *u8, nb: i64, steps_out: *i64, us_out: *i64) -> i64 { 54 let mem: *u8 = sys_mmap(GMEM_SIZE) 55 var i: i64=0; while i<nb { mem[i]=code[i]; i=i+1 } 56 let nw: i64 = nb/4 57 let opk: *i64=sys_mmap(nw*8) as *i64; let rd: *i64=sys_mmap(nw*8) as *i64; let rs1: *i64=sys_mmap(nw*8) as *i64; let rs2: *i64=sys_mmap(nw*8) as *i64; let imm: *i64=sys_mmap(nw*8) as *i64 58 let nc: i64 = fk_predecode(code, nb, opk, rd, rs1, rs2, imm) 59 let reg: *i64 = sys_mmap(32*8) as *i64 60 var z: i64=0; while z<32 { reg[z]=0; z=z+1 } 61 let t0: i64=sys_now_us() 62 let st: i64 = fk_run(opk, rd, rs1, rs2, imm, nc, reg, mem, GMEM_SIZE, 200000000, (0 as i64) as *NxVirtioMmio) 63 let t1: i64=sys_now_us() 64 steps_out[0]=st; us_out[0]=t1-t0 65 return fk_ld(mem, GSCRATCH_OFF, 4) 66} 67func asm_it(src: *u8, out: *u8) -> i64 { return rvasm_assemble_str(src, out, 65536) } 68func mips_of(steps: i64, us: i64) -> i64 { if us<=0 { return 0 } return steps/us } // steps/us == millions/sec 69 70func main() -> i64 { 71 g_puts("nx_rv64_fast_gate (predecoded interpreter: EQUIVALENCE vs the golden behavioral sim + measured SPEEDUP)\n" as *u8) 72 var pass: i64=0; var total: i64=0 73 let code: *u8 = sys_mmap(65536) 74 let so: *i64=sys_mmap(8) as *i64; let uo: *i64=sys_mmap(8) as *i64 75 let so2: *i64=sys_mmap(8) as *i64; let uo2: *i64=sys_mmap(8) as *i64 76 77 // --- equivalence: 3 programs (arith / loop-sum / logic-shift), each stores its result to scratch --- 78 let nA: i64 = asm_it(" li t0, 100\n addi t0, t0, 50\n li t1, 30\n sub t0, t0, t1\n lui s0, 0x80008\n slli s0, s0, 32\n srli s0, s0, 32\n sw t0, 0(s0)\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) 79 let simA: i64=run_on_sim(code,nA,so,uo); let fastA: i64=run_on_fast(code,nA,so2,uo2) 80 var t1: i64=0; if simA==120 { if fastA==120 { t1=1 } } 81 g_puts(" EQ prog A (arith): sim="); g_pn(simA); g_puts(" fast="); g_pn(fastA); g_puts(" (expect 120)\n" as *u8) 82 pass=pass+ck("T1: program A -- fast == sim == 120 (arith equivalence)" as *u8, t1); total=total+1 83 84 let nB: i64 = asm_it(" li t0, 0\n li t1, 1\n li t2, 11\nloop:\n add t0, t0, t1\n addi t1, t1, 1\n blt t1, t2, loop\n lui s0, 0x80008\n slli s0, s0, 32\n srli s0, s0, 32\n sw t0, 0(s0)\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) 85 let simB: i64=run_on_sim(code,nB,so,uo); let fastB: i64=run_on_fast(code,nB,so2,uo2) 86 var t2: i64=0; if simB==55 { if fastB==55 { t2=1 } } 87 g_puts(" EQ prog B (loop sum 1..10): sim="); g_pn(simB); g_puts(" fast="); g_pn(fastB); g_puts(" (expect 55)\n" as *u8) 88 pass=pass+ck("T2: program B -- fast == sim == 55 (loop + branch equivalence)" as *u8, t2); total=total+1 89 90 let nC: i64 = asm_it(" li t0, 240\n andi t0, t0, 60\n li t1, 1\n slli t1, t1, 6\n add t0, t0, t1\n lui s0, 0x80008\n slli s0, s0, 32\n srli s0, s0, 32\n sw t0, 0(s0)\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) 91 let simC: i64=run_on_sim(code,nC,so,uo); let fastC: i64=run_on_fast(code,nC,so2,uo2) 92 var t3: i64=0; if simC==112 { if fastC==112 { t3=1 } } 93 g_puts(" EQ prog C (logic+shift): sim="); g_pn(simC); g_puts(" fast="); g_pn(fastC); g_puts(" (expect 112)\n" as *u8) 94 pass=pass+ck("T3: program C -- fast == sim == 112 (logic/shift equivalence)" as *u8, t3); total=total+1 95 96 // --- benchmark: the big compute loop, timed on both; equivalence on a large workload + measured speedup --- 97 let nX: i64 = asm_it(" li t0, 0\n lui t1, 0x800\nloop:\n addi t0, t0, 1\n blt t0, t1, loop\n lui s0, 0x80008\n slli s0, s0, 32\n srli s0, s0, 32\n sw t0, 0(s0)\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) 98 let simX: i64=run_on_sim(code,nX,so,uo); let sim_steps: i64=so[0]; let sim_us: i64=uo[0] 99 let fastX: i64=run_on_fast(code,nX,so2,uo2); let fast_steps: i64=so2[0]; let fast_us: i64=uo2[0] 100 let sim_mips: i64=mips_of(sim_steps, sim_us); let fast_mips: i64=mips_of(fast_steps, fast_us) 101 g_puts(" BENCH (count to 0x800000): result sim="); g_pn(simX); g_puts(" fast="); g_pn(fastX); g_puts(" (both 8388608)\n" as *u8) 102 g_puts(" sim: steps="); g_pn(sim_steps); g_puts(" time="); g_pn(sim_us); g_puts("us -> "); g_pn(sim_mips); g_puts(" MIPS\n" as *u8) 103 g_puts(" fast: steps="); g_pn(fast_steps); g_puts(" time="); g_pn(fast_us); g_puts("us -> "); g_pn(fast_mips); g_puts(" MIPS\n" as *u8) 104 var speedup_x100: i64=0; if fast_us>0 { speedup_x100 = (sim_us*100)/fast_us } 105 g_puts(" SPEEDUP = "); g_pn(speedup_x100/100); g_puts("."); let frac: i64=speedup_x100%100; if frac<10 { g_puts("0" as *u8) } g_pn(frac); g_puts("x (predecode vs decode-per-step)\n" as *u8) 106 var t4: i64=0; if simX==8388608 { if fastX==8388608 { if sim_steps==fast_steps { t4=1 } } } 107 pass=pass+ck("T4: big-loop result + step count IDENTICAL on both engines (equivalence at scale)" as *u8, t4); total=total+1 108 var t5: i64=0; if fast_us < sim_us { if fast_mips > sim_mips { t5=1 } } 109 pass=pass+ck("T5: the predecoded interpreter is MEASURABLY FASTER than the behavioral sim (decode-once wins)" as *u8, t5); total=total+1 110 111 var okall: i64=0; if pass==total { okall=1 } 112 g_puts("---- nx_rv64_fast_gate: passed "); g_pn(pass); g_puts(" / "); g_pn(total); g_puts(" ----\n" as *u8) 113 if okall==1 { 114 let logf: i64=sys_openat_append("knowledge/status/rv64_fast.log" as *u8, 420) 115 if logf>=0 { let z: i64=sys_write(logf,"NXRV64FAST GREEN: predecoded interpreter EQUIVALENT to the golden behavioral sim on arith/loop/logic + big loop; MEASURABLY FASTER (decode-once vs decode-per-step); the sovereign emu perf-climb first rung\n" as *u8,201); sys_close(logf) } 116 g_puts("verdict=GREEN (predecoded interpreter: equivalent to the golden sim + measurably faster -- the sovereign emulator's perf climb toward QEMU, validated not asserted)\n" as *u8); sys_exit(0); return 0 117 } 118 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1 119}