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}