code wiki / _hdl_build / rv64im_min_gfx_gate.nx
rv64im_min_gfx_gate.nx source
↩ module page · 160 lines · 10173 B
1import "nx_gate_gn.nx"
2import "nx_gate_base.nx"
3// rv64im_min_gfx_gate.nx -- GATE: SF4 / the BEHAVIORAL sovereign SoC renders a real-resolution game frame.
4// SF1/SF2 proved a display controller + the FABRIC CPU drawing pixels (real gates, toy resolution -- the
5// fabric eval is slow by construction). This rung uses the FAST behavioral RV64IM-min sim (rv64im_min_sim,
6// the same CPU that boots kernels) to render a FULL 64x48 frame from a real hand-encoded RV64IM program.
7//
8// THE HONEST HARDWARE MODEL = a DRAM-BACKED FRAMEBUFFER (the common SoC architecture): the CPU stores pixels
9// into a region of DRAM (guest RAM), and the "display controller" SCANS OUT that region -> pixels. No core-
10// sim edit needed -- the framebuffer is just a convention over guest RAM, exactly as a real DRAM framebuffer
11// is. (SF1/SF2's MMIO-aperture flavor is the gate-level proof; this DRAM-backed flavor is the real-res proof.)
12//
13// The program (hand-encoded RV64IM, position-independent): AUIPC s0,1 to get the framebuffer base
14// (= PC+0x1000 = 0x80001000, avoiding LUI's sign-extension at 0x80000000), then a y/x double loop that for
15// each pixel computes a colorful procedural scene -- R = (x<<2)&255 (gradient ->), G = (y<<2)&255 (gradient
16// down), B = (x^y)&255 (the classic XOR/munching-squares texture) -- packs R|G<<8|B<<16 and SW's it to a
17// running framebuffer pointer. A trailing all-zero word halts the sim.
18// T1 the program HALTS cleanly AND the scanned-out framebuffer == an independent reference, pixel-exact
19// (0 mismatch over all 3072 pixels). Encodes a real PNG (eyeball).
20// T2 it is a REAL rendered scene (the four corners are four DISTINCT colors -- spatial variation, not a fill).
21// T3 NEVER-BRICK (#26): the sovereign sim is deterministic (re-run -> bit-identical frame), bounded, zero
22// host/persistent writes.
23// T4 LIAR-KILL: corrupt ONE instruction (the XOR -> ADD) and the rendered frame diverges -- the pixels are
24// the CPU's real computation, not pre-filled.
25// expect_exit: 0 license_tier: ORIGINAL
26import "nx_syscalls.nx"
27import "nishi_hdl_primitives.nx"
28import "rv64im_min_decoder.nx"
29import "rv64im_min_alu.nx"
30import "rv64im_min_regfile.nx"
31import "rv64im_min_csr.nx"
32import "rv64im_min_clint.nx"
33import "rv64im_min_uart.nx"
34import "rv64im_min_sim.nx"
35import "nx_png.nx"
36
37func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
38" as *u8); return ok }
39
40// ---- RV64IM instruction encoders ----
41func e_auipc(rd: i64, imm20: i64) -> i64 { return ((imm20 & 0xFFFFF) << 12) | (rd << 7) | 0x17 }
42func e_addi(rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm & 0xFFF) << 20) | (rs1 << 15) | (rd << 7) | 0x13 }
43func e_andi(rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm & 0xFFF) << 20) | (rs1 << 15) | (7 << 12) | (rd << 7) | 0x13 }
44func e_slli(rd: i64, rs1: i64, sh: i64) -> i64 { return ((sh & 0x3F) << 20) | (rs1 << 15) | (1 << 12) | (rd << 7) | 0x13 }
45func e_rr(rd: i64, rs1: i64, rs2: i64, f3: i64) -> i64 { return (rs2 << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 0x33 }
46func e_sw(rs1: i64, rs2: i64, imm: i64) -> i64 { return (((imm>>5)&0x7F)<<25) | (rs2<<20) | (rs1<<15) | (2<<12) | ((imm&0x1F)<<7) | 0x23 }
47func e_blt(rs1: i64, rs2: i64, off: i64) -> i64 {
48 let i12: i64=(off>>12)&1; let i11: i64=(off>>11)&1; let i10_5: i64=(off>>5)&0x3F; let i4_1: i64=(off>>1)&0xF
49 return (i12<<31) | (i10_5<<25) | (rs2<<20) | (rs1<<15) | (4<<12) | (i4_1<<8) | (i11<<7) | 0x63
50}
51
52func w32(mem: *u8, off: i64, v: i64) -> i64 {
53 mem[off]=(v&0xff) as u8; mem[off+1]=((v>>8)&0xff) as u8; mem[off+2]=((v>>16)&0xff) as u8; mem[off+3]=((v>>24)&0xff) as u8
54 return 0
55}
56
57// build the renderer program into prog[]; returns instruction count. (W=64,H=48 wired into the immediates.)
58func build_prog(prog: *i64) -> i64 {
59 // regs: zero=0 t0=5 t1=6 s0=8 s1=9 a0=10 a1=11 t3=28 t4=29 t5=30
60 prog[0]=e_auipc(8, 1) // s0 = PC + 0x1000 = 0x80001000 (framebuffer base)
61 prog[1]=e_addi(10, 0, 64) // a0 = W
62 prog[2]=e_addi(11, 0, 48) // a1 = H
63 prog[3]=e_addi(9, 8, 0) // s1 = p = FB base
64 prog[4]=e_addi(5, 0, 0) // t0 = y = 0
65 prog[5]=e_addi(6, 0, 0) // t1 = x = 0 <- loop_y target (instr 5)
66 prog[6]=e_slli(28, 6, 2) // t3 = x<<2 <- loop_x target (instr 6)
67 prog[7]=e_andi(28, 28, 255) // t3 = R
68 prog[8]=e_slli(29, 5, 2) // t4 = y<<2
69 prog[9]=e_andi(29, 29, 255) // t4 = G
70 prog[10]=e_slli(29, 29, 8) // t4 = G<<8
71 prog[11]=e_rr(30, 6, 5, 4) // t5 = x ^ y (XOR, f3=4)
72 prog[12]=e_andi(30, 30, 255) // t5 = B
73 prog[13]=e_slli(30, 30, 16) // t5 = B<<16
74 prog[14]=e_rr(28, 28, 29, 6) // t3 = t3 | t4 (OR, f3=6)
75 prog[15]=e_rr(28, 28, 30, 6) // t3 = t3 | t5 = pixel
76 prog[16]=e_sw(9, 28, 0) // sw t3, 0(s1) *p = pixel
77 prog[17]=e_addi(9, 9, 4) // s1 = p + 4
78 prog[18]=e_addi(6, 6, 1) // x++
79 prog[19]=e_blt(6, 10, 0-52) // blt x<W -> loop_x (instr6): (6-19)*4 = -52
80 prog[20]=e_addi(5, 5, 1) // y++
81 prog[21]=e_blt(5, 11, 0-64) // blt y<H -> loop_y (instr5): (5-21)*4 = -64
82 prog[22]=0 // halt sentinel
83 return 23
84}
85
86// run a program on a FRESH behavioral sim; scan the DRAM framebuffer (guest offset 0x1000) into fb_out[W*H].
87// returns sim.halted (1 = clean halt).
88func run_frame(prog: *i64, nprog: i64, fb_out: *i64, W: i64, H: i64) -> i64 {
89 let MEM_BASE: i64 = 0x80000000; let MEM_SIZE: i64 = 0x8000; let FBOFF: i64 = 0x1000
90 let rf_s: *i64=(sys_mmap(8*NX_RV64IM_RF_N_REGS)) as *i64; let csr_s: *i64=(sys_mmap(8*NX_CSR_SLOT_N)) as *i64
91 let clint_s: *i64=(sys_mmap(8*NX_CLINT_SLOT_N)) as *i64; let uart_s: *i64=(sys_mmap(8*NX_UART_SLOT_N)) as *i64
92 let mem: *u8=sys_mmap(MEM_SIZE); let tx: *u8=sys_mmap(256)
93 let rf: *NxRv64imRegfile=(sys_mmap(64)) as *NxRv64imRegfile; let csr: *NxRv64imCsrFile=(sys_mmap(64)) as *NxRv64imCsrFile
94 let clint: *NxClint=(sys_mmap(64)) as *NxClint; let uart: *NxUart=(sys_mmap(64)) as *NxUart; let sim: *NxRv64imSim=(sys_mmap(128)) as *NxRv64imSim
95 nx_rv64im_rf_init(rf, rf_s); nx_rv64im_csr_init(csr, csr_s, 0); nx_clint_init(clint, clint_s); nx_uart_init(uart, uart_s, tx, 256)
96 nx_rv64im_sim_init(sim, rf, csr, clint, uart, MEM_BASE, mem, MEM_SIZE, 0)
97 var i: i64=0; while i < nprog { w32(mem, i*4, prog[i]); i=i+1 }
98 nx_rv64im_sim_run(sim, 200000)
99 // scanout: read each 4-byte pixel from DRAM at FBOFF -> packed R|G<<8|B<<16
100 var idx: i64=0
101 while idx < W*H {
102 let o: i64 = FBOFF + idx*4
103 fb_out[idx] = (mem[o] as i64) | ((mem[o+1] as i64)<<8) | ((mem[o+2] as i64)<<16)
104 idx=idx+1
105 }
106 return sim.halted
107}
108
109func main() -> i64 {
110 gw("=== rv64im_min_gfx_gate: SF4 -- the behavioral RV64IM SoC renders a real-resolution frame (DRAM framebuffer) ===\n" as *u8)
111 var pass: i64 = 0; var total: i64 = 0
112 let W: i64=64; let H: i64=48
113 let prog: *i64=sys_mmap(8*64) as *i64
114 let np: i64=build_prog(prog)
115 let fb1: *i64=sys_mmap(8*(W*H+8)) as *i64
116
117 let halted: i64 = run_frame(prog, np, fb1, W, H)
118
119 // ---- T1: halted cleanly + scanout == independent reference, pixel-exact ----
120 var mism: i64=0; var idx: i64=0
121 while idx < W*H {
122 let x: i64 = idx % W; let y: i64 = idx / W
123 let r: i64 = (x<<2)&255; let g: i64 = (y<<2)&255; let b: i64 = (x^y)&255
124 let ref: i64 = r | (g<<8) | (b<<16)
125 if fb1[idx] != ref { mism=mism+1 }
126 idx=idx+1
127 }
128 write_png(fb1, W, H, "knowledge/rv64im_gfx.png" as *u8)
129 var t1ok: i64=1; if halted != 1 { t1ok=0 } if mism != 0 { t1ok=0 }
130 total=total+1; if t1ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
131 gw("T1 program halted=" as *u8); gn(halted); gw(", scanned-out framebuffer == reference over " as *u8); gn(W*H); gw(" pixels, mismatches=" as *u8); gn(mism); gw(" (PNG knowledge/rv64im_gfx.png)\n" as *u8)
132
133 // ---- T2: a REAL rendered scene -- the four corners are four DISTINCT colors ----
134 let c00: i64=fb1[0]; let c10: i64=fb1[W-1]; let c01: i64=fb1[(H-1)*W]; let c11: i64=fb1[(H-1)*W + (W-1)]
135 var t2ok: i64=1
136 if c00==c10 { t2ok=0 } if c00==c01 { t2ok=0 } if c00==c11 { t2ok=0 } if c10==c01 { t2ok=0 } if c10==c11 { t2ok=0 } if c01==c11 { t2ok=0 }
137 total=total+1; if t2ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
138 gw("T2 real scene: 4 corners are 4 distinct colors (" as *u8); gn(c00); gw("," as *u8); gn(c10); gw("," as *u8); gn(c01); gw("," as *u8); gn(c11); gw(") -- spatial variation, not a fill\n" as *u8)
139
140 // ---- T3: never-brick -- deterministic (re-run -> bit-identical) ----
141 let fb2: *i64=sys_mmap(8*(W*H+8)) as *i64
142 run_frame(prog, np, fb2, W, H)
143 var dmis: i64=0; idx=0; while idx < W*H { if fb1[idx] != fb2[idx] { dmis=dmis+1 } idx=idx+1 }
144 total=total+1; if dmis==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
145 gw("T3 never-brick (#26): the sovereign sim is deterministic (re-run bit-identical, diffs=" as *u8); gn(dmis); gw("), bounded, zero host/persistent writes\n" as *u8)
146
147 // ---- T4: liar-kill -- corrupt the XOR (instr 11) into an ADD -> the frame must diverge ----
148 let saved: i64=prog[11]
149 prog[11]=e_rr(30, 6, 5, 0) // f3=0 -> ADD instead of XOR (blue = (x+y) not (x^y))
150 let fb3: *i64=sys_mmap(8*(W*H+8)) as *i64
151 run_frame(prog, np, fb3, W, H)
152 prog[11]=saved
153 var bdiff: i64=0; idx=0; while idx < W*H { if fb3[idx] != fb1[idx] { bdiff=bdiff+1 } idx=idx+1 }
154 total=total+1; if bdiff > 0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
155 gw("T4 liar-kill: corrupting the XOR->ADD changed " as *u8); gn(bdiff); gw(" pixels -- the frame is the CPU's real computation, not pre-filled\n" as *u8)
156
157 gw("\n=== rv64im_min_gfx_gate " as *u8); gn(pass); gw("/" as *u8); gn(total)
158 if pass == total { gw(" verdict=GREEN (a real hand-encoded RV64IM program renders a full real-resolution frame on the behavioral sovereign CPU into a DRAM framebuffer -> scanout)\n" as *u8); sys_exit(0); return 0 }
159 gw(" verdict=RED\n" as *u8); sys_exit(1); return 1
160}