CharlesCNorton commited on
Commit
a854d3d
·
1 Parent(s): 2bbc3fe

Consolidate routing modules into single routing.py

Browse files

- Merge generate_routing.py, routing_schema.md, validate_packed_memory.py
- Schema docs now in module docstring
- Add --validate-only CLI flag
- Remove duplicate routing.json from repo root

routing.json DELETED
The diff for this file is too large to render. See raw diff
 
routing/{generate_routing.py → routing.py} RENAMED
@@ -1,425 +1,570 @@
1
- """
2
- Generate routing.json for the 8-bit threshold computer.
3
- Maps each gate to its input sources.
4
- """
5
-
6
- import json
7
- from safetensors import safe_open
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  from collections import defaultdict
 
 
 
 
9
 
10
  ADDR_BITS = 16
11
  MEM_BYTES = 1 << ADDR_BITS
12
-
13
- def get_all_gates(tensors_path):
14
- """Extract all unique gate paths from tensors file."""
15
- gates = set()
16
- with safe_open(tensors_path, framework='pt') as f:
17
- for key in f.keys():
18
- if key.endswith('.weight'):
19
- gates.add(key[:-7])
20
- elif key.endswith('.bias'):
21
- gates.add(key[:-5])
22
- else:
23
- gates.add(key)
24
- return sorted(gates)
25
-
26
-
27
- def generate_boolean_routing():
28
- """Generate routing for boolean gates."""
29
- routing = {}
30
-
31
- # Single-layer 2-input gates
32
- for gate in ['and', 'or', 'nand', 'nor', 'implies']:
33
- routing[f'boolean.{gate}'] = {
34
- 'inputs': ['$a', '$b'],
35
- 'type': 'single_layer'
36
- }
37
-
38
- # NOT gate (1 input)
39
- routing['boolean.not'] = {
40
- 'inputs': ['$a'],
41
- 'type': 'single_layer'
42
- }
43
-
44
- # Two-layer gates (XOR, XNOR, BIIMPLIES)
45
- for gate in ['xor', 'xnor', 'biimplies']:
46
- routing[f'boolean.{gate}'] = {
47
- 'inputs': ['$a', '$b'],
48
- 'type': 'two_layer_neuron',
49
- 'internal': {
50
- 'layer1.neuron1': ['$a', '$b'],
51
- 'layer1.neuron2': ['$a', '$b'],
52
- 'layer2': ['layer1.neuron1', 'layer1.neuron2']
53
- },
54
- 'output': 'layer2'
55
- }
56
-
57
- return routing
58
-
59
-
60
- def generate_arithmetic_halfadder_routing():
61
- """Generate routing for half adder."""
62
- return {
63
- 'arithmetic.halfadder': {
64
- 'inputs': ['$a', '$b'],
65
- 'type': 'composite',
66
- 'internal': {
67
- # Sum is XOR(a, b)
68
- 'sum.layer1.or': ['$a', '$b'],
69
- 'sum.layer1.nand': ['$a', '$b'],
70
- 'sum.layer2': ['sum.layer1.or', 'sum.layer1.nand'],
71
- # Carry is AND(a, b)
72
- 'carry': ['$a', '$b']
73
- },
74
- 'outputs': {
75
- 'sum': 'sum.layer2',
76
- 'carry': 'carry'
77
- }
78
- }
79
- }
80
-
81
-
82
- def generate_arithmetic_fulladder_routing():
83
- """Generate routing for full adder."""
84
- return {
85
- 'arithmetic.fulladder': {
86
- 'inputs': ['$a', '$b', '$cin'],
87
- 'type': 'composite',
88
- 'internal': {
89
- # HA1: a + b
90
- 'ha1.sum.layer1.or': ['$a', '$b'],
91
- 'ha1.sum.layer1.nand': ['$a', '$b'],
92
- 'ha1.sum.layer2': ['ha1.sum.layer1.or', 'ha1.sum.layer1.nand'],
93
- 'ha1.carry': ['$a', '$b'],
94
- # HA2: ha1.sum + cin
95
- 'ha2.sum.layer1.or': ['ha1.sum.layer2', '$cin'],
96
- 'ha2.sum.layer1.nand': ['ha1.sum.layer2', '$cin'],
97
- 'ha2.sum.layer2': ['ha2.sum.layer1.or', 'ha2.sum.layer1.nand'],
98
- 'ha2.carry': ['ha1.sum.layer2', '$cin'],
99
- # Carry out
100
- 'carry_or': ['ha1.carry', 'ha2.carry']
101
- },
102
- 'outputs': {
103
- 'sum': 'ha2.sum.layer2',
104
- 'cout': 'carry_or'
105
- }
106
- }
107
- }
108
-
109
-
110
- def generate_ripplecarry_routing(bits):
111
- """Generate routing for N-bit ripple carry adder."""
112
- name = f'arithmetic.ripplecarry{bits}bit'
113
- internal = {}
114
-
115
- for i in range(bits):
116
- prefix = f'fa{i}'
117
- if i == 0:
118
- cin = '#0' # First carry in is 0
119
- else:
120
- cin = f'fa{i-1}.carry_or'
121
-
122
- a_bit = f'$a[{i}]'
123
- b_bit = f'$b[{i}]'
124
-
125
- # Full adder structure
126
- internal[f'{prefix}.ha1.sum.layer1.or'] = [a_bit, b_bit]
127
- internal[f'{prefix}.ha1.sum.layer1.nand'] = [a_bit, b_bit]
128
- internal[f'{prefix}.ha1.sum.layer2'] = [f'{prefix}.ha1.sum.layer1.or', f'{prefix}.ha1.sum.layer1.nand']
129
- internal[f'{prefix}.ha1.carry'] = [a_bit, b_bit]
130
-
131
- internal[f'{prefix}.ha2.sum.layer1.or'] = [f'{prefix}.ha1.sum.layer2', cin]
132
- internal[f'{prefix}.ha2.sum.layer1.nand'] = [f'{prefix}.ha1.sum.layer2', cin]
133
- internal[f'{prefix}.ha2.sum.layer2'] = [f'{prefix}.ha2.sum.layer1.or', f'{prefix}.ha2.sum.layer1.nand']
134
- internal[f'{prefix}.ha2.carry'] = [f'{prefix}.ha1.sum.layer2', cin]
135
-
136
- internal[f'{prefix}.carry_or'] = [f'{prefix}.ha1.carry', f'{prefix}.ha2.carry']
137
-
138
- outputs = {f'sum[{i}]': f'fa{i}.ha2.sum.layer2' for i in range(bits)}
139
- outputs['cout'] = f'fa{bits-1}.carry_or'
140
-
141
- return {
142
- name: {
143
- 'inputs': [f'$a[0:{bits-1}]', f'$b[0:{bits-1}]'],
144
- 'type': 'ripple_carry',
145
- 'internal': internal,
146
- 'outputs': outputs
147
- }
148
- }
149
-
150
-
151
- def generate_comparator_routing():
152
- """Generate routing for 8-bit comparators."""
153
- routing = {}
154
-
155
- # These are single-layer weighted comparators
156
- for name in ['greaterthan8bit', 'lessthan8bit', 'greaterorequal8bit', 'lessorequal8bit']:
157
- routing[f'arithmetic.{name}'] = {
158
- 'inputs': ['$a[0:7]', '$b[0:7]'],
159
- 'type': 'weighted_comparator',
160
- 'internal': {
161
- 'comparator': ['$a[0:7]', '$b[0:7]'] # Difference weighted by position
162
- },
163
- 'output': 'comparator'
164
- }
165
-
166
- return routing
167
-
168
-
169
- def generate_threshold_routing():
170
- """Generate routing for threshold gates."""
171
- routing = {}
172
-
173
- # k-of-8 gates
174
- for name in ['oneoutof8', 'twooutof8', 'threeoutof8', 'fouroutof8',
175
- 'fiveoutof8', 'sixoutof8', 'sevenoutof8', 'alloutof8']:
176
- routing[f'threshold.{name}'] = {
177
- 'inputs': ['$x[0:7]'],
178
- 'type': 'threshold_gate',
179
- 'internal': {
180
- '': ['$x[0]', '$x[1]', '$x[2]', '$x[3]', '$x[4]', '$x[5]', '$x[6]', '$x[7]']
181
- }
182
- }
183
-
184
- # Majority/minority
185
- routing['threshold.majority'] = {
186
- 'inputs': ['$x[0:7]'],
187
- 'type': 'threshold_gate',
188
- 'internal': {'': ['$x[0:7]']}
189
- }
190
- routing['threshold.minority'] = {
191
- 'inputs': ['$x[0:7]'],
192
- 'type': 'threshold_gate',
193
- 'internal': {'': ['$x[0:7]']}
194
- }
195
-
196
- # atleastk, atmostk, exactlyk for 4-bit
197
- routing['threshold.atleastk_4'] = {
198
- 'inputs': ['$x[0:3]'],
199
- 'type': 'threshold_gate'
200
- }
201
- routing['threshold.atmostk_4'] = {
202
- 'inputs': ['$x[0:3]'],
203
- 'type': 'threshold_gate'
204
- }
205
- routing['threshold.exactlyk_4'] = {
206
- 'inputs': ['$x[0:3]'],
207
- 'type': 'composite',
208
- 'internal': {
209
- 'atleast': ['$x[0:3]'],
210
- 'atmost': ['$x[0:3]'],
211
- 'and': ['atleast', 'atmost']
212
- },
213
- 'output': 'and'
214
- }
215
-
216
- return routing
217
-
218
-
219
- def generate_modular_routing():
220
- """Generate routing for modular arithmetic circuits."""
221
- routing = {}
222
-
223
- # Powers of 2 are single-layer
224
- for mod in [2, 4, 8]:
225
- routing[f'modular.mod{mod}'] = {
226
- 'inputs': ['$x[0:7]'],
227
- 'type': 'single_layer',
228
- 'internal': {'': ['$x[0:7]']}
229
- }
230
-
231
- # Non-powers of 2 use 3-layer detection
232
- for mod in [3, 5, 6, 7, 9, 10, 11, 12]:
233
- # Count detectors (values divisible by mod in range 0-255)
234
- num_detectors = len([v for v in range(256) if v % mod == 0])
235
-
236
- internal = {}
237
- layer2_inputs = []
238
-
239
- for idx in range(num_detectors):
240
- internal[f'layer1.geq{idx}'] = ['$x[0:7]']
241
- internal[f'layer1.leq{idx}'] = ['$x[0:7]']
242
- internal[f'layer2.eq{idx}'] = [f'layer1.geq{idx}', f'layer1.leq{idx}']
243
- layer2_inputs.append(f'layer2.eq{idx}')
244
-
245
- internal['layer3.or'] = layer2_inputs
246
-
247
- routing[f'modular.mod{mod}'] = {
248
- 'inputs': ['$x[0:7]'],
249
- 'type': 'modular_detector',
250
- 'internal': internal,
251
- 'output': 'layer3.or'
252
- }
253
-
254
- return routing
255
-
256
-
257
- def generate_equality_routing():
258
- """Generate routing for 8-bit equality circuit."""
259
- internal = {}
260
- xnor_outputs = []
261
-
262
- for i in range(8):
263
- # Each XNOR compares bit i of a and b
264
- internal[f'xnor{i}.layer1.and'] = [f'$a[{i}]', f'$b[{i}]']
265
- internal[f'xnor{i}.layer1.nor'] = [f'$a[{i}]', f'$b[{i}]']
266
- internal[f'xnor{i}.layer2'] = [f'xnor{i}.layer1.and', f'xnor{i}.layer1.nor']
267
- xnor_outputs.append(f'xnor{i}.layer2')
268
-
269
- # Final AND of all XNORs
270
- internal['and'] = xnor_outputs
271
-
272
- return {
273
- 'arithmetic.equality8bit': {
274
- 'inputs': ['$a[0:7]', '$b[0:7]'],
275
- 'type': 'equality',
276
- 'internal': internal,
277
- 'output': 'and'
278
- }
279
- }
280
-
281
-
282
- def generate_neg8bit_routing():
283
- """Generate routing for 8-bit negation (two's complement)."""
284
- internal = {}
285
-
286
- # NOT gates for each bit
287
- for i in range(8):
288
- internal[f'not{i}'] = [f'$x[{i}]']
289
-
290
- # Add 1 using half/full adders (simplified view)
291
- # Bit 0: XOR(not0, 1) = NOT(not0) = x0, carry = not0
292
- internal['sum0'] = ['not0'] # Actually just passes through for +1
293
- internal['carry0'] = ['not0']
294
-
295
- # Remaining bits use XOR with carry propagation
296
- for i in range(1, 8):
297
- prev_carry = f'carry{i-1}' if i > 1 else 'carry0'
298
- internal[f'xor{i}.layer1.nand'] = [f'not{i}', prev_carry]
299
- internal[f'xor{i}.layer1.or'] = [f'not{i}', prev_carry]
300
- internal[f'xor{i}.layer2'] = [f'xor{i}.layer1.nand', f'xor{i}.layer1.or']
301
- internal[f'and{i}'] = [f'not{i}', prev_carry]
302
-
303
- internal['overflow'] = ['not7', 'carry6'] if 8 > 1 else ['not0']
304
-
305
- return {
306
- 'arithmetic.neg8bit': {
307
- 'inputs': ['$x[0:7]'],
308
- 'type': 'negation',
309
- 'internal': internal,
310
- 'outputs': {f'out[{i}]': f'xor{i}.layer2' if i > 0 else 'sum0' for i in range(8)}
311
- }
312
- }
313
-
314
-
315
- def generate_multiplier2x2_routing():
316
- """Generate routing for 2x2 multiplier."""
317
- internal = {}
318
-
319
- # Partial products (AND gates)
320
- for a in range(2):
321
- for b in range(2):
322
- internal[f'and{a}{b}'] = [f'$a[{a}]', f'$b[{b}]']
323
-
324
- # Product bits:
325
- # p0 = a0*b0
326
- # p1 = a1*b0 + a0*b1
327
- # p2 = a1*b1 + carry from p1
328
- # p3 = carry from p2
329
-
330
- # Half adder for bit 1
331
- internal['ha0.sum.layer1.or'] = ['and10', 'and01']
332
- internal['ha0.sum.layer1.nand'] = ['and10', 'and01']
333
- internal['ha0.sum.layer2'] = ['ha0.sum.layer1.or', 'ha0.sum.layer1.nand']
334
- internal['ha0.carry'] = ['and10', 'and01']
335
-
336
- # Full adder for bit 2
337
- internal['fa0.ha1.sum.layer1.or'] = ['and11', 'ha0.carry']
338
- internal['fa0.ha1.sum.layer1.nand'] = ['and11', 'ha0.carry']
339
- internal['fa0.ha1.sum.layer2'] = ['fa0.ha1.sum.layer1.or', 'fa0.ha1.sum.layer1.nand']
340
- internal['fa0.ha1.carry'] = ['and11', 'ha0.carry']
341
- internal['fa0.ha2.sum.layer1.or'] = ['fa0.ha1.sum.layer2', '#0']
342
- internal['fa0.ha2.sum.layer1.nand'] = ['fa0.ha1.sum.layer2', '#0']
343
- internal['fa0.ha2.sum.layer2'] = ['fa0.ha2.sum.layer1.or', 'fa0.ha2.sum.layer1.nand']
344
- internal['fa0.ha2.carry'] = ['fa0.ha1.sum.layer2', '#0']
345
- internal['fa0.carry_or'] = ['fa0.ha1.carry', 'fa0.ha2.carry']
346
-
347
- return {
348
- 'arithmetic.multiplier2x2': {
349
- 'inputs': ['$a[0:1]', '$b[0:1]'],
350
- 'type': 'multiplier',
351
- 'internal': internal,
352
- 'outputs': {
353
- 'p[0]': 'and00',
354
- 'p[1]': 'ha0.sum.layer2',
355
- 'p[2]': 'fa0.ha2.sum.layer2',
356
- 'p[3]': 'fa0.carry_or'
357
- }
358
- }
359
- }
360
-
361
-
362
- def generate_pattern_routing():
363
- """Generate routing for pattern recognition circuits."""
364
- routing = {}
365
-
366
- # Simple threshold-based patterns
367
- for name in ['popcount', 'allzeros', 'allones', 'leadingones', 'trailingones', 'runlength']:
368
- routing[f'pattern_recognition.{name}'] = {
369
- 'inputs': ['$x[0:7]'],
370
- 'type': 'weighted_sum'
371
- }
372
-
373
- # One-hot detector (atleast1 AND atmost1)
374
- routing['pattern_recognition.onehotdetector'] = {
375
- 'inputs': ['$x[0:7]'],
376
- 'type': 'composite',
377
- 'internal': {
378
- 'atleast1': ['$x[0:7]'],
379
- 'atmost1': ['$x[0:7]'],
380
- 'and': ['atleast1', 'atmost1']
381
- },
382
- 'output': 'and'
383
- }
384
-
385
- # Alternating pattern (2 pattern matchers)
386
- routing['pattern_recognition.alternating8bit'] = {
387
- 'inputs': ['$x[0:7]'],
388
- 'type': 'composite',
389
- 'internal': {
390
- 'pattern1': ['$x[0:7]'], # 10101010
391
- 'pattern2': ['$x[0:7]'] # 01010101
392
- }
393
- }
394
-
395
- # Hamming distance (XOR + popcount)
396
- routing['pattern_recognition.hammingdistance8bit'] = {
397
- 'inputs': ['$a[0:7]', '$b[0:7]'],
398
- 'type': 'composite',
399
- 'internal': {
400
- 'xor': ['$a[0:7]', '$b[0:7]'],
401
- 'popcount': ['xor']
402
- },
403
- 'output': 'popcount'
404
- }
405
-
406
- # Symmetry detector (4 XNORs + AND)
407
- routing['pattern_recognition.symmetry8bit'] = {
408
- 'inputs': ['$x[0:7]'],
409
- 'type': 'composite',
410
- 'internal': {
411
- 'xnor0': ['$x[0]', '$x[7]'],
412
- 'xnor1': ['$x[1]', '$x[6]'],
413
- 'xnor2': ['$x[2]', '$x[5]'],
414
- 'xnor3': ['$x[3]', '$x[4]'],
415
- 'and': ['xnor0', 'xnor1', 'xnor2', 'xnor3']
416
- },
417
- 'output': 'and'
418
- }
419
-
420
- return routing
421
-
422
-
423
  def generate_manifest_routing():
