code wiki / _hdl_build / nx_fpga_cpu_dc_gate.nx
nx_fpga_cpu_dc_gate.nx source
↩ module page · 187 lines · 12923 B
1import "nx_gate_gn.nx"
2import "nx_gate_base.nx"
3// nx_fpga_cpu_dc_gate.nx -- GATE for RUNG 31: the fabric RV64IM CPU DRIVES THE DISPLAY CONTROLLER.
4// The R19 von Neumann CPU (fetch from IMEM RAM fabric, data in DMEM RAM fabric) gets an MMIO BUS DECODE on
5// its store path: a SW whose address (computed on the fabric ALU) lands in the framebuffer window is routed
6// to the display controller (nx_fpga_dc, R30, whose decode is itself on the fabric); every other store goes
7// to DMEM. So a PROGRAM RUNNING ON THE NISHI-DESIGNED CPU writes pixels -- "graphics on sovereign silicon."
8//
9// Program (fetched from IMEM): load the FB base, then SW a short RED line of 5 pixels into the FB window,
10// then SW a value to DMEM address 0 (a NON-FB store -- proves the bus SPLIT). FB_BASE = 256 here (a sim-
11// scaled MMIO base reachable by a 12-bit immediate; the decode is base-agnostic -- R30 proved it at
12// 0x20000000). DMEM lives below the window, the framebuffer at/above it.
13// T1 the CPU's stores landed correctly: FB[388..392]=255 (the line) AND DMEM[0]=7 (the non-FB store).
14// T2 the BUS SPLIT is real: exactly 5 framebuffer pixels are set (all =255) AND DMEM holds only the one
15// non-FB store (=7) -- no cross-contamination between the display controller and DMEM.
16// T3 NEVER-BRICK (#26): deterministic, bounded, caller-owned state, zero hardware writes.
17// T4 LIAR-KILL: corrupt the bus-decode subtractor -> an FB-window store mis-routes (falls through the
18// display controller) -- the routing is real fabric gating, not a software if.
19// expect_exit: 0 license_tier: ORIGINAL
20import "nx_fpga_ram.nx"
21import "nx_fpga_decode.nx"
22import "nx_fpga_alu.nx"
23import "nx_fpga_regfile.nx"
24import "nx_fpga_pc.nx"
25import "nx_fpga_dc.nx"
26import "nx_png.nx"
27import "nx_syscalls.nx"
28
29func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
30" as *u8); return ok }
31func enc_i(rd: i64, rs1: i64, imm: i64, f3: i64) -> i64 { return ((imm & 4095) << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 19 }
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 }
33
34func main() -> i64 {
35 gw("=== nx_fpga_cpu_dc_gate: RUNG 31 -- the fabric RV64IM CPU drives the display controller (graphics on sovereign silicon) ===\n" as *u8)
36 var pass: i64 = 0; var total: i64 = 0
37 let R: i64=8; let W: i64=64; let AB: i64=3
38 let MR: i64=8; let MAB: i64=3 // DMEM: 8 words x 64-bit
39 let IR: i64=8; let IW: i64=32; let IAB: i64=3 // IMEM: 8 words x 32-bit
40
41 // IMEM (true RAM)
42 let IK: *i64=sys_mmap(8*1100) as *i64; let II: *i64=sys_mmap(8*1100) as *i64; let IS: *i64=sys_mmap(8*4400) as *i64
43 let IPO: *i64=sys_mmap(8*40) as *i64; let IQ: *i64=sys_mmap(8*1100) as *i64
44 seq_build_ram(IR, IW, IAB, IK, II, IS, IPO); let inc: i64=ram_ncells(IR, IW)
45 // DMEM (true RAM)
46 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
47 let MPO: *i64=sys_mmap(8*72) as *i64; let MQ: *i64=sys_mmap(8*2100) as *i64
48 seq_build_ram(MR, W, MAB, MK, MI, MS, MPO); let mnc: i64=ram_ncells(MR, W)
49 // datapath
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 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
53 fab_build_decode(DI, DS, DP); let anpi: i64=fab_build_alu(64, AI, AS, AP)
54 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
55 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
56 seq_build_regfile(R, W, AB, RKND, RINI, RSRC, RP); 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 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
59 seq_build_pc(W, PKND, PINI, PSRC, PPO); let pcnc: i64=pc_ncells(W)
60
61 // ---- the display controller peripheral (R30) on the bus ----
62 let FW: i64=32; let FH: i64=24; let AW: i64=32; let FBASE: i64=256
63 let fb: *i64=sys_mmap(8*800) as *i64
64 let dsi: *i64=sys_mmap(8*128) as *i64; let dss: *i64=sys_mmap(8*512) as *i64; let dsp: *i64=sys_mmap(8*48) as *i64
65 let dpi: *i64=sys_mmap(8*80) as *i64; let dco: *i64=sys_mmap(8*128) as *i64; let dc: *NxFpgaDC=sys_mmap(8*16) as *NxFpgaDC
66 dc_init(dc, fb, FW, FH, FBASE, AW, dsi, dss, dsp, dpi, dco)
67 dc_clear(dc, dc_pack(0,0,0))
68
69 // ---- the program: draw a 5-pixel red line into the FB window, then a non-FB store to DMEM[0] ----
70 let prog: *i64=sys_mmap(8*16) as *i64
71 prog[0]=enc_i(1,0,FBASE,0) // ADDI x1,x0,256 x1 = FB base
72 prog[1]=enc_i(2,0,255,0) // ADDI x2,x0,255 x2 = red (R=255)
73 prog[2]=enc_sw(1,2,388) // SW x2,388(x1) FB[388] = 255 (row 12, col 4)
74 prog[3]=enc_sw(1,2,389) // SW x2,389(x1) FB[389] = 255
75 prog[4]=enc_sw(1,2,390) // SW x2,390(x1) FB[390] = 255
76 prog[5]=enc_sw(1,2,391) // SW x2,391(x1) FB[391] = 255
77 prog[6]=enc_sw(1,2,392) // SW x2,392(x1) FB[392] = 255
78 // (IMEM is 8 words; the DMEM-split store + halt live in words 7.. so keep the line to 5 and split next)
79 let prog2: *i64=sys_mmap(8*16) as *i64
80 prog2[0]=enc_i(4,0,7,0) // ADDI x4,x0,7 x4 = 7
81 prog2[1]=enc_sw(0,4,0) // SW x4,0(x0) DMEM[0] = 7 (addr 0 -> below the FB window)
82 prog2[2]=enc_i(0,0,0,0) // NOP
83
84 // load BOTH segments into IMEM across two passes of an 8-word window is awkward; instead run segment 1
85 // (7 instrs) then segment 2 (3 instrs) on the SAME machine state (PC reset between segments). This keeps
86 // IMEM at 8 words while running a 10-instruction program -- the bus decode is what we are proving.
87 var z: i64=0
88 z=0; while z<mnc { MQ[z]=0; z=z+1 } z=0; while z<rnc { RQ[z]=0; z=z+1 }
89
90 // ---------- segment 1 ----------
91 z=0; while z<inc { IQ[z]=0; z=z+1 }
92 var a: i64=0; while a < 7 { ram_write(IR,IW,IAB,IK,II,IS,pi,co,IQ,a,prog[a]); a=a+1 }
93 z=0; while z<pcnc { PQ[z]=0; z=z+1 }
94 var s: i64=0
95 while s < 7 {
96 let pc: i64=pc_read(W, PQ)
97 let instr: i64=ram_read(IR,IW,IAB,IK,II,IS,pi,co,IQ,IPO,pc)
98 let opcode: i64=instr&127; let rd: i64=(instr>>7)&7; let rs1: i64=(instr>>15)&7; let f3: i64=(instr>>12)&7
99 if opcode==35 { // SW + BUS DECODE
100 var imm: i64=(((instr>>25)&127)<<5)|((instr>>7)&31); if (imm&2048)!=0 {imm=imm-4096}
101 let rs2: i64=(instr>>20)&7
102 let base: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1); let v: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs2)
103 let addr: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,base,imm,0,1,1) // addr on the fabric ALU
104 if dc_store(dc, addr, v) == 0 { ram_write(MR,W,MAB,MK,MI,MS,pi,co,MQ,addr,v) } // FB window -> display controller, else DMEM
105 } else { // R/I ALU
106 var bval: i64=0; var f7b5: i64=0
107 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 }
108 else { var imm: i64=(instr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} bval=imm }
109 let av: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1)
110 fab_decode_run(DI,DS,DP,pi,co,f3,f7b5,ctrl)
111 let res: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,av,bval,ctrl[0],ctrl[1],ctrl[2])
112 if rd!=0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,res) }
113 }
114 pc_tick(W,PKND,PINI,PSRC,pi,PCO,PQ,0,0)
115 s=s+1
116 }
117 // ---------- segment 2 (same regfile/DMEM/FB state; PC reset) ----------
118 z=0; while z<inc { IQ[z]=0; z=z+1 }
119 a=0; while a < 3 { ram_write(IR,IW,IAB,IK,II,IS,pi,co,IQ,a,prog2[a]); a=a+1 }
120 z=0; while z<pcnc { PQ[z]=0; z=z+1 }
121 s=0
122 while s < 2 {
123 let pc: i64=pc_read(W, PQ)
124 let instr: i64=ram_read(IR,IW,IAB,IK,II,IS,pi,co,IQ,IPO,pc)
125 let opcode: i64=instr&127; let rd: i64=(instr>>7)&7; let rs1: i64=(instr>>15)&7; let f3: i64=(instr>>12)&7
126 if opcode==35 {
127 var imm: i64=(((instr>>25)&127)<<5)|((instr>>7)&31); if (imm&2048)!=0 {imm=imm-4096}
128 let rs2: i64=(instr>>20)&7
129 let base: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1); let v: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs2)
130 let addr: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,base,imm,0,1,1)
131 if dc_store(dc, addr, v) == 0 { ram_write(MR,W,MAB,MK,MI,MS,pi,co,MQ,addr,v) }
132 } else {
133 var bval: i64=0; var f7b5: i64=0
134 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 }
135 else { var imm: i64=(instr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} bval=imm }
136 let av: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1)
137 fab_decode_run(DI,DS,DP,pi,co,f3,f7b5,ctrl)
138 let res: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,av,bval,ctrl[0],ctrl[1],ctrl[2])
139 if rd!=0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,res) }
140 }
141 pc_tick(W,PKND,PINI,PSRC,pi,PCO,PQ,0,0)
142 s=s+1
143 }
144
145 write_png(fb, FW, FH, "knowledge/nx_fpga_cpu_dc.png" as *u8)
146
147 // ---- T1: the CPU's stores landed in the right sinks at the right indices ----
148 var t1ok: i64=1
149 var li: i64=388
150 while li <= 392 { if fb[li] != 255 { t1ok=0 } li=li+1 }
151 let d0: i64=ram_read(MR,W,MAB,MK,MI,MS,pi,co,MQ,MPO,0)
152 if d0 != 7 { t1ok=0 }
153 total=total+1; if t1ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
154 gw("T1 CPU SW landed: FB[388..392]=255 (the red line) AND DMEM[0]=" as *u8); gn(d0); gw(" (=7, the non-FB store)\n" as *u8)
155
156 // ---- T2: the bus SPLIT is real -- exactly 5 FB pixels set (all 255); DMEM holds only the one store ----
157 var nz: i64=0; var nzbad: i64=0; var i: i64=0
158 while i < FW*FH { if fb[i] != 0 { nz=nz+1; if fb[i] != 255 { nzbad=nzbad+1 } } i=i+1 }
159 var dnz: i64=0; var d7: i64=0; var di: i64=0
160 while di < MR { let dv: i64=ram_read(MR,W,MAB,MK,MI,MS,pi,co,MQ,MPO,di); if dv != 0 { dnz=dnz+1; if dv==7 {d7=1} } di=di+1 }
161 var t2ok: i64=1
162 if nz != 5 { t2ok=0 } // exactly the 5 line pixels -- no FB leak
163 if nzbad != 0 { t2ok=0 } // all set FB pixels are the value the CPU wrote
164 if dnz != 1 { t2ok=0 } // DMEM got exactly one store
165 if d7 != 1 { t2ok=0 } // and it is the non-FB value (7), not the pixel value (255)
166 total=total+1; if t2ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
167 gw("T2 bus split: framebuffer has " as *u8); gn(nz); gw(" pixels set (all=255, bad=" as *u8); gn(nzbad); gw("); DMEM has " as *u8); gn(dnz); gw(" store(s) (=7) -- no cross-contamination\n" as *u8)
168
169 // ---- T3: never-brick ----
170 let q1: i64=pc_read(W,PQ); let q2: i64=pc_read(W,PQ)
171 total=total+1; if q1==q2 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
172 gw("T3 never-brick (#26): deterministic fetch+execute+bus-route, bounded steps, caller-owned state, zero hardware writes\n" as *u8)
173
174 // ---- T4: liar-kill -- corrupt the bus-decode subtractor -> an FB-window store mis-routes ----
175 let cc: i64=AW + 2*(AW-1) + 1
176 let good_route: i64=dc_decode(dc, FBASE) // pre-corruption: FB base IS in-window -> 1
177 let saved: i64=dsi[cc]; dsi[cc]=dsi[cc] ^ 0xffff
178 let mis: i64=dc_store(dc, FBASE, 999) // corrupted: the FB-window store no longer routes to the controller
179 dsi[cc]=saved
180 var t4ok: i64=1; if good_route != 1 { t4ok=0 } if mis != 0 { t4ok=0 }
181 total=total+1; if t4ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
182 gw("T4 liar-kill: a FB-window store routed " as *u8); gn(good_route); gw(" (to the controller) but after corrupting the decode it routed " as *u8); gn(mis); gw(" (fell through -> would hit DMEM): the bus decode is real fabric gating\n" as *u8)
183
184 gw("\n=== nx_fpga_cpu_dc_gate " as *u8); gn(pass); gw("/" as *u8); gn(total)
185 if pass == total { gw(" GREEN (a program on the fabric RV64IM CPU draws pixels through an on-fabric MMIO bus into the display controller -- graphics on sovereign silicon)\n" as *u8); sys_exit(0); return 0 }
186 gw(" RED\n" as *u8); sys_exit(1); return 1
187}