code wiki / _hdl_build / nx_isa_equiv_sov.nx
nx_isa_equiv_sov.nx source
↩ module page · 358 lines · 18319 B
1// nx_isa_equiv_sov.nx -- THE DIFFERENTIAL-REPLACEMENT TEST, ON THE HARDWARE.
2//
3// The operator's own method ("comp a working child, turn it off, emit ours, see if they
4// function the same") performed at the genesis-hardware level, with NO Claude crutch left:
5//
6// (1) the team COMPOSES its own random non-trivial target program T (over the REAL RV64IM
7// ALU ops that rv64im_min_sim executes -- ADD/SUB/MUL/AND/OR/SLT on a register machine),
8// (2) the team DERIVES a program G by evolutionary search that matches T's TRAIN examples
9// (search sees only (a,b)->T(a,b) points, never T's source),
10// (3) we ENCODE BOTH T and G to real RV64 machine code, run BOTH on rv64im_min_sim (god)
11// over 12 HELD-OUT inputs, and verify sim(T) == sim(G) as FULL 64-bit values.
12//
13// The equivalence verdict reads the hardware -- the interpreter only GUIDES the search, it is
14// NOT in the proof. Two controls keep it honest: a FAITHFUL-ENCODE control (all 6 ops
15// encode->run->match the ALU) and a NEGATIVE control (a perturbed G the differential MUST
16// catch -- proving the test discriminates, not rubber-stamps).
17//
18// Full 64-bit read-back: each program does `sd x3, RES_OFF(x5)` to scratch RAM; store64 writes
19// through to mem_buf (verified rv64im_min_sim.nx:415), which we read back directly. license_tier: ORIGINAL
20import "nx_syscalls.nx"
21import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
22import "nishi_hdl_primitives.nx"
23import "rv64im_min_decoder.nx"
24import "rv64im_min_alu.nx"
25import "rv64im_min_regfile.nx"
26import "rv64im_min_csr.nx"
27import "rv64im_min_clint.nx"
28import "rv64im_min_uart.nx"
29import "rv64im_min_sim.nx"
30const EVO_MAGIC_2862933555777941757: i64 = 2862933555777941757
31const EVO_MAGIC_3037000493: i64 = 3037000493
32const EVO_MAGIC_1000000000000: i64 = 1000000000000
33const EVO_MAGIC_10000000000000: i64 = 10000000000000
34const EVO_MAGIC_1000000000: i64 = 1000000000
35const EVO_MAGIC_2654435761: i64 = 2654435761
36const EVO_MAGIC_12345: i64 = 12345
37
38const NINSTR: i64 = 3
39const GLEN: i64 = 9 // NINSTR * 3 (op, rs1, rs2); rd = x3 fixed
40const EVO_P: i64 = 320
41const EVO_G: i64 = 1400
42const EVO_T: i64 = 5
43const NTRAIN: i64 = 6
44const NHELD: i64 = 12
45
46const IR_MEM_BASE: i64 = 0x80000000
47const IR_MEM_SIZE: i64 = 8192
48const IR_TX_CAP: i64 = 256
49const RES_OFF: i64 = 0x700 // scratch result slot (positive S-imm, past the ~52-byte code)
50
51// ---- prng / util -----------------------------------------------------------------------
52func ie_rand(s: *i64) -> i64 { s[0] = s[0] * EVO_MAGIC_2862933555777941757 + EVO_MAGIC_3037000493; return (s[0] >> 17) & 0x3fffffff }
53func ie_abs(x: i64) -> i64 { if x < 0 { return 0 - x } return x }
54
55// train points (search fits these) and held-out points (verdict checks these; disjoint).
56func ie_pa(i: i64) -> i64 { if i==0 {return 6} if i==1 {return 4} if i==2 {return 9} if i==3 {return 5} if i==4 {return 7} return 2 }
57func ie_pb(i: i64) -> i64 { if i==0 {return 4} if i==1 {return 7} if i==2 {return 2} if i==3 {return 9} if i==4 {return 3} return 5 }
58func ie_ha(i: i64) -> i64 { if i==0 {return 3} if i==1 {return 8} if i==2 {return 11} if i==3 {return 2} if i==4 {return 10} if i==5 {return 13} if i==6 {return 7} if i==7 {return 4} if i==8 {return 12} if i==9 {return 6} if i==10 {return 9} return 5 }
59func ie_hb(i: i64) -> i64 { if i==0 {return 5} if i==1 {return 2} if i==2 {return 7} if i==3 {return 11} if i==4 {return 4} if i==5 {return 3} if i==6 {return 9} if i==7 {return 8} if i==8 {return 2} if i==9 {return 13} if i==10 {return 6} return 10 }
60
61// ---- the REAL RV64IM ALU op (semantics matched to rv64im_min_sim; 6 total functions) ----
62func ie_op(op: i64, x: i64, y: i64) -> i64 {
63 if op == 0 { return x + y } // ADD
64 if op == 1 { return x - y } // SUB
65 if op == 2 { return x * y } // MUL
66 if op == 3 { return x & y } // AND
67 if op == 4 { return x | y } // OR
68 if x < y { return 1 } return 0 // SLT
69}
70// op index -> the sim's funct3 / funct7 (rv64im_min_sim.nx alu_select, verified).
71func ie_f3(op: i64) -> i64 { if op==3 {return 7} if op==4 {return 6} if op==5 {return 2} return 0 } // AND=7 OR=6 SLT=2 else 0
72func ie_f7(op: i64) -> i64 { if op==1 {return 0x20} if op==2 {return 0x01} return 0x00 } // SUB=0x20 MUL=0x01 else 0
73// register select: r0=0(x0), r1=a(x1), r2=b(x2), r3=acc(x3) -- index == x-register number.
74func ie_reg(idx: i64, a: i64, b: i64, acc: i64) -> i64 {
75 if idx == 0 { return 0 }
76 if idx == 1 { return a }
77 if idx == 2 { return b }
78 return acc
79}
80// interpreter: execute a genome (register-machine program) on inputs a,b -> acc (=x3).
81func ie_eval(gen: *i64, base: i64, a: i64, b: i64) -> i64 {
82 var acc: i64 = 0
83 var j: i64 = 0
84 while j < NINSTR {
85 let op: i64 = gen[base + j*3]
86 let rs1: i64 = gen[base + j*3 + 1]
87 let rs2: i64 = gen[base + j*3 + 2]
88 let x: i64 = ie_reg(rs1, a, b, acc)
89 let y: i64 = ie_reg(rs2, a, b, acc)
90 acc = ie_op(op, x, y)
91 j = j + 1
92 }
93 return acc
94}
95
96// ---- RV64 encoders (rv_rtype/rv_addi/rv_lui proven in nx_isa_run_sov; rv_store_imm = S-type) ----
97func rv_addi(rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm & 0xFFF) << 20) | (rs1 << 15) | (rd << 7) | 0x13 }
98func rv_lui(rd: i64, imm20: i64) -> i64 { return ((imm20 & 0xFFFFF) << 12) | (rd << 7) | 0x37 }
99func rv_rtype(f7: i64, rs2: i64, rs1: i64, f3: i64, rd: i64) -> i64 { return (f7 << 25) | (rs2 << 20) | (rs1 << 15) | (f3 << 12) | (rd << 7) | 0x33 }
100func rv_store_imm(rs2: i64, rs1: i64, f3: i64, imm: i64) -> i64 {
101 let hi: i64 = (imm >> 5) & 0x7f
102 let lo: i64 = imm & 0x1f
103 return (hi << 25) | (rs2 << 20) | (rs1 << 15) | (f3 << 12) | (lo << 7) | 0x23
104}
105func rv_w32(buf: *u8, off: i64, w: i64) -> i64 {
106 buf[off] = (w & 0xff) as u8
107 buf[off+1] = ((w >> 8) & 0xff) as u8
108 buf[off+2] = ((w >> 16) & 0xff) as u8
109 buf[off+3] = ((w >> 24) & 0xff) as u8
110 return off + 4
111}
112// emit: x1=a; x2=b; x3=0; <genome as R-type, rd=x3>; x5=RAM base; sd x3,RES_OFF(x5); finisher halt; spin.
113func ie_emit(mem: *u8, gen: *i64, base: i64, a: i64, b: i64) -> i64 {
114 var o: i64 = 0
115 o = rv_w32(mem, o, rv_addi(1, 0, a)) // addi x1, x0, a
116 o = rv_w32(mem, o, rv_addi(2, 0, b)) // addi x2, x0, b
117 o = rv_w32(mem, o, rv_addi(3, 0, 0)) // addi x3, x0, 0 (acc = 0)
118 var j: i64 = 0
119 while j < NINSTR {
120 let op: i64 = gen[base + j*3]
121 let rs1: i64 = gen[base + j*3 + 1]
122 let rs2: i64 = gen[base + j*3 + 2]
123 o = rv_w32(mem, o, rv_rtype(ie_f7(op), rs2, rs1, ie_f3(op), 3)) // x3 = op(x[rs1], x[rs2])
124 j = j + 1
125 }
126 o = rv_w32(mem, o, rv_lui(5, 0x80000)) // lui x5, 0x80000 (x5 = RAM base 0x80000000)
127 o = rv_w32(mem, o, rv_store_imm(3, 5, 3, RES_OFF)) // sd x3, RES_OFF(x5) (full 64-bit result to RAM)
128 o = rv_w32(mem, o, rv_lui(7, 0x100)) // lui x7, 0x100 (finisher 0x100000)
129 o = rv_w32(mem, o, rv_lui(6, 0x5)) // lui x6, 0x5
130 o = rv_w32(mem, o, rv_addi(6, 6, 0x555)) // addi x6, x6, 0x555 (x6 = 0x5555 PASS)
131 o = rv_w32(mem, o, rv_store_imm(6, 7, 2, 0)) // sw x6, 0(x7) (finisher <- 0x5555 -> halt)
132 o = rv_w32(mem, o, 0x6F) // jal x0, 0 (spin)
133 return o
134}
135// read a little-endian i64 back from the scratch RAM slot (mirrors sim load64).
136func ie_read_i64(mem: *u8, off: i64) -> i64 {
137 var v: i64 = 0
138 var i: i64 = 0
139 while i < 8 { v = v | ((mem[off + i] as i64) << (i * 8)); i = i + 1 }
140 return v
141}
142// RUN a genome on the GENESIS SIM (fresh devices each call -> clean reset) and return its 64-bit result.
143func ie_run_prog(gen: *i64, base: i64, a: i64, b: i64) -> i64 {
144 let rf_storage: *i64 = (sys_mmap(8 * NX_RV64IM_RF_N_REGS)) as *i64
145 let csr_storage: *i64 = (sys_mmap(8 * NX_CSR_SLOT_N)) as *i64
146 let clint_storage: *i64 = (sys_mmap(8 * NX_CLINT_SLOT_N)) as *i64
147 let uart_storage: *i64 = (sys_mmap(8 * NX_UART_SLOT_N)) as *i64
148 let mem: *u8 = sys_mmap(IR_MEM_SIZE)
149 let tx_buf: *u8 = sys_mmap(IR_TX_CAP)
150 let rf: *NxRv64imRegfile = (sys_mmap(64)) as *NxRv64imRegfile
151 let csr: *NxRv64imCsrFile = (sys_mmap(64)) as *NxRv64imCsrFile
152 let clint: *NxClint = (sys_mmap(64)) as *NxClint
153 let uart: *NxUart = (sys_mmap(64)) as *NxUart
154 let sim: *NxRv64imSim = (sys_mmap(128)) as *NxRv64imSim
155 nx_rv64im_rf_init(rf, rf_storage)
156 nx_rv64im_csr_init(csr, csr_storage, 0)
157 nx_clint_init(clint, clint_storage)
158 nx_uart_init(uart, uart_storage, tx_buf, IR_TX_CAP)
159 nx_rv64im_sim_init(sim, rf, csr, clint, uart, IR_MEM_BASE, mem, IR_MEM_SIZE, 0)
160 ie_emit(mem, gen, base, a, b)
161 nx_rv64im_sim_run(sim, 400)
162 return ie_read_i64(mem, RES_OFF)
163}
164
165// ---- compose / derive (GA) -------------------------------------------------------------
166func ie_setinstr(gen: *i64, j: i64, state: *i64) -> i64 {
167 gen[j*3] = ie_rand(state) % 6 // op (6 total-function ops)
168 gen[j*3 + 1] = ie_rand(state) % 4 // rs1 (x0..x3)
169 gen[j*3 + 2] = ie_rand(state) % 4 // rs2
170 return 0
171}
172// COMPOSE: random non-trivial target (depends on BOTH a and b).
173func ie_compose(state: *i64, T: *i64) -> i64 {
174 var tries: i64 = 0
175 while tries < 400 {
176 var j: i64 = 0
177 while j < NINSTR { ie_setinstr(T, j, state); j = j + 1 }
178 let y00: i64 = ie_eval(T, 0, 6, 4)
179 let ya: i64 = ie_eval(T, 0, 9, 4)
180 let yb: i64 = ie_eval(T, 0, 6, 9)
181 if y00 != ya { if y00 != yb { return 1 } }
182 tries = tries + 1
183 }
184 return 0
185}
186func ie_fit(pop: *i64, base: i64, T: *i64) -> i64 {
187 var err: i64 = 0; var i: i64 = 0
188 while i < NTRAIN {
189 let a: i64 = ie_pa(i); let b: i64 = ie_pb(i)
190 let r: i64 = ie_eval(pop, base, a, b)
191 let y: i64 = ie_eval(T, 0, a, b)
192 var d: i64 = ie_abs(r - y)
193 if r > EVO_MAGIC_1000000000000 { d = EVO_MAGIC_10000000000000 }
194 if r < (0 - EVO_MAGIC_1000000000000) { d = EVO_MAGIC_10000000000000 }
195 err = err + d; i = i + 1
196 }
197 return err
198}
199func ie_tourney(fit: *i64, state: *i64) -> i64 {
200 var bi: i64 = ie_rand(state) % EVO_P; var bd: i64 = fit[bi]; var k: i64 = 1
201 while k < EVO_T { let i: i64 = ie_rand(state) % EVO_P; if fit[i] < bd { bd = fit[i]; bi = i } k = k + 1 }
202 return bi
203}
204func ie_mut(gen: *i64, base: i64, state: *i64) -> i64 {
205 let j: i64 = ie_rand(state) % NINSTR
206 let f: i64 = ie_rand(state) % 3
207 if f == 0 { gen[base + j*3] = ie_rand(state) % 6 } else { gen[base + j*3 + f] = ie_rand(state) % 4 }
208 return 0
209}
210// DERIVE: evolve a program G matching T's TRAIN examples; store in G, return best_err.
211func ie_derive(T: *i64, state: *i64, G: *i64) -> i64 {
212 let pop: *i64 = sys_mmap(EVO_P * GLEN * 8) as *i64
213 let nxt: *i64 = sys_mmap(EVO_P * GLEN * 8) as *i64
214 let fit: *i64 = sys_mmap(EVO_P * 8) as *i64
215 var p: i64 = 0
216 while p < EVO_P { var j: i64 = 0; while j < NINSTR { ie_setinstr(pop, p*3 + j, state); j = j + 1 } p = p + 1 }
217 var best_err: i64 = EVO_MAGIC_1000000000
218 var g: i64 = 0
219 while g < EVO_G {
220 var gbest: i64 = EVO_MAGIC_1000000000; var gbp: i64 = 0
221 p = 0
222 while p < EVO_P { let e: i64 = ie_fit(pop, p*GLEN, T); fit[p] = e; if e < gbest { gbest = e; gbp = p } p = p + 1 }
223 if gbest < best_err { best_err = gbest; var j: i64 = 0; while j < GLEN { G[j] = pop[gbp*GLEN + j]; j = j + 1 } }
224 if best_err == 0 { g = EVO_G } else {
225 var j2: i64 = 0; while j2 < GLEN { nxt[j2] = G[j2]; j2 = j2 + 1 }
226 p = 1
227 while p < EVO_P {
228 let pa: i64 = ie_tourney(fit, state) * GLEN
229 let pb: i64 = ie_tourney(fit, state) * GLEN
230 let cut: i64 = ((ie_rand(state) % (NINSTR - 1)) + 1) * 3
231 var jj: i64 = 0
232 while jj < GLEN { if jj < cut { nxt[p*GLEN + jj] = pop[pa + jj] } else { nxt[p*GLEN + jj] = pop[pb + jj] } jj = jj + 1 }
233 ie_mut(nxt, p*GLEN, state)
234 p = p + 1
235 }
236 var c: i64 = 0; while c < EVO_P * GLEN { pop[c] = nxt[c]; c = c + 1 }
237 g = g + 1
238 }
239 }
240 return best_err
241}
242
243// ---- print -----------------------------------------------------------------------------
244func ie_p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
245// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
246// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
247// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
248// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
249func ie_pn(v: i64) -> i64 { nxi_out(v); return 0 }
250
251// CONTROL 1: every op encodes -> runs on the sim -> matches the ALU (faithful encode). 1-instr
252// genome [op,1,2] padded with no-ops [0,3,0] (acc = acc + x0 = acc). Returns 1 if all 6 pass.
253func ie_faithful() -> i64 {
254 let G: *i64 = sys_mmap(GLEN * 8) as *i64
255 var op: i64 = 0; var ok: i64 = 0
256 while op < 6 {
257 G[0] = op; G[1] = 1; G[2] = 2
258 G[3] = 0; G[4] = 3; G[5] = 0
259 G[6] = 0; G[7] = 3; G[8] = 0
260 let got: i64 = ie_run_prog(G, 0, 7, 3)
261 let exp: i64 = ie_op(op, 7, 3)
262 if got == exp { ok = ok + 1 }
263 op = op + 1
264 }
265 ie_p(" control-faithful: " as *u8); ie_pn(ok); ie_p("/6 ops encode->run-on-god->match ALU\n" as *u8)
266 if ok == 6 { return 1 }
267 return 0
268}
269
270// the HARDWARE differential: run BOTH genomes on god over held-out, count points where sim(A)==sim(B).
271func ie_hw_agree(A: *i64, B: *i64) -> i64 {
272 var agree: i64 = 0; var i: i64 = 0
273 while i < NHELD {
274 let a: i64 = ie_ha(i); let b: i64 = ie_hb(i)
275 if ie_run_prog(A, 0, a, b) == ie_run_prog(B, 0, a, b) { agree = agree + 1 }
276 i = i + 1
277 }
278 return agree
279}
280// faithfulness over held-out: sim(T) must equal the interpreter on every held-out point.
281func ie_hw_faithful(T: *i64) -> i64 {
282 var ok: i64 = 0; var i: i64 = 0
283 while i < NHELD {
284 let a: i64 = ie_ha(i); let b: i64 = ie_hb(i)
285 if ie_run_prog(T, 0, a, b) == ie_eval(T, 0, a, b) { ok = ok + 1 }
286 i = i + 1
287 }
288 return ok
289}
290
291// one seed: compose T, derive G, then PROVE on the hardware sim(T)==sim(G) over held-out.
292func ie_seed(seed: i64) -> i64 {
293 let state: *i64 = sys_mmap(8) as *i64; state[0] = seed * EVO_MAGIC_2654435761 + EVO_MAGIC_12345
294 let T: *i64 = sys_mmap(GLEN * 8) as *i64
295 let G: *i64 = sys_mmap(GLEN * 8) as *i64
296 if ie_compose(state, T) != 1 { ie_p("seed=" as *u8); ie_pn(seed); ie_p(" COMPOSE-FAIL\n" as *u8); return 0 }
297 let de: i64 = ie_derive(T, state, G)
298 // interpreter generalization (search saw only train; check it holds on held-out)
299 var imatch: i64 = 0; var i: i64 = 0
300 while i < NHELD { if ie_eval(G, 0, ie_ha(i), ie_hb(i)) == ie_eval(T, 0, ie_ha(i), ie_hb(i)) { imatch = imatch + 1 } i = i + 1 }
301 // THE PROOF: both run on god; verdict reads the hardware, not the interpreter.
302 let hf: i64 = ie_hw_faithful(T)
303 let hw: i64 = ie_hw_agree(T, G)
304 ie_p("seed=" as *u8); ie_pn(seed); ie_p(" derive_err=" as *u8); ie_pn(de)
305 ie_p(" interp_held=" as *u8); ie_pn(imatch); ie_p("/" as *u8); ie_pn(NHELD)
306 ie_p(" sim(T)==interp=" as *u8); ie_pn(hf); ie_p("/" as *u8); ie_pn(NHELD)
307 ie_p(" sim(T)==sim(G)=" as *u8); ie_pn(hw); ie_p("/" as *u8); ie_pn(NHELD)
308 if de == 0 { if imatch == NHELD { if hf == NHELD { if hw == NHELD {
309 ie_p(" GREEN\n" as *u8); return 1
310 }}}}
311 ie_p(" RED\n" as *u8); return 0
312}
313
314// CONTROL 2 (negative): perturb the derived G into G' that the INTERPRETER says differs from T
315// on some held-out point, then confirm the HARDWARE differential CATCHES it (sim(T) != sim(G')
316// somewhere). Proves the test discriminates -- a wrong program is NOT rubber-stamped GREEN.
317func ie_negctl(seed: i64) -> i64 {
318 let state: *i64 = sys_mmap(8) as *i64; state[0] = seed * EVO_MAGIC_2654435761 + 999
319 let T: *i64 = sys_mmap(GLEN * 8) as *i64
320 let G: *i64 = sys_mmap(GLEN * 8) as *i64
321 let Gp: *i64 = sys_mmap(GLEN * 8) as *i64
322 if ie_compose(state, T) != 1 { ie_p(" control-negative: COMPOSE-FAIL\n" as *u8); return 0 }
323 ie_derive(T, state, G)
324 // build G' = G with the last op swapped; pick a swap the interpreter says changes a held-out value.
325 var found_diff: i64 = 0
326 var cand: i64 = 0
327 while cand < 6 {
328 var c: i64 = 0; while c < GLEN { Gp[c] = G[c]; c = c + 1 }
329 Gp[(NINSTR-1)*3] = cand
330 var differs: i64 = 0; var i: i64 = 0
331 while i < NHELD { if ie_eval(Gp, 0, ie_ha(i), ie_hb(i)) != ie_eval(T, 0, ie_ha(i), ie_hb(i)) { differs = 1 } i = i + 1 }
332 if differs == 1 { found_diff = 1; cand = 6 } else { cand = cand + 1 }
333 }
334 if found_diff == 0 { ie_p(" control-negative: no distinguishing perturbation (inconclusive)\n" as *u8); return 0 }
335 let agree: i64 = ie_hw_agree(T, Gp)
336 ie_p(" control-negative: sim(T)==sim(G_wrong)=" as *u8); ie_pn(agree); ie_p("/" as *u8); ie_pn(NHELD)
337 if agree < NHELD { ie_p(" -> differential CAUGHT the wrong program (good)\n" as *u8); return 1 }
338 ie_p(" -> differential MISSED it (test broken)\n" as *u8); return 0
339}
340
341func main() -> i64 {
342 ie_p("ISAEQUIVSOV: differential-replacement test ON THE HARDWARE (compose T -> derive G -> run both on god)\n" as *u8)
343 let cf: i64 = ie_faithful()
344 var ok: i64 = 0
345 if ie_seed(101) == 1 { ok = ok + 1 }
346 if ie_seed(202) == 1 { ok = ok + 1 }
347 if ie_seed(303) == 1 { ok = ok + 1 }
348 if ie_seed(404) == 1 { ok = ok + 1 }
349 let nc: i64 = ie_negctl(101)
350 ie_p("ISAEQUIVSOV: " as *u8); ie_pn(ok); ie_p("/4 seeds: team-composed target == team-derived program, PROVEN on rv64im_min_sim over held-out\n" as *u8)
351 if cf == 1 { if ok == 4 { if nc == 1 {
352 ie_p("ISAEQUIVSOV GREEN: faithful-encode control PASS + negative control CAUGHT a wrong program + 4/4 hardware-verified equivalences\n" as *u8)
353 sys_exit(0); return 0
354 }}}
355 ie_p("ISAEQUIVSOV RED\n" as *u8)
356 sys_exit(1)
357 return 1
358}