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}