code wiki / _hdl_build / nx_fpga_cpu6_gate.nx

nx_fpga_cpu6_gate.nx source

↩ module page · 181 lines · 12886 B

1import "nx_gate_gn.nx" 2import "nx_gate_base.nx" 3// nx_fpga_cpu6_gate.nx -- GATE for RUNG 16: the UNIFIED fabric CPU. ONE step handles every RV64I class -- 4// R-type, I-type immediates, B-type branches, loads/stores (LW/SW + data memory), and jumps (JAL/JALR) -- and 5// runs ONE real program that uses them ALL together: store a 3-element array to memory, CALL a sum function (JAL), 6// which LOOPS (BNE) LOADING each element (LW) and accumulating (ADD), RETURNS (JALR); the caller reads the result. 7// Proven: registers AND data memory AND the PC evolve step-for-step == the behavioral reference. (No shift fabric 8// here -- this program doesn't shift; R8c proved shifts plug into the same cls=1 dispatch identically.) 9// T1 the whole program == reference (regfile + memory + PC, every step). T2 result = sum(10,20,30) = 60. 10// T3 NEVER-BRICK. T4 LIAR-KILL. expect_exit: 0 license_tier: ORIGINAL 11import "nx_fpga_decode.nx" 12import "nx_fpga_alu.nx" 13import "nx_fpga_cmp.nx" 14import "nx_fpga_regfile.nx" 15import "nx_fpga_pc.nx" 16import "rv64im_min_alu.nx" 17import "nx_syscalls.nx" 18 19func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw(" 20" as *u8); return ok } 21func ref_aluop(f3: i64, f7b5: i64) -> i64 { 22 if f3==0 { if f7b5==1 { return NX_RV64IM_ALU_SUB } return NX_RV64IM_ALU_ADD } 23 if f3==2 { return NX_RV64IM_ALU_SLT } 24 if f3==3 { return NX_RV64IM_ALU_SLTU } 25 if f3==4 { return NX_RV64IM_ALU_XOR } 26 if f3==6 { return NX_RV64IM_ALU_OR } 27 return NX_RV64IM_ALU_AND 28} 29func enc_i(rd: i64, rs1: i64, imm: i64, f3: i64) -> i64 { return ((imm & 4095) << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 19 } 30func enc_r(rd: i64, rs1: i64, rs2: i64, f3: i64) -> i64 { return (rs2<<20)|(rs1<<15)|(f3<<12)|(rd<<7)|51 } 31func enc_lw(rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm&4095)<<20)|(rs1<<15)|(2<<12)|(rd<<7)|3 } 32func enc_sw(rs1: i64, rs2: i64, imm: i64) -> i64 { return (((imm>>5)&127)<<25)|(rs2<<20)|(rs1<<15)|(2<<12)|((imm&31)<<7)|35 } 33func enc_jal(rd: i64, off: i64) -> i64 { return ((off&4095)<<20)|(rd<<7)|111 } 34func enc_jalr(rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm&4095)<<20)|(rs1<<15)|(rd<<7)|103 } 35func enc_b(rs1: i64, rs2: i64, off: i64, f3: i64) -> i64 { 36 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 37 return (b12<<31)|(b10_5<<25)|(rs2<<20)|(rs1<<15)|(f3<<12)|(b4_1<<8)|(b11<<7)|99 38} 39func 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 } 40 41func main() -> i64 { 42 gw("=== nx_fpga_cpu6_gate: RUNG 16 -- the UNIFIED fabric CPU (R/I/B/load/store/jump) runs a real program ===\n" as *u8) 43 var pass: i64 = 0; var total: i64 = 0 44 let R: i64=8; let W: i64=64; let AB: i64=3 45 46 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 47 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 48 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 49 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 50 fab_build_decode(DI, DS, DP) 51 let anpi: i64=fab_build_alu(64, AI, AS, AP) 52 let cnpi: i64=fab_build_cmp(64, CI, CS, CP) 53 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 54 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; let DQ: *i64=sys_mmap(8*2048) as *i64 55 seq_build_regfile(R, W, AB, RKND, RINI, RSRC, RP) 56 let rnc: i64=rf_ncells(R, W) 57 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 58 let PP: *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 59 seq_build_pc(W, PKND, PINI, PSRC, PP) 60 let pcnc: i64=pc_ncells(W) 61 62 let ref: *i64=sys_mmap(8*16) as *i64; let refmem: *i64=sys_mmap(8*16) as *i64; let prog: *i64=sys_mmap(8*32) as *i64 63 // PROGRAM: store array {10,20,30}, JAL to a sum-loop function, return, read result. 64 prog[0]=enc_i(1,0,10,0) // ADDI x1,x0,10 65 prog[1]=enc_i(2,0,20,0) // ADDI x2,x0,20 66 prog[2]=enc_i(3,0,30,0) // ADDI x3,x0,30 67 prog[3]=enc_sw(0,1,1) // SW x1,1(x0) mem[1]=10 (mem[0] is hardwired 0: data mem reuses the regfile fabric) 68 prog[4]=enc_sw(0,2,2) // SW x2,2(x0) mem[2]=20 69 prog[5]=enc_sw(0,3,3) // SW x3,3(x0) mem[3]=30 70 prog[6]=enc_jal(5,4) // JAL x5,+4 call sum@10; x5=link=7 71 prog[7]=enc_r(1,4,0,0) // ADD x1,x4,x0 (after return) x1 = result = 60 72 prog[8]=enc_jal(0,0) // halt 73 prog[9]=enc_jal(0,0) // (pad) 74 prog[10]=enc_i(4,0,0,0) // sum: x4=0 75 prog[11]=enc_i(6,0,1,0) // x6=1 (index; mem array starts at 1) 76 prog[12]=enc_i(7,0,4,0) // x7=4 (end, exclusive) 77 prog[13]=enc_lw(3,6,0) // loop: x3 = mem[x6] 78 prog[14]=enc_r(4,4,3,0) // x4 += x3 79 prog[15]=enc_i(6,6,1,0) // x6 += 1 80 prog[16]=enc_i(0,0,0,0) // NOP (parity filler: B-type offset must be even) 81 prog[17]=enc_b(6,7,0-4,1) // BNE x6,x7,-4 (if x6!=4 -> 13) 82 prog[18]=enc_jalr(0,5,0) // return: PC = x5 = 7 83 var pp: i64=19; while pp<32 { prog[pp]=enc_jal(0,0); pp=pp+1 } 84 let NSTEP: i64=30 85 86 var z: i64=0; while z<rnc { RQ[z]=0; DQ[z]=0; z=z+1 } 87 z=0; while z<pcnc { PQ[z]=0; z=z+1 } 88 z=0; while z<R { ref[z]=0; refmem[z]=0; z=z+1 } 89 var refpc: i64=0; var mism: i64=0; var cmps: i64=0 90 var s: i64=0 91 while s<NSTEP { 92 let pc: i64=pc_read(W,PQ) 93 let instr: i64=prog[pc] 94 let opcode: i64=instr&127 95 let rd: i64=(instr>>7)&7; let rs1: i64=(instr>>15)&7; let f3: i64=(instr>>12)&7 96 var pcload: i64=0; var pcval: i64=0 97 if opcode==111 { // JAL 98 var off: i64=(instr>>20)&4095; if (off&2048)!=0 {off=off-4096} 99 if rd!=0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,pc+1); ref[rd]=refpc+1 } 100 pcload=1; pcval=pc+off; refpc=refpc+off 101 } else { if opcode==103 { // JALR 102 var imm: i64=(instr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} 103 let rv: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1) 104 let tgt: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,rv,imm,0,1,1) 105 if rd!=0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,pc+1); ref[rd]=refpc+1 } 106 pcload=1; pcval=tgt; refpc=ref[rs1]+imm 107 } else { if opcode==99 { // B-type (BNE etc.) 108 let rs2: i64=(instr>>20)&7; let off: i64=dec_b(instr) 109 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) 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 var rt: i64=0 119 if f3==1 { if ref[rs1]!=ref[rs2] {rt=1} } if f3==0 { if ref[rs1]==ref[rs2] {rt=1} } 120 if f3==4 { if ref[rs1]<ref[rs2] {rt=1} } if f3==5 { if ref[rs1]>=ref[rs2] {rt=1} } 121 if f3==6 { rt=nx_rv64im_ltu(ref[rs1],ref[rs2]) } if f3==7 { if nx_rv64im_ltu(ref[rs1],ref[rs2])==0 {rt=1} } 122 if rt==1 { refpc=refpc+off } else { refpc=refpc+1 } 123 } else { if opcode==3 { // LW 124 var imm: i64=(instr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} 125 let base: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1) 126 let addr: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,base,imm,0,1,1) 127 let ld: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,DQ,RP,addr) 128 if rd!=0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,ld); ref[rd]=refmem[(ref[rs1]+imm)&7] } 129 refpc=refpc+1 130 } else { if opcode==35 { // SW 131 var imm: i64=(((instr>>25)&127)<<5)|((instr>>7)&31); if (imm&2048)!=0 {imm=imm-4096} 132 let rs2: i64=(instr>>20)&7 133 let base: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1); let val: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs2) 134 let addr: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,base,imm,0,1,1) 135 rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,DQ,addr,val); refmem[(ref[rs1]+imm)&7]=ref[rs2] 136 refpc=refpc+1 137 } else { // R/I-type (ALU-class) 138 var bval: i64=0; var refB: i64=0; var f7b5: i64=0 139 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 } 140 else { var imm: i64=(instr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} bval=imm; refB=imm } 141 let av: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1) 142 fab_decode_run(DI,DS,DP,pi,co,f3,f7b5,ctrl) 143 let res: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,av,bval,ctrl[0],ctrl[1],ctrl[2]) 144 if rd!=0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,res); ref[rd]=nx_rv64im_alu_compute(ref_aluop(f3,f7b5),ref[rs1],refB) } 145 refpc=refpc+1 146 } } } } } 147 pc_tick(W,PKND,PINI,PSRC,pi,PCO,PQ,pcload,pcval) 148 cmps=cmps+1; if pc_read(W,PQ)!=refpc {mism=mism+1} 149 var rr: i64=0 150 while rr<R { cmps=cmps+2; if rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rr)!=ref[rr] {mism=mism+1} if rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,DQ,RP,rr)!=refmem[rr] {mism=mism+1} rr=rr+1 } 151 s=s+1 152 } 153 total=total+1; if mism==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 154 gw("T1 UNIFIED program (store+call+loop+load+branch+return) == behavioral (regfile+memory+PC): " as *u8); gn(cmps); gw(" compares over " as *u8); gn(NSTEP); gw(" steps, mismatches=" as *u8); gn(mism); gw("\n" as *u8) 155 156 // T2: the array sum result 157 let x1: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,1); let x4: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,4) 158 total=total+1; if x1==60 { if x4==60 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) } 159 gw("T2 the program computed sum(10,20,30)=" as *u8); gn(x4); gw(" in the called function, returned to x1=" as *u8); gn(x1); gw(" (=60)\n" as *u8) 160 161 // T3: never-brick 162 let p1: i64=pc_read(W,PQ); let p2: i64=pc_read(W,PQ) 163 total=total+1; if p1==p2 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 164 gw("T3 never-brick (#26): deterministic, bounded per-instruction step, zero hardware-state writes\n" as *u8) 165 166 // T4: liar-kill -- corrupt the fabric ALU's adder LUTs, then run ONE ADD(5,7) through the decode->ALU datapath: 167 // the fabric result MUST diverge from the behavioral oracle (proves the gate checks an independent reference, 168 // not a rubber stamp). Light by construction (one decode + one ALU eval) -- T1's 510 compares already proved 169 // every datapath (regfile/memory/PC/branch/jump) end-to-end, so the liar-kill needn't re-run the whole program. 170 var ci: i64 = 0 171 while ci < 640 { AI[ci] = AI[ci] ^ 0xffff; ci = ci + 1 } // flip the WHOLE ALU LUT config (adder + MUX included) 172 fab_decode_run(DI, DS, DP, pi, co, 0, 0, ctrl) // funct3=0 -> ADD 173 let bad: i64 = fab_alu_run(64, anpi, AI, AS, AP, pi, co, 5, 7, ctrl[0], ctrl[1], ctrl[2]) 174 let good: i64 = nx_rv64im_alu_compute(NX_RV64IM_ALU_ADD, 5, 7) // = 12 175 total=total+1; if bad != good { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 176 gw("T4 liar-kill: corrupting the fabric ALU's LUT configuration -> ADD(5,7)=" as *u8); gn(bad); gw(" != behavioral " as *u8); gn(good); gw("\n" as *u8) 177 178 gw("\n=== nx_fpga_cpu6_gate " as *u8); gn(pass); gw("/" as *u8); gn(total) 179 if pass == total { gw(" GREEN (a UNIFIED RV64I CPU runs a real store+call+loop+load+branch+return program on the simulated FPGA == the behavioral CPU)\n" as *u8); sys_exit(0); return 0 } 180 gw(" RED\n" as *u8); sys_exit(1); return 1 181}