code wiki / _hdl_build / nx_fpga_cpu3_gate.nx

nx_fpga_cpu3_gate.nx source

↩ module page · 199 lines · 12696 B

1import "nx_gate_gn.nx" 2import "nx_gate_base.nx" 3// nx_fpga_cpu3_gate.nx -- GATE for RUNG 13: the fabric CPU with CONTROL FLOW. The PC is now a FABRIC REGISTER 4// (nx_fpga_pc, loadable up-counter) and the CPU executes B-TYPE BRANCHES: it fetches instr[PC], and on a branch 5// it computes the condition (rs1 vs rs2) ON THE FABRIC (ALU SUB for ==, the compare fabric for </>=) and either 6// LOADS the branch target into the PC or lets it increment. Runs a real LOOP (sum 1..3 with a backward BNE) and 7// proves the PC AND the register file evolve step-for-step == the behavioral reference. (PC is instruction-indexed; 8// branch offsets in instruction units -- a faithfulness simplification of RV byte addressing.) 9// T1 LOOP-RUN == reference: PC + all registers match every step (13 steps of a 3-iteration loop). 10// T2 final state: x2 = sum(1..3) = 6, x1 = 0 (loop terminated). T3 NEVER-BRICK. T4 LIAR-KILL. 11// GREEN iff all pass. expect_exit: 0 license_tier: ORIGINAL 12import "nx_fpga_decode.nx" 13import "nx_fpga_alu.nx" 14import "nx_fpga_cmp.nx" 15import "nx_fpga_shift.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 } 23 24func ref_aluop(f3: i64, f7b5: i64) -> i64 { 25 if f3==0 { if f7b5==1 { return NX_RV64IM_ALU_SUB } return NX_RV64IM_ALU_ADD } 26 if f3==1 { return NX_RV64IM_ALU_SLL } 27 if f3==2 { return NX_RV64IM_ALU_SLT } 28 if f3==3 { return NX_RV64IM_ALU_SLTU } 29 if f3==4 { return NX_RV64IM_ALU_XOR } 30 if f3==5 { if f7b5==1 { return NX_RV64IM_ALU_SRA } return NX_RV64IM_ALU_SRL } 31 if f3==6 { return NX_RV64IM_ALU_OR } 32 return NX_RV64IM_ALU_AND 33} 34func enc_i(rd: i64, rs1: i64, imm: i64, f3: i64) -> i64 { return ((imm & 4095) << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 19 } 35func enc_r(rd: i64, rs1: i64, rs2: i64, f3: i64, f7b5: i64) -> i64 { var f7: i64=0; if f7b5==1 {f7=32} return (f7<<25)|(rs2<<20)|(rs1<<15)|(f3<<12)|(rd<<7)|51 } 36// B-type: real RV immediate split (interpreted as instruction offset). off must be even (imm[0]=0). 37func enc_b(rs1: i64, rs2: i64, off: i64, f3: i64) -> i64 { 38 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 39 return (b12<<31)|(b10_5<<25)|(rs2<<20)|(rs1<<15)|(f3<<12)|(b4_1<<8)|(b11<<7)|99 // 0x63 40} 41func dec_btype(instr: i64) -> i64 { 42 var imm: i64 = (((instr>>31)&1)<<12) | (((instr>>7)&1)<<11) | (((instr>>25)&63)<<5) | (((instr>>8)&15)<<1) 43 if (imm & 4096) != 0 { imm = imm - 8192 } 44 return imm 45} 46 47func cpu_exec(DI: *i64, DS: *i64, DP: *i64, AI: *i64, AS: *i64, AP: *i64, anpi: i64, CI: *i64, CS: *i64, CP: *i64, cnpi: i64, SI: *i64, SS: *i64, SP: *i64, snpi: i64, pi: *i64, co: *i64, ctrl: *i64, f3: i64, f7b5: i64, a: i64, b: i64) -> i64 { 48 fab_decode_run(DI, DS, DP, pi, co, f3, f7b5, ctrl) 49 let cls: i64 = ctrl[6] | (ctrl[7] << 1) 50 if cls == 0 { return fab_alu_run(64, anpi, AI, AS, AP, pi, co, a, b, ctrl[0], ctrl[1], ctrl[2]) } 51 if cls == 1 { return fab_shift_run(64, snpi, SI, SS, SP, pi, co, a, b & 63, ctrl[3], ctrl[4]) } 52 return fab_cmp_run(64, cnpi, CI, CS, CP, pi, co, a, b, ctrl[5]) 53} 54 55func main() -> i64 { 56 gw("=== nx_fpga_cpu3_gate: RUNG 13 -- the fabric CPU with CONTROL FLOW (fabric PC + branches + a LOOP) ===\n" as *u8) 57 var pass: i64 = 0; var total: i64 = 0 58 let R: i64 = 8; let W: i64 = 64; let AB: i64 = 3 59 60 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 61 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 62 let CI: *i64=sys_mmap(8*256) as *i64; let CS: *i64=sys_mmap(8*1056) as *i64; let CP: *i64=sys_mmap(8*72) as *i64 63 let SI: *i64=sys_mmap(8*800) as *i64; let SS: *i64=sys_mmap(8*3200) as *i64; let SP: *i64=sys_mmap(8*72) as *i64 64 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 65 fab_build_decode(DI, DS, DP) 66 let anpi: i64 = fab_build_alu(64, AI, AS, AP) 67 let cnpi: i64 = fab_build_cmp(64, CI, CS, CP) 68 let snpi: i64 = fab_build_shifter(64, SI, SS, SP) 69 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 70 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 71 seq_build_regfile(R, W, AB, RKND, RINI, RSRC, RP) 72 let rnc: i64 = rf_ncells(R, W) 73 // fabric PC register 74 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 75 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 76 seq_build_pc(W, PKND, PINI, PSRC, PPO) 77 let pcnc: i64 = pc_ncells(W) 78 79 // PROGRAM (instruction-indexed): sum 1..3 via a backward-branch loop. padded with NOPs. 80 let prog: *i64 = sys_mmap(8 * 24) as *i64 81 prog[0]=enc_i(1,0,3,0) // ADDI x1,x0,3 (counter=3) 82 prog[1]=enc_i(2,0,0,0) // ADDI x2,x0,0 (sum=0) 83 prog[2]=enc_r(2,2,1,0,0) // ADD x2,x2,x1 (sum += counter) <- loop top (PC=2) 84 prog[3]=enc_i(1,1,0-1,0) // ADDI x1,x1,-1 (counter -= 1) 85 prog[4]=enc_b(1,0,0-2,1) // BNE x1,x0,-2 (if x1!=0 -> PC=2) 86 var pz: i64 = 5; while pz < 24 { prog[pz]=enc_i(0,0,0,0); pz=pz+1 } // NOP (ADDI x0,x0,0) 87 88 let ref: *i64 = sys_mmap(8 * 16) as *i64 89 let NSTEP: i64 = 13 90 91 // ---- T1: run; compare PC + regfile to the reference after every step ---- 92 var z: i64 = 0; while z < rnc { RQ[z]=0; z=z+1 } 93 z = 0; while z < pcnc { PQ[z]=0; z=z+1 } 94 z = 0; while z < R { ref[z]=0; z=z+1 } 95 var refpc: i64 = 0 96 var mism: i64 = 0; var cmps: i64 = 0 97 var s: i64 = 0 98 while s < NSTEP { 99 let pc: i64 = pc_read(W, PQ) 100 let instr: i64 = prog[pc] 101 let opcode: i64 = instr & 127 102 let rd: i64=(instr>>7)&7; let rs1: i64=(instr>>15)&7; let f3: i64=(instr>>12)&7 103 var pcload: i64 = 0; var pcval: i64 = 0 104 if opcode == 99 { // B-type branch 105 let rs2: i64=(instr>>20)&7 106 let av: i64 = rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1) 107 let bv: i64 = rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs2) 108 let off: i64 = dec_btype(instr) 109 // condition ON THE FABRIC: BNE(1) via ALU SUB != 0 110 var taken: i64 = 0 111 if f3==1 { let sub: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,av,bv,1,1,1); if sub!=0 {taken=1} } 112 if f3==0 { let sub: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,av,bv,1,1,1); if sub==0 {taken=1} } 113 if f3==4 { taken=fab_cmp_run(64,cnpi,CI,CS,CP,pi,co,av,bv,0) } 114 if f3==5 { let lt: i64=fab_cmp_run(64,cnpi,CI,CS,CP,pi,co,av,bv,0); if lt==0 {taken=1} } 115 if f3==6 { taken=fab_cmp_run(64,cnpi,CI,CS,CP,pi,co,av,bv,1) } 116 if f3==7 { let lt: i64=fab_cmp_run(64,cnpi,CI,CS,CP,pi,co,av,bv,1); if lt==0 {taken=1} } 117 if taken==1 { pcload=1; pcval=pc+off } 118 // reference 119 var rtaken: i64 = 0 120 if f3==1 { if ref[rs1]!=ref[rs2] {rtaken=1} } 121 if f3==0 { if ref[rs1]==ref[rs2] {rtaken=1} } 122 if f3==4 { if ref[rs1]<ref[rs2] {rtaken=1} } 123 if f3==5 { if ref[rs1]>=ref[rs2] {rtaken=1} } 124 if f3==6 { rtaken=nx_rv64im_ltu(ref[rs1],ref[rs2]) } 125 if f3==7 { if nx_rv64im_ltu(ref[rs1],ref[rs2])==0 {rtaken=1} } 126 if rtaken==1 { refpc = refpc + off } else { refpc = refpc + 1 } 127 } else { // R/I-type 128 var bval: i64=0; var refB: i64=0; var f7b5: i64=0 129 if opcode == 51 { let rs2: i64=(instr>>20)&7; bval=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs2); refB=ref[rs2]; f7b5=(instr>>30)&1 } 130 else { var imm: i64=(instr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} bval=imm; refB=imm; if f3==1 {f7b5=(instr>>30)&1} if f3==5 {f7b5=(instr>>30)&1} } 131 let av: i64 = rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1) 132 let res: i64 = cpu_exec(DI,DS,DP,AI,AS,AP,anpi,CI,CS,CP,cnpi,SI,SS,SP,snpi,pi,co,ctrl,f3,f7b5,av,bval) 133 if rd != 0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,res) } 134 if rd != 0 { ref[rd] = nx_rv64im_alu_compute(ref_aluop(f3,f7b5), ref[rs1], refB) } 135 refpc = refpc + 1 136 } 137 pc_tick(W, PKND, PINI, PSRC, pi, PCO, PQ, pcload, pcval) 138 // COMPARE PC + all registers 139 cmps = cmps + 1 140 if pc_read(W, PQ) != refpc { mism = mism + 1 } 141 var rr: i64 = 0 142 while rr < R { cmps = cmps + 1; if rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rr) != ref[rr] { mism = mism + 1 } rr = rr + 1 } 143 s = s + 1 144 } 145 total=total+1; if mism==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 146 gw("T1 LOOP runs on the fabric == behavioral, step-for-step (PC + regfile): " as *u8); gn(cmps); gw(" compares over " as *u8); gn(NSTEP); gw(" steps, mismatches=" as *u8); gn(mism); gw("\n" as *u8) 147 148 // ---- T2: final state -- the loop computed sum(1..3) = 6, counter x1 = 0 ---- 149 let x2: i64 = rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,2) 150 let x1: i64 = rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,1) 151 total=total+1; if x2==6 { if x1==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) } 152 gw("T2 the loop computed sum(1..3): x2=" as *u8); gn(x2); gw(" (=6), x1=" as *u8); gn(x1); gw(" (counter hit 0 -> loop exited)\n" as *u8) 153 154 // ---- T3: never-brick (deterministic re-read of the PC) ---- 155 let p1: i64 = pc_read(W, PQ); let p2: i64 = pc_read(W, PQ) 156 total=total+1; if p1==p2 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 157 gw("T3 never-brick (#26): deterministic PC + bounded step, zero hardware-state writes\n" as *u8) 158 159 // ---- T4: liar-kill -- corrupt the PC increment net -> the loop diverges from the reference ---- 160 seq_build_pc(W, PKND, PINI, PSRC, PPO) 161 PINI[W + 1] = PINI[W + 1] ^ 0xffff 162 z = 0; while z < rnc { RQ[z]=0; z=z+1 } 163 z = 0; while z < pcnc { PQ[z]=0; z=z+1 } 164 let ref2: *i64 = sys_mmap(8*16) as *i64; z=0; while z<R { ref2[z]=0; z=z+1 } 165 var rpc2: i64 = 0; var s2: i64 = 0; var liar: i64 = 0 166 while s2 < NSTEP { 167 let pc: i64 = pc_read(W, PQ) 168 var fpc: i64 = pc; if fpc < 0 { fpc = 0 } if fpc > 23 { fpc = 23 } 169 let instr: i64 = prog[fpc] 170 let opcode: i64=instr&127; let rd: i64=(instr>>7)&7; let rs1: i64=(instr>>15)&7; let f3: i64=(instr>>12)&7 171 var pcload: i64=0; var pcval: i64=0 172 if opcode == 99 { 173 let rs2: i64=(instr>>20)&7 174 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) 175 let off: i64=dec_btype(instr) 176 var taken: i64=0; if f3==1 { let sub: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,av,bv,1,1,1); if sub!=0 {taken=1} } 177 if taken==1 { pcload=1; pcval=pc+off } 178 if ref2[rs1]!=ref2[rs2] { rpc2=rpc2+off } else { rpc2=rpc2+1 } 179 } else { 180 var bval: i64=0; var refB: i64=0; var f7b5: i64=0 181 if opcode==51 { let rs2: i64=(instr>>20)&7; bval=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs2); refB=ref2[rs2]; f7b5=(instr>>30)&1 } 182 else { var imm: i64=(instr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} bval=imm; refB=imm } 183 let av: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1) 184 let res: i64=cpu_exec(DI,DS,DP,AI,AS,AP,anpi,CI,CS,CP,cnpi,SI,SS,SP,snpi,pi,co,ctrl,f3,f7b5,av,bval) 185 if rd!=0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,res) } 186 if rd!=0 { ref2[rd]=nx_rv64im_alu_compute(ref_aluop(f3,f7b5),ref2[rs1],refB) } 187 rpc2=rpc2+1 188 } 189 pc_tick(W, PKND, PINI, PSRC, pi, PCO, PQ, pcload, pcval) 190 if pc_read(W, PQ) != rpc2 { liar = liar + 1 } 191 s2 = s2 + 1 192 } 193 total=total+1; if liar > 0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 194 gw("T4 liar-kill: corrupting the fabric PC increment net -> the program-counter diverges in " as *u8); gn(liar); gw(" steps\n" as *u8) 195 196 gw("\n=== nx_fpga_cpu3_gate " as *u8); gn(pass); gw("/" as *u8); gn(total) 197 if pass == total { gw(" GREEN (a LOOP with branches runs on the simulated FPGA -- fabric PC + regfile + execute -- == the behavioral CPU, step-for-step)\n" as *u8); sys_exit(0); return 0 } 198 gw(" RED\n" as *u8); sys_exit(1); return 1 199}