code wiki / _hdl_build / nx_rv64_amo_gate.nx
nx_rv64_amo_gate.nx source
↩ module page · 112 lines · 7491 B
1// nx_rv64_amo_gate.nx -- RV64A (atomics) conformance gate for the sovereign behavioral sim.
2// Closes the F107h atomics rung: proves lr/sc + amo{swap,add,and,or,xor,min,max,minu,maxu}.{w,d}
3// decode+execute correctly on rv64im_min_sim by asserting rd (old value) + memory (new value)
4// against HAND-COMPUTED expected results. Self-checking, deterministic, reproducible. expect_exit:0
5import "nx_syscalls.nx"
6import "nishi_hdl_primitives.nx"
7import "rv64im_min_decoder.nx"
8import "rv64im_min_alu.nx"
9import "rv64im_min_regfile.nx"
10import "rv64im_min_csr.nx"
11import "rv64im_min_clint.nx"
12import "rv64im_min_uart.nx"
13import "rv64im_min_virtio.nx"
14import "rv64im_min_sim.nx"
15
16func 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 }
17func g_ph(v: i64) -> i64 {
18 let b: *u8=sys_mmap(19); b[0]=48; b[1]=120; var i: i64=0
19 while i<16 { let nyb: i64=(v>>((15-i)*4))&15; if nyb<10 { b[2+i]=(48+nyb) as u8 } if nyb>=10 { b[2+i]=(87+nyb) as u8 } i=i+1 }
20 sys_write(1,b,18); return 0
21}
22func chk(name: *u8, got: i64, exp: i64, fails: *i64) -> i64 {
23 if got != exp {
24 fails[0]=fails[0]+1
25 g_puts(" FAIL "); g_puts(name); g_puts(" exp="); g_ph(exp); g_puts(" got="); g_ph(got); g_puts("\n" as *u8)
26 }
27 return 0
28}
29
30const AMO_MEMB: i64 = 0x80000000
31const AMO_MEMS: i64 = 65536
32const AMO_SCR: i64 = 0x80002000 // scratch address the amo targets
33
34// AMO instruction word: funct5[31:27] aq[26] rl[25] rs2[24:20] rs1[19:15] funct3[14:12] rd[11:7] opcode=0x2f
35func amo_enc(funct5: i64, rs2: i64, rs1: i64, funct3: i64, rd: i64) -> i64 {
36 return (funct5<<27) | (rs2<<20) | (rs1<<15) | (funct3<<12) | (rd<<7) | 0x2f
37}
38func wr64(mem: *u8, off: i64, v: i64) -> i64 { var i: i64=0; while i<8 { mem[off+i]=((v>>(i*8))&0xff) as u8; i=i+1 } return 0 }
39func rd64(mem: *u8, off: i64) -> i64 { var v: i64=0; var i: i64=0; while i<8 { v=v|((mem[off+i] as i64)<<(i*8)); i=i+1 } return v }
40func put_inst(mem: *u8, off: i64, inst: i64) -> i64 {
41 mem[off]=(inst&0xff) as u8; mem[off+1]=((inst>>8)&0xff) as u8; mem[off+2]=((inst>>16)&0xff) as u8; mem[off+3]=((inst>>24)&0xff) as u8; return 0
42}
43
44// execute ONE `amo<op>.<w|d> x7, x6, (x15)`: mem[SCR]=initval, x6=operand. out[0]=rd (old), out[1]=mem[SCR] (new).
45func run_amo(funct5: i64, funct3: i64, initval: i64, operand: i64, out: *i64) -> i64 {
46 let rf_st: *i64=sys_mmap(8*NX_RV64IM_RF_N_REGS) as *i64; let csr_st: *i64=sys_mmap(8*NX_CSR_SLOT_N) as *i64
47 let clint_st: *i64=sys_mmap(8*NX_CLINT_SLOT_N) as *i64; let uart_st: *i64=sys_mmap(8*NX_UART_SLOT_N) as *i64
48 let tx: *u8=sys_mmap(64); let mem: *u8=sys_mmap(AMO_MEMS)
49 let rf: *NxRv64imRegfile=sys_mmap(64) as *NxRv64imRegfile; let csr: *NxRv64imCsrFile=sys_mmap(64) as *NxRv64imCsrFile
50 let clint: *NxClint=sys_mmap(64) as *NxClint; let uart: *NxUart=sys_mmap(64) as *NxUart; let sim: *NxRv64imSim=sys_mmap(256) as *NxRv64imSim
51 nx_rv64im_rf_init(rf, rf_st); nx_rv64im_csr_init(csr, csr_st, 0); nx_clint_init(clint, clint_st); nx_uart_init(uart, uart_st, tx, 64)
52 nx_rv64im_sim_init(sim, rf, csr, clint, uart, AMO_MEMB, mem, AMO_MEMS, 0)
53 var z: i64=0; while z<AMO_MEMS { mem[z]=0 as u8; z=z+1 }
54 wr64(mem, AMO_SCR-AMO_MEMB, initval)
55 nx_rv64im_rf_write(rf, 15, AMO_SCR); nx_rv64im_rf_write(rf, 6, operand)
56 put_inst(mem, 0, amo_enc(funct5, 6, 15, funct3, 7))
57 nx_rv64im_sim_step(sim)
58 out[0]=nx_rv64im_rf_read(rf, 7); out[1]=rd64(mem, AMO_SCR-AMO_MEMB)
59 return 0
60}
61
62// lr.d x7,(x15) [if do_lr] then sc.d x8,x6,(x15). out[0]=lr old, out[1]=sc result (0 ok / 1 fail), out[2]=mem[SCR].
63func run_lrsc(initval: i64, scval: i64, do_lr: i64, out: *i64) -> i64 {
64 let rf_st: *i64=sys_mmap(8*NX_RV64IM_RF_N_REGS) as *i64; let csr_st: *i64=sys_mmap(8*NX_CSR_SLOT_N) as *i64
65 let clint_st: *i64=sys_mmap(8*NX_CLINT_SLOT_N) as *i64; let uart_st: *i64=sys_mmap(8*NX_UART_SLOT_N) as *i64
66 let tx: *u8=sys_mmap(64); let mem: *u8=sys_mmap(AMO_MEMS)
67 let rf: *NxRv64imRegfile=sys_mmap(64) as *NxRv64imRegfile; let csr: *NxRv64imCsrFile=sys_mmap(64) as *NxRv64imCsrFile
68 let clint: *NxClint=sys_mmap(64) as *NxClint; let uart: *NxUart=sys_mmap(64) as *NxUart; let sim: *NxRv64imSim=sys_mmap(256) as *NxRv64imSim
69 nx_rv64im_rf_init(rf, rf_st); nx_rv64im_csr_init(csr, csr_st, 0); nx_clint_init(clint, clint_st); nx_uart_init(uart, uart_st, tx, 64)
70 nx_rv64im_sim_init(sim, rf, csr, clint, uart, AMO_MEMB, mem, AMO_MEMS, 0)
71 var z: i64=0; while z<AMO_MEMS { mem[z]=0 as u8; z=z+1 }
72 wr64(mem, AMO_SCR-AMO_MEMB, initval)
73 nx_rv64im_rf_write(rf, 15, AMO_SCR); nx_rv64im_rf_write(rf, 6, scval)
74 var pcoff: i64=0
75 if do_lr==1 { put_inst(mem, 0, amo_enc(0x02, 0, 15, 3, 7)); pcoff=4 } // lr.d x7,(x15) (rs2=0)
76 put_inst(mem, pcoff, amo_enc(0x03, 6, 15, 3, 8)) // sc.d x8,x6,(x15)
77 if do_lr==1 { nx_rv64im_sim_step(sim) }
78 nx_rv64im_sim_step(sim)
79 out[0]=nx_rv64im_rf_read(rf, 7); out[1]=nx_rv64im_rf_read(rf, 8); out[2]=rd64(mem, AMO_SCR-AMO_MEMB)
80 return 0
81}
82
83func main() -> i64 {
84 g_puts("nx_rv64_amo_gate (RV64A atomics conformance on the sovereign behavioral sim)\n" as *u8)
85 let out: *i64=sys_mmap(32) as *i64
86 let fails: *i64=sys_mmap(8) as *i64; fails[0]=0
87
88 // ---- amo* .d (funct3=3) ----
89 run_amo(0x00, 3, 100, 23, out); chk("amoadd.d/rd" as *u8, out[0], 100, fails); chk("amoadd.d/mem" as *u8, out[1], 123, fails)
90 run_amo(0x01, 3, 7, 99, out); chk("amoswap.d/rd" as *u8, out[0], 7, fails); chk("amoswap.d/mem" as *u8, out[1], 99, fails)
91 run_amo(0x0c, 3, 0xFF, 0x0F, out); chk("amoand.d/rd" as *u8, out[0], 0xFF, fails); chk("amoand.d/mem" as *u8, out[1], 0x0F, fails)
92 run_amo(0x08, 3, 0xF0, 0x0F, out); chk("amoor.d/rd" as *u8, out[0], 0xF0, fails); chk("amoor.d/mem" as *u8, out[1], 0xFF, fails)
93 run_amo(0x04, 3, 0xF0, 0x3C, out); chk("amoxor.d/rd" as *u8, out[0], 0xF0, fails); chk("amoxor.d/mem" as *u8, out[1], 0xCC, fails)
94 run_amo(0x10, 3, 10, 0-5, out); chk("amomin.d/rd" as *u8, out[0], 10, fails); chk("amomin.d/mem" as *u8, out[1], 0-5, fails)
95 run_amo(0x14, 3, 0-5, 10, out); chk("amomax.d/rd" as *u8, out[0], 0-5, fails); chk("amomax.d/mem" as *u8, out[1], 10, fails)
96 run_amo(0x18, 3, 10, 0-5, out); chk("amominu.d/rd" as *u8, out[0], 10, fails); chk("amominu.d/mem" as *u8, out[1], 10, fails)
97 run_amo(0x1c, 3, 10, 0-5, out); chk("amomaxu.d/rd" as *u8, out[0], 10, fails); chk("amomaxu.d/mem" as *u8, out[1], 0-5, fails)
98 // ---- amo* .w (funct3=2; rd sign-extended, store low-32) ----
99 run_amo(0x00, 2, 100, 23, out); chk("amoadd.w/rd" as *u8, out[0], 100, fails); chk("amoadd.w/mem" as *u8, out[1], 123, fails)
100 run_amo(0x00, 2, 0xFFFFFFFF, 1, out); chk("amoadd.w.sext/rd" as *u8, out[0], 0-1, fails); chk("amoadd.w.sext/mem" as *u8, out[1], 0, fails)
101 // ---- lr/sc .d ----
102 run_lrsc(42, 99, 1, out); chk("lr.d/old" as *u8, out[0], 42, fails); chk("sc.d.ok/res" as *u8, out[1], 0, fails); chk("sc.d.ok/mem" as *u8, out[2], 99, fails)
103 run_lrsc(42, 99, 0, out); chk("sc.d.nores/res" as *u8, out[1], 1, fails); chk("sc.d.nores/mem" as *u8, out[2], 42, fails)
104
105 g_puts("\n" as *u8)
106 if fails[0]==0 {
107 g_puts("verdict=GREEN (RV64A: lr/sc + 9 amo ops x {w,d} decode+execute match hand-computed reference; 27 assertions)\n" as *u8)
108 sys_exit(0); return 0
109 }
110 g_puts("verdict=RED ("); g_ph(fails[0]); g_puts(" assertion failures)\n" as *u8)
111 sys_exit(1); return 1
112}