424
  """Generate routing for manifest (constants, no actual routing)."""
425
  return {
@@ -433,587 +578,535 @@ def generate_manifest_routing():
433
  'manifest.turing_complete': {'type': 'constant', 'value': 1},
434
  'manifest.version': {'type': 'constant', 'value': 3}
435
  }
436
-
437
-
438
- def generate_multiplier8x8_routing():
439
- """Generate routing for 8x8 multiplier."""
440
- internal = {}
441
-
442
- # Partial products: 64 AND gates (8x8)
443
- for row in range(8):
444
- for col in range(8):
445
- internal[f'pp.r{row}.c{col}'] = [f'$a[{col}]', f'$b[{row}]']
446
-
447
- # Stage adders: 7 stages, each stage adds one row of partial products
448
- # Stage i adds row i+1, result width grows from 9 to 15 bits
449
- for stage in range(7):
450
- row_idx = stage + 1
451
- shift = row_idx
452
- sum_width = 8 + stage + 1 # 9, 10, 11, 12, 13, 14, 15
453
-
454
- for bit in range(sum_width):
455
- prefix = f'stage{stage}.bit{bit}'
456
-
457
- # Determine inputs for this bit position
458
- if bit < shift:
459
- # Below shift: comes from previous result only
460
- if stage == 0:
461
- prev = f'pp.r0.c{bit}' if bit < 8 else '#0'
462
- else:
463
- prev = f'stage{stage-1}.bit{bit}.ha2.sum' if bit < 8 + stage else '#0'
464
- pp_bit = '#0'
465
- elif bit <= shift + 7:
466
- # In range of partial products
467
- if stage == 0:
468
- prev = f'pp.r0.c{bit}' if bit < 8 else '#0'
469
- else:
470
- prev = f'stage{stage-1}.bit{bit}.ha2.sum' if bit < 8 + stage else f'stage{stage-1}.bit{8+stage-1}.carry_or'
471
- pp_bit = f'pp.r{row_idx}.c{bit-shift}'
472
- else:
473
- # Above partial product range
474
- prev = f'stage{stage-1}.bit{bit-1}.carry_or' if stage > 0 else '#0'
475
- pp_bit = '#0'
476
-
477
- # Carry from previous bit
478
- if bit == 0:
479
- cin = '#0'
480
- else:
481
- cin = f'stage{stage}.bit{bit-1}.carry_or'
482
-
483
- # Full adder structure
484
- internal[f'{prefix}.ha1.sum.layer1.or'] = [prev, pp_bit]
485
- internal[f'{prefix}.ha1.sum.layer1.nand'] = [prev, pp_bit]
486
- internal[f'{prefix}.ha1.sum.layer2'] = [f'{prefix}.ha1.sum.layer1.or', f'{prefix}.ha1.sum.layer1.nand']
487
- internal[f'{prefix}.ha1.carry'] = [prev, pp_bit]
488
-
489
- internal[f'{prefix}.ha2.sum.layer1.or'] = [f'{prefix}.ha1.sum.layer2', cin]
490
- internal[f'{prefix}.ha2.sum.layer1.nand'] = [f'{prefix}.ha1.sum.layer2', cin]
491
- internal[f'{prefix}.ha2.sum.layer2'] = [f'{prefix}.ha2.sum.layer1.or', f'{prefix}.ha2.sum.layer1.nand']
492
- internal[f'{prefix}.ha2.carry'] = [f'{prefix}.ha1.sum.layer2', cin]
493
-
494
- internal[f'{prefix}.carry_or'] = [f'{prefix}.ha1.carry', f'{prefix}.ha2.carry']
495
-
496
- # Outputs: 16 bits
497
- outputs = {}
498
- for i in range(8):
499
- outputs[f'p[{i}]'] = f'pp.r0.c{i}' if i < 8 else f'stage6.bit{i}.ha2.sum.layer2'
500
- for i in range(8, 16):
501
- outputs[f'p[{i}]'] = f'stage6.bit{i}.ha2.sum.layer2' if i < 15 else 'stage6.bit14.carry_or'
502
-
503
- return {
504
- 'arithmetic.multiplier8x8': {
505
- 'inputs': ['$a[0:7]', '$b[0:7]'],
506
- 'type': 'multiplier',
507
- 'internal': internal,
508
- 'outputs': outputs
509
- }
510
- }
511
-
512
-
513
- def generate_div8bit_routing():
514
- """Generate routing for 8-bit restoring division."""
515
- internal = {}
516
-
517
- for stage in range(8):
518
- prefix = f'stage{stage}'
519
-
520
- # Previous remainder (from previous stage or initial 0)
521
- if stage == 0:
522
- prev_rem = ['#0'] * 8
523
- else:
524
- prev_rem = [f'stage{stage-1}.mux{i}' for i in range(8)]
525
-
526
- # Shift left and bring in dividend bit
527
- for bit in range(8):
528
- if bit < 7:
529
- internal[f'{prefix}.shift.bit{bit}'] = [prev_rem[bit + 1]]
530
- else:
531
- internal[f'{prefix}.shift.bit{bit}'] = [f'$dividend[{7-stage}]']
532
-
533
- # OR dividend bit into LSB
534
- internal[f'{prefix}.or_dividend'] = [f'{prefix}.shift.bit7', f'$dividend[{7-stage}]']
535
-
536
- # NOT divisor for subtraction
537
- for bit in range(8):
538
- internal[f'{prefix}.sub.notd{bit}'] = [f'$divisor[{bit}]']
539
-
540
- # Subtractor: 8 full adders
541
- for bit in range(8):
542
- fa_prefix = f'{prefix}.sub.fa{bit}'
543
- shifted = f'{prefix}.shift.bit{bit}'
544
- notd = f'{prefix}.sub.notd{bit}'
545
-
546
- if bit == 0:
547
- cin = '#1' # +1 for two's complement
548
- else:
549
- cin = f'{prefix}.sub.fa{bit-1}.or_carry'
550
-
551
- # XOR1: shifted ^ notd
552
- internal[f'{fa_prefix}.xor1.layer1.nand'] = [shifted, notd]
553
- internal[f'{fa_prefix}.xor1.layer1.or'] = [shifted, notd]
554
- internal[f'{fa_prefix}.xor1.layer2'] = [f'{fa_prefix}.xor1.layer1.nand', f'{fa_prefix}.xor1.layer1.or']
555
-
556
- # XOR2: xor1 ^ cin
557
- internal[f'{fa_prefix}.xor2.layer1.nand'] = [f'{fa_prefix}.xor1.layer2', cin]
558
- internal[f'{fa_prefix}.xor2.layer1.or'] = [f'{fa_prefix}.xor1.layer2', cin]
559
- internal[f'{fa_prefix}.xor2.layer2'] = [f'{fa_prefix}.xor2.layer1.nand', f'{fa_prefix}.xor2.layer1.or']
560
-
561
- # AND1, AND2, OR_carry
562
- internal[f'{fa_prefix}.and1'] = [shifted, notd]
563
- internal[f'{fa_prefix}.and2'] = [f'{fa_prefix}.xor1.layer2', cin]
564
- internal[f'{fa_prefix}.or_carry'] = [f'{fa_prefix}.and1', f'{fa_prefix}.and2']
565
-
566
- # Comparator: check if subtraction result >= 0 (carry out = 1 means no borrow)
567
- internal[f'{prefix}.cmp'] = [f'{prefix}.sub.fa7.or_carry']
568
-
569
- # MUX: select subtracted value if cmp=1, else keep original
570
- for bit in range(8):
571
- mux_prefix = f'{prefix}.mux{bit}'
572
- original = f'{prefix}.shift.bit{bit}'
573
- subtracted = f'{prefix}.sub.fa{bit}.xor2.layer2'
574
- sel = f'{prefix}.cmp'
575
-
576
- internal[f'{mux_prefix}.not_sel'] = [sel]
577
- internal[f'{mux_prefix}.and0'] = [original, f'{mux_prefix}.not_sel']
578
- internal[f'{mux_prefix}.and1'] = [subtracted, sel]
579
- internal[f'{mux_prefix}.or'] = [f'{mux_prefix}.and0', f'{mux_prefix}.and1']
580
-
581
- # Quotient outputs: cmp values from each stage
582
- for i in range(8):
583
- internal[f'quotient{i}'] = [f'stage{i}.cmp']
584
-
585
- # Remainder outputs: final mux values
586
- for i in range(8):
587
- internal[f'remainder{i}'] = [f'stage7.mux{i}.or']
588
-
589
- return {
590
- 'arithmetic.div8bit': {
591
- 'inputs': ['$dividend[0:7]', '$divisor[0:7]'],
592
- 'type': 'divider',
593
- 'internal': internal,
594
- 'outputs': {
595
- 'quotient': [f'quotient{i}' for i in range(8)],
596
- 'remainder': [f'remainder{i}' for i in range(8)]
597
- }
598
- }
599
- }
600
-
601
-
602
- def generate_adc_sbc_routing():
603
- """Generate routing for ADC and SBC circuits."""
604
- routing = {}
605
-
606
- # ADC: Add with Carry (8-bit)
607
- internal_adc = {}
608
- for i in range(8):
609
- fa_prefix = f'fa{i}'
610
- a_bit = f'$a[{i}]'
611
- b_bit = f'$b[{i}]'
612
- cin = '$cin' if i == 0 else f'fa{i-1}.or_carry'
613
-
614
- internal_adc[f'{fa_prefix}.xor1.layer1.nand'] = [a_bit, b_bit]
615
- internal_adc[f'{fa_prefix}.xor1.layer1.or'] = [a_bit, b_bit]
616
- internal_adc[f'{fa_prefix}.xor1.layer2'] = [f'{fa_prefix}.xor1.layer1.nand', f'{fa_prefix}.xor1.layer1.or']
617
-
618
- internal_adc[f'{fa_prefix}.xor2.layer1.nand'] = [f'{fa_prefix}.xor1.layer2', cin]
619
- internal_adc[f'{fa_prefix}.xor2.layer1.or'] = [f'{fa_prefix}.xor1.layer2', cin]
620
- internal_adc[f'{fa_prefix}.xor2.layer2'] = [f'{fa_prefix}.xor2.layer1.nand', f'{fa_prefix}.xor2.layer1.or']
621
-
622
- internal_adc[f'{fa_prefix}.and1'] = [a_bit, b_bit]
623
- internal_adc[f'{fa_prefix}.and2'] = [f'{fa_prefix}.xor1.layer2', cin]
624
- internal_adc[f'{fa_prefix}.or_carry'] = [f'{fa_prefix}.and1', f'{fa_prefix}.and2']
625
-
626
- routing['arithmetic.adc8bit'] = {
627
- 'inputs': ['$a[0:7]', '$b[0:7]', '$cin'],
628
- 'type': 'adder_with_carry',
629
- 'internal': internal_adc
630
- }
631
-
632
- # SBC: Subtract with Carry (A - B - borrow)
633
- internal_sbc = {}
634
- for i in range(8):
635
- internal_sbc[f'notb{i}'] = [f'$b[{i}]']
636
-
637
- for i in range(8):
638
- fa_prefix = f'fa{i}'
639
- a_bit = f'$a[{i}]'
640
- notb_bit = f'notb{i}'
641
- cin = '$cin' if i == 0 else f'fa{i-1}.or_carry'
642
-
643
- internal_sbc[f'{fa_prefix}.xor1.layer1.nand'] = [a_bit, notb_bit]
644
- internal_sbc[f'{fa_prefix}.xor1.layer1.or'] = [a_bit, notb_bit]
645
- internal_sbc[f'{fa_prefix}.xor1.layer2'] = [f'{fa_prefix}.xor1.layer1.nand', f'{fa_prefix}.xor1.layer1.or']
646
-
647
- internal_sbc[f'{fa_prefix}.xor2.layer1.nand'] = [f'{fa_prefix}.xor1.layer2', cin]
648
- internal_sbc[f'{fa_prefix}.xor2.layer1.or'] = [f'{fa_prefix}.xor1.layer2', cin]
649
- internal_sbc[f'{fa_prefix}.xor2.layer2'] = [f'{fa_prefix}.xor2.layer1.nand', f'{fa_prefix}.xor2.layer1.or']
650
-
651
- internal_sbc[f'{fa_prefix}.and1'] = [a_bit, notb_bit]
652
- internal_sbc[f'{fa_prefix}.and2'] = [f'{fa_prefix}.xor1.layer2', cin]
653
- internal_sbc[f'{fa_prefix}.or_carry'] = [f'{fa_prefix}.and1', f'{fa_prefix}.and2']
654
-
655
- routing['arithmetic.sbc8bit'] = {
656
- 'inputs': ['$a[0:7]', '$b[0:7]', '$cin'],
657
- 'type': 'subtractor_with_carry',
658
- 'internal': internal_sbc
659
- }
660
-
661
- # SUB: Subtract (A - B), carry_in forced to 1
662
- internal_sub = {'carry_in': ['#1']}
663
- for i in range(8):
664
- internal_sub[f'notb{i}'] = [f'$b[{i}]']
665
-
666
- for i in range(8):
667
- fa_prefix = f'fa{i}'
668
- a_bit = f'$a[{i}]'
669
- notb_bit = f'notb{i}'
670
- cin = 'carry_in' if i == 0 else f'fa{i-1}.or_carry'
671
-
672
- internal_sub[f'{fa_prefix}.xor1.layer1.nand'] = [a_bit, notb_bit]
673
- internal_sub[f'{fa_prefix}.xor1.layer1.or'] = [a_bit, notb_bit]
674
- internal_sub[f'{fa_prefix}.xor1.layer2'] = [f'{fa_prefix}.xor1.layer1.nand', f'{fa_prefix}.xor1.layer1.or']
675
-
676
- internal_sub[f'{fa_prefix}.xor2.layer1.nand'] = [f'{fa_prefix}.xor1.layer2', cin]
677
- internal_sub[f'{fa_prefix}.xor2.layer1.or'] = [f'{fa_prefix}.xor1.layer2', cin]
678
- internal_sub[f'{fa_prefix}.xor2.layer2'] = [f'{fa_prefix}.xor2.layer1.nand', f'{fa_prefix}.xor2.layer1.or']
679
-
680
- internal_sub[f'{fa_prefix}.and1'] = [a_bit, notb_bit]
681
- internal_sub[f'{fa_prefix}.and2'] = [f'{fa_prefix}.xor1.layer2', cin]
682
- internal_sub[f'{fa_prefix}.or_carry'] = [f'{fa_prefix}.and1', f'{fa_prefix}.and2']
683
-
684
- routing['arithmetic.sub8bit'] = {
685
- 'inputs': ['$a[0:7]', '$b[0:7]'],
686
- 'type': 'subtractor',
687
- 'internal': internal_sub
688
- }
689
-
690
- # CMP: Compare (like SUB but only sets flags)
691
- internal_cmp = {}
692
- for i in range(8):
693
- internal_cmp[f'notb{i}'] = [f'$b[{i}]']
694
-
695
- for i in range(8):
696
- fa_prefix = f'fa{i}'
697
- a_bit = f'$a[{i}]'
698
- notb_bit = f'notb{i}'
699
- cin = '#1' if i == 0 else f'fa{i-1}.or_carry'
700
-
701
- internal_cmp[f'{fa_prefix}.xor1.layer1.nand'] = [a_bit, notb_bit]
702
- internal_cmp[f'{fa_prefix}.xor1.layer1.or'] = [a_bit, notb_bit]
703
- internal_cmp[f'{fa_prefix}.xor1.layer2'] = [f'{fa_prefix}.xor1.layer1.nand', f'{fa_prefix}.xor1.layer1.or']
704
-
705
- internal_cmp[f'{fa_prefix}.xor2.layer1.nand'] = [f'{fa_prefix}.xor1.layer2', cin]
706
- internal_cmp[f'{fa_prefix}.xor2.layer1.or'] = [f'{fa_prefix}.xor1.layer2', cin]
707
- internal_cmp[f'{fa_prefix}.xor2.layer2'] = [f'{fa_prefix}.xor2.layer1.nand', f'{fa_prefix}.xor2.layer1.or']
708
-
709
- internal_cmp[f'{fa_prefix}.and1'] = [a_bit, notb_bit]
710
- internal_cmp[f'{fa_prefix}.and2'] = [f'{fa_prefix}.xor1.layer2', cin]
711
- internal_cmp[f'{fa_prefix}.or_carry'] = [f'{fa_prefix}.and1', f'{fa_prefix}.and2']
712
-
713
- # Flags
714
- internal_cmp['flags.zero_or'] = [f'fa{i}.xor2.layer2' for i in range(8)]
715
- internal_cmp['flags.zero'] = ['flags.zero_or']
716
- internal_cmp['flags.negative'] = ['fa7.xor2.layer2']
717
- internal_cmp['flags.carry'] = ['fa7.or_carry']
718
-
719
- routing['arithmetic.cmp8bit'] = {
720
- 'inputs': ['$a[0:7]', '$b[0:7]'],
721
- 'type': 'compare',
722
- 'internal': internal_cmp
723
- }
724
-
725
- return routing
726
-
727
-
728
- def generate_rotate_routing():
729
- """Generate routing for ROL and ROR circuits."""
730
- routing = {}
731
-
732
- # ROL: Rotate Left (bit 7 goes to carry, carry goes to bit 0)
733
- internal_rol = {}
734
- for i in range(8):
735
- if i == 0:
736
- internal_rol[f'bit{i}'] = ['$cin']
737
- else:
738
- internal_rol[f'bit{i}'] = [f'$x[{i-1}]']
739
- internal_rol['cout'] = ['$x[7]']
740
-
741
- routing['arithmetic.rol8bit'] = {
742
- 'inputs': ['$x[0:7]', '$cin'],
743
- 'type': 'rotate',
744
- 'internal': internal_rol
745
- }
746
-
747
- # ROR: Rotate Right (bit 0 goes to carry, carry goes to bit 7)
748
- internal_ror = {}
749
- for i in range(8):
750
- if i == 7:
751
- internal_ror[f'bit{i}'] = ['$cin']
752
- else:
753
- internal_ror[f'bit{i}'] = [f'$x[{i+1}]']
754
- internal_ror['cout'] = ['$x[0]']
755
-
756
- routing['arithmetic.ror8bit'] = {
757
- 'inputs': ['$x[0:7]', '$cin'],
758
- 'type': 'rotate',
759
- 'internal': internal_ror
760
- }
761
-
762
- return routing
763
-
764
-
765
- def generate_combinational_routing():
766
- """Generate routing for combinational circuits."""
767
- routing = {}
768
-
769
- # 3-to-8 Decoder
770
- internal_dec = {}
771
- for out in range(8):
772
- # out = sel[2]*4 + sel[1]*2 + sel[0]
773
- # Each output is AND of (possibly inverted) select lines
774
- internal_dec[f'out{out}'] = ['$sel[0]', '$sel[1]', '$sel[2]']
775
-
776
- routing['combinational.decoder3to8'] = {
777
- 'inputs': ['$sel[0:2]'],
778
- 'type': 'decoder',
779
- 'internal': internal_dec
780
- }
781
-
782
- # 8-to-3 Encoder
783
- internal_enc = {}
784
- for bit in range(3):
785
- internal_enc[f'bit{bit}'] = ['$x[0:7]']
786
-
787
- routing['combinational.encoder8to3'] = {
788
- 'inputs': ['$x[0:7]'],
789
- 'type': 'encoder',
790
- 'internal': internal_enc
791
- }
792
-
793
- # 2-to-1 MUX
794
- routing['combinational.multiplexer2to1'] = {
795
- 'inputs': ['$a', '$b', '$sel'],
796
- 'type': 'mux',
797
- 'internal': {
798
- 'not_s': ['$sel'],
799
- 'and0': ['$a', 'not_s'],
800
- 'and1': ['$b', '$sel'],
801
- 'or': ['and0', 'and1']
802
- },
803
- 'output': 'or'
804
- }
805
-
806
- # 4-to-1 MUX
807
- routing['combinational.multiplexer4to1'] = {
808
- 'inputs': ['$x[0:3]', '$sel[0:1]'],
809
- 'type': 'mux',
810
- 'internal': {'select': ['$x[0:3]', '$sel[0:1]']}
811
- }
812
-
813
- # 8-to-1 MUX
814
- routing['combinational.multiplexer8to1'] = {
815
- 'inputs': ['$x[0:7]', '$sel[0:2]'],
816
- 'type': 'mux',
817
- 'internal': {'select': ['$x[0:7]', '$sel[0:2]']}
818
- }
819
-
820
- # 1-to-2 DEMUX
821
- routing['combinational.demultiplexer1to2'] = {
822
- 'inputs': ['$x', '$sel'],
823
- 'type': 'demux',
824
- 'internal': {
825
- 'and0': ['$x', '$sel'], # sel inverted internally
826
- 'and1': ['$x', '$sel']
827
- }
828
- }
829
-
830
- # 1-to-4 DEMUX
831
- routing['combinational.demultiplexer1to4'] = {
832
- 'inputs': ['$x', '$sel[0:1]'],
833
- 'type': 'demux',
834
- 'internal': {'decode': ['$x', '$sel[0:1]']}
835
- }
836
-
837
- # 1-to-8 DEMUX
838
- routing['combinational.demultiplexer1to8'] = {
839
- 'inputs': ['$x', '$sel[0:2]'],
840
- 'type': 'demux',
841
- 'internal': {'decode': ['$x', '$sel[0:2]']}
842
- }
843
-
844
- # Barrel Shifter
845
- routing['combinational.barrelshifter8bit'] = {
846
- 'inputs': ['$x[0:7]', '$shift[0:2]'],
847
- 'type': 'barrel_shifter',
848
- 'internal': {'shift': ['$x[0:7]', '$shift[0:2]']}
849
- }
850
-
851
- # Priority Encoder
852
- routing['combinational.priorityencoder8bit'] = {
853
- 'inputs': ['$x[0:7]'],
854
- 'type': 'priority_encoder',
855
- 'internal': {'priority': ['$x[0:7]']}
856
- }
857
-
858
- # Register MUX 4-to-1 (8-bit wide)
859
- internal_regmux = {'not_s0': ['$sel[0]'], 'not_s1': ['$sel[1]']}
860
- for bit in range(8):
861
- for and_idx in range(4):
862
- sel0 = 'not_s0' if (and_idx & 1) == 0 else '$sel[0]'
863
- sel1 = 'not_s1' if (and_idx & 2) == 0 else '$sel[1]'
864
- internal_regmux[f'bit{bit}.and{and_idx}'] = [f'$r{and_idx}[{bit}]', sel0, sel1]
865
- internal_regmux[f'bit{bit}.or'] = [f'bit{bit}.and{i}' for i in range(4)]
866
-
867
- routing['combinational.regmux4to1'] = {
868
- 'inputs': ['$r0[0:7]', '$r1[0:7]', '$r2[0:7]', '$r3[0:7]', '$sel[0:1]'],
869
- 'type': 'register_mux',
870
- 'internal': internal_regmux
871
- }
872
-
873
- return routing
874
-
875
-
876
  def generate_control_routing():
