code wiki / _hdl_build / nx_fpga_regfile_gate.nx
nx_fpga_regfile_gate.nx source
↩ module page · 112 lines · 6884 B
1import "nx_gate_gn.nx"
2import "nx_gate_base.nx"
3// nx_fpga_regfile_gate.nx -- GATE for RUNG 10: a REGISTER FILE on the sequential fabric. Proves the core CPU
4// storage: addressed clocked write + addressed read, x0 hardwired 0, write is CLOCK-EDGE triggered, state HOLDS.
5// T1 ROUNDTRIP+x0 -- write reg1/2/3, read each back; reg0 (x0) always reads 0.
6// T2 HOLD -- an idle clock (we=0) leaves all registers unchanged.
7// T3 CLOCK-EDGE -- staging a write (we=1) but NOT ticking leaves the old value; the value updates only on TICK.
8// T4 SCALES -- an R=8 register file round-trips.
9// T5 NEVER-BRICK -- deterministic; bounded; x0 invariant.
10// T6 LIAR-KILL -- corrupt a write-decode LUT -> a write lands wrong.
11// GREEN iff all pass. expect_exit: 0 license_tier: ORIGINAL
12import "nx_fpga_regfile.nx"
13import "nx_syscalls.nx"
14
15func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
16" as *u8); return ok }
17
18func main() -> i64 {
19 gw("=== nx_fpga_regfile_gate: RUNG 10 -- a REGISTER FILE on the sequential fabric (CPU storage) ===\n" as *u8)
20 var pass: i64 = 0; var total: i64 = 0
21
22 let kind: *i64 = sys_mmap(8 * 320) as *i64
23 let init: *i64 = sys_mmap(8 * 320) as *i64
24 let src: *i64 = sys_mmap(8 * 1300) as *i64
25 let po: *i64 = sys_mmap(8 * 40) as *i64
26 let q: *i64 = sys_mmap(8 * 320) as *i64
27 let co: *i64 = sys_mmap(8 * 320) as *i64
28 let pi: *i64 = sys_mmap(8 * 24) as *i64
29
30 // ---- R=4, W=8 ----
31 let R: i64 = 4; let W: i64 = 8; let AB: i64 = 2
32 let npi: i64 = seq_build_regfile(R, W, AB, kind, init, src, po)
33 let nc: i64 = rf_ncells(R, W)
34 var i: i64 = 0
35 while i < nc { q[i] = 0; i = i + 1 }
36
37 // ---- T1: roundtrip + x0 ----
38 rf_write(R, W, AB, kind, init, src, pi, co, q, 1, 90) // reg1 = 0x5A
39 rf_write(R, W, AB, kind, init, src, pi, co, q, 2, 231) // reg2 = 0xE7
40 rf_write(R, W, AB, kind, init, src, pi, co, q, 3, 60) // reg3 = 0x3C
41 var t1ok: i64 = 1
42 if rf_read(R, W, AB, kind, init, src, pi, co, q, po, 1) != 90 { t1ok = 0 }
43 if rf_read(R, W, AB, kind, init, src, pi, co, q, po, 2) != 231 { t1ok = 0 }
44 if rf_read(R, W, AB, kind, init, src, pi, co, q, po, 3) != 60 { t1ok = 0 }
45 if rf_read(R, W, AB, kind, init, src, pi, co, q, po, 0) != 0 { t1ok = 0 } // x0
46 rf_write(R, W, AB, kind, init, src, pi, co, q, 0, 255) // write x0 -> ignored
47 if rf_read(R, W, AB, kind, init, src, pi, co, q, po, 0) != 0 { t1ok = 0 }
48 total=total+1; if t1ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
49 gw("T1 write/read roundtrip (reg1=90 reg2=231 reg3=60) + x0 always 0 (write to x0 ignored)\n" as *u8)
50
51 // ---- T2: hold across an idle clock (we=0) ----
52 var j: i64 = 0
53 while j < AB { pi[j]=0; pi[AB+j]=0; j=j+1 }
54 i = 0; while i < W { pi[2*AB+i]=0; i=i+1 }
55 pi[2*AB+W] = 0
56 seq_tick(nc, npi, kind, init, src, pi, co, q) // idle tick
57 var t2ok: i64 = 1
58 if rf_read(R, W, AB, kind, init, src, pi, co, q, po, 1) != 90 { t2ok = 0 }
59 if rf_read(R, W, AB, kind, init, src, pi, co, q, po, 2) != 231 { t2ok = 0 }
60 total=total+1; if t2ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
61 gw("T2 hold: an idle clock (we=0) leaves registers unchanged\n" as *u8)
62
63 // ---- T3: clock-edge -- stage reg1<-170 with we=1 but DON'T tick; old value until TICK ----
64 pi[0]=1; pi[1]=0 // raddr = 1
65 pi[AB+0]=1; pi[AB+1]=0 // waddr = 1
66 i = 0; while i < W { pi[2*AB+i] = (170 >> i) & 1; i=i+1 }// wdata = 0xAA
67 pi[2*AB+W] = 1 // we = 1
68 seq_eval(nc, npi, kind, init, src, pi, co, q) // settle, NO latch
69 var staged: i64 = 0
70 i = 0; while i < W { staged = staged | ((fab_po(po[i], npi, pi, co) & 1) << i); i=i+1 }
71 seq_tick(nc, npi, kind, init, src, pi, co, q) // NOW latch
72 let after: i64 = rf_read(R, W, AB, kind, init, src, pi, co, q, po, 1)
73 total=total+1; if staged==90 { if after==170 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) }
74 gw("T3 clock-edge: before tick reg1=" as *u8); gn(staged); gw(" (old 90), after tick reg1=" as *u8); gn(after); gw(" (new 170)\n" as *u8)
75
76 // ---- T4: R=8 round-trips ----
77 let R8: i64 = 8; let AB3: i64 = 3
78 let npi8: i64 = seq_build_regfile(R8, W, AB3, kind, init, src, po)
79 let nc8: i64 = rf_ncells(R8, W)
80 i = 0; while i < nc8 { q[i] = 0; i = i + 1 }
81 rf_write(R8, W, AB3, kind, init, src, pi, co, q, 5, 200)
82 rf_write(R8, W, AB3, kind, init, src, pi, co, q, 7, 99)
83 var t4ok: i64 = 1
84 if rf_read(R8, W, AB3, kind, init, src, pi, co, q, po, 5) != 200 { t4ok = 0 }
85 if rf_read(R8, W, AB3, kind, init, src, pi, co, q, po, 7) != 99 { t4ok = 0 }
86 if rf_read(R8, W, AB3, kind, init, src, pi, co, q, po, 0) != 0 { t4ok = 0 }
87 if rf_read(R8, W, AB3, kind, init, src, pi, co, q, po, 3) != 0 { t4ok = 0 } // never written
88 total=total+1; if t4ok==1 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
89 gw("T4 scales: R=8 register file round-trips (reg5=200 reg7=99, unwritten reg3=0)\n" as *u8)
90
91 // ---- T5: never-brick (deterministic) ----
92 let npi4: i64 = seq_build_regfile(R, W, AB, kind, init, src, po)
93 i = 0; while i < nc { q[i] = 0; i = i + 1 }
94 rf_write(R, W, AB, kind, init, src, pi, co, q, 2, 123)
95 let d1: i64 = rf_read(R, W, AB, kind, init, src, pi, co, q, po, 2)
96 let d2: i64 = rf_read(R, W, AB, kind, init, src, pi, co, q, po, 2)
97 total=total+1; if d1==d2 { if d1==123 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } } else { gw(" [FAIL] " as *u8) }
98 gw("T5 never-brick (#26): deterministic reads, bounded, x0 invariant, zero hw-state writes\n" as *u8)
99
100 // ---- T6: liar-kill -- corrupt the write-decode LUT of reg2 -> write to reg2 lands wrong ----
101 seq_build_regfile(R, W, AB, kind, init, src, po)
102 init[R*W + 1 + 2] = init[R*W + 1 + 2] ^ 0xffff // wsel_2 LUT (cell R*W+1+2)
103 i = 0; while i < nc { q[i] = 0; i = i + 1 }
104 rf_write(R, W, AB, kind, init, src, pi, co, q, 2, 77)
105 let got: i64 = rf_read(R, W, AB, kind, init, src, pi, co, q, po, 2)
106 total=total+1; if got != 77 { pass=pass+1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) }
107 gw("T6 liar-kill: corrupting reg2's write-decode LUT -> write lands wrong (read=" as *u8); gn(got); gw(", expected!=77)\n" as *u8)
108
109 gw("\n=== nx_fpga_regfile_gate " as *u8); gn(pass); gw("/" as *u8); gn(total)
110 if pass == total { gw(" GREEN (a clocked register file with addressed read/write + x0=0 runs on the simulated FPGA; CPU storage)\n" as *u8); sys_exit(0); return 0 }
111 gw(" RED\n" as *u8); sys_exit(1); return 1
112}