CharlesCNorton commited on
Commit
9847b25
·
1 Parent(s): 3baa17d

Remove routing/ folder, add schema docs to build.py

Browse files

- Delete routing/routing.py and routing/routing.json
- Add routing schema documentation to build.py docstring
- Update README TODO with eval consolidation plan

Files changed (4) hide show
  1. README.md +15 -0
  2. build.py +92 -0
  3. routing/routing.json +0 -0
  4. routing/routing.py +0 -1591
README.md CHANGED
@@ -480,6 +480,21 @@ The interface generalizes to **all** 65,536 8-bit additions once trained—no me
480
 
481
  ---
482
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
483
  ## License
484
 
485
  MIT
 
480
 
481
  ---
482
 
483
+ ## TODO
484
+
485
+ - [x] Deprecate `routing.json` — routing info now embedded in safetensors via `.inputs` tensors
486
+ - [x] Remove `routing/` folder (schema docs moved to `build.py` docstring)
487
+ - [ ] Consolidate eval scripts into single `eval.py` with subcommands:
488
+ - [ ] Merge `iron_eval.py` (4533 lines) — GPU-batched fitness for evolution
489
+ - [ ] Merge `comprehensive_eval.py` (3224 lines) — per-circuit correctness testing
490
+ - [ ] Merge `prune_weights.py` (481 lines) — weight magnitude pruning
491
+ - [ ] Extract shared utilities: `heaviside()`, `load_model()`, signal registry
492
+ - [ ] Subcommands: `eval.py fitness`, `eval.py test`, `eval.py prune`
493
+ - [ ] Read signal registry from safetensors metadata instead of routing.json
494
+ - [ ] Remove `eval/` folder once consolidated to root `eval.py`
495
+
496
+ ---
497
+
498
  ## License
499
 
500
  MIT
build.py CHANGED
@@ -5,6 +5,98 @@ Subcommands:
5
  python build.py memory - Generate 64KB memory circuits
6
  python build.py inputs - Add .inputs metadata tensors