877
  """Generate routing for control circuits."""
878
  routing = {}
879
-
880
- # Instruction Decoder (4-bit to 16 one-hot)
881
- internal_dec = {}
882
- for op in range(16):
883
- internal_dec[f'decode{op}'] = ['$opcode[0:3]']
884
- for op in range(4):
885
- internal_dec[f'not_op{op}'] = [f'$opcode[{op}]']
886
- internal_dec['is_alu'] = ['$opcode[0:3]']
887
- internal_dec['is_control'] = ['$opcode[0:3]']
888
-
889
- routing['control.decoder'] = {
890
- 'inputs': ['$opcode[0:3]'],
891
- 'type': 'instruction_decoder',
892
- 'internal': internal_dec
893
- }
894
-
895
- # Jump (unconditional)
896
- internal_jump = {}
897
- for bit in range(8):
898
- internal_jump[f'bit{bit}'] = [f'$target[{bit}]']
899
-
900
- routing['control.jump'] = {
901
- 'inputs': ['$target[0:7]'],
902
- 'type': 'jump',
903
- 'internal': internal_jump
904
- }
905
-
906
- # Conditional Jump (generic template)
907
- def make_cond_jump(name, flag):
908
- internal = {}
909
- for bit in range(8):
910
- internal[f'bit{bit}.not_sel'] = [f'${flag}']
911
- internal[f'bit{bit}.and_a'] = [f'$pc[{bit}]', f'bit{bit}.not_sel']
912
- internal[f'bit{bit}.and_b'] = [f'$target[{bit}]', f'${flag}']
913
- internal[f'bit{bit}.or'] = [f'bit{bit}.and_a', f'bit{bit}.and_b']
914
-
915
- routing[f'control.{name}'] = {
916
- 'inputs': ['$pc[0:7]', '$target[0:7]', f'${flag}'],
917
- 'type': 'conditional_jump',
918
- 'internal': internal
919
- }
920
-
921
- make_cond_jump('conditionaljump', 'cond')
922
- make_cond_jump('jc', 'carry')
923
- make_cond_jump('jn', 'negative')
924
- make_cond_jump('jz', 'zero')
925
- make_cond_jump('jv', 'overflow')
926
- make_cond_jump('jnc', 'not_carry')
927
- make_cond_jump('jnz', 'not_zero')
928
- make_cond_jump('jnv', 'not_overflow')
929
- make_cond_jump('jp', 'positive')
930
-
931
- # CALL and RET
932
- routing['control.call'] = {
933
- 'inputs': ['$target[0:7]'],
934
- 'type': 'call',
935
- 'internal': {'jump': ['$target[0:7]'], 'push': ['$pc[0:7]']}
936
- }
937
-
938
- routing['control.ret'] = {
939
- 'inputs': ['$stack_top[0:7]'],
940
- 'type': 'return',
941
- 'internal': {'jump': ['$stack_top[0:7]'], 'pop': ['#1']}
942
- }
943
-
944
- # PUSH and POP
945
- routing['control.push'] = {
946
- 'inputs': ['$value[0:7]', '$sp[0:7]'],
947
- 'type': 'push',
948
- 'internal': {'sp_dec': ['$sp[0:7]'], 'store': ['$value[0:7]']}
949
- }
950
-
951
- routing['control.pop'] = {
952
- 'inputs': ['$sp[0:7]'],
953
- 'type': 'pop',
954
- 'internal': {'load': ['$sp[0:7]'], 'sp_inc': ['$sp[0:7]']}
955
- }
956
-
957
- # SP increment/decrement
958
- routing['control.sp_dec'] = {'inputs': ['$sp[0:7]'], 'type': 'sp_dec', 'internal': {'uses': ['$sp[0:7]']}}
959
- routing['control.sp_inc'] = {'inputs': ['$sp[0:7]'], 'type': 'sp_inc', 'internal': {'uses': ['$sp[0:7]']}}
960
-
961
- # PC Increment
962
- internal_pc_inc = {'sum0': ['$pc[0]'], 'carry0': ['$pc[0]'], 'overflow': ['$pc[7]']}
963
- for bit in range(1, 8):
964
- prev_carry = f'carry{bit-1}' if bit > 1 else 'carry0'
965
- internal_pc_inc[f'xor{bit}.layer1.nand'] = [f'$pc[{bit}]', prev_carry]
966
- internal_pc_inc[f'xor{bit}.layer1.or'] = [f'$pc[{bit}]', prev_carry]
967
- internal_pc_inc[f'xor{bit}.layer2'] = [f'xor{bit}.layer1.nand', f'xor{bit}.layer1.or']
968
- internal_pc_inc[f'and{bit}'] = [f'$pc[{bit}]', prev_carry]
969
-
970
- routing['control.pc_inc'] = {
971
- 'inputs': ['$pc[0:7]'],
972
- 'type': 'pc_increment',
973
- 'internal': internal_pc_inc
974
- }
975
-
976
- # PC Load (mux between PC+1 and jump target)
977
- internal_pc_load = {'not_jump': ['$jump']}
978
- for bit in range(8):
979
- internal_pc_load[f'bit{bit}.and_pc'] = [f'$pc_inc[{bit}]', 'not_jump']
980
- internal_pc_load[f'bit{bit}.and_jump'] = [f'$target[{bit}]', '$jump']
981
- internal_pc_load[f'bit{bit}.or'] = [f'bit{bit}.and_pc', f'bit{bit}.and_jump']
982
-
983
- routing['control.pc_load'] = {
984
- 'inputs': ['$pc_inc[0:7]', '$target[0:7]', '$jump'],
985
- 'type': 'pc_load',
986
- 'internal': internal_pc_load
987
- }
988
-
989
- # HALT
990
- internal_halt = {'signal': ['$halt']}
991
- for flag in ['flag_c', 'flag_n', 'flag_v', 'flag_z']:
992
- internal_halt[flag] = [f'${flag}']
993
- for bit in range(8):
994
- internal_halt[f'pc.bit{bit}'] = [f'$pc[{bit}]']
995
- internal_halt[f'value.bit{bit}'] = [f'$value[{bit}]']
996
-
997
- routing['control.halt'] = {
998
- 'inputs': ['$halt', '$flag_c', '$flag_n', '$flag_v', '$flag_z', '$pc[0:7]', '$value[0:7]'],
999
- 'type': 'halt',
1000
- 'internal': internal_halt
1001
- }
1002
-
1003
- # NOP
1004
- internal_nop = {'output': ['#1']}
1005
- for bit in range(8):
1006
- internal_nop[f'bit{bit}'] = [f'$x[{bit}]']
1007
- for flag in ['flag_c', 'flag_n', 'flag_v', 'flag_z']:
1008
- internal_nop[flag] = [f'${flag}']
1009
-
1010
  routing['control.nop'] = {
1011
  'inputs': ['$x[0:7]', '$flag_c', '$flag_n', '$flag_v', '$flag_z'],
1012
  'type': 'nop',
1013
  'internal': internal_nop
1014
  }
