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}