7
  python build.py all - Run both (memory first, then inputs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  """
9
 
10
  from __future__ import annotations
 
5
  python build.py memory - Generate 64KB memory circuits
6
  python build.py inputs - Add .inputs metadata tensors
7
  python build.py all - Run both (memory first, then inputs)
8
+
9
+
10
+ ROUTING SCHEMA (formerly routing.json)
11
+ ======================================
12
+
13
+ Routing info is now embedded in safetensors via .inputs tensors and signal registry metadata.
14
+
15
+
16
+ INPUT SOURCE TYPES
17
+ ------------------
18
+
19
+ 1. External input: "$input_name" - Named input to the circuit
20
+ - Example: "$a", "$b", "$cin"
21
+
22
+ 2. Gate output: "path.to.gate" - Output of another gate
23
+ - Example: "ha1.sum", "layer1.or"
24
+
25
+ 3. Bit extraction: "$input[i]" - Single bit from multi-bit input
26
+ - Example: "$a[0]" (LSB), "$a[7]" (MSB for 8-bit)
27
+
28
+ 4. Constant: "#0" or "#1" - Fixed value
29
+ - Example: "#1" for carry-in in two's complement
30
+
31
+
32
+ CIRCUIT TYPES
33
+ -------------
34
+
35
+ Single-Layer Gates: .weight and .bias only
36
+ "boolean.and": ["$a", "$b"]
37
+
38
+ Two-Layer Gates (XOR, XNOR): layer1 + layer2
39
+ "boolean.xor.layer1.or": ["$a", "$b"]
40
+ "boolean.xor.layer1.nand": ["$a", "$b"]
41
+ "boolean.xor.layer2": ["layer1.or", "layer1.nand"]
42
+
43
+ Hierarchical Circuits: nested sub-components
44
+ "arithmetic.fulladder": {
45
+ "ha1.sum.layer1.or": ["$a", "$b"],
46
+ "ha1.carry": ["$a", "$b"],
47
+ "ha2.sum.layer1.or": ["ha1.sum", "$cin"],
48
+ "carry_or": ["ha1.carry", "ha2.carry"]
49
+ }
50
+
51
+ Bit-Indexed Circuits: multi-bit operations
52
+ "arithmetic.ripplecarry8bit.fa0": ["$a[0]", "$b[0]", "#0"]
53
+ "arithmetic.ripplecarry8bit.fa1": ["$a[1]", "$b[1]", "fa0.cout"]
54
+
55
+
56
+ PACKED MEMORY CIRCUITS
57
+ ----------------------
58
+
59
+ 64KB memory uses packed tensors (shapes for 16-bit address, 8-bit data):
60
+
61
+ memory.addr_decode.weight: [65536, 16]
62
+ memory.addr_decode.bias: [65536]
63
+ memory.read.and.weight: [8, 65536, 2]
64
+ memory.read.and.bias: [8, 65536]
65
+ memory.read.or.weight: [8, 65536]
66
+ memory.read.or.bias: [8]
67
+ memory.write.sel.weight: [65536, 2]
68
+ memory.write.sel.bias: [65536]
69
+ memory.write.nsel.weight: [65536, 1]
70
+ memory.write.nsel.bias: [65536]
71
+ memory.write.and_old.weight: [65536, 8, 2]
72
+ memory.write.and_old.bias: [65536, 8]
73
+ memory.write.and_new.weight: [65536, 8, 2]
74
+ memory.write.and_new.bias: [65536, 8]
75
+ memory.write.or.weight: [65536, 8, 2]
76
+ memory.write.or.bias: [65536, 8]
77
+
78
+ Semantics:
79
+ decode: sel[i] = H(sum(addr_bits * weight[i]) + bias[i])
80
+ read: bit[b] = H(sum(H([mem_bit, sel] * and_w) + and_b) * or_w + or_b)
81
+ write: new = H(H([old, nsel] * and_old) + H([data, sel] * and_new) - 1)
82
+
83
+
84
+ SIGNAL REGISTRY
85
+ ---------------
86
+
87
+ Signal IDs are stored in safetensors metadata as JSON:
88
+
89
+ {"0": "#0", "1": "#1", "2": "$a", "3": "$b", ...}
90
+
91
+ Each gate's .inputs tensor contains integer IDs referencing this registry.
92
+
93
+
94
+ NAMING CONVENTIONS
95
+ ------------------
96
+
97
+ - External inputs: $name or $name[bit]
98
+ - Constants: #0, #1
99
+ - Internal gates: relative path from circuit root
100
  """
101
 
102
  from __future__ import annotations
routing/routing.json DELETED
The diff for this file is too large to render. See raw diff
 
routing/routing.py DELETED
@@ -1,1591 +0,0 @@
1
- """
2
- Routing Schema and Generator for 8-bit Threshold Computer
3
- ==========================================================
4
-
5
- Generates routing.json — a complete map of how every gate connects to its inputs.
6
-
7
-
8
- SCHEMA FORMAT
9
- -------------
10
-
11
- ```json
12
- {
13
- "version": 1,
14
- "external_inputs": {
15
- "circuit_path": ["input_name", ...]
16
- },
17
- "routing": {
18
- "gate_path": ["source1", "source2", ...]
19
- }
20
- }
21
- ```
22
-
23
-
24
- INPUT SOURCE TYPES
25
- ------------------
26
-
27
- 1. **External input**: `"$input_name"` - Named input to the circuit
28
- - Example: `"$a"`, `"$b"`, `"$cin"`
29
-
30
- 2. **Gate output**: `"path.to.gate"` - Output of another gate
31
- - Example: `"ha1.sum"`, `"layer1.or"`
32
-
33
- 3. **Bit extraction**: `"$input[i]"` - Single bit from multi-bit input
34
- - Example: `"$a[0]"` (LSB), `"$a[7]"` (MSB for 8-bit)
35
-
36
- 4. **Constant**: `"#0"` or `"#1"` - Fixed value
37
- - Example: `"#1"` for carry-in in two's complement
38
-
39
- 5. **Relative reference**: `"../sibling.gate"` - Reference to sibling in hierarchy
40
- - Example: `"../fa0.cout"` from fa1
41
-
42
- 6. **Memory indexing**: `"$mem[addr][bit]"` or `"$sel[addr]"` - Addressed memory bit or one-hot select line
43
- - Example: `"$mem[42][3]"` (addr 42, bit 3), `"$sel[42]"`
44
-
45
- 7. **Packed memory tensors**: For 64KB memory, routing uses packed tensor blocks instead of per-gate entries.
46
- - Example: `memory.addr_decode.weight`, `memory.read.and.weight`, `memory.write.and_old.weight`
47
-
48
-
49
- CIRCUIT TYPES
50
- -------------
51
-
52
- ### Single-Layer Gates
53
- Gates with just `.weight` and `.bias`:
54
- ```json
55
- "boolean.and": ["$a", "$b"]
56
- ```
57
-
58
- ### Two-Layer Gates (XOR, XNOR)
59
- Gates decomposed into layer1 + layer2:
60
- ```json
61
- "boolean.xor.layer1.or": ["$a", "$b"],
62
- "boolean.xor.layer1.nand": ["$a", "$b"],
63
- "boolean.xor.layer2": ["layer1.or", "layer1.nand"]
64
- ```
65
-
66
- ### Hierarchical Circuits
67
- Complex circuits with sub-components:
68
- ```json
69
- "arithmetic.fulladder": {
70
- "external_inputs": ["$a", "$b", "$cin"],
71
- "gates": {
72
- "ha1.sum.layer1.or": ["$a", "$b"],
73
- "ha1.sum.layer1.nand": ["$a", "$b"],
74
- "ha1.sum.layer2": ["ha1.sum.layer1.or", "ha1.sum.layer1.nand"],
75
- "ha1.carry": ["$a", "$b"],
76
- "ha2.sum.layer1.or": ["ha1.sum", "$cin"],
77
- "ha2.sum.layer1.nand": ["ha1.sum", "$cin"],
78
- "ha2.sum.layer2": ["ha2.sum.layer1.or", "ha2.sum.layer1.nand"],
79
- "ha2.carry": ["ha1.sum", "$cin"],
80
- "carry_or": ["ha1.carry", "ha2.carry"]
81
- },
82
- "outputs": {
83
- "sum": "ha2.sum",
84
- "cout": "carry_or"
85
- }
86
- }
87
- ```
88
-
89
- ### Bit-Indexed Circuits
90
- Circuits operating on multi-bit values:
91
- ```json
92
- "arithmetic.ripplecarry8bit": {
93
- "external_inputs": ["$a[0:7]", "$b[0:7]"],
94
- "gates": {
95
- "fa0": {"inputs": ["$a[0]", "$b[0]", "#0"], "type": "fulladder"},
96
- "fa1": {"inputs": ["$a[1]", "$b[1]", "fa0.cout"], "type": "fulladder"},
97
- ...
98
- }
99
- }
100
- ```
101
-
102
- ### Packed Memory Circuits
103
- 64KB memory routing uses packed tensors to avoid exploding the header size. The routing entry
104
- declares a packed type and lists the tensor blocks used for the operation.
105
-
106
- ```json
107
- "memory.addr_decode": {
108
- "inputs": ["$addr[0:15]"],
109
- "type": "decoder_packed",
110
- "internal": {
111
- "weight": ["memory.addr_decode.weight"],
112
- "bias": ["memory.addr_decode.bias"]
113
- }
114
- }
115
-
116
- "memory.read": {
117
- "inputs": ["$mem[0:65535][0:7]", "$sel[0:65535]"],
118
- "type": "read_mux_packed",
119
- "internal": {
120
- "and": ["memory.read.and.weight", "memory.read.and.bias"],
121
- "or": ["memory.read.or.weight", "memory.read.or.bias"]
122
- },
123
- "outputs": { "bit0": "bit0", ..., "bit7": "bit7" }
124
- }
125
-
126
- "memory.write": {
127
- "inputs": ["$mem[0:65535][0:7]", "$write_data[0:7]", "$sel[0:65535]", "$we"],
128
- "type": "write_mux_packed",
129
- "internal": {
130
- "sel": ["memory.write.sel.weight", "memory.write.sel.bias"],
131
- "nsel": ["memory.write.nsel.weight", "memory.write.nsel.bias"],
132
- "and_old": ["memory.write.and_old.weight", "memory.write.and_old.bias"],
133
- "and_new": ["memory.write.and_new.weight", "memory.write.and_new.bias"],
134
- "or": ["memory.write.or.weight", "memory.write.or.bias"]
135
- }
136
- }
137
- ```
138
-
139
- Packed tensor shapes (16-bit address, 8-bit data):
140
- - `memory.addr_decode.weight`: [65536, 16]
141
- - `memory.addr_decode.bias`: [65536]
142
- - `memory.read.and.weight`: [8, 65536, 2]
143
- - `memory.read.and.bias`: [8, 65536]
144
- - `memory.read.or.weight`: [8, 65536]
145
- - `memory.read.or.bias`: [8]
146
- - `memory.write.sel.weight`: [65536, 2]
147
- - `memory.write.sel.bias`: [65536]
148
- - `memory.write.nsel.weight`: [65536, 1]
149
- - `memory.write.nsel.bias`: [65536]
150
- - `memory.write.and_old.weight`: [65536, 8, 2]
151
- - `memory.write.and_old.bias`: [65536, 8]
152
- - `memory.write.and_new.weight`: [65536, 8, 2]
153
- - `memory.write.and_new.bias`: [65536, 8]
154
- - `memory.write.or.weight`: [65536, 8, 2]
155
- - `memory.write.or.bias`: [65536, 8]
156
-
157
- Semantics:
158
- - decode: `sel[i] = H(sum(addr_bits * weight[i]) + bias[i])`
159
- - read: `bit[b] = H(sum(H([mem_bit, sel] * and_w[b,i] + and_b[b,i]) * or_w[b]) + or_b[b])`
160
- - write: `new_bit = H(H([old_bit, nsel] * and_old_w + and_old_b) + H([data_bit, sel] * and_new_w + and_new_b) - 1)`
161
-
162
-
163
- NAMING CONVENTIONS
164
- ------------------
165
-
166
- - External inputs: `$name` or `$name[bit]`
167
- - Constants: `#0`, `#1`
168
- - Internal gates: relative path from circuit root
169
- - Outputs: named in `outputs` section
170
-
171
-
172
- VALIDATION RULES
173
- ----------------
174
-
175
- 1. Every gate in routing must exist in tensors file
176
- 2. Every tensor must have routing entry
177
- 3. Input count must match weight dimensions
178
- 4. No circular dependencies (DAG only)
179
- 5. All referenced sources must exist
180
- 6. Packed memory circuits are valid when the packed tensor blocks exist and match expected shapes
181
- """
182
-
183
- from __future__ import annotations
184
-
185
- import argparse
186
- import json
187
- import sys
188
- from collections import defaultdict
189
- from pathlib import Path
190
- from typing import Dict, Iterable, List, Tuple
191
-
192
- from safetensors import safe_open
193
-
194
- ADDR_BITS = 16
195
- MEM_BYTES = 1 << ADDR_BITS
196
-
197
-
198
- def get_all_gates(tensors_path):
199
- """Extract all unique gate paths from tensors file."""
200
- gates = set()
201
- with safe_open(tensors_path, framework='pt') as f:
202
- for key in f.keys():
203
- if key.endswith('.weight'):
204
- gates.add(key[:-7])
205
- elif key.endswith('.bias'):
206
- gates.add(key[:-5])
207
- else:
208
- gates.add(key)
209
- return sorted(gates)
210
-
211
-
212
- def generate_boolean_routing():
213
- """Generate routing for boolean gates."""
214
- routing = {}
215
-
216
- for gate in ['and', 'or', 'nand', 'nor', 'implies']:
217
- routing[f'boolean.{gate}'] = {
218
- 'inputs': ['$a', '$b'],
219
- 'type': 'single_layer'
220
- }
221
-
222
- routing['boolean.not'] = {
223
- 'inputs': ['$a'],
224
- 'type': 'single_layer'
225
- }
226
-
227
- for gate in ['xor', 'xnor', 'biimplies']:
228
- routing[f'boolean.{gate}'] = {
229
- 'inputs': ['$a', '$b'],
230
- 'type': 'two_layer_neuron',
231
- 'internal': {
232
- 'layer1.neuron1': ['$a', '$b'],
233
- 'layer1.neuron2': ['$a', '$b'],
234
- 'layer2': ['layer1.neuron1', 'layer1.neuron2']
235
- },
236
- 'output': 'layer2'
237
- }
238
-
239
- return routing
240
-
241
-
242
- def generate_arithmetic_halfadder_routing():
243
- """Generate routing for half adder."""
244
- return {
245
- 'arithmetic.halfadder': {
246
- 'inputs': ['$a', '$b'],
247
- 'type': 'composite',
248
- 'internal': {
249
- 'sum.layer1.or': ['$a', '$b'],
250
- 'sum.layer1.nand': ['$a', '$b'],
251
- 'sum.layer2': ['sum.layer1.or', 'sum.layer1.nand'],
252
- 'carry': ['$a', '$b']
253
- },
254
- 'outputs': {
255
- 'sum': 'sum.layer2',
256
- 'carry': 'carry'
257
- }
258
- }
259
- }
260
-
261
-
262
- def generate_arithmetic_fulladder_routing():
263
- """Generate routing for full adder."""
264
- return {
265
- 'arithmetic.fulladder': {
266
- 'inputs': ['$a', '$b', '$cin'],
267
- 'type': 'composite',
268
- 'internal': {
269
- 'ha1.sum.layer1.or': ['$a', '$b'],
270
- 'ha1.sum.layer1.nand': ['$a', '$b'],
271
- 'ha1.sum.layer2': ['ha1.sum.layer1.or', 'ha1.sum.layer1.nand'],
272
- 'ha1.carry': ['$a', '$b'],
273
- 'ha2.sum.layer1.or': ['ha1.sum.layer2', '$cin'],
274
- 'ha2.sum.layer1.nand': ['ha1.sum.layer2', '$cin'],
275
- 'ha2.sum.layer2': ['ha2.sum.layer1.or', 'ha2.sum.layer1.nand'],
276
- 'ha2.carry': ['ha1.sum.layer2', '$cin'],
277
- 'carry_or': ['ha1.carry', 'ha2.carry']
278
- },
279
- 'outputs': {
280
- 'sum': 'ha2.sum.layer2',
281
- 'cout': 'carry_or'
282
- }
283
- }
284
- }
285
-
286
-
287
- def generate_ripplecarry_routing(bits):
288
- """Generate routing for N-bit ripple carry adder."""
289
- name = f'arithmetic.ripplecarry{bits}bit'
290
- internal = {}
291
-
292
- for i in range(bits):
293
- prefix = f'fa{i}'
294
- cin = '#0' if i == 0 else f'fa{i-1}.carry_or'
295
- a_bit = f'$a[{i}]'
296
- b_bit = f'$b[{i}]'
297
-
298
- internal[f'{prefix}.ha1.sum.layer1.or'] = [a_bit, b_bit]
299
- internal[f'{prefix}.ha1.sum.layer1.nand'] = [a_bit, b_bit]
300
- internal[f'{prefix}.ha1.sum.layer2'] = [f'{prefix}.ha1.sum.layer1.or', f'{prefix}.ha1.sum.layer1.nand']
301
- internal[f'{prefix}.ha1.carry'] = [a_bit, b_bit]
302
-
303
- internal[f'{prefix}.ha2.sum.layer1.or'] = [f'{prefix}.ha1.sum.layer2', cin]
304
- internal[f'{prefix}.ha2.sum.layer1.nand'] = [f'{prefix}.ha1.sum.layer2', cin]
305
- internal[f'{prefix}.ha2.sum.layer2'] = [f'{prefix}.ha2.sum.layer1.or', f'{prefix}.ha2.sum.layer1.nand']
306
- internal[f'{prefix}.ha2.carry'] = [f'{prefix}.ha1.sum.layer2', cin]
307
-
308
- internal[f'{prefix}.carry_or'] = [f'{prefix}.ha1.carry', f'{prefix}.ha2.carry']
309
-
310
- outputs = {f'sum[{i}]': f'fa{i}.ha2.sum.layer2' for i in range(bits)}
311
- outputs['cout'] = f'fa{bits-1}.carry_or'
312
-
313
- return {
314
- name: {
315
- 'inputs': [f'$a[0:{bits-1}]', f'$b[0:{bits-1}]'],
316
- 'type': 'ripple_carry',
317
- 'internal': internal,
318
- 'outputs': outputs
319
- }
320
- }
321
-
322
-
323
- def generate_comparator_routing():
324
- """Generate routing for 8-bit comparators."""
325
- routing = {}
326
-
327
- for name in ['greaterthan8bit', 'lessthan8bit', 'greaterorequal8bit', 'lessorequal8bit']:
328
- routing[f'arithmetic.{name}'] = {
329
- 'inputs': ['$a[0:7]', '$b[0:7]'],
330
- 'type': 'weighted_comparator',
331
- 'internal': {
332
- 'comparator': ['$a[0:7]', '$b[0:7]']
333
- },
334
- 'output': 'comparator'
335
- }
336
-
337
- return routing
338
-
339
-
340
- def generate_threshold_routing():
341
- """Generate routing for threshold gates."""
342
- routing = {}
343
-
344
- for name in ['oneoutof8', 'twooutof8', 'threeoutof8', 'fouroutof8',
345
- 'fiveoutof8', 'sixoutof8', 'sevenoutof8', 'alloutof8']:
346
- routing[f'threshold.{name}'] = {
347
- 'inputs': ['$x[0:7]'],
348
- 'type': 'threshold_gate',
349
- 'internal': {
350
- '': ['$x[0]', '$x[1]', '$x[2]', '$x[3]', '$x[4]', '$x[5]', '$x[6]', '$x[7]']
351
- }
352
- }
353
-
354
- routing['threshold.majority'] = {
355
- 'inputs': ['$x[0:7]'],
356
- 'type': 'threshold_gate',
357
- 'internal': {'': ['$x[0:7]']}
358
- }
359
- routing['threshold.minority'] = {
360
- 'inputs': ['$x[0:7]'],
361
- 'type': 'threshold_gate',
362
- 'internal': {'': ['$x[0:7]']}
363
- }
364
-
365
- routing['threshold.atleastk_4'] = {
366
- 'inputs': ['$x[0:3]'],
367
- 'type': 'threshold_gate'
368
- }
369
- routing['threshold.atmostk_4'] = {
370
- 'inputs': ['$x[0:3]'],
371
- 'type': 'threshold_gate'
372
- }
373
- routing['threshold.exactlyk_4'] = {
374
- 'inputs': ['$x[0:3]'],
375
- 'type': 'composite',
376
- 'internal': {
377
- 'atleast': ['$x[0:3]'],
378
- 'atmost': ['$x[0:3]'],
379
- 'and': ['atleast', 'atmost']
380
- },
381
- 'output': 'and'
382
- }
383
-
384
- return routing
385
-
386
-
387
- def generate_modular_routing():
388
- """Generate routing for modular arithmetic circuits."""
389
- routing = {}
390
-
391
- for mod in [2, 4, 8]:
392
- routing[f'modular.mod{mod}'] = {
393
- 'inputs': ['$x[0:7]'],
394
- 'type': 'single_layer',
395
- 'internal': {'': ['$x[0:7]']}
396
- }
397
-
398
- for mod in [3, 5, 6, 7, 9, 10, 11, 12]:
399
- num_detectors = len([v for v in range(256) if v % mod == 0])
400
-
401
- internal = {}
402
- layer2_inputs = []
403
-
404
- for idx in range(num_detectors):
405
- internal[f'layer1.geq{idx}'] = ['$x[0:7]']
406
- internal[f'layer1.leq{idx}'] = ['$x[0:7]']
407
- internal[f'layer2.eq{idx}'] = [f'layer1.geq{idx}', f'layer1.leq{idx}']
408
- layer2_inputs.append(f'layer2.eq{idx}')
409
-
410
- internal['layer3.or'] = layer2_inputs
411
-
412
- routing[f'modular.mod{mod}'] = {
413
- 'inputs': ['$x[0:7]'],
414
- 'type': 'modular_detector',
415
- 'internal': internal,
416
- 'output': 'layer3.or'
417
- }
418
-
419
- return routing
420
-
421
-
422
- def generate_equality_routing():
423
- """Generate routing for 8-bit equality circuit."""
424
- internal = {}
425
- xnor_outputs = []
426
-
427
- for i in range(8):
428
- internal[f'xnor{i}.layer1.and'] = [f'$a[{i}]', f'$b[{i}]']
429
- internal[f'xnor{i}.layer1.nor'] = [f'$a[{i}]', f'$b[{i}]']
430
- internal[f'xnor{i}.layer2'] = [f'xnor{i}.layer1.and', f'xnor{i}.layer1.nor']
431
- xnor_outputs.append(f'xnor{i}.layer2')
432
-
433
- internal['and'] = xnor_outputs
434
-
435
- return {
436
- 'arithmetic.equality8bit': {
437
- 'inputs': ['$a[0:7]', '$b[0:7]'],
438
- 'type': 'equality',
439
- 'internal': internal,
440
- 'output': 'and'
441
- }
442
- }
443
-
444
-
445
- def generate_neg8bit_routing():
446
- """Generate routing for 8-bit negation (two's complement)."""
447
- internal = {}
448
-
449
- for i in range(8):
450
- internal[f'not{i}'] = [f'$x[{i}]']
451
-
452
- internal['sum0'] = ['not0']
453
- internal['carry0'] = ['not0']
454
-
455
- for i in range(1, 8):
456
- prev_carry = f'carry{i-1}' if i > 1 else 'carry0'
457
- internal[f'xor{i}.layer1.nand'] = [f'not{i}', prev_carry]
458
- internal[f'xor{i}.layer1.or'] = [f'not{i}', prev_carry]
459
- internal[f'xor{i}.layer2'] = [f'xor{i}.layer1.nand', f'xor{i}.layer1.or']
460
- internal[f'and{i}'] = [f'not{i}', prev_carry]
461
-
462
- internal['overflow'] = ['not7', 'carry6'] if 8 > 1 else ['not0']
463
-
464
- return {
465
- 'arithmetic.neg8bit': {
466
- 'inputs': ['$x[0:7]'],
467
- 'type': 'negation',
468
- 'internal': internal,
469
- 'outputs': {f'out[{i}]': f'xor{i}.layer2' if i > 0 else 'sum0' for i in range(8)}
470
- }
471
- }
472
-
473
-
474
- def generate_multiplier2x2_routing():
475
- """Generate routing for 2x2 multiplier."""
476
- internal = {}
477
-
478
- for a in range(2):
479
- for b in range(2):
480
- internal[f'and{a}{b}'] = [f'$a[{a}]', f'$b[{b}]']
481
-
482
- internal['ha0.sum.layer1.or'] = ['and10', 'and01']
483
- internal['ha0.sum.layer1.nand'] = ['and10', 'and01']
484
- internal['ha0.sum.layer2'] = ['ha0.sum.layer1.or', 'ha0.sum.layer1.nand']
485
- internal['ha0.carry'] = ['and10', 'and01']
486
-
487
- internal['fa0.ha1.sum.layer1.or'] = ['and11', 'ha0.carry']
488
- internal['fa0.ha1.sum.layer1.nand'] = ['and11', 'ha0.carry']
489
- internal['fa0.ha1.sum.layer2'] = ['fa0.ha1.sum.layer1.or', 'fa0.ha1.sum.layer1.nand']
490
- internal['fa0.ha1.carry'] = ['and11', 'ha0.carry']
491
- internal['fa0.ha2.sum.layer1.or'] = ['fa0.ha1.sum.layer2', '#0']
492
- internal['fa0.ha2.sum.layer1.nand'] = ['fa0.ha1.sum.layer2', '#0']
493
- internal['fa0.ha2.sum.layer2'] = ['fa0.ha2.sum.layer1.or', 'fa0.ha2.sum.layer1.nand']
494
- internal['fa0.ha2.carry'] = ['fa0.ha1.sum.layer2', '#0']
495
- internal['fa0.carry_or'] = ['fa0.ha1.carry', 'fa0.ha2.carry']
496
-
497
- return {
498
- 'arithmetic.multiplier2x2': {
499
- 'inputs': ['$a[0:1]', '$b[0:1]'],
500
- 'type': 'multiplier',
501
- 'internal': internal,
502
- 'outputs': {
503
- 'p[0]': 'and00',
504
- 'p[1]': 'ha0.sum.layer2',
505
- 'p[2]': 'fa0.ha2.sum.layer2',
506
- 'p[3]': 'fa0.carry_or'
507
- }
508
- }
509
- }
510
-
511
-
512
- def generate_pattern_routing():
513
- """Generate routing for pattern recognition circuits."""
514
- routing = {}
515
-
516
- for name in ['popcount', 'allzeros', 'allones', 'leadingones', 'trailingones', 'runlength']:
517
- routing[f'pattern_recognition.{name}'] = {
518
- 'inputs': ['$x[0:7]'],
519
- 'type': 'weighted_sum'
520
- }
521
-
522
- routing['pattern_recognition.onehotdetector'] = {
523
- 'inputs': ['$x[0:7]'],
524
- 'type': 'composite',
525
- 'internal': {
526
- 'atleast1': ['$x[0:7]'],
527
- 'atmost1': ['$x[0:7]'],
528
- 'and': ['atleast1', 'atmost1']
529
- },
530
- 'output': 'and'
531
- }
532
-
533
- routing['pattern_recognition.alternating8bit'] = {
534
- 'inputs': ['$x[0:7]'],
535
- 'type': 'composite',
536
- 'internal': {
537
- 'pattern1': ['$x[0:7]'],
538
- 'pattern2': ['$x[0:7]']
539
- }
540
- }
541
-
542
- routing['pattern_recognition.hammingdistance8bit'] = {
543
- 'inputs': ['$a[0:7]', '$b[0:7]'],
544
- 'type': 'composite',
545
- 'internal': {
546
- 'xor': ['$a[0:7]', '$b[0:7]'],
547
- 'popcount': ['xor']
548
- },
549
- 'output': 'popcount'
550
- }
551
-
552
- routing['pattern_recognition.symmetry8bit'] = {
553
- 'inputs': ['$x[0:7]'],
554
- 'type': 'composite',
555
- 'internal': {
556
- 'xnor0': ['$x[0]', '$x[7]'],
557
- 'xnor1': ['$x[1]', '$x[6]'],
558
- 'xnor2': ['$x[2]', '$x[5]'],
559
- 'xnor3': ['$x[3]', '$x[4]'],
560
- 'and': ['xnor0', 'xnor1', 'xnor2', 'xnor3']
561
- },
562
- 'output': 'and'
563
- }
564
-
565
- return routing
566
-
567
-
568
- def generate_manifest_routing():
569
- """Generate routing for manifest (constants, no actual routing)."""
570
- return {
571
- 'manifest.alu_operations': {'type': 'constant', 'value': 16},
572
- 'manifest.flags': {'type': 'constant', 'value': 4},
573
- 'manifest.instruction_width': {'type': 'constant', 'value': 16},
574
- 'manifest.memory_bytes': {'type': 'constant', 'value': 65536},
575
- 'manifest.pc_width': {'type': 'constant', 'value': 16},
576
- 'manifest.register_width': {'type': 'constant', 'value': 8},
577
- 'manifest.registers': {'type': 'constant', 'value': 4},
578
- 'manifest.turing_complete': {'type': 'constant', 'value': 1},
579
- 'manifest.version': {'type': 'constant', 'value': 3}
580
- }
581
-
582
-
583
- def generate_multiplier8x8_routing():
584
- """Generate routing for 8x8 multiplier."""
585
- internal = {}
586
-
587
- for row in range(8):
588
- for col in range(8):
589
- internal[f'pp.r{row}.c{col}'] = [f'$a[{col}]', f'$b[{row}]']
590
-
591
- for stage in range(7):
592
- row_idx = stage + 1
593
- shift = row_idx
594
- sum_width = 8 + stage + 1
595
-
596
- for bit in range(sum_width):
597
- prefix = f'stage{stage}.bit{bit}'
598
-
599
- if bit < shift:
600
- if stage == 0:
601
- prev = f'pp.r0.c{bit}' if bit < 8 else '#0'
602
- else:
603
- prev = f'stage{stage-1}.bit{bit}.ha2.sum' if bit < 8 + stage else '#0'
604
- pp_bit = '#0'
605
- elif bit <= shift + 7:
606
- if stage == 0:
607
- prev = f'pp.r0.c{bit}' if bit < 8 else '#0'
608
- else:
609
- prev = f'stage{stage-1}.bit{bit}.ha2.sum' if bit < 8 + stage else f'stage{stage-1}.bit{8+stage-1}.carry_or'
610
- pp_bit = f'pp.r{row_idx}.c{bit-shift}'
611
- else:
612
- prev = f'stage{stage-1}.bit{bit-1}.carry_or' if stage > 0 else '#0'
613
- pp_bit = '#0'
614
-
615
- cin = '#0' if bit == 0 else f'stage{stage}.bit{bit-1}.carry_or'
616
-
617
- internal[f'{prefix}.ha1.sum.layer1.or'] = [prev, pp_bit]
618
- internal[f'{prefix}.ha1.sum.layer1.nand'] = [prev, pp_bit]
619
- internal[f'{prefix}.ha1.sum.layer2'] = [f'{prefix}.ha1.sum.layer1.or', f'{prefix}.ha1.sum.layer1.nand']
620
- internal[f'{prefix}.ha1.carry'] = [prev, pp_bit]
621
-
622
- internal[f'{prefix}.ha2.sum.layer1.or'] = [f'{prefix}.ha1.sum.layer2', cin]
623
- internal[f'{prefix}.ha2.sum.layer1.nand'] = [f'{prefix}.ha1.sum.layer2', cin]
624
- internal[f'{prefix}.ha2.sum.layer2'] = [f'{prefix}.ha2.sum.layer1.or', f'{prefix}.ha2.sum.layer1.nand']
625
- internal[f'{prefix}.ha2.carry'] = [f'{prefix}.ha1.sum.layer2', cin]
626
-
627
- internal[f'{prefix}.carry_or'] = [f'{prefix}.ha1.carry', f'{prefix}.ha2.carry']
628
-
629
- outputs = {}
630
- for i in range(8):
631
- outputs[f'p[{i}]'] = f'pp.r0.c{i}' if i < 8 else f'stage6.bit{i}.ha2.sum.layer2'
632
- for i in range(8, 16):
633
- outputs[f'p[{i}]'] = f'stage6.bit{i}.ha2.sum.layer2' if i < 15 else 'stage6.bit14.carry_or'
634
-
635
- return {
636
- 'arithmetic.multiplier8x8': {
637
- 'inputs': ['$a[0:7]', '$b[0:7]'],
638
- 'type': 'multiplier',
639
- 'internal': internal,
640
- 'outputs': outputs
641
- }
642
- }
643
-
644
-
645
- def generate_div8bit_routing():
646
- """Generate routing for 8-bit restoring division circuit."""
647
- internal = {}
648
-
649
- for stage in range(8):
650
- prefix = f'stage{stage}'
651
-
652
- if stage == 0:
653
- prev_rem = ['#0'] * 8
654
- else:
655
- prev_rem = [f'stage{stage-1}.mux{i}.or' for i in range(8)]
656
-
657
- dividend_bit = f'$dividend[{7-stage}]'
658
-
659
- for bit in range(8):
660
- if bit == 0:
661
- internal[f'{prefix}.shift.bit{bit}'] = [dividend_bit]
662
- else:
663
- internal[f'{prefix}.shift.bit{bit}'] = [prev_rem[bit - 1] if stage > 0 else '#0']
664
-
665
- shifted = [f'{prefix}.shift.bit{i}' for i in range(8)]
666
-
667
- for bit in range(8):
668
- internal[f'{prefix}.sub.notd{bit}'] = [f'$divisor[{bit}]']
669
-
670
- not_divisor = [f'{prefix}.sub.notd{i}' for i in range(8)]
671
-
672
- carry = '#1'
673
- for bit in range(8):
674
- fa_prefix = f'{prefix}.sub.fa{bit}'
675
- a = shifted[bit]
676
- b = not_divisor[bit]
677
-
678
- internal[f'{fa_prefix}.xor1.layer1.or'] = [a, b]
679
- internal[f'{fa_prefix}.xor1.layer1.nand'] = [a, b]
680
- internal[f'{fa_prefix}.xor1.layer2'] = [f'{fa_prefix}.xor1.layer1.or', f'{fa_prefix}.xor1.layer1.nand']
681
-
682
- internal[f'{fa_prefix}.xor2.layer1.or'] = [f'{fa_prefix}.xor1.layer2', carry]
683
- internal[f'{fa_prefix}.xor2.layer1.nand'] = [f'{fa_prefix}.xor1.layer2', carry]
684
- internal[f'{fa_prefix}.xor2.layer2'] = [f'{fa_prefix}.xor2.layer1.or', f'{fa_prefix}.xor2.layer1.nand']
685
-
686
- internal[f'{fa_prefix}.and1'] = [a, b]
687
- internal[f'{fa_prefix}.and2'] = [f'{fa_prefix}.xor1.layer2', carry]
688
- internal[f'{fa_prefix}.or_carry'] = [f'{fa_prefix}.and1', f'{fa_prefix}.and2']
689
-
690
- carry = f'{fa_prefix}.or_carry'
691
-
692
- sub_result = [f'{prefix}.sub.fa{i}.xor2.layer2' for i in range(8)]
693
-
694
- internal[f'{prefix}.cmp'] = [f'{prefix}.sub.fa7.or_carry']
695
-
696
- cmp_out = f'{prefix}.cmp'
697
-
698
- internal[f'{prefix}.or_dividend'] = [shifted[7], dividend_bit]
699
-
700
- for bit in range(8):
701
- mux_prefix = f'{prefix}.mux{bit}'
702
- internal[f'{mux_prefix}.not_sel'] = [cmp_out]
703
- internal[f'{mux_prefix}.and0'] = [shifted[bit], f'{mux_prefix}.not_sel']
704
- internal[f'{mux_prefix}.and1'] = [sub_result[bit], cmp_out]
705
- internal[f'{mux_prefix}.or'] = [f'{mux_prefix}.and0', f'{mux_prefix}.and1']
706
-
707
- for i in range(8):
708
- internal[f'quotient{i}'] = [f'stage{i}.cmp']
709
-
710
- for i in range(8):
711
- internal[f'remainder{i}'] = [f'stage7.mux{i}.or']
712
-
713
- outputs = {f'quotient[{i}]': f'quotient{i}' for i in range(8)}
714
- outputs.update({f'remainder[{i}]': f'remainder{i}' for i in range(8)})
715
-
716
- return {
717
- 'arithmetic.div8bit': {
718
- 'inputs': ['$dividend[0:7]', '$divisor[0:7]'],
719
- 'type': 'restoring_division',
720
- 'internal': internal,
721
- 'outputs': outputs
722
- }
723
- }
724
-
725
-
726
- def generate_adc_sbc_routing():
727
- """Generate routing for ADC and SBC circuits."""
728
- routing = {}
729
-
730
- internal_adc = {}
731
- for i in range(8):
732
- fa_prefix = f'fa{i}'
733
- a_bit = f'$a[{i}]'
734
- b_bit = f'$b[{i}]'
735
- cin = '$cin' if i == 0 else f'fa{i-1}.or_carry'
736
-
737
- internal_adc[f'{fa_prefix}.xor1.layer1.nand'] = [a_bit, b_bit]
738
- internal_adc[f'{fa_prefix}.xor1.layer1.or'] = [a_bit, b_bit]
739
- internal_adc[f'{fa_prefix}.xor1.layer2'] = [f'{fa_prefix}.xor1.layer1.nand', f'{fa_prefix}.xor1.layer1.or']
740
-
741
- internal_adc[f'{fa_prefix}.xor2.layer1.nand'] = [f'{fa_prefix}.xor1.layer2', cin]
742
- internal_adc[f'{fa_prefix}.xor2.layer1.or'] = [f'{fa_prefix}.xor1.layer2', cin]
743
- internal_adc[f'{fa_prefix}.xor2.layer2'] = [f'{fa_prefix}.xor2.layer1.nand', f'{fa_prefix}.xor2.layer1.or']
744
-
745
- internal_adc[f'{fa_prefix}.and1'] = [a_bit, b_bit]
746
- internal_adc[f'{fa_prefix}.and2'] = [f'{fa_prefix}.xor1.layer2', cin]
747
- internal_adc[f'{fa_prefix}.or_carry'] = [f'{fa_prefix}.and1', f'{fa_prefix}.and2']
748
-
749
- routing['arithmetic.adc8bit'] = {
750
- 'inputs': ['$a[0:7]', '$b[0:7]', '$cin'],
751
- 'type': 'adder_with_carry',
752
- 'internal': internal_adc
753
- }
754
-
755
- internal_sbc = {}
756
- for i in range(8):
757
- internal_sbc[f'notb{i}'] = [f'$b[{i}]']
758
-
759
- for i in range(8):
760
- fa_prefix = f'fa{i}'
761
- a_bit = f'$a[{i}]'
762
- notb_bit = f'notb{i}'
763
- cin = '$cin' if i == 0 else f'fa{i-1}.or_carry'
764
-
765
- internal_sbc[f'{fa_prefix}.xor1.layer1.nand'] = [a_bit, notb_bit]
766
- internal_sbc[f'{fa_prefix}.xor1.layer1.or'] = [a_bit, notb_bit]
767
- internal_sbc[f'{fa_prefix}.xor1.layer2'] = [f'{fa_prefix}.xor1.layer1.nand', f'{fa_prefix}.xor1.layer1.or']
768
-
769
- internal_sbc[f'{fa_prefix}.xor2.layer1.nand'] = [f'{fa_prefix}.xor1.layer2', cin]
770
- internal_sbc[f'{fa_prefix}.xor2.layer1.or'] = [f'{fa_prefix}.xor1.layer2', cin]
771
- internal_sbc[f'{fa_prefix}.xor2.layer2'] = [f'{fa_prefix}.xor2.layer1.nand', f'{fa_prefix}.xor2.layer1.or']
772
-
773
- internal_sbc[f'{fa_prefix}.and1'] = [a_bit, notb_bit]
774
- internal_sbc[f'{fa_prefix}.and2'] = [f'{fa_prefix}.xor1.layer2', cin]
775
- internal_sbc[f'{fa_prefix}.or_carry'] = [f'{fa_prefix}.and1', f'{fa_prefix}.and2']
776
-
777
- routing['arithmetic.sbc8bit'] = {
778
- 'inputs': ['$a[0:7]', '$b[0:7]', '$cin'],
779
- 'type': 'subtractor_with_carry',
780
- 'internal': internal_sbc
781
- }
782
-
783
- internal_sub = {'carry_in': ['#1']}
784
- for i in range(8):
785
- internal_sub[f'notb{i}'] = [f'$b[{i}]']
786
-
787
- for i in range(8):
788
- fa_prefix = f'fa{i}'
789
- a_bit = f'$a[{i}]'
790
- notb_bit = f'notb{i}'
791
- cin = 'carry_in' if i == 0 else f'fa{i-1}.or_carry'
792
-
793
- internal_sub[f'{fa_prefix}.xor1.layer1.nand'] = [a_bit, notb_bit]
794
- internal_sub[f'{fa_prefix}.xor1.layer1.or'] = [a_bit, notb_bit]
795
- internal_sub[f'{fa_prefix}.xor1.layer2'] = [f'{fa_prefix}.xor1.layer1.nand', f'{fa_prefix}.xor1.layer1.or']
796
-
797
- internal_sub[f'{fa_prefix}.xor2.layer1.nand'] = [f'{fa_prefix}.xor1.layer2', cin]
798
- internal_sub[f'{fa_prefix}.xor2.layer1.or'] = [f'{fa_prefix}.xor1.layer2', cin]
799
- internal_sub[f'{fa_prefix}.xor2.layer2'] = [f'{fa_prefix}.xor2.layer1.nand', f'{fa_prefix}.xor2.layer1.or']
800
-
801
- internal_sub[f'{fa_prefix}.and1'] = [a_bit, notb_bit]
802
- internal_sub[f'{fa_prefix}.and2'] = [f'{fa_prefix}.xor1.layer2', cin]
803
- internal_sub[f'{fa_prefix}.or_carry'] = [f'{fa_prefix}.and1', f'{fa_prefix}.and2']
804
-
805
- routing['arithmetic.sub8bit'] = {
806
- 'inputs': ['$a[0:7]', '$b[0:7]'],
807
- 'type': 'subtractor',
808
- 'internal': internal_sub
809
- }
810
-
811
- internal_cmp = {}
812
- for i in range(8):
813
- internal_cmp[f'notb{i}'] = [f'$b[{i}]']
814
-
815
- for i in range(8):
816
- fa_prefix = f'fa{i}'
817
- a_bit = f'$a[{i}]'
818
- notb_bit = f'notb{i}'
819
- cin = '#1' if i == 0 else f'fa{i-1}.or_carry'
820
-
821
- internal_cmp[f'{fa_prefix}.xor1.layer1.nand'] = [a_bit, notb_bit]
822
- internal_cmp[f'{fa_prefix}.xor1.layer1.or'] = [a_bit, notb_bit]
823
- internal_cmp[f'{fa_prefix}.xor1.layer2'] = [f'{fa_prefix}.xor1.layer1.nand', f'{fa_prefix}.xor1.layer1.or']
824
-
825
- internal_cmp[f'{fa_prefix}.xor2.layer1.nand'] = [f'{fa_prefix}.xor1.layer2', cin]
826
- internal_cmp[f'{fa_prefix}.xor2.layer1.or'] = [f'{fa_prefix}.xor1.layer2', cin]
827
- internal_cmp[f'{fa_prefix}.xor2.layer2'] = [f'{fa_prefix}.xor2.layer1.nand', f'{fa_prefix}.xor2.layer1.or']
828
-
829
- internal_cmp[f'{fa_prefix}.and1'] = [a_bit, notb_bit]
830
- internal_cmp[f'{fa_prefix}.and2'] = [f'{fa_prefix}.xor1.layer2', cin]
831
- internal_cmp[f'{fa_prefix}.or_carry'] = [f'{fa_prefix}.and1', f'{fa_prefix}.and2']
832
-
833
- internal_cmp['flags.zero_or'] = [f'fa{i}.xor2.layer2' for i in range(8)]
834
- internal_cmp['flags.zero'] = ['flags.zero_or']
835
- internal_cmp['flags.negative'] = ['fa7.xor2.layer2']
836
- internal_cmp['flags.carry'] = ['fa7.or_carry']
837
-
838
- routing['arithmetic.cmp8bit'] = {
839
- 'inputs': ['$a[0:7]', '$b[0:7]'],
840
- 'type': 'compare',
841
- 'internal': internal_cmp
842
- }
843
-
844
- return routing
845
-
846
-
847
- def generate_rotate_routing():
848
- """Generate routing for ROL and ROR circuits."""
849
- routing = {}
850
-
851
- internal_rol = {}
852
- for i in range(8):
853
- if i == 0:
854
- internal_rol[f'bit{i}'] = ['$cin']
855
- else:
856
- internal_rol[f'bit{i}'] = [f'$x[{i-1}]']
857
- internal_rol['cout'] = ['$x[7]']
858
-
859
- routing['arithmetic.rol8bit'] = {
860
- 'inputs': ['$x[0:7]', '$cin'],
861
- 'type': 'rotate',
862
- 'internal': internal_rol
863
- }
864
-
865
- internal_ror = {}
866
- for i in range(8):
867
- if i == 7:
868
- internal_ror[f'bit{i}'] = ['$cin']
869
- else:
870
- internal_ror[f'bit{i}'] = [f'$x[{i+1}]']
871
- internal_ror['cout'] = ['$x[0]']
872
-
873
- routing['arithmetic.ror8bit'] = {
874
- 'inputs': ['$x[0:7]', '$cin'],
875
- 'type': 'rotate',
876
- 'internal': internal_ror
877
- }
878
-
879
- return routing
880
-
881
-
882
- def generate_combinational_routing():
883
- """Generate routing for combinational circuits."""
884
- routing = {}
885
-
886
- internal_dec = {}
887
- for out in range(8):
888
- internal_dec[f'out{out}'] = ['$sel[0]', '$sel[1]', '$sel[2]']
889
-
890
- routing['combinational.decoder3to8'] = {
891
- 'inputs': ['$sel[0:2]'],
892
- 'type': 'decoder',
893
- 'internal': internal_dec
894
- }
895
-
896
- internal_enc = {}
897
- for bit in range(3):
898
- internal_enc[f'bit{bit}'] = ['$x[0:7]']
899
-
900
- routing['combinational.encoder8to3'] = {
901
- 'inputs': ['$x[0:7]'],
902
- 'type': 'encoder',
903
- 'internal': internal_enc
904
- }
905
-
906
- routing['combinational.multiplexer2to1'] = {
907
- 'inputs': ['$a', '$b', '$sel'],
908
- 'type': 'mux',
909
- 'internal': {
910
- 'not_s': ['$sel'],
911
- 'and0': ['$a', 'not_s'],
912
- 'and1': ['$b', '$sel'],
913
- 'or': ['and0', 'and1']
914
- },
915
- 'output': 'or'
916
- }
917
-
918
- routing['combinational.multiplexer4to1'] = {
919
- 'inputs': ['$x[0:3]', '$sel[0:1]'],
920
- 'type': 'mux',
921
- 'internal': {'select': ['$x[0:3]', '$sel[0:1]']}
922
- }
923
-
924
- routing['combinational.multiplexer8to1'] = {
925
- 'inputs': ['$x[0:7]', '$sel[0:2]'],
926
- 'type': 'mux',
927
- 'internal': {'select': ['$x[0:7]', '$sel[0:2]']}
928
- }
929
-
930
- routing['combinational.demultiplexer1to2'] = {
931
- 'inputs': ['$x', '$sel'],
932
- 'type': 'demux',
933
- 'internal': {
934
- 'and0': ['$x', '$sel'],
935
- 'and1': ['$x', '$sel']
936
- }
937
- }
938
-
939
- routing['combinational.demultiplexer1to4'] = {
940
- 'inputs': ['$x', '$sel[0:1]'],
941
- 'type': 'demux',
942
- 'internal': {'decode': ['$x', '$sel[0:1]']}
943
- }
944
-
945
- routing['combinational.demultiplexer1to8'] = {
946
- 'inputs': ['$x', '$sel[0:2]'],
947
- 'type': 'demux',
948
- 'internal': {'decode': ['$x', '$sel[0:2]']}
949
- }
950
-
951
- routing['combinational.barrelshifter8bit'] = {
952
- 'inputs': ['$x[0:7]', '$shift[0:2]'],
953
- 'type': 'barrel_shifter',
954
- 'internal': {'shift': ['$x[0:7]', '$shift[0:2]']}
955
- }
956
-
957
- routing['combinational.priorityencoder8bit'] = {
958
- 'inputs': ['$x[0:7]'],
959
- 'type': 'priority_encoder',
960
- 'internal': {'priority': ['$x[0:7]']}
961
- }
962
-
963
- internal_regmux = {'not_s0': ['$sel[0]'], 'not_s1': ['$sel[1]']}
964
- for bit in range(8):
965
- for and_idx in range(4):
966
- sel0 = 'not_s0' if (and_idx & 1) == 0 else '$sel[0]'
967
- sel1 = 'not_s1' if (and_idx & 2) == 0 else '$sel[1]'
968
- internal_regmux[f'bit{bit}.and{and_idx}'] = [f'$r{and_idx}[{bit}]', sel0, sel1]
969
- internal_regmux[f'bit{bit}.or'] = [f'bit{bit}.and{i}' for i in range(4)]
970
-
971
- routing['combinational.regmux4to1'] = {
972
- 'inputs': ['$r0[0:7]', '$r1[0:7]', '$r2[0:7]', '$r3[0:7]', '$sel[0:1]'],
973
- 'type': 'register_mux',
974
- 'internal': internal_regmux
975
- }
976
-
977
- return routing
978
-
979
-
980
- def generate_control_routing():
981
- """Generate routing for control circuits."""
982
- routing = {}
983
-
984
- internal_dec = {}
985
- for op in range(16):
986
- internal_dec[f'decode{op}'] = ['$opcode[0:3]']
987
- for op in range(4):
988
- internal_dec[f'not_op{op}'] = [f'$opcode[{op}]']
989
- internal_dec['is_alu'] = ['$opcode[0:3]']
990
- internal_dec['is_control'] = ['$opcode[0:3]']
991
-
992
- routing['control.decoder'] = {
993
- 'inputs': ['$opcode[0:3]'],
994
- 'type': 'instruction_decoder',
995
- 'internal': internal_dec
996
- }
997
-
998
- internal_jump = {}
999
- for bit in range(8):
1000
- internal_jump[f'bit{bit}'] = [f'$target[{bit}]']
1001
-
1002
- routing['control.jump'] = {
1003
- 'inputs': ['$target[0:7]'],
1004
- 'type': 'jump',
1005
- 'internal': internal_jump
1006
- }
1007
-
1008
- def make_cond_jump(name, flag):
1009
- internal = {}
1010
- for bit in range(8):
1011
- internal[f'bit{bit}.not_sel'] = [f'${flag}']
1012
- internal[f'bit{bit}.and_a'] = [f'$pc[{bit}]', f'bit{bit}.not_sel']
1013
- internal[f'bit{bit}.and_b'] = [f'$target[{bit}]', f'${flag}']
1014
- internal[f'bit{bit}.or'] = [f'bit{bit}.and_a', f'bit{bit}.and_b']
1015
-
1016
- routing[f'control.{name}'] = {
1017
- 'inputs': ['$pc[0:7]', '$target[0:7]', f'${flag}'],
1018
- 'type': 'conditional_jump',
1019
- 'internal': internal
1020
- }
1021
-
1022
- make_cond_jump('conditionaljump', 'cond')
1023
- make_cond_jump('jc', 'carry')
1024
- make_cond_jump('jn', 'negative')
1025
- make_cond_jump('jz', 'zero')
1026
- make_cond_jump('jv', 'overflow')
1027
- make_cond_jump('jnc', 'not_carry')
1028
- make_cond_jump('jnz', 'not_zero')
1029
- make_cond_jump('jnv', 'not_overflow')
1030
- make_cond_jump('jp', 'positive')
1031
-
1032
- routing['control.call'] = {
1033
- 'inputs': ['$target[0:7]'],
1034
- 'type': 'call',
1035
- 'internal': {'jump': ['$target[0:7]'], 'push': ['$pc[0:7]']}
1036
- }
1037
-
1038
- routing['control.ret'] = {
1039
- 'inputs': ['$stack_top[0:7]'],
1040
- 'type': 'return',
1041
- 'internal': {'jump': ['$stack_top[0:7]'], 'pop': ['#1']}
1042
- }
1043
-
1044
- routing['control.push'] = {
1045
- 'inputs': ['$value[0:7]', '$sp[0:7]'],
1046
- 'type': 'push',
1047
- 'internal': {'sp_dec': ['$sp[0:7]'], 'store': ['$value[0:7]']}
1048
- }
1049
-
1050
- routing['control.pop'] = {
1051
- 'inputs': ['$sp[0:7]'],
1052
- 'type': 'pop',
1053
- 'internal': {'load': ['$sp[0:7]'], 'sp_inc': ['$sp[0:7]']}
1054
- }
1055
-
1056
- routing['control.sp_dec'] = {'inputs': ['$sp[0:7]'], 'type': 'sp_dec', 'internal': {'uses': ['$sp[0:7]']}}
1057
- routing['control.sp_inc'] = {'inputs': ['$sp[0:7]'], 'type': 'sp_inc', 'internal': {'uses': ['$sp[0:7]']}}
1058
-
1059
- internal_pc_inc = {'sum0': ['$pc[0]'], 'carry0': ['$pc[0]'], 'overflow': ['$pc[7]']}
1060
- for bit in range(1, 8):
1061
- prev_carry = f'carry{bit-1}' if bit > 1 else 'carry0'
1062
- internal_pc_inc[f'xor{bit}.layer1.nand'] = [f'$pc[{bit}]', prev_carry]
1063
- internal_pc_inc[f'xor{bit}.layer1.or'] = [f'$pc[{bit}]', prev_carry]
1064
- internal_pc_inc[f'xor{bit}.layer2'] = [f'xor{bit}.layer1.nand', f'xor{bit}.layer1.or']
1065
- internal_pc_inc[f'and{bit}'] = [f'$pc[{bit}]', prev_carry]
1066
-
1067
- routing['control.pc_inc'] = {
1068
- 'inputs': ['$pc[0:7]'],
1069
- 'type': 'pc_increment',
1070
- 'internal': internal_pc_inc
1071
- }
1072
-
1073
- internal_pc_load = {'not_jump': ['$jump']}
1074
- for bit in range(8):
1075
- internal_pc_load[f'bit{bit}.and_pc'] = [f'$pc_inc[{bit}]', 'not_jump']
1076
- internal_pc_load[f'bit{bit}.and_jump'] = [f'$target[{bit}]', '$jump']
1077
- internal_pc_load[f'bit{bit}.or'] = [f'bit{bit}.and_pc', f'bit{bit}.and_jump']
1078
-
1079
- routing['control.pc_load'] = {
1080
- 'inputs': ['$pc_inc[0:7]', '$target[0:7]', '$jump'],
1081
- 'type': 'pc_load',
1082
- 'internal': internal_pc_load
1083
- }
1084
-
1085
- internal_halt = {'signal': ['$halt']}
1086
- for flag in ['flag_c', 'flag_n', 'flag_v', 'flag_z']:
1087
- internal_halt[flag] = [f'${flag}']
1088
- for bit in range(8):
1089
- internal_halt[f'pc.bit{bit}'] = [f'$pc[{bit}]']
1090
- internal_halt[f'value.bit{bit}'] = [f'$value[{bit}]']
1091
-
1092
- routing['control.halt'] = {
1093
- 'inputs': ['$halt', '$flag_c', '$flag_n', '$flag_v', '$flag_z', '$pc[0:7]', '$value[0:7]'],
1094
- 'type': 'halt',
1095
- 'internal': internal_halt
1096
- }
1097
-
1098
- internal_nop = {'output': ['#1']}
1099
- for bit in range(8):
1100
- internal_nop[f'bit{bit}'] = [f'$x[{bit}]']
1101
- for flag in ['flag_c', 'flag_n', 'flag_v', 'flag_z']:
1102
- internal_nop[flag] = [f'${flag}']
1103
-
1104
- routing['control.nop'] = {
1105
- 'inputs': ['$x[0:7]', '$flag_c', '$flag_n', '$flag_v', '$flag_z'],
1106
- 'type': 'nop',
1107
- 'internal': internal_nop
1108
- }
1109
-
1110
- internal_fetch = {f'bit{bit}': [f'$data[{bit}]'] for bit in range(16)}
1111
- routing['control.fetch.ir'] = {
1112
- 'inputs': ['$data[0:15]'],
1113
- 'type': 'buffer',
1114
- 'internal': internal_fetch
1115
- }
1116
-
1117
- internal_load = {f'bit{bit}': [f'$data[{bit}]'] for bit in range(8)}
1118
- routing['control.load'] = {
1119
- 'inputs': ['$data[0:7]'],
1120
- 'type': 'buffer',
1121
- 'internal': internal_load
1122
- }
1123
-
1124
- internal_store = {f'bit{bit}': [f'$data[{bit}]'] for bit in range(8)}
1125
- routing['control.store'] = {
1126
- 'inputs': ['$data[0:7]'],
1127
- 'type': 'buffer',
1128
- 'internal': internal_store
1129
- }
1130
-
1131
- internal_mem_addr = {f'bit{bit}': [f'$addr[{bit}]'] for bit in range(ADDR_BITS)}
1132
- routing['control.mem_addr'] = {
1133
- 'inputs': [f'$addr[0:{ADDR_BITS - 1}]'],
1134
- 'type': 'buffer',
1135
- 'internal': internal_mem_addr
1136
- }
1137
-
1138
- return routing
1139
-
1140
-
1141
- def generate_memory_routing():
1142
- """Generate routing for packed memory decoder, read mux, and write cell update."""
1143
- routing = {}
1144
-
1145
- routing['memory.addr_decode'] = {
1146
- 'inputs': [f'$addr[0:{ADDR_BITS - 1}]'],
1147
- 'type': 'decoder_packed',
1148
- 'internal': {
1149
- 'weight': ['memory.addr_decode.weight'],
1150
- 'bias': ['memory.addr_decode.bias'],
1151
- }
1152
- }
1153
-
1154
- routing['memory.read'] = {
1155
- 'inputs': [f'$mem[0:{MEM_BYTES - 1}][0:7]', f'$sel[0:{MEM_BYTES - 1}]'],
1156
- 'type': 'read_mux_packed',
1157
- 'internal': {
1158
- 'and': ['memory.read.and.weight', 'memory.read.and.bias'],
1159
- 'or': ['memory.read.or.weight', 'memory.read.or.bias'],
1160
- },
1161
- 'outputs': {f'bit{bit}': f'bit{bit}' for bit in range(8)}
1162
- }
1163
-
1164
- routing['memory.write'] = {
1165
- 'inputs': [f'$mem[0:{MEM_BYTES - 1}][0:7]', '$write_data[0:7]', f'$sel[0:{MEM_BYTES - 1}]', '$we'],
1166
- 'type': 'write_mux_packed',
1167
- 'internal': {
1168
- 'sel': ['memory.write.sel.weight', 'memory.write.sel.bias'],
1169
- 'nsel': ['memory.write.nsel.weight', 'memory.write.nsel.bias'],
1170
- 'and_old': ['memory.write.and_old.weight', 'memory.write.and_old.bias'],
1171
- 'and_new': ['memory.write.and_new.weight', 'memory.write.and_new.bias'],
1172
- 'or': ['memory.write.or.weight', 'memory.write.or.bias'],
1173
- }
1174
- }
1175
-
1176
- return routing
1177
-
1178
-
1179
- def generate_error_detection_routing():
1180
- """Generate routing for error detection circuits."""
1181
- routing = {}
1182
-
1183
- routing['error_detection.evenparitychecker'] = {
1184
- 'inputs': ['$x[0:7]'],
1185
- 'type': 'parity',
1186
- 'internal': {'': ['$x[0:7]']}
1187
- }
1188
-
1189
- routing['error_detection.oddparitychecker'] = {
1190
- 'inputs': ['$x[0:7]'],
1191
- 'type': 'parity',
1192
- 'internal': {
1193
- 'parity': ['$x[0:7]'],
1194
- 'not': ['parity']
1195
- }
1196
- }
1197
-
1198
- routing['error_detection.checksum8bit'] = {
1199
- 'inputs': ['$x[0:7]'],
1200
- 'type': 'checksum',
1201
- 'internal': {'sum': ['$x[0:7]']}
1202
- }
1203
-
1204
- routing['error_detection.crc4'] = {
1205
- 'inputs': ['$data[0:7]'],
1206
- 'type': 'crc',
1207
- 'internal': {'divisor': ['#1', '#0', '#0', '#1', '#1']}
1208
- }
1209
-
1210
- routing['error_detection.crc8'] = {
1211
- 'inputs': ['$data[0:7]'],
1212
- 'type': 'crc',
1213
- 'internal': {'divisor': ['#1', '#0', '#0', '#0', '#0', '#0', '#1', '#1', '#1']}
1214
- }
1215
-
1216
- routing['error_detection.hammingencode4bit'] = {
1217
- 'inputs': ['$d[0:3]'],
1218
- 'type': 'hamming_encode',
1219
- 'internal': {
1220
- 'p0': ['$d[0:3]'],
1221
- 'p1': ['$d[0:3]'],
1222
- 'p2': ['$d[0:3]'],
1223
- 'p3': ['$d[0:3]']
1224
- }
1225
- }
1226
-
1227
- routing['error_detection.hammingdecode7bit'] = {
1228
- 'inputs': ['$c[0:6]'],
1229
- 'type': 'hamming_decode',
1230
- 'internal': {
1231
- 's1': ['$c[0:6]'],
1232
- 's2': ['$c[0:6]'],
1233
- 's3': ['$c[0:6]']
1234
- }
1235
- }
1236
-
1237
- routing['error_detection.hammingsyndrome'] = {
1238
- 'inputs': ['$c[0:6]'],
1239
- 'type': 'hamming_syndrome',
1240
- 'internal': {
1241
- 's1': ['$c[0:6]'],
1242
- 's2': ['$c[0:6]'],
1243
- 's3': ['$c[0:6]']
1244
- }
1245
- }
1246
-
1247
- routing['error_detection.longitudinalparity'] = {
1248
- 'inputs': ['$data'],
1249
- 'type': 'longitudinal_parity',
1250
- 'internal': {
1251
- 'col_parity': ['$data'],
1252
- 'row_parity': ['$data']
1253
- }
1254
- }
1255
-
1256
- internal_parity_check = {}
1257
- for stage in range(1, 4):
1258
- num_xors = 4 if stage == 1 else (2 if stage == 2 else 1)
1259
- for i in range(num_xors):
1260
- prefix = f'stage{stage}.xor{i}'
1261
- internal_parity_check[f'{prefix}.layer1.nand'] = [f'$stage{stage}_in[{2*i}]', f'$stage{stage}_in[{2*i+1}]']
1262
- internal_parity_check[f'{prefix}.layer1.or'] = [f'$stage{stage}_in[{2*i}]', f'$stage{stage}_in[{2*i+1}]']
1263
- internal_parity_check[f'{prefix}.layer2'] = [f'{prefix}.layer1.nand', f'{prefix}.layer1.or']
1264
- internal_parity_check['output.not'] = ['stage3.xor0.layer2']
1265
-
1266
- routing['error_detection.paritychecker8bit'] = {
1267
- 'inputs': ['$x[0:7]'],
1268
- 'type': 'parity_tree',
1269
- 'internal': internal_parity_check
1270
- }
1271
-
1272
- routing['error_detection.paritygenerator8bit'] = {
1273
- 'inputs': ['$x[0:7]'],
1274
- 'type': 'parity_tree',
1275
- 'internal': internal_parity_check.copy()
1276
- }
1277
-
1278
- return routing
1279
-
1280
-
1281
- def generate_alu_routing():
1282
- """Generate routing for ALU circuits."""
1283
- routing = {}
1284
-
1285
- internal_ctrl = {}
1286
- for op in range(16):
1287
- internal_ctrl[f'op{op}'] = ['$opcode[0:3]']
1288
-
1289
- routing['alu.alucontrol'] = {
1290
- 'inputs': ['$opcode[0:3]'],
1291
- 'type': 'alu_control',
1292
- 'internal': internal_ctrl
1293
- }
1294
-
1295
- routing['alu.aluflags'] = {
1296
- 'inputs': ['$result[0:7]', '$carry', '$overflow'],
1297
- 'type': 'alu_flags',
1298
- 'internal': {
1299
- 'zero': ['$result[0:7]'],
1300
- 'negative': ['$result[7]'],
1301
- 'carry': ['$carry'],
1302
- 'overflow': ['$overflow']
1303
- }
1304
- }
1305
-
1306
- routing['alu.alu8bit.and'] = {
1307
- 'inputs': ['$a[0:7]', '$b[0:7]'],
1308
- 'type': 'bitwise_and',
1309
- 'internal': {f'bit{i}': [f'$a[{i}]', f'$b[{i}]'] for i in range(8)}
1310
- }
1311
-
1312
- routing['alu.alu8bit.or'] = {
1313
- 'inputs': ['$a[0:7]', '$b[0:7]'],
1314
- 'type': 'bitwise_or',
1315
- 'internal': {f'bit{i}': [f'$a[{i}]', f'$b[{i}]'] for i in range(8)}
1316
- }
1317
-
1318
- routing['alu.alu8bit.not'] = {
1319
- 'inputs': ['$a[0:7]'],
1320
- 'type': 'bitwise_not',
1321
- 'internal': {f'bit{i}': [f'$a[{i}]'] for i in range(8)}
1322
- }
1323
-
1324
- internal_xor = {}
1325
- for i in range(8):
1326
- internal_xor[f'layer1.nand.bit{i}'] = [f'$a[{i}]', f'$b[{i}]']
1327
- internal_xor[f'layer1.or.bit{i}'] = [f'$a[{i}]', f'$b[{i}]']
1328
- internal_xor[f'layer2.bit{i}'] = [f'layer1.nand.bit{i}', f'layer1.or.bit{i}']
1329
-
1330
- routing['alu.alu8bit.xor'] = {
1331
- 'inputs': ['$a[0:7]', '$b[0:7]'],
1332
- 'type': 'bitwise_xor',
1333
- 'internal': internal_xor
1334
- }
1335
-
1336
- routing['alu.alu8bit.shl'] = {
1337
- 'inputs': ['$a[0:7]'],
1338
- 'type': 'shift_left',
1339
- 'internal': {'': ['$a[0:7]']}
1340
- }
1341
-
1342
- routing['alu.alu8bit.shr'] = {
1343
- 'inputs': ['$a[0:7]'],
1344
- 'type': 'shift_right',
1345
- 'internal': {'': ['$a[0:7]']}
1346
- }
1347
-
1348
- routing['alu.alu8bit.add'] = {
1349
- 'inputs': ['$a[0:7]', '$b[0:7]'],
1350
- 'type': 'add',
1351
- 'internal': {'': ['$a[0:7]', '$b[0:7]']}
1352
- }
1353
-
1354
- routing['alu.alu8bit.output_mux'] = {
1355
- 'inputs': ['$results[0:15]', '$opcode[0:3]'],
1356
- 'type': 'output_mux',
1357
- 'internal': {'': ['$results[0:15]', '$opcode[0:3]']}
1358
- }
1359
-
1360
- return routing
1361
-
1362
-
1363
- def generate_routing():
1364
- """Generate complete routing.json for the threshold computer."""
1365
- routing = {
1366
- 'version': 1,
1367
- 'description': 'Routing information for 8-bit threshold computer',
1368
- 'circuits': {}
1369
- }
1370
-
1371
- print('Generating routing...')
1372
-
1373
- print(' Boolean gates')
1374
- routing['circuits'].update(generate_boolean_routing())
1375
-
1376
- print(' Half adder')
1377
- routing['circuits'].update(generate_arithmetic_halfadder_routing())
1378
-
1379
- print(' Full adder')
1380
- routing['circuits'].update(generate_arithmetic_fulladder_routing())
1381
-
1382
- print(' Ripple carry adders')
1383
- routing['circuits'].update(generate_ripplecarry_routing(2))
1384
- routing['circuits'].update(generate_ripplecarry_routing(4))
1385
- routing['circuits'].update(generate_ripplecarry_routing(8))
1386
-
1387
- print(' Comparators')
1388
- routing['circuits'].update(generate_comparator_routing())
1389
-
1390
- print(' Equality')
1391
- routing['circuits'].update(generate_equality_routing())
1392
-
1393
- print(' Negation')
1394
- routing['circuits'].update(generate_neg8bit_routing())
1395
-
1396
- print(' 2x2 Multiplier')
1397
- routing['circuits'].update(generate_multiplier2x2_routing())
1398
-
1399
- print(' 8x8 Multiplier')
1400
- routing['circuits'].update(generate_multiplier8x8_routing())
1401
-
1402
- print(' 8-bit Division')
1403
- routing['circuits'].update(generate_div8bit_routing())
1404
-
1405
- print(' ADC/SBC')
1406
- routing['circuits'].update(generate_adc_sbc_routing())
1407
-
1408
- print(' Rotate')
1409
- routing['circuits'].update(generate_rotate_routing())
1410
-
1411
- print(' Combinational')
1412
- routing['circuits'].update(generate_combinational_routing())
1413
-
1414
- print(' Control')
1415
- routing['circuits'].update(generate_control_routing())
1416
-
1417
- print(' Memory')
1418
- routing['circuits'].update(generate_memory_routing())
1419
-
1420
- print(' Error detection')
1421
- routing['circuits'].update(generate_error_detection_routing())
1422
-
1423
- print(' ALU')
1424
- routing['circuits'].update(generate_alu_routing())
1425
-
1426
- print(' Threshold gates')
1427
- routing['circuits'].update(generate_threshold_routing())
1428
-
1429
- print(' Modular arithmetic')
1430
- routing['circuits'].update(generate_modular_routing())
1431
-
1432
- print(' Pattern recognition')
1433
- routing['circuits'].update(generate_pattern_routing())
1434
-
1435
- print(' Manifest')
1436
- routing['circuits'].update(generate_manifest_routing())
1437
-
1438
- return routing
1439
-
1440
-
1441
- def _get_scalar_tensor(f, name: str, default: int) -> int:
1442
- if name not in f.keys():
1443
- return default
1444
- tensor = f.get_tensor(name)
1445
- return int(tensor.item())
1446
-
1447
-
1448
- def _gather_internal_keys(routing: Dict, circuit_name: str) -> List[str]:
1449
- circuit = routing.get("circuits", {}).get(circuit_name)
1450
- if circuit is None:
1451
- return []
1452
- internal = circuit.get("internal", {})
1453
- keys: List[str] = []
1454
- for value in internal.values():
1455
- if isinstance(value, list):
1456
- keys.extend(value)
1457
- return keys
1458
-
1459
-
1460
- def _shape_matches(actual: Iterable[int], expected: Iterable[int]) -> bool:
1461
- return tuple(actual) == tuple(expected)
1462
-
1463
-
1464
- def validate_packed_memory(routing: Dict, model_path: Path) -> List[str]:
1465
- """Validate packed memory tensors against expected shapes."""
1466
- routing_keys = set()
1467
- for name in ("memory.addr_decode", "memory.read", "memory.write"):
1468
- routing_keys.update(_gather_internal_keys(routing, name))
1469
-
1470
- errors = []
1471
-
1472
- with safe_open(str(model_path), framework="pt") as f:
1473
- mem_bytes = _get_scalar_tensor(f, "manifest.memory_bytes", 65536)
1474
- pc_width = _get_scalar_tensor(f, "manifest.pc_width", 16)
1475
- reg_width = _get_scalar_tensor(f, "manifest.register_width", 8)
1476
-
1477
- expected_shapes: Dict[str, Tuple[int, ...]] = {
1478
- "memory.addr_decode.weight": (mem_bytes, pc_width),
1479
- "memory.addr_decode.bias": (mem_bytes,),
1480
- "memory.read.and.weight": (reg_width, mem_bytes, 2),
1481
- "memory.read.and.bias": (reg_width, mem_bytes),
1482
- "memory.read.or.weight": (reg_width, mem_bytes),
1483
- "memory.read.or.bias": (reg_width,),
1484
- "memory.write.sel.weight": (mem_bytes, 2),
1485
- "memory.write.sel.bias": (mem_bytes,),
1486
- "memory.write.nsel.weight": (mem_bytes, 1),
1487
- "memory.write.nsel.bias": (mem_bytes,),
1488
- "memory.write.and_old.weight": (mem_bytes, reg_width, 2),
1489
- "memory.write.and_old.bias": (mem_bytes, reg_width),
1490
- "memory.write.and_new.weight": (mem_bytes, reg_width, 2),
1491
- "memory.write.and_new.bias": (mem_bytes, reg_width),
1492
- "memory.write.or.weight": (mem_bytes, reg_width, 2),
1493
- "memory.write.or.bias": (mem_bytes, reg_width),
1494
- }
1495
-
1496
- for key, expected in expected_shapes.items():
1497
- if key not in routing_keys:
1498
- errors.append(f"routing.json missing key: {key}")
1499
- continue
1500
- if key not in f.keys():
1501
- errors.append(f"safetensors missing key: {key}")
1502
- continue
1503
- actual = f.get_tensor(key).shape
1504
- if not _shape_matches(actual, expected):
1505
- errors.append(f"{key} shape {tuple(actual)} != {expected}")
1506
-
1507
- return errors
1508
-
1509
-
1510
- def report_missing_circuits(routing: Dict, tensors_path: str):
1511
- """Report any circuits in tensors not covered by routing."""
1512
- all_gates = get_all_gates(tensors_path)
1513
- missing = [g for g in all_gates if not any(g.startswith(c) for c in routing['circuits'].keys())]
1514
-
1515
- if missing:
1516
- print(f'\nMissing circuits ({len(missing)}):')
1517
- by_cat = defaultdict(list)
1518
- for g in missing:
1519
- cat = g.split('.')[0]
1520
- by_cat[cat].append(g)
1521
- for cat in sorted(by_cat.keys()):
1522
- print(f' {cat}: {len(by_cat[cat])} gates')
1523
- for g in by_cat[cat][:5]:
1524
- print(f' - {g}')
1525
- if len(by_cat[cat]) > 5:
1526
- print(f' ... and {len(by_cat[cat])-5} more')
1527
-
1528
-
1529
- def main() -> int:
1530
- parser = argparse.ArgumentParser(description="Generate and validate routing.json")
1531
- parser.add_argument(
1532
- "--output",
1533
- type=Path,
1534
- default=Path(__file__).resolve().parent / "routing.json",
1535
- help="Output path for routing.json",
1536
- )
1537
- parser.add_argument(
1538
- "--model",
1539
- type=Path,
1540
- default=Path(__file__).resolve().parent.parent / "neural_computer.safetensors",
1541
- help="Path to neural_computer.safetensors",
1542
- )
1543
- parser.add_argument(
1544
- "--validate-only",
1545
- action="store_true",
1546
- help="Only validate existing routing.json, don't regenerate",
1547
- )
1548
- args = parser.parse_args()
1549
-
1550
- if args.validate_only:
1551
- if not args.output.exists():
1552
- print(f"Error: {args.output} does not exist", file=sys.stderr)
1553
- return 1
1554
-
1555
- with args.output.open("r", encoding="utf-8") as fh:
1556
- routing = json.load(fh)
1557
-
1558
- errors = validate_packed_memory(routing, args.model)
1559
- if errors:
1560
- print("Packed memory validation failed:", file=sys.stderr)
1561
- for err in errors:
1562
- print(f" - {err}", file=sys.stderr)
1563
- return 1
1564
-
1565
- print("Packed memory routing validation: ok")
1566
- return 0
1567
-
1568
- routing = generate_routing()
1569
-
1570
- with open(args.output, 'w', encoding='utf-8') as f:
1571
- json.dump(routing, f, indent=2)
1572
-
1573
- print(f'\nGenerated routing for {len(routing["circuits"])} circuits')
1574
- print(f'Saved to {args.output}')
1575
-
1576
- if args.model.exists():
1577
- report_missing_circuits(routing, str(args.model))
1578
-
1579
- errors = validate_packed_memory(routing, args.model)
1580
- if errors:
1581
- print("\nPacked memory validation errors:")
1582
- for err in errors:
1583
- print(f" - {err}")
1584
- else:
1585
- print("\nPacked memory validation: ok")
1586
-
1587
- return 0
1588
-
1589
-
1590
- if __name__ == '__main__':
1591
- raise SystemExit(main())