code wiki / _hdl_build / nx_rv64_timer_oracle.nx

nx_rv64_timer_oracle.nx source

↩ module page · 92 lines · 7403 B

1// nx_rv64_timer_oracle.nx -- ROADMAP R2 (privilege, part 4 = LAST): validate the golden sim's TIMER INTERRUPT (CLINT 2// MTIP) -- what a PREEMPTIVE OS scheduler runs on -- against QEMU. All M-mode. Sets mtimecmp[0]=1 (so MTIME reaches it 3// almost immediately), points mtvec at a handler, enables mie.MTIE + mstatus.MIE, then SPINS until the timer fires. The 4// handler reads mcause (= machine-timer-interrupt 0x8000..0007), masks the low byte (0x07), disarms the timer 5// (mtimecmp=-1 so MTIP clears -> no re-fire), mret's back; the spin flag is now set so the loop exits. Emits mcause low 6// byte + sentinel. The FIRING TIME differs sim-vs-QEMU (mtime rates differ) but the emitted mcause is DETERMINISTIC -> 7// the spin-loop makes the output timing-independent. expect UART = 07 2a. expect_exit: 0 NEVER-BRICK: simulation. 8import "nx_syscalls.nx" 9import "nishi_hdl_primitives.nx" 10import "rv64im_min_decoder.nx" 11import "rv64im_min_alu.nx" 12import "rv64im_min_regfile.nx" 13import "rv64im_min_csr.nx" 14import "rv64im_min_clint.nx" 15import "rv64im_min_uart.nx" 16import "rv64im_min_virtio.nx" 17import "rv64im_min_mmu.nx" 18import "rv64im_min_sim.nx" 19import "nx_rv64_asm.nx" 20const DMEM_MAGIC_4096: i64 = 4096 21const DMEM_MAGIC_10000000: i64 = 10000000 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 } 24func g_pn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var x: i64=v; 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 } 25func 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 } 26func 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 } 27 28const DMEM_BASE: i64 = 0x80000000 29const DMEM_SIZE: i64 = 65536 30const CSR_MTVEC: i64 = 0x305 31const CSR_MIE: i64 = 0x304 32const CSR_MSTATUS: i64 = 0x300 33const CSR_MCAUSE: i64 = 0x342 34 35func main() -> i64 { 36 g_puts("nx_rv64_timer_oracle (R2: CLINT timer interrupt -- preemptive-scheduler primitive -- on the golden sim, for QEMU diff)\n" as *u8) 37 var pass: i64=0; var total: i64=0 38 let code: *u8=sys_mmap(DMEM_MAGIC_4096); let po: *i64=sys_mmap(8) as *i64; po[0]=0 39 po[0]=ra_put(code, po[0], ra_u(0x37, 1, 0x10000)); po[0]=ra_put(code, po[0], ra_u(0x37, 2, 0x100)) 40 po[0]=ra_put(code, po[0], ra_u(0x37, 3, 0x5)); po[0]=ra_put(code, po[0], ra_i(0x13, 0, 3, 3, 0x555)) 41 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 30, 0, 0)) // addi x30,x0,0 (spin flag, BEFORE enabling interrupts) 42 // mtimecmp[0] (0x02004000) = 1 -> MTIME reaches it almost immediately 43 po[0]=ra_put(code, po[0], ra_u(0x37, 4, 0x02004)) // lui x4, 0x02004 -> 0x02004000 (no sign ext) 44 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 5, 0, 1)); po[0]=ra_put(code, po[0], ra_s(0x23, 3, 4, 5, 0)) // sd x5(=1), 0(x4) 45 // mtvec = handler (auipc + patched addi) 46 let A: i64 = po[0]; po[0]=ra_put(code, po[0], ra_u(0x17, 6, 0)) 47 let addi6: i64 = po[0]; po[0]=ra_put(code, po[0], ra_i(0x13, 0, 6, 6, 0)) 48 po[0]=ra_put(code, po[0], ra_i(0x73, 1, 0, 6, CSR_MTVEC)) // csrrw x0, mtvec, x6 49 // enable mie.MTIE (bit 7) then mstatus.MIE (bit 3) LAST 50 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 7, 0, 0x80)); po[0]=ra_put(code, po[0], ra_i(0x73, 2, 0, 7, CSR_MIE)) 51 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 8, 0, 0x8)); po[0]=ra_put(code, po[0], ra_i(0x73, 2, 0, 8, CSR_MSTATUS)) 52 // spin: beq x30, x0, self (offset 0 -> branch to self while flag==0; the interrupt breaks it) 53 po[0]=ra_put(code, po[0], ra_b(0, 30, 0, 0)) // beq x30, x0, . 54 // interrupt fired -> x30 = mcause low byte (0x07); emit + halt 55 po[0]=ra_put(code, po[0], ra_s(0x23, 0, 1, 30, 0)) // sb x30, 0(x1) -> 0x07 56 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 9, 0, 0x2A)); po[0]=ra_put(code, po[0], ra_s(0x23, 0, 1, 9, 0)) // sentinel 57 po[0]=ra_put(code, po[0], ra_s(0x23, 2, 2, 3, 0)); po[0]=ra_put(code, po[0], ra_j(0, 0)) // finisher halt + spin 58 let handler: i64 = po[0] // machine timer interrupt handler (M-mode) 59 po[0]=ra_put(code, po[0], ra_i(0x73, 2, 30, 0, CSR_MCAUSE)) // csrrs x30, mcause, x0 -> 0x8000..0007 60 po[0]=ra_put(code, po[0], ra_i(0x13, 7, 30, 30, 0xFF)) // andi x30, x30, 0xff -> 0x07 (cause number) 61 po[0]=ra_put(code, po[0], ra_u(0x37, 11, 0x02004)) // lui x11, 0x02004 (mtimecmp addr) 62 po[0]=ra_put(code, po[0], ra_i(0x13, 0, 12, 0, 0-1)); po[0]=ra_put(code, po[0], ra_s(0x23, 3, 11, 12, 0)) // sd -1, 0(x11) disarm (MTIP clears) 63 po[0]=ra_put(code, po[0], ra_i(0x73, 0, 0, 0, 0x302)) // mret -> back to the spin (flag now set -> loop exits) 64 let nb: i64 = po[0] 65 ra_put(code, addi6, ra_i(0x13, 0, 6, 6, handler - A)) // patch mtvec-addr addi 66 67 g_puts(" emitted "); g_pn(nb/4); g_puts(" instr; handler@"); g_pn(handler); g_puts("\n" as *u8) 68 69 let rf_storage: *i64=sys_mmap(8*NX_RV64IM_RF_N_REGS) as *i64; let csr_storage: *i64=sys_mmap(8*NX_CSR_SLOT_N) as *i64 70 let clint_storage: *i64=sys_mmap(8*NX_CLINT_SLOT_N) as *i64; let uart_storage: *i64=sys_mmap(8*NX_UART_SLOT_N) as *i64 71 let mem: *u8=sys_mmap(DMEM_SIZE); let tx_buf: *u8=sys_mmap(256) 72 let rf: *NxRv64imRegfile=sys_mmap(64) as *NxRv64imRegfile; let csr: *NxRv64imCsrFile=sys_mmap(64) as *NxRv64imCsrFile 73 let clint: *NxClint=sys_mmap(64) as *NxClint; let uart: *NxUart=sys_mmap(64) as *NxUart; let sim: *NxRv64imSim=sys_mmap(128) as *NxRv64imSim 74 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) 75 nx_rv64im_sim_init(sim, rf, csr, clint, uart, DMEM_BASE, mem, DMEM_SIZE, 0) 76 var i: i64=0; while i<nb { mem[i]=code[i]; i=i+1 } 77 nx_rv64im_sim_run(sim, DMEM_MAGIC_10000000) 78 let n: i64=nx_uart_tx_count(uart) 79 g_puts(" golden sim UART ("); 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) 80 g_puts(" expected : 07 2a (mcause=machine-timer-interrupt cause 0x07, sentinel)\n" as *u8) 81 82 var t1: i64=0; if n==2 { if tx_buf[0]==(0x07 as u8) { if tx_buf[1]==(0x2a as u8) { t1=1 } } } 83 pass=pass+ck("T1: golden sim fires the CLINT timer interrupt (mcause=0x07) + mret round-trip -> UART = 07 2a" as *u8, t1); total=total+1 84 let fd: i64=sys_openat_wr("knowledge/hw/timer.bin" as *u8, 420) 85 var t2: i64=0; if fd>=0 { let wn: i64=sys_write(fd, code, nb); sys_close(fd); if wn==nb { t2=1 } } 86 pass=pass+ck("T2: flat binary written to knowledge/hw/timer.bin for the QEMU cross-check" as *u8, t2); total=total+1 87 88 var okall: i64=0; if pass==total { okall=1 } 89 g_puts("---- nx_rv64_timer_oracle: passed "); g_pn(pass); g_puts(" / "); g_pn(total); g_puts(" ----\n" as *u8) 90 if okall==1 { g_puts("verdict=GREEN (golden-sim CLINT timer interrupt bytes emitted + binary written; run in qemu-system-riscv64 and diff)\n" as *u8); sys_exit(0); return 0 } 91 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1 92}