code wiki / _hdl_build / nx_recip_synth_test.nx
nx_recip_synth_test.nx source
↩ module page · 146 lines · 5924 B
1// nx_recip_synth_test.nx -- TRIANGULATED validation of the Newton-Raphson
2// reciprocal divider instantiated as an NxGsim GATE-NETWORK (nx_recip_synth.nx).
3//
4// Three INDEPENDENT legs must agree on every vector (a bug would have to fool
5// all three), exactly mirroring how nx_alu_divider_test proves the restoring
6// divider gate-network and how nx_alu_divider_newton proves the scalar math:
7// LEG A the GATE-NETWORK Newton divider, run through nx_nxgate_sim (q and r)
8// LEG B an independent restoring (shift-subtract) divider, SCALAR (a DIFFERENT
9// algorithm -- the radix-2 reference)
10// LEG C the i64 oracle / and %
11// Battery = the SAME 2,297,160-vector triangulation the scalar proof passed:
12// dense D in [1,256] x N in [0,8191] (2,097,152 vectors)
13// + 200,000 random full-range W=16 (N,D in [1,65535])
14// + 8 explicit edges
15// Known answer (FAIL LOUD): "<ok> <total> <q_chk> <r_chk>", ok must == total.
16// On success prints e.g. "2297160 2297160 3 1" and sys_exit(0); any mismatch
17// prints the bad counts and exits non-zero.
18
19import "nx_recip_synth.nx"
20
21const RT_W: i64 = 16
22const RT_LCG_A: i64 = 6364136223846793005
23const RT_LCG_C: i64 = 1442695040888963407
24
25func _emit_num(v: i64) -> i64 {
26 let b: *u8 = sys_mmap(28); var n: i64 = v; if n < 0 { n = 0 - n }
27 let t2: *u8 = sys_mmap(28); var t: i64 = 0
28 if n == 0 { t2[0] = 48; t = 1 }
29 while n > 0 { t2[t] = 48 + (n % 10); n = n / 10; t = t + 1 }
30 var i: i64 = 0; while i < t { b[i] = t2[t - 1 - i]; i = i + 1 }
31 b[t] = 32; sys_write(1, b, t + 1); return 0
32}
33func _nl() -> i64 { let z: *u8 = sys_mmap(2); z[0] = 10; sys_write(1, z, 1); return 0 }
34
35// LEG B -- independent restoring (shift-subtract) divider, W=16, SCALAR.
36func rt_restoring(N: i64, D: i64) -> i64 {
37 var q: i64 = 0
38 var r: i64 = 0
39 var i: i64 = RT_W - 1
40 while i >= 0 {
41 r = (r << 1) | ((N >> i) & 1)
42 if r >= D { r = r - D; q = q | (1 << i) }
43 i = i - 1
44 }
45 return q
46}
47
48// run the prebuilt gate-network once for (N,D); returns the quotient net value,
49// writes the remainder net value to r_out[0].
50func rt_run(g: *NxGsim, qnet: i64, rnet: i64, N: i64, D: i64, r_out: *i64) -> i64 {
51 g.vals[0] = N
52 g.vals[1] = D
53 if nx_gsim_run(g) != NX_GSIM_OK { r_out[0] = 0 - 1; return 0 - 1 }
54 r_out[0] = g.vals[rnet]
55 return g.vals[qnet]
56}
57
58func main() -> i64 {
59 // Build the gate-network ONCE; inputs are nets 0 (N) and 1 (D). The Newton
60 // divider at W=16 emits a few thousand cells, so size generously.
61 let vals: *i64 = sys_mmap(8 * 4096) as *i64
62 let cells: *NxGsimCell = sys_mmap(48 * 4096) as *NxGsimCell
63 let g: *NxGsim = sys_mmap(64) as *NxGsim
64 g.vals = vals; g.n_nets = 2; g.cells = cells; g.n_cells = 0
65
66 let ro_build: *i64 = sys_mmap(8) as *i64; ro_build[0] = 0
67 let qnet: i64 = nx_recip_div_synth(g, 0, 1, ro_build)
68 let rnet: i64 = ro_build[0]
69
70 let r_out: *i64 = sys_mmap(8) as *i64; r_out[0] = 0
71
72 var total: i64 = 0
73 var ok: i64 = 0
74 var q_chk: i64 = 0 // a sample quotient (D=3,N=12345) to print as a fixed KAT
75 var r_chk: i64 = 0 // a sample remainder for the same KAT
76
77 // dense exhaustive: D in [1,256], N in [0,8191] -- all 3 legs must agree
78 var D: i64 = 1
79 while D <= 256 {
80 var N: i64 = 0
81 while N <= 8191 {
82 let a: i64 = rt_run(g, qnet, rnet, N, D, r_out) // LEG A (gate-net q)
83 let ar: i64 = r_out[0] // LEG A (gate-net r)
84 let b: i64 = rt_restoring(N, D) // LEG B
85 let c: i64 = N / D // LEG C (oracle q)
86 let cr: i64 = N - (N / D) * D // LEG C (oracle r)
87 total = total + 1
88 if a == b { if a == c { if ar == cr { ok = ok + 1 } } }
89 N = N + 1
90 }
91 D = D + 1
92 }
93
94 // a printed fixed KAT (single known answer) for the summary line
95 q_chk = rt_run(g, qnet, rnet, 12345, 7, r_out) // 12345 / 7 = 1763 rem 4
96 r_chk = r_out[0]
97
98 // random full-range W=16: N,D in [1,65535]
99 var seed: i64 = 2463534242
100 var nr: i64 = 0
101 while nr < 200000 {
102 seed = seed * RT_LCG_A + RT_LCG_C; var N: i64 = seed & 65535
103 seed = seed * RT_LCG_A + RT_LCG_C; var D: i64 = (seed & 65535)
104 if D == 0 { D = 1 }
105 let a: i64 = rt_run(g, qnet, rnet, N, D, r_out)
106 let ar: i64 = r_out[0]
107 let b: i64 = rt_restoring(N, D)
108 let c: i64 = N / D
109 let cr: i64 = N - (N / D) * D
110 total = total + 1
111 if a == b { if a == c { if ar == cr { ok = ok + 1 } } }
112 nr = nr + 1
113 }
114
115 // edges
116 let en: *i64 = sys_mmap(8 * 8) as *i64
117 let ed: *i64 = sys_mmap(8 * 8) as *i64
118 en[0]=65535; ed[0]=1
119 en[1]=65535; ed[1]=65535
120 en[2]=65535; ed[2]=32768
121 en[3]=0; ed[3]=65535
122 en[4]=1; ed[4]=1
123 en[5]=65535; ed[5]=2
124 en[6]=12345; ed[6]=255
125 en[7]=65535; ed[7]=257
126 var e: i64 = 0
127 while e < 8 {
128 let a: i64 = rt_run(g, qnet, rnet, en[e], ed[e], r_out)
129 let ar: i64 = r_out[0]
130 let b: i64 = rt_restoring(en[e], ed[e])
131 let c: i64 = en[e] / ed[e]
132 let cr: i64 = en[e] - (en[e] / ed[e]) * ed[e]
133 total = total + 1
134 if a == b { if a == c { if ar == cr { ok = ok + 1 } } }
135 e = e + 1
136 }
137
138 _emit_num(ok); _emit_num(total); _emit_num(q_chk); _emit_num(r_chk); _nl()
139 // FAIL LOUD: every gate-net result agrees 1:1 with BOTH the restoring divider
140 // and the i64 oracle (quotient AND remainder), over the full battery.
141 if ok != total { sys_exit(1); return 1 }
142 if total != 2297160 { sys_exit(2); return 2 } // dense 2097152 + 200000 + 8
143 if q_chk != 1763 { sys_exit(3); return 3 } // 12345/7 KAT quotient
144 if r_chk != 4 { sys_exit(4); return 4 } // 12345/7 KAT remainder
145 sys_exit(0); return 0
146}