code wiki / _hdl_build / nx_fpga_cpu4_gate.nx
nx_fpga_cpu4_gate.nx source
↩ module page · 151 lines · 10222 B
1import "nx_gate_gn.nx"
2import "nx_gate_base.nx"
3// nx_fpga_cpu4_gate.nx -- GATE for RUNG 14: MEMORY -- the fabric CPU gains LOADS/STORES (LW/SW) + a DATA MEMORY.
4// A data memory IS an addressed clocked RAM == the register file (R10), so ONE regfile fabric is built and given
5// TWO state arrays: RQ (the 8 registers) + DQ (an 8-word data memory). The load/store address (rs1 + imm) is
6// computed ON THE FABRIC ALU; the access reads/writes the DQ memory fabric. Proven: a store->load roundtrip
7// program leaves the register file AND the data memory == the behavioral reference, step-for-step.
8// T1 ROUNDTRIP == reference: SW 100->mem[3], 200->mem[5]; LW back to x4/x5; ADD x6=300 -- regfile + memory match.
9// T2 final state (x4=100, x5=200, x6=300; mem[3]=100, mem[5]=200). T3 NEVER-BRICK. T4 LIAR-KILL.
10// (Builds on R12 R/I-type; branches are R13 -- combined in R15. expect_exit: 0 license_tier: ORIGINAL)
11import "nx_fpga_decode.nx"
12import "nx_fpga_alu.nx"
13import "nx_fpga_cmp.nx"
14import "nx_fpga_shift.nx"
15import "nx_fpga_regfile.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 }
21
22func ref_aluop(f3: i64, f7b5: i64) -> i64 {
23 if f3==0 { if f7b5==1 { return NX_RV64IM_ALU_SUB } return NX_RV64IM_ALU_ADD }
24 if f3==1 { return NX_RV64IM_ALU_SLL }
25 if f3==2 { return NX_RV64IM_ALU_SLT }
26 if f3==3 { return NX_RV64IM_ALU_SLTU }
27 if f3==4 { return NX_RV64IM_ALU_XOR }
28 if f3==5 { if f7b5==1 { return NX_RV64IM_ALU_SRA } return NX_RV64IM_ALU_SRL }
29 if f3==6 { return NX_RV64IM_ALU_OR }
30 return NX_RV64IM_ALU_AND
31}
32func enc_i(rd: i64, rs1: i64, imm: i64, f3: i64) -> i64 { return ((imm & 4095) << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 19 }
33func 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 }
34func enc_lw(rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm & 4095) << 20) | (rs1 << 15) | (2 << 12) | (rd << 7) | 3 } // 0x03, funct3=2
35func enc_sw(rs1: i64, rs2: i64, imm: i64) -> i64 { return (((imm >> 5) & 127) << 25) | (rs2 << 20) | (rs1 << 15) | (2 << 12) | ((imm & 31) << 7) | 35 } // 0x23, funct3=2
36
37func 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 {
38 fab_decode_run(DI, DS, DP, pi, co, f3, f7b5, ctrl)
39 let cls: i64 = ctrl[6] | (ctrl[7] << 1)
40 if cls == 0 { return fab_alu_run(64, anpi, AI, AS, AP, pi, co, a, b, ctrl[0], ctrl[1], ctrl[2]) }
41 if cls == 1 { return fab_shift_run(64, snpi, SI, SS, SP, pi, co, a, b & 63, ctrl[3], ctrl[4]) }
42 return fab_cmp_run(64, cnpi, CI, CS, CP, pi, co, a, b, ctrl[5])
43}
44
45func main() -> i64 {
46 gw("=== nx_fpga_cpu4_gate: RUNG 14 -- MEMORY: the fabric CPU + LOADS/STORES (LW/SW) + a data-memory fabric ===\n" as *u8)
47 var pass: i64 = 0; var total: i64 = 0
48 let R: i64 = 8; let W: i64 = 64; let AB: i64 = 3
49
50 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
51 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
52 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
53 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
54 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
55 fab_build_decode(DI, DS, DP)
56 let anpi: i64 = fab_build_alu(64, AI, AS, AP)
57 let cnpi: i64 = fab_build_cmp(64, CI, CS, CP)
58 let snpi: i64 = fab_build_shifter(64, SI, SS, SP)
59 // ONE regfile fabric, TWO state arrays: RQ = registers, DQ = data memory
60 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
61 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
62 let DQ: *i64=sys_mmap(8*2048) as *i64
63 seq_build_regfile(R, W, AB, RKND, RINI, RSRC, RP)
64 let rnc: i64 = rf_ncells(R, W)
65
66 let ref: *i64 = sys_mmap(8 * 16) as *i64
67 let refmem: *i64 = sys_mmap(8 * 16) as *i64
68 let prog: *i64 = sys_mmap(8 * 16) as *i64
69 prog[0]=enc_i(1, 0, 100, 0) // ADDI x1,x0,100
70 prog[1]=enc_i(2, 0, 200, 0) // ADDI x2,x0,200
71 prog[2]=enc_sw(0, 1, 3) // SW x1, 3(x0) -> mem[3]=100
72 prog[3]=enc_sw(0, 2, 5) // SW x2, 5(x0) -> mem[5]=200
73 prog[4]=enc_lw(4, 0, 3) // LW x4, 3(x0) -> x4=mem[3]=100
74 prog[5]=enc_lw(5, 0, 5) // LW x5, 5(x0) -> x5=mem[5]=200
75 prog[6]=enc_r(6, 4, 5, 0, 0) // ADD x6,x4,x5 -> 300 (uses loaded values)
76 let nprog: i64 = 7
77
78 // ---- T1: run; compare regfile + data memory to the reference after every step ----
79 var z: i64 = 0; while z < rnc { RQ[z]=0; DQ[z]=0; z=z+1 }
80 z = 0; while z < R { ref[z]=0; refmem[z]=0; z=z+1 }
81 var mism: i64 = 0; var cmps: i64 = 0
82 var ic: i64 = 0
83 while ic < nprog {
84 let instr: i64 = prog[ic]
85 let opcode: i64 = instr & 127
86 let rd: i64=(instr>>7)&7; let rs1: i64=(instr>>15)&7; let f3: i64=(instr>>12)&7
87 if opcode == 3 { // LW (load)
88 var imm: i64=(instr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096}
89 let base: i64 = rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1)
90 let addr: i64 = fab_alu_run(64,anpi,AI,AS,AP,pi,co,base,imm,0,1,1) // addr = rs1 + imm (fabric ALU)
91 let loaded: i64 = rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,DQ,RP,addr) // data-memory read
92 if rd != 0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,loaded) }
93 if rd != 0 { ref[rd] = refmem[(ref[rs1]+imm)&7] }
94 } else { if opcode == 35 { // SW (store)
95 var imm: i64=(((instr>>25)&127)<<5)|((instr>>7)&31); if (imm&2048)!=0 {imm=imm-4096}
96 let rs2: i64=(instr>>20)&7
97 let base: i64 = rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1)
98 let val: i64 = rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs2)
99 let addr: i64 = fab_alu_run(64,anpi,AI,AS,AP,pi,co,base,imm,0,1,1)
100 rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,DQ,addr,val) // data-memory write
101 refmem[(ref[rs1]+imm)&7] = ref[rs2]
102 } else { // R/I-type
103 var bval: i64=0; var refB: i64=0; var f7b5: i64=0
104 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 }
105 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} }
106 let av: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1)
107 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)
108 if rd != 0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,res) }
109 if rd != 0 { ref[rd]=nx_rv64im_alu_compute(ref_aluop(f3,f7b5),ref[rs1],refB) }
110 } }
111 // compare registers + memory
112 var rr: i64 = 0
113 while rr < R {
114 cmps = cmps + 2
115 if rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rr) != ref[rr] { mism = mism + 1 }
116 if rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,DQ,RP,rr) != refmem[rr] { mism = mism + 1 }
117 rr = rr + 1
118 }
119 ic = ic + 1
120 }
121 total=total+1; if mism==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
122 gw("T1 store->load roundtrip == behavioral (regfile + data memory), step-for-step: " as *u8); gn(cmps); gw(" compares, mismatches=" as *u8); gn(mism); gw("\n" as *u8)
123
124 // ---- T2: final state ----
125 let x4: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,4); let x5: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,5); let x6: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,6)
126 let m3: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,DQ,RP,3); let m5: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,DQ,RP,5)
127 total=total+1
128 var t2ok: i64 = 1
129 if x4!=100 {t2ok=0} if x5!=200 {t2ok=0} if x6!=300 {t2ok=0} if m3!=100 {t2ok=0} if m5!=200 {t2ok=0}
130 if t2ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
131 gw("T2 final: mem[3]=" as *u8); gn(m3); gw(" mem[5]=" as *u8); gn(m5); gw(" -> LW -> x4=" as *u8); gn(x4); gw(" x5=" as *u8); gn(x5); gw(" -> ADD x6=" as *u8); gn(x6); gw(" (=300)\n" as *u8)
132
133 // ---- T3: never-brick ----
134 let y1: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,DQ,RP,3); let y2: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,DQ,RP,3)
135 total=total+1; if y1==y2 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
136 gw("T3 never-brick (#26): deterministic memory reads, bounded, zero hardware-state writes\n" as *u8)
137
138 // ---- T4: liar-kill -- corrupt a data-memory write-decode LUT -> a store lands wrong ----
139 RINI[R*W + 1 + 3] = RINI[R*W + 1 + 3] ^ 0xffff // wsel LUT of memory word 3 (cell R*W+1+3)
140 z = 0; while z < rnc { RQ[z]=0; DQ[z]=0; z=z+1 }
141 let base0: i64=0
142 let a0: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,base0,3,0,1,1)
143 rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,DQ,a0,12345)
144 let got: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,DQ,RP,3)
145 total=total+1; if got != 12345 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
146 gw("T4 liar-kill: corrupting a data-memory write-decode LUT -> store to mem[3] lands wrong (read=" as *u8); gn(got); gw(")\n" as *u8)
147
148 gw("\n=== nx_fpga_cpu4_gate " as *u8); gn(pass); gw("/" as *u8); gn(total)
149 if pass == total { gw(" GREEN (LW/SW + a data memory run on the simulated FPGA == the behavioral CPU; a program stores to memory and loads it back)\n" as *u8); sys_exit(0); return 0 }
150 gw(" RED\n" as *u8); sys_exit(1); return 1
151}