code wiki / _hdl_build / nx_fpga_boot_gate.nx
nx_fpga_boot_gate.nx source
↩ module page · 153 lines · 11950 B
1import "nx_gate_gn.nx"
2import "nx_gate_base.nx"
3// nx_fpga_boot_gate.nx -- GATE for RUNG 22: a SELF-LOADING BOOTLOADER on a UNIFIED von Neumann memory. A single RAM
4// fabric holds BOTH code and data (true von Neumann -- a store can write a word the PC later fetches as an instruction).
5// A small LOADER ("bootrom") at MEM[0..12] copies a pre-staged PAYLOAD IMAGE (MEM[13], "as if from storage") into a
6// RUN slot (MEM[14]) with a LW/SW copy loop, then JALR jumps to the run slot -- and the CPU FETCHES and runs the
7// just-loaded payload (which computes x6=42). This is what real bootloaders do: copy load-address -> run-address, jump.
8// NOTE: the RAM address-decode is a single LUT4 (4 inputs) so it supports R<=16 / AB<=4 -- the unified MEM is R=16.
9// T1 the whole boot (loader copy-loop + jump + payload) runs on the fabric == the behavioral von Neumann reference.
10// T2 the payload RAN (x6 = 42, by the copied-in code) AND the code was actually copied (MEM[14]==MEM[13]).
11// T3 NEVER-BRICK. T4 LIAR-KILL (corrupt the fabric ALU -> the boot's execute path diverges). expect_exit: 0
12// license_tier: ORIGINAL
13import "nx_fpga_ram.nx"
14import "nx_fpga_decode.nx"
15import "nx_fpga_alu.nx"
16import "nx_fpga_regfile.nx"
17import "nx_fpga_pc.nx"
18import "rv64im_min_alu.nx"
19import "nx_syscalls.nx"
20
21func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
22" as *u8); return ok }
23func enc_i(rd: i64, rs1: i64, imm: i64, f3: i64) -> i64 { return ((imm & 4095) << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 19 }
24func enc_lw(rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm&4095)<<20)|(rs1<<15)|(2<<12)|(rd<<7)|3 }
25func enc_sw(rs1: i64, rs2: i64, imm: i64) -> i64 { return (((imm>>5)&127)<<25)|(rs2<<20)|(rs1<<15)|(2<<12)|((imm&31)<<7)|35 }
26func enc_jalr(rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm&4095)<<20)|(rs1<<15)|(rd<<7)|103 }
27func enc_b(rs1: i64, rs2: i64, off: i64, f3: i64) -> i64 {
28 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
29 return (b12<<31)|(b10_5<<25)|(rs2<<20)|(rs1<<15)|(f3<<12)|(b4_1<<8)|(b11<<7)|99
30}
31func 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 }
32
33func main() -> i64 {
34 gw("=== nx_fpga_boot_gate: RUNG 22 -- a SELF-LOADING BOOTLOADER on a unified von Neumann memory ===\n" as *u8)
35 var pass: i64 = 0; var total: i64 = 0
36 let R: i64=8; let W: i64=64; let AB: i64=3
37 let MR: i64=16; let MW: i64=32; let MAB: i64=4 // unified MEM: 16 words x 32-bit (code + data); LUT4 decode -> R<=16
38
39 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
40 let MPO: *i64=sys_mmap(8*40) as *i64; let MQ: *i64=sys_mmap(8*2100) as *i64
41 seq_build_ram(MR, MW, MAB, MK, MI, MS, MPO); let mnc: i64=ram_ncells(MR, MW)
42 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
43 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
44 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
45 fab_build_decode(DI, DS, DP); let anpi: i64=fab_build_alu(64, AI, AS, AP)
46 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
47 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
48 seq_build_regfile(R, W, AB, RKND, RINI, RSRC, RP); let rnc: i64=rf_ncells(R, W)
49 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
50 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
51 seq_build_pc(W, PKND, PINI, PSRC, PPO); let pcnc: i64=pc_ncells(W)
52
53 // the boot IMAGE: loader[0..12] + payload-image[13]; run slot[14] starts NOP (loader fills it); [15] self-loop halt
54 let img: *i64=sys_mmap(8*20) as *i64
55 img[0]=enc_i(1,0,13,0) // ADDI x1,x0,13 src = image addr (S)
56 img[1]=enc_i(2,0,14,0) // ADDI x2,x0,14 dst = run addr (E)
57 img[2]=enc_i(3,0,1,0) // ADDI x3,x0,1 count = n = 1
58 img[3]=enc_i(4,0,0,0) // ADDI x4,x0,0 i = 0
59 img[4]=enc_lw(5,1,0) // loop@4: LW x5,0(x1) x5 = MEM[src]
60 img[5]=enc_sw(2,5,0) // SW x5,0(x2) MEM[dst] = x5 (writes the payload instr into code space)
61 img[6]=enc_i(1,1,1,0) // ADDI x1,x1,1 src++
62 img[7]=enc_i(2,2,1,0) // ADDI x2,x2,1 dst++
63 img[8]=enc_i(4,4,1,0) // ADDI x4,x4,1 i++
64 img[9]=enc_i(0,0,0,0) // NOP (parity filler -> even branch offset)
65 img[10]=enc_b(4,3,0-6,1) // BNE x4,x3,-6 if i != n -> 4
66 img[11]=enc_i(7,0,14,0) // ADDI x7,x0,14 x7 = run entry (E)
67 img[12]=enc_jalr(0,7,0) // JALR x0,x7,0 jump to the loaded payload
68 img[13]=enc_i(6,0,42,0) // PAYLOAD-IMAGE: ADDI x6,x0,42 -> x6 = 42
69 img[14]=enc_i(0,0,0,0) // run slot (filled by the loader's SW)
70 img[15]=enc_b(1,0,0,1) // BNE x1,x0,0 -> self-loop HALT (x1=14 != 0, offset 0)
71 let NSTEP: i64=18
72
73 let refmem: *i64=sys_mmap(8*20) as *i64; let ref: *i64=sys_mmap(8*16) as *i64
74 var z: i64=0; while z<mnc { MQ[z]=0; z=z+1 }
75 var a: i64=0; while a < MR { ram_write(MR,MW,MAB,MK,MI,MS,pi,co,MQ,a,img[a]); refmem[a]=img[a]; a=a+1 }
76 z=0; while z<rnc { RQ[z]=0; z=z+1 } z=0; while z<pcnc { PQ[z]=0; z=z+1 } z=0; while z<R { ref[z]=0; z=z+1 }
77 var refpc: i64=0; var mism: i64=0; var s: i64=0
78 while s < NSTEP {
79 let pc: i64=pc_read(W, PQ)
80 let instr: i64=ram_read(MR,MW,MAB,MK,MI,MS,pi,co,MQ,MPO,pc) // FETCH from the unified MEM
81 let rinstr: i64=refmem[refpc & 15]
82 let opcode: i64=instr&127; let rd: i64=(instr>>7)&7; let rs1: i64=(instr>>15)&7; let f3: i64=(instr>>12)&7
83 let ro: i64=rinstr&127; let rrd: i64=(rinstr>>7)&7; let rrs1: i64=(rinstr>>15)&7
84 var pcload: i64=0; var pcval: i64=0
85 if opcode==103 { // JALR
86 var imm: i64=(instr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096}
87 let rv: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1)
88 let tgt: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,rv,imm,0,1,1)
89 if rd!=0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,pc+1) }
90 pcload=1; pcval=tgt
91 } else { if opcode==99 { // BNE
92 let rs2: i64=(instr>>20)&7; let off: i64=dec_b(instr)
93 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)
94 let sub: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,av,bv,1,1,1)
95 if sub != 0 { pcload=1; pcval=pc+off }
96 } else { if opcode==3 { // LW
97 var imm: i64=(instr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096}
98 let base: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1)
99 let addr: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,base,imm,0,1,1)
100 let ld: i64=ram_read(MR,MW,MAB,MK,MI,MS,pi,co,MQ,MPO,addr)
101 if rd!=0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,ld) }
102 } else { if opcode==35 { // SW (can write CODE)
103 var imm: i64=(((instr>>25)&127)<<5)|((instr>>7)&31); if (imm&2048)!=0 {imm=imm-4096}
104 let rs2: i64=(instr>>20)&7
105 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)
106 let addr: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,base,imm,0,1,1)
107 ram_write(MR,MW,MAB,MK,MI,MS,pi,co,MQ,addr,v)
108 } else { // R/I (ALU)
109 var bval: i64=0; var f7b5: i64=0
110 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 }
111 else { var imm: i64=(instr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} bval=imm }
112 let av: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,rs1)
113 fab_decode_run(DI,DS,DP,pi,co,f3,f7b5,ctrl)
114 let res: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,av,bval,ctrl[0],ctrl[1],ctrl[2])
115 if rd!=0 { rf_write(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,rd,res) }
116 } } } }
117 pc_tick(W,PKND,PINI,PSRC,pi,PCO,PQ,pcload,pcval)
118 // reference (software von Neumann) -- mirror, fetching from refmem
119 if ro==103 { var imm: i64=(rinstr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} if rrd!=0 {ref[rrd]=refpc+1} refpc=ref[rrs1]+imm }
120 else { if ro==99 { let rrs2: i64=(rinstr>>20)&7; let off: i64=dec_b(rinstr); if ref[rrs1]!=ref[rrs2] {refpc=refpc+off} else {refpc=refpc+1} }
121 else { if ro==3 { var imm: i64=(rinstr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} if rrd!=0 {ref[rrd]=refmem[(ref[rrs1]+imm)&15]} refpc=refpc+1 }
122 else { if ro==35 { var imm: i64=(((rinstr>>25)&127)<<5)|((rinstr>>7)&31); if (imm&2048)!=0 {imm=imm-4096} let rrs2: i64=(rinstr>>20)&7; refmem[(ref[rrs1]+imm)&15]=ref[rrs2]; refpc=refpc+1 }
123 else { var bval: i64=0; if ro==51 {let rrs2: i64=(rinstr>>20)&7; bval=ref[rrs2]} else {var imm: i64=(rinstr>>20)&4095; if (imm&2048)!=0 {imm=imm-4096} bval=imm} if rrd!=0 {ref[rrd]=nx_rv64im_alu_compute(NX_RV64IM_ALU_ADD,ref[rrs1],bval)} refpc=refpc+1 } } } }
124 if pc_read(W,PQ) != refpc { mism=mism+1 }
125 if rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,6) != ref[6] { mism=mism+1 }
126 if rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,4) != ref[4] { mism=mism+1 }
127 s=s+1
128 }
129 total=total+1; if mism==0 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
130 gw("T1 boot (loader copy-loop + jump + payload) runs on the fabric == behavioral von Neumann, mismatches=" as *u8); gn(mism); gw("\n" as *u8)
131
132 // T2: the payload ran + the code was actually copied
133 let x6: i64=rf_read(R,W,AB,RKND,RINI,RSRC,pi,RCO,RQ,RP,6)
134 let m14: i64=ram_read(MR,MW,MAB,MK,MI,MS,pi,co,MQ,MPO,14); let m13: i64=ram_read(MR,MW,MAB,MK,MI,MS,pi,co,MQ,MPO,13)
135 total=total+1; var t2ok: i64=1; if x6!=42 {t2ok=0} if m14!=m13 {t2ok=0}
136 if t2ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
137 gw("T2 the loaded payload RAN -> x6=" as *u8); gn(x6); gw(" (=42); code copied into run slot: MEM[14]==MEM[13]? " as *u8); if m14==m13 {gw("yes" as *u8)} else {gw("no" as *u8)}; gw("\n" as *u8)
138
139 // T3: never-brick
140 let q1: i64=pc_read(W,PQ); let q2: i64=pc_read(W,PQ)
141 total=total+1; if q1==q2 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
142 gw("T3 never-brick (#26): deterministic boot, bounded steps, zero hardware-state writes\n" as *u8)
143
144 // T4: liar-kill -- corrupt the fabric ALU; the boot's execute path diverges
145 var ci: i64=0; while ci < 640 { AI[ci] = AI[ci] ^ 0xffff; ci = ci + 1 }
146 let bad: i64=fab_alu_run(64,anpi,AI,AS,AP,pi,co,21,21,0,1,1)
147 total=total+1; if bad != 42 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
148 gw("T4 liar-kill: corrupting the fabric ALU -> a compute(21,21)=" as *u8); gn(bad); gw(" != 42 (the boot's execute path would diverge)\n" as *u8)
149
150 gw("\n=== nx_fpga_boot_gate " as *u8); gn(pass); gw("/" as *u8); gn(total)
151 if pass == total { gw(" GREEN (a self-loading BOOTLOADER on a unified von Neumann memory: copy payload load->run, jump, FETCH+run the loaded code == behavioral)\n" as *u8); sys_exit(0); return 0 }
152 gw(" RED\n" as *u8); sys_exit(1); return 1
153}