1015
 
1016
- # Fetch/load/store buffer gates
1017
  internal_fetch = {f'bit{bit}': [f'$data[{bit}]'] for bit in range(16)}
1018
  routing['control.fetch.ir'] = {
1019
  'inputs': ['$data[0:15]'],
@@ -1086,328 +1179,220 @@ def generate_memory_routing():
1086
  def generate_error_detection_routing():
1087
  """Generate routing for error detection circuits."""
1088
  routing = {}
1089
-
1090
- # Even Parity Checker
1091
- routing['error_detection.evenparitychecker'] = {
1092
- 'inputs': ['$x[0:7]'],
1093
- 'type': 'parity',
1094
- 'internal': {'': ['$x[0:7]']}
1095
- }
1096
-
1097
- # Odd Parity Checker
1098
- routing['error_detection.oddparitychecker'] = {
1099
- 'inputs': ['$x[0:7]'],
1100
- 'type': 'parity',
1101
- 'internal': {
1102
- 'parity': ['$x[0:7]'],
1103
- 'not': ['parity']
1104
- }
1105
- }
1106
-
1107
- # Checksum
1108
- routing['error_detection.checksum8bit'] = {
1109
- 'inputs': ['$x[0:7]'],
1110
- 'type': 'checksum',
1111
- 'internal': {'sum': ['$x[0:7]']}
1112
- }
1113
-
1114
- # CRC
1115
- routing['error_detection.crc4'] = {
1116
- 'inputs': ['$data[0:7]'],
1117
- 'type': 'crc',
1118
- 'internal': {'divisor': ['#1', '#0', '#0', '#1', '#1']}
1119
- }
1120
-
1121
- routing['error_detection.crc8'] = {
1122
- 'inputs': ['$data[0:7]'],
1123
- 'type': 'crc',
1124
- 'internal': {'divisor': ['#1', '#0', '#0', '#0', '#0', '#0', '#1', '#1', '#1']}
1125
- }
1126
-
1127
- # Hamming Encode (4 data bits -> 7 code bits)
1128
- routing['error_detection.hammingencode4bit'] = {
1129
- 'inputs': ['$d[0:3]'],
1130
- 'type': 'hamming_encode',
1131
- 'internal': {
1132
- 'p0': ['$d[0:3]'],
1133
- 'p1': ['$d[0:3]'],
1134
- 'p2': ['$d[0:3]'],
1135
- 'p3': ['$d[0:3]']
1136
- }
1137
- }
1138
-
1139
- # Hamming Decode (7 code bits -> syndrome)
1140
- routing['error_detection.hammingdecode7bit'] = {
1141
- 'inputs': ['$c[0:6]'],
1142
- 'type': 'hamming_decode',
1143
- 'internal': {
1144
- 's1': ['$c[0:6]'],
1145
- 's2': ['$c[0:6]'],
1146
- 's3': ['$c[0:6]']
1147
- }
1148
- }
1149
-
1150
- # Hamming Syndrome
1151
- routing['error_detection.hammingsyndrome'] = {
1152
- 'inputs': ['$c[0:6]'],
1153
- 'type': 'hamming_syndrome',
1154
- 'internal': {
1155
- 's1': ['$c[0:6]'],
1156
- 's2': ['$c[0:6]'],
1157
- 's3': ['$c[0:6]']
1158
- }
1159
- }
1160
-
1161
- # Longitudinal Parity
1162
- routing['error_detection.longitudinalparity'] = {
1163
- 'inputs': ['$data'],
1164
- 'type': 'longitudinal_parity',
1165
- 'internal': {
1166
- 'col_parity': ['$data'],
1167
- 'row_parity': ['$data']
1168
- }
1169
- }
1170
-
1171
- # Parity Checker (3-stage XOR tree)
1172
- internal_parity_check = {}
1173
- for stage in range(1, 4):
1174
- num_xors = 4 if stage == 1 else (2 if stage == 2 else 1)
1175
- for i in range(num_xors):
1176
- prefix = f'stage{stage}.xor{i}'
1177
- internal_parity_check[f'{prefix}.layer1.nand'] = [f'$stage{stage}_in[{2*i}]', f'$stage{stage}_in[{2*i+1}]']
1178
- internal_parity_check[f'{prefix}.layer1.or'] = [f'$stage{stage}_in[{2*i}]', f'$stage{stage}_in[{2*i+1}]']
1179
- internal_parity_check[f'{prefix}.layer2'] = [f'{prefix}.layer1.nand', f'{prefix}.layer1.or']
1180
- internal_parity_check['output.not'] = ['stage3.xor0.layer2']
1181
-
1182
- routing['error_detection.paritychecker8bit'] = {
1183
- 'inputs': ['$x[0:7]'],
1184
- 'type': 'parity_tree',
1185
- 'internal': internal_parity_check
1186
- }
1187
-
1188
- # Parity Generator (same structure as checker)
1189
- routing['error_detection.paritygenerator8bit'] = {
1190
- 'inputs': ['$x[0:7]'],
1191
- 'type': 'parity_tree',
1192
- 'internal': internal_parity_check.copy()
1193
- }
1194
-
1195
- return routing
1196
-
1197
-
1198
- def generate_alu_routing():
1199
- """Generate routing for ALU circuits."""
1200
- routing = {}
1201
-
1202
- # ALU Control (opcode to operation select)
1203
- internal_ctrl = {}
1204
- for op in range(16):
1205
- internal_ctrl[f'op{op}'] = ['$opcode[0:3]']
1206
-
1207
- routing['alu.alucontrol'] = {
1208
- 'inputs': ['$opcode[0:3]'],
1209
- 'type': 'alu_control',
1210
- 'internal': internal_ctrl
1211
- }
1212
-
1213
- # ALU Flags
1214
- routing['alu.aluflags'] = {
1215
- 'inputs': ['$result[0:7]', '$carry', '$overflow'],
1216
- 'type': 'alu_flags',
1217
- 'internal': {
1218
- 'zero': ['$result[0:7]'],
1219
- 'negative': ['$result[7]'],
1220
- 'carry': ['$carry'],
1221
- 'overflow': ['$overflow']
1222
- }
1223
- }
1224
-
1225
- # ALU 8-bit operations
1226
- routing['alu.alu8bit.and'] = {
1227
- 'inputs': ['$a[0:7]', '$b[0:7]'],
1228
- 'type': 'bitwise_and',
1229
- 'internal': {f'bit{i}': [f'$a[{i}]', f'$b[{i}]'] for i in range(8)}
1230
- }
1231
-
1232
- routing['alu.alu8bit.or'] = {
1233
- 'inputs': ['$a[0:7]', '$b[0:7]'],
1234
- 'type': 'bitwise_or',
1235
- 'internal': {f'bit{i}': [f'$a[{i}]', f'$b[{i}]'] for i in range(8)}
1236
- }
1237
-
1238
- routing['alu.alu8bit.not'] = {
1239
- 'inputs': ['$a[0:7]'],
1240
- 'type': 'bitwise_not',
1241
- 'internal': {f'bit{i}': [f'$a[{i}]'] for i in range(8)}
1242
- }
1243
-
1244
- # XOR with two-layer structure
1245
- internal_xor = {}
1246
- for i in range(8):
1247
- internal_xor[f'layer1.nand.bit{i}'] = [f'$a[{i}]', f'$b[{i}]']
1248
- internal_xor[f'layer1.or.bit{i}'] = [f'$a[{i}]', f'$b[{i}]']
1249
- internal_xor[f'layer2.bit{i}'] = [f'layer1.nand.bit{i}', f'layer1.or.bit{i}']
1250
-
1251
- routing['alu.alu8bit.xor'] = {
1252
- 'inputs': ['$a[0:7]', '$b[0:7]'],
1253
- 'type': 'bitwise_xor',
1254
- 'internal': internal_xor
1255
- }
1256
-
1257
- # Shifts
1258
- routing['alu.alu8bit.shl'] = {
1259
- 'inputs': ['$a[0:7]'],
1260
- 'type': 'shift_left',
1261
- 'internal': {'': ['$a[0:7]']}
1262
- }
1263
-
1264
- routing['alu.alu8bit.shr'] = {
1265
- 'inputs': ['$a[0:7]'],
1266
- 'type': 'shift_right',
1267
- 'internal': {'': ['$a[0:7]']}
1268
- }
1269
-
1270
- # ADD (references ripple carry)
1271
- routing['alu.alu8bit.add'] = {
1272
- 'inputs': ['$a[0:7]', '$b[0:7]'],
1273
- 'type': 'add',
1274
- 'internal': {'': ['$a[0:7]', '$b[0:7]']}
1275
- }
1276
-
1277
- # Output MUX
1278
- routing['alu.alu8bit.output_mux'] = {
1279
- 'inputs': ['$results[0:15]', '$opcode[0:3]'],
1280
- 'type': 'output_mux',
1281
- 'internal': {'': ['$results[0:15]', '$opcode[0:3]']}
1282
- }
1283
-
1284
- return routing
1285
-
1286
-
1287
- def generate_div8bit_routing():
1288
- """Generate routing for 8-bit restoring division circuit."""
1289
- internal = {}
1290
-
1291
- for stage in range(8):
1292
- prefix = f'stage{stage}'
1293
-
1294
- # Previous stage remainder (or 0 for stage 0)
1295
- if stage == 0:
1296
- prev_rem = ['#0'] * 8
1297
- else:
1298
- prev_rem = [f'stage{stage-1}.mux{i}.or' for i in range(8)]
1299
-
1300
- # Dividend bit for this stage (MSB first)
1301
- dividend_bit = f'$dividend[{7-stage}]'
1302
-
1303
- # Shift: shift remainder left, bring in dividend bit at LSB
1304
- for bit in range(8):
1305
- if bit == 0:
1306
- internal[f'{prefix}.shift.bit{bit}'] = [dividend_bit]
1307
- else:
1308
- internal[f'{prefix}.shift.bit{bit}'] = [prev_rem[bit - 1] if stage > 0 else '#0']
1309
-
1310
- shifted = [f'{prefix}.shift.bit{i}' for i in range(8)]
1311
-
1312
- # NOT divisor for two's complement subtraction
1313
- for bit in range(8):
1314
- internal[f'{prefix}.sub.notd{bit}'] = [f'$divisor[{bit}]']
1315
-
1316
- not_divisor = [f'{prefix}.sub.notd{i}' for i in range(8)]
1317
-
1318
- # Subtractor: 8 full adders computing shifted - divisor (LSB to MSB)
1319
- carry = '#1' # +1 for two's complement
1320
- for bit in range(8): # LSB (bit 0) to MSB (bit 7)
1321
- fa_prefix = f'{prefix}.sub.fa{bit}'
1322
- a = shifted[bit]
1323
- b = not_divisor[bit]
1324
-
1325
- internal[f'{fa_prefix}.xor1.layer1.or'] = [a, b]
1326
- internal[f'{fa_prefix}.xor1.layer1.nand'] = [a, b]
1327
- internal[f'{fa_prefix}.xor1.layer2'] = [f'{fa_prefix}.xor1.layer1.or', f'{fa_prefix}.xor1.layer1.nand']
1328
-
1329
- internal[f'{fa_prefix}.xor2.layer1.or'] = [f'{fa_prefix}.xor1.layer2', carry]
1330
- internal[f'{fa_prefix}.xor2.layer1.nand'] = [f'{fa_prefix}.xor1.layer2', carry]
1331
- internal[f'{fa_prefix}.xor2.layer2'] = [f'{fa_prefix}.xor2.layer1.or', f'{fa_prefix}.xor2.layer1.nand']
1332
-
1333
- internal[f'{fa_prefix}.and1'] = [a, b]
1334
- internal[f'{fa_prefix}.and2'] = [f'{fa_prefix}.xor1.layer2', carry]
1335
- internal[f'{fa_prefix}.or_carry'] = [f'{fa_prefix}.and1', f'{fa_prefix}.and2']
1336
-
1337
- carry = f'{fa_prefix}.or_carry'
1338
-
1339
- sub_result = [f'{prefix}.sub.fa{i}.xor2.layer2' for i in range(8)]
1340
-
1341
- # Comparator: carry out from MSB (fa7) = 1 means no borrow (shifted >= divisor)
1342
- internal[f'{prefix}.cmp'] = [f'{prefix}.sub.fa7.or_carry']
1343
-
1344
- cmp_out = f'{prefix}.cmp'
1345
-
1346
- # OR dividend (combine shifted LSB with dividend bit)
1347
- internal[f'{prefix}.or_dividend'] = [shifted[7], dividend_bit]
1348
-
1349
- # MUX: select sub_result if cmp=1 (no borrow), else shifted
1350
- for bit in range(8):
1351
- mux_prefix = f'{prefix}.mux{bit}'
1352
- internal[f'{mux_prefix}.not_sel'] = [cmp_out]
1353
- internal[f'{mux_prefix}.and0'] = [shifted[bit], f'{mux_prefix}.not_sel']
1354
- internal[f'{mux_prefix}.and1'] = [sub_result[bit], cmp_out]
1355
- internal[f'{mux_prefix}.or'] = [f'{mux_prefix}.and0', f'{mux_prefix}.and1']
1356
-
1357
- # Output quotient bits (cmp outputs from each stage)
1358
- for i in range(8):
1359
- internal[f'quotient{i}'] = [f'stage{i}.cmp']
1360
-
1361
- # Output remainder bits (final mux outputs)
1362
- for i in range(8):
1363
- internal[f'remainder{i}'] = [f'stage7.mux{i}.or']
1364
-
1365
- outputs = {f'quotient[{i}]': f'quotient{i}' for i in range(8)}
1366
- outputs.update({f'remainder[{i}]': f'remainder{i}' for i in range(8)})
1367
-
1368
- return {
1369
- 'arithmetic.div8bit': {
1370
- 'inputs': ['$dividend[0:7]', '$divisor[0:7]'],
1371
- 'type': 'restoring_division',
1372
- 'internal': internal,
1373
- 'outputs': outputs
1374
- }
1375
- }
1376
-
1377
-
1378
- def main():
1379
- routing = {
1380
- 'version': 1,
1381
- 'description': 'Routing information for 8-bit threshold computer',
1382
- 'circuits': {}
1383
- }
1384
-
1385
- # Generate routing for each category
1386
- print('Generating routing...')
1387
-
1388
- print(' Boolean gates')
1389
- routing['circuits'].update(generate_boolean_routing())
1390
-
1391
- print(' Half adder')
1392
- routing['circuits'].update(generate_arithmetic_halfadder_routing())
1393
-
1394
- print(' Full adder')
1395
- routing['circuits'].update(generate_arithmetic_fulladder_routing())
1396
-
1397
- print(' Ripple carry adders')
1398
- routing['circuits'].update(generate_ripplecarry_routing(2))
1399
- routing['circuits'].update(generate_ripplecarry_routing(4))
1400
- routing['circuits'].update(generate_ripplecarry_routing(8))
1401
-
1402
- print(' Comparators')
1403
- routing['circuits'].update(generate_comparator_routing())
1404
-
1405
- print(' Equality')
1406
- routing['circuits'].update(generate_equality_routing())
1407
-
1408
- print(' Negation')
1409
- routing['circuits'].update(generate_neg8bit_routing())
1410
-
1411
  print(' 2x2 Multiplier')
1412
  routing['circuits'].update(generate_multiplier2x2_routing())
1413
 
@@ -1440,50 +1425,167 @@ def main():
1440
 
1441
  print(' Threshold gates')
1442
  routing['circuits'].update(generate_threshold_routing())
1443
-
1444
- print(' Modular arithmetic')
1445
- routing['circuits'].update(generate_modular_routing())
1446
-
1447
- print(' Pattern recognition')
1448
- routing['circuits'].update(generate_pattern_routing())
1449
-
1450
- print(' Manifest')
1451
- routing['circuits'].update(generate_manifest_routing())
1452
-
1453
- # Save to file
1454
- with open('routing.json', 'w') as f:
1455
- json.dump(routing, f, indent=2)
1456
-
1457
- print(f'\nGenerated routing for {len(routing["circuits"])} circuits')
1458
- print('Saved to routing.json')
1459
-
1460
- # Show what's missing
1461
- all_gates = get_all_gates('neural_computer.safetensors')
1462
- covered = set()
1463
- for circuit_path in routing['circuits'].keys():
1464
- # Add the circuit itself and all internal gates
1465
- covered.add(circuit_path)
1466
- circuit = routing['circuits'][circuit_path]
1467
- if 'internal' in circuit:
1468
- for gate in circuit['internal'].keys():
1469
- if gate:
1470
- covered.add(f'{circuit_path}.{gate}')
1471
-
1472
- missing = [g for g in all_gates if not any(g.startswith(c) for c in routing['circuits'].keys())]
1473
- if missing:
1474
- print(f'\nMissing circuits ({len(missing)}):')
1475
- # Group by category
1476
- by_cat = defaultdict(list)
1477
- for g in missing:
1478
- cat = g.split('.')[0]
1479
- by_cat[cat].append(g)
1480
- for cat in sorted(by_cat.keys()):
1481
- print(f' {cat}: {len(by_cat[cat])} gates')
1482
- for g in by_cat[cat][:5]:
1483
- print(f' - {g}')
1484
- if len(by_cat[cat]) > 5:
1485
- print(f' ... and {len(by_cat[cat])-5} more')
1486
-
1487
-
1488
- if __name__ == '__main__':
1489
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 {
 
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]'],
 
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
 
 
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())
routing/routing_schema.md DELETED
@@ -1,171 +0,0 @@
1
- # Routing Schema for 8-bit Threshold Computer
2
-
3
- ## Overview
4
-
5
- The routing file (`routing.json`) defines how gates are interconnected. Each entry maps a gate path to its input sources.
6
-
7
- ## Schema Format
8
-
9
- ```json
10
- {
11
- "version": 1,
12
- "external_inputs": {
13
- "circuit_path": ["input_name", ...]
14
- },
15
- "routing": {
16
- "gate_path": ["source1", "source2", ...]
17
- }
18
- }
19
- ```
20
-
21
- ## Input Source Types
22
-
23
- 1. **External input**: `"$input_name"` - Named input to the circuit
24
- - Example: `"$a"`, `"$b"`, `"$cin"`
25
-
26
- 2. **Gate output**: `"path.to.gate"` - Output of another gate
27
- - Example: `"ha1.sum"`, `"layer1.or"`
28
-
29
- 3. **Bit extraction**: `"$input[i]"` - Single bit from multi-bit input
30
- - Example: `"$a[0]"` (LSB), `"$a[7]"` (MSB for 8-bit)
31
-
32
- 4. **Constant**: `"#0"` or `"#1"` - Fixed value
33
- - Example: `"#1"` for carry-in in two's complement
34
-
35
- 5. **Relative reference**: `"../sibling.gate"` - Reference to sibling in hierarchy
36
- - Example: `"../fa0.cout"` from fa1
37
-
38
- 6. **Memory indexing**: `"$mem[addr][bit]"` or `"$sel[addr]"` - Addressed memory bit or one-hot select line
39
- - Example: `"$mem[42][3]"` (addr 42, bit 3), `"$sel[42]"`
40
-
41
- 7. **Packed memory tensors**: For 64KB memory, routing uses packed tensor blocks instead of per-gate entries.
42
- - Example: `memory.addr_decode.weight`, `memory.read.and.weight`, `memory.write.and_old.weight`
43
-
44
- ## Circuit Types
45
-
46
- ### Single-Layer Gates
47
- Gates with just `.weight` and `.bias`:
48
- ```json
49
- "boolean.and": ["$a", "$b"]
50
- ```
51
-
52
- ### Two-Layer Gates (XOR, XNOR)
53
- Gates decomposed into layer1 + layer2:
54
- ```json
55
- "boolean.xor.layer1.or": ["$a", "$b"],
56
- "boolean.xor.layer1.nand": ["$a", "$b"],
57
- "boolean.xor.layer2": ["layer1.or", "layer1.nand"]
58
- ```
59
-
60
- ### Hierarchical Circuits
61
- Complex circuits with sub-components:
62
- ```json
63
- "arithmetic.fulladder": {
64
- "external_inputs": ["$a", "$b", "$cin"],
65
- "gates": {
66
- "ha1.sum.layer1.or": ["$a", "$b"],
67
- "ha1.sum.layer1.nand": ["$a", "$b"],
68
- "ha1.sum.layer2": ["ha1.sum.layer1.or", "ha1.sum.layer1.nand"],
69
- "ha1.carry": ["$a", "$b"],
70
- "ha2.sum.layer1.or": ["ha1.sum", "$cin"],
71
- "ha2.sum.layer1.nand": ["ha1.sum", "$cin"],
72
- "ha2.sum.layer2": ["ha2.sum.layer1.or", "ha2.sum.layer1.nand"],
73
- "ha2.carry": ["ha1.sum", "$cin"],
74
- "carry_or": ["ha1.carry", "ha2.carry"]
75
- },
76
- "outputs": {
77
- "sum": "ha2.sum",
78
- "cout": "carry_or"
79
- }
80
- }
81
- ```
82
-
83
- ### Bit-Indexed Circuits
84
- Circuits operating on multi-bit values:
85
- ```json
86
- "arithmetic.ripplecarry8bit": {
87
- "external_inputs": ["$a[0:7]", "$b[0:7]"],
88
- "gates": {
89
- "fa0": {"inputs": ["$a[0]", "$b[0]", "#0"], "type": "fulladder"},
90
- "fa1": {"inputs": ["$a[1]", "$b[1]", "fa0.cout"], "type": "fulladder"},
91
- ...
92
- }
93
- }
94
- ```
95
-
96
- ### Packed Memory Circuits
97
- 64KB memory routing uses packed tensors to avoid exploding the header size. The routing entry
98
- declares a packed type and lists the tensor blocks used for the operation.
99
-
100
- ```json
101
- "memory.addr_decode": {
102
- "inputs": ["$addr[0:15]"],
103
- "type": "decoder_packed",
104
- "internal": {
105
- "weight": ["memory.addr_decode.weight"],
106
- "bias": ["memory.addr_decode.bias"]
107
- }
108
- }
109
-
110
- "memory.read": {
111
- "inputs": ["$mem[0:65535][0:7]", "$sel[0:65535]"],
112
- "type": "read_mux_packed",
113
- "internal": {
114
- "and": ["memory.read.and.weight", "memory.read.and.bias"],
115
- "or": ["memory.read.or.weight", "memory.read.or.bias"]
116
- },
117
- "outputs": { "bit0": "bit0", "bit1": "bit1", "bit2": "bit2", "bit3": "bit3",
118
- "bit4": "bit4", "bit5": "bit5", "bit6": "bit6", "bit7": "bit7" }
119
- }
120
-
121
- "memory.write": {
122
- "inputs": ["$mem[0:65535][0:7]", "$write_data[0:7]", "$sel[0:65535]", "$we"],
123
- "type": "write_mux_packed",
124
- "internal": {
125
- "sel": ["memory.write.sel.weight", "memory.write.sel.bias"],
126
- "nsel": ["memory.write.nsel.weight", "memory.write.nsel.bias"],
127
- "and_old": ["memory.write.and_old.weight", "memory.write.and_old.bias"],
128
- "and_new": ["memory.write.and_new.weight", "memory.write.and_new.bias"],
129
- "or": ["memory.write.or.weight", "memory.write.or.bias"]
130
- }
131
- }
132
- ```
133
-
134
- Packed tensor mapping (shapes assume 16-bit address, 8-bit data):
135
- - `memory.addr_decode.weight`: [65536, 16]
136
- - `memory.addr_decode.bias`: [65536]
137
- - `memory.read.and.weight`: [8, 65536, 2]
138
- - `memory.read.and.bias`: [8, 65536]
139
- - `memory.read.or.weight`: [8, 65536]
140
- - `memory.read.or.bias`: [8]
141
- - `memory.write.sel.weight`: [65536, 2]
142
- - `memory.write.sel.bias`: [65536]
143
- - `memory.write.nsel.weight`: [65536, 1]
144
- - `memory.write.nsel.bias`: [65536]
145
- - `memory.write.and_old.weight`: [65536, 8, 2]
146
- - `memory.write.and_old.bias`: [65536, 8]
147
- - `memory.write.and_new.weight`: [65536, 8, 2]
148
- - `memory.write.and_new.bias`: [65536, 8]
149
- - `memory.write.or.weight`: [65536, 8, 2]
150
- - `memory.write.or.bias`: [65536, 8]
151
-
152
- Semantics are the same as the unrolled circuits, but computed in bulk:
153
- - decode: `sel[i] = H(sum(addr_bits * weight[i]) + bias[i])`
154
- - read: `bit[b] = H(sum(H([mem_bit, sel] * and_w[b,i] + and_b[b,i]) * or_w[b]) + or_b[b])`
155
- - 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)`
156
-
157
- ## Naming Conventions
158
-
159
- - External inputs: `$name` or `$name[bit]`
160
- - Constants: `#0`, `#1`
161
- - Internal gates: relative path from circuit root
162
- - Outputs: named in `outputs` section
163
-
164
- ## Validation Rules
165
-
166
- 1. Every gate in routing must exist in tensors file
167
- 2. Every tensor must have routing entry
168
- 3. Input count must match weight dimensions
169
- 4. No circular dependencies (DAG only)
170
- 5. All referenced sources must exist
171
- 6. Packed memory circuits are valid when the packed tensor blocks exist and match the expected shapes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
routing/validate_packed_memory.py DELETED
@@ -1,117 +0,0 @@
1
- """
2
- Validate packed memory tensor references in routing.json against safetensors.
3
- """
4
-
5
- from __future__ import annotations
6
-
7
- import argparse
8
- import json
9
- import sys
10
- from pathlib import Path
11
- from typing import Dict, Iterable, List, Tuple
12
-
13
- from safetensors import safe_open
14
-
15
-
16
- def _load_json(path: Path) -> Dict:
17
- with path.open("r", encoding="utf-8") as fh:
18
- return json.load(fh)
19
-
20
-
21
- def _get_scalar_tensor(f, name: str, default: int) -> int:
22
- if name not in f.keys():
23
- return default
24
- tensor = f.get_tensor(name)
25
- return int(tensor.item())
26
-
27
-
28
- def _gather_internal_keys(routing: Dict, circuit_name: str) -> List[str]:
29
- circuit = routing.get("circuits", {}).get(circuit_name)
30
- if circuit is None:
31
- return []
32
- internal = circuit.get("internal", {})
33
- keys: List[str] = []
34
- for value in internal.values():
35
- if isinstance(value, list):
36
- keys.extend(value)
37
- return keys
38
-
39
-
40
- def _shape_matches(actual: Iterable[int], expected: Iterable[int]) -> bool:
41
- return tuple(actual) == tuple(expected)
42
-
43
-
44
- def main() -> int:
45
- parser = argparse.ArgumentParser(description="Validate packed memory routing tensors.")
46
- parser.add_argument(
47
- "--routing",
48
- type=Path,
49
- default=Path(__file__).resolve().parent / "routing.json",
50
- help="Path to routing.json",
51
- )
52
- parser.add_argument(
53
- "--model",
54
- type=Path,
55
- default=Path(__file__).resolve().parent.parent / "neural_computer.safetensors",
56
- help="Path to neural_computer.safetensors",
57
- )
58
- args = parser.parse_args()
59
-
60
- routing = _load_json(args.routing)
61
- routing_keys = set()
62
- for name in ("memory.addr_decode", "memory.read", "memory.write"):
63
- routing_keys.update(_gather_internal_keys(routing, name))
64
-
65
- missing_routing = [k for k in routing_keys if not k]
66
- if missing_routing:
67
- print("routing.json contains empty packed tensor entries.", file=sys.stderr)
68
- return 1
69
-
70
- with safe_open(str(args.model), framework="pt") as f:
71
- mem_bytes = _get_scalar_tensor(f, "manifest.memory_bytes", 65536)
72
- pc_width = _get_scalar_tensor(f, "manifest.pc_width", 16)
73
- reg_width = _get_scalar_tensor(f, "manifest.register_width", 8)
74
-
75
- expected_shapes: Dict[str, Tuple[int, ...]] = {
76
- "memory.addr_decode.weight": (mem_bytes, pc_width),
77
- "memory.addr_decode.bias": (mem_bytes,),
78
- "memory.read.and.weight": (reg_width, mem_bytes, 2),
79
- "memory.read.and.bias": (reg_width, mem_bytes),
80
- "memory.read.or.weight": (reg_width, mem_bytes),
81
- "memory.read.or.bias": (reg_width,),
82
- "memory.write.sel.weight": (mem_bytes, 2),
83
- "memory.write.sel.bias": (mem_bytes,),
84
- "memory.write.nsel.weight": (mem_bytes, 1),
85
- "memory.write.nsel.bias": (mem_bytes,),
86
- "memory.write.and_old.weight": (mem_bytes, reg_width, 2),
87
- "memory.write.and_old.bias": (mem_bytes, reg_width),
88
- "memory.write.and_new.weight": (mem_bytes, reg_width, 2),
89
- "memory.write.and_new.bias": (mem_bytes, reg_width),
90
- "memory.write.or.weight": (mem_bytes, reg_width, 2),
91
- "memory.write.or.bias": (mem_bytes, reg_width),
92
- }
93
-
94
- errors = []
95
- for key, expected in expected_shapes.items():
96
- if key not in routing_keys:
97
- errors.append(f"routing.json missing key: {key}")
98
- continue
99
- if key not in f.keys():
100
- errors.append(f"safetensors missing key: {key}")
101
- continue
102
- actual = f.get_tensor(key).shape
103
- if not _shape_matches(actual, expected):
104
- errors.append(f"{key} shape {tuple(actual)} != {expected}")
105
-
106
- if errors:
107
- print("Packed memory validation failed:", file=sys.stderr)
108
- for err in errors:
109
- print(f" - {err}", file=sys.stderr)
110
- return 1
111
-
112
- print("Packed memory routing validation: ok")
113
- return 0
114
-
115
-
116
- if __name__ == "__main__":
117
- raise SystemExit(main())