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}