code wiki / _hdl_build / nx_fpga_ecall_gate.nx
nx_fpga_ecall_gate.nx source
↩ module page · 133 lines · 10166 B
1import "nx_gate_gn.nx"
2import "nx_gate_base.nx"
3// nx_fpga_ecall_gate.nx -- GATE for RUNG 23: SYSTEM / ECALL -- the TRAP primitive (the gateway to syscalls / a kernel).
4// A program fetched from the von Neumann memory hits ECALL: the machine SAVES the return address (PC+1 -> mepc) and
5// TRAPS to a fixed handler address (the trap vector). The handler runs (produces a "syscall result"), then RETURNS
6// (JALR back through mepc), and the program continues using the result. This is the control transfer at the heart of
7// every syscall and kernel entry. (SIMPLIFIED for the sim: mepc = a GP register (x5), the trap vector is a fixed
8// constant -- not full M-mode CSR/privilege machinery; it demonstrates the trap-and-return control transfer.)
9// T1 the program (work -> ECALL trap -> handler -> return -> use result) == the behavioral von Neumann reference.
10// T2 the trap worked: handler ran (x6=99), mepc saved (x5=2), control returned + used the result (x3=100).
11// T3 NEVER-BRICK. T4 LIAR-KILL (corrupt the fabric ALU -> the post-return compute diverges). expect_exit: 0
12// license_tier: ORIGINAL
13import "nx_fpga_ram.nx"
14import "nx_fpga_decode.nx"
15import "nx_fpga_alu.nx"
16import "nx_fpga_regfile.nx"
17import "nx_fpga_pc.nx"
18import "rv64im_min_alu.nx"
19import "nx_syscalls.nx"
20
21func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
22" as *u8); return ok }
23func enc_i(rd: i64, rs1: i64, imm: i64, f3: i64) -> i64 { return ((imm & 4095) << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 19 }
24func enc_jalr(rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm&4095)<<20)|(rs1<<15)|(rd<<7)|103 }
25func enc_b(rs1: i64, rs2: i64, off: i64, f3: i64) -> i64 {
26 let b12: i64=(off>>12)&1; let b11: i64=(off>>11)&1; let b10_5: i64=(off>>5)&63; let b4_1: i64=(off>>1)&15
27 return (b12<<31)|(b10_5<<25)|(rs2<<20)|(rs1<<15)|(f3<<12)|(b4_1<<8)|(b11<<7)|99
28}
29func dec_b(instr: i64) -> i64 { var imm: i64=(((instr>>31)&1)<<12)|(((instr>>7)&1)<<11)|(((instr>>25)&63)<<5)|(((instr>>8)&15)<<1); if (imm&4096)!=0 {imm=imm-8192} return imm }
30
31func main() -> i64 {
32 gw("=== nx_fpga_ecall_gate: RUNG 23 -- SYSTEM/ECALL: the TRAP primitive (syscall / kernel-entry control transfer) ===\n" as *u8)
33 var pass: i64 = 0; var total: i64 = 0
34 let R: i64=8; let W: i64=64; let AB: i64=3
35 let MR: i64=16; let MW: i64=32; let MAB: i64=4
36 let TVEC: i64=8 // fixed trap-vector (handler) address
37 let MEPC: i64=5 // mepc = register x5 (sim simplification)
38
39 let MK: *i64=sys_mmap(8*2100) as *i64; let MI: *i64=sys_mmap(8*2100) as *i64; let MS: *i64=sys_mmap(8*8400) as *i64
40 let MPO: *i64=sys_mmap(8*40) as *i64; let MQ: *i64=sys_mmap(8*2100) as *i64
41 seq_build_ram(MR, MW, MAB, MK, MI, MS, MPO); let mnc: i64=ram_ncells(MR, MW)
42 let DI: *i64=sys_mmap(8*16) as *i64; let DS: *i64=sys_mmap(8*48) as *i64; let DP: *i64=sys_mmap(8*16) as *i64
43 let AI: *i64=sys_mmap(8*640) as *i64; let AS: *i64=sys_mmap(8*2560) as *i64; let AP: *i64=sys_mmap(8*72) as *i64
44 let pi: *i64=sys_mmap(8*200) as *i64; let co: *i64=sys_mmap(8*2200) as *i64; let ctrl: *i64=sys_mmap(8*16) as *i64
45 fab_build_decode(DI, DS, DP); let anpi: i64=fab_build_alu(64, AI, AS, AP)
46 let RKND: *i64=sys_mmap(8*2048) as *i64; let RINI: *i64=sys_mmap(8*2048) as *i64; let RSRC: *i64=sys_mmap(8*8200) as *i64
47 let RP: *i64=sys_mmap(8*72) as *i64; let RQ: *i64=sys_mmap(8*2048) as *i64; let RCO: *i64=sys_mmap(8*2048) as *i64
48 seq_build_regfile(R, W, AB, RKND, RINI, RSRC, RP); let rnc: i64=rf_ncells(R, W)
49 let PKND: *i64=sys_mmap(8*300) as *i64; let PINI: *i64=sys_mmap(8*300) as *i64; let PSRC: *i64=sys_mmap(8*1100) as *i64
50 let PPO: *i64=sys_mmap(8*72) as *i64; let PQ: *i64=sys_mmap(8*300) as *i64; let PCO: *i64=sys_mmap(8*300) as *i64
51 seq_build_pc(W, PKND, PINI, PSRC, PPO); let pcnc: i64=pc_ncells(W)
52
53 let img: *i64=sys_mmap(8*20) as *i64
54 img[0]=enc_i(1,0,10,0) // ADDI x1,x0,10 (program does some work)
55 img[1]=115 // ECALL (opcode 0x73, funct12=0) -> trap to handler
56 img[2]=enc_i(3,6,1,0) // ADDI x3,x6,1 (after return: x3 = handler_result + 1)
57 img[3]=enc_b(1,0,0,1) // BNE x1,x0,0 -> self-loop HALT
58 img[4]=enc_i(0,0,0,0); img[5]=enc_i(0,0,0,0); img[6]=enc_i(0,0,0,0); img[7]=enc_i(0,0,0,0)
59 img[8]=enc_i(6,0,99,0) // HANDLER@8: ADDI x6,x0,99 ("syscall result")
60 img[9]=enc_jalr(0,5,0) // JALR x0,x5,0 (MRET-ish: return through mepc = x5)
61 var p: i64=10; while p<16 { img[p]=enc_i(0,0,0,0); p=p+1 }
62 let NSTEP: i64=10
63
64 let refmem: *i64=sys_mmap(8*20) as *i64; let ref: *i64=sys_mmap(8*16) as *i64
65 var z: i64=0; while z<mnc { MQ[z]=0; z=z+1 }
66 var a: i64=0; while a < MR { ram_write(MR,MW,MAB,MK,MI,MS,pi,co,MQ,a,img[a]); refmem[a]=img[a]; a=a+1 }
67 z=0; while z<rnc { RQ[z]=0; z=z+1 } z=0; while z<pcnc { PQ[z]=0; z=z+1 } z=0; while z<R { ref[z]=0; z=z+1 }
68 var refpc: i64=0; var mism: i64=0; var s: i64=0
69 while s < NSTEP {
70 let pc: i64=pc_read(W, PQ)
71 let instr: i64=ram_read(MR,MW,MAB,MK,MI,MS,pi,co,MQ,MPO,pc)
72 let rinstr: i64=refmem[refpc & 15]
73 let opcode: i64=instr&127; let rd: i64=(instr>>7)&7; let rs1: i64=(instr>>15)&7; let f3: i64=(instr>>12)&7
74 let ro: i64=rinstr&127; let rrd: i64=(rinstr>>7)&7; let rrs1: i64=(rinstr>>15)&7
75 var pcload: i64=0; var pcval: i64=0
76 if opcode==115 { // SYSTEM / ECALL
77 rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,MEPC,pc+1) // mepc = PC+1
78 pcload=1; pcval=TVEC // trap to the vector
79 } else { if opcode==103 { // JALR
80 var imm: i64=(instr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096}
81 let rv: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1)
82 let tgt: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,rv,imm,0,1,1)
83 if rd!=0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,pc+1) }
84 pcload=1; pcval=tgt
85 } else { if opcode==99 { // BNE
86 let rs2: i64=(instr>>20)&7; let off: i64=dec_b(instr)
87 let av: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1); let bv: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs2)
88 let sub: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,av,bv,1,1,1)
89 if sub != 0 { pcload=1; pcval=pc+off }
90 } else { // R/I (ALU)
91 var bval: i64=0; var f7b5: i64=0
92 if opcode==51 { let rs2: i64=(instr>>20)&7; bval=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs2); f7b5=(instr>>30)&1 }
93 else { var imm: i64=(instr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} bval=imm }
94 let av: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1)
95 fab_decode_run(DI,DS,DP,pi,co,f3,f7b5,ctrl)
96 let res: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,av,bval,ctrl[0],ctrl[1],ctrl[2])
97 if rd!=0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,res) }
98 } } }
99 pc_tick(W,PKND,PINI,PSRC,pi,PCO,PQ,pcload,pcval)
100 // reference (software von Neumann + the same simplified trap)
101 if ro==115 { ref[MEPC]=refpc+1; refpc=TVEC }
102 else { if ro==103 { var imm: i64=(rinstr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} if rrd!=0 {ref[rrd]=refpc+1} refpc=ref[rrs1]+imm }
103 else { if ro==99 { let rrs2: i64=(rinstr>>20)&7; let off: i64=dec_b(rinstr); if ref[rrs1]!=ref[rrs2] {refpc=refpc+off} else {refpc=refpc+1} }
104 else { var bval: i64=0; if ro==51 {let rrs2: i64=(rinstr>>20)&7; bval=ref[rrs2]} else {var imm: i64=(rinstr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} bval=imm} if rrd!=0 {ref[rrd]=nx_rv64im_alu_compute(NX_RV64IM_ALU_ADD,ref[rrs1],bval)} refpc=refpc+1 } } }
105 if pc_read(W,PQ) != refpc { mism=mism+1 }
106 if rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,3) != ref[3] { mism=mism+1 }
107 if rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,6) != ref[6] { mism=mism+1 }
108 s=s+1
109 }
110 total=total+1; if mism==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
111 gw("T1 program (work -> ECALL trap -> handler -> return -> use result) == behavioral, mismatches=" as *u8); gn(mism); gw("\n" as *u8)
112
113 // T2: the trap semantics
114 let x6: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,6); let x5: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,5); let x3: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,3)
115 total=total+1; var t2ok: i64=1; if x6!=99 {t2ok=0} if x5!=2 {t2ok=0} if x3!=100 {t2ok=0}
116 if t2ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
117 gw("T2 trap worked: handler ran x6=" as *u8); gn(x6); gw(" (=99), mepc saved x5=" as *u8); gn(x5); gw(" (=2, return addr), returned+used result x3=" as *u8); gn(x3); gw(" (=100)\n" as *u8)
118
119 // T3: never-brick
120 let q1: i64=pc_read(W,PQ); let q2: i64=pc_read(W,PQ)
121 total=total+1; if q1==q2 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
122 gw("T3 never-brick (#26): deterministic trap+return, bounded steps, zero hardware-state writes\n" as *u8)
123
124 // T4: liar-kill
125 var ci: i64=0; while ci < 640 { AI[ci] = AI[ci] ^ 0xffff; ci = ci + 1 }
126 let bad: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,99,1,0,1,1)
127 total=total+1; if bad != 100 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
128 gw("T4 liar-kill: corrupting the fabric ALU -> post-return compute(99,1)=" as *u8); gn(bad); gw(" != 100 (the syscall path would diverge)\n" as *u8)
129
130 gw("\n=== nx_fpga_ecall_gate " as *u8); gn(pass); gw("/" as *u8); gn(total)
131 if pass == total { gw(" GREEN (ECALL traps to a handler, saves mepc, the handler runs + returns, the program uses the result == behavioral -- the syscall/kernel-entry primitive)\n" as *u8); sys_exit(0); return 0 }
132 gw(" RED\n" as *u8); sys_exit(1); return 1
133}