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}