phanerozoic commited on
Commit
acd53bb
·
verified ·
1 Parent(s): d2212af

Add memory circuits and CPU state layout

Browse files
README.md CHANGED
@@ -17,8 +17,8 @@ tags:
17
  Every logic gate is a threshold neuron: `output = 1 if (Σ wᵢxᵢ + b) ≥ 0 else 0`
18
 
19
  ```
20
- Tensors: 3,122
21
- Parameters: 5,648
22
  ```
23
 
24
  ---
@@ -85,15 +85,16 @@ The weights in this repository implement a complete 8-bit computer: registers, A
85
  | Arithmetic | 18 | Half/full adder, 2/4/8-bit ripple carry, comparators |
86
  | ALU | 3 | 8-bit ALU, control decoder, flag computation |
87
  | Combinational | 10 | MUX (2:1, 4:1, 8:1), DEMUX, encoders, decoders |
88
- | Control Flow | 16 | JMP, conditional jumps, CALL, RET, PUSH, POP |
89
- | Error Detection | 11 | Parity (XOR tree), checksum, CRC, Hamming |
90
- | Modular | 11 | Divisibility by 2-12 (multi-layer for non-powers-of-2) |
91
- | Threshold | 13 | k-of-n gates, majority, minority, exactly-k |
92
- | Pattern | 10 | Popcount, leading/trailing ones, symmetry |
 
93
 
94
  ---
95
 
96
- ## Usage
97
 
98
  ```python
99
  import torch
@@ -112,11 +113,44 @@ for a, b_in in [(0,0), (0,1), (1,0), (1,1)]:
112
  inp = torch.tensor([a, b_in], dtype=torch.float32)
113
  out = heaviside(inp @ w + b)
114
  print(f"AND({a}, {b_in}) = {int(out.item())}")
115
- ```
116
-
117
- ---
118
-
119
- ## Verification
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
  The model includes `iron_eval.py` which exhaustively tests all circuits:
122
 
@@ -175,7 +209,7 @@ All weights are integers. All activations are Heaviside step. Designed for:
175
 
176
  | File | Description |
177
  |------|-------------|
178
- | `neural_computer.safetensors` | 3,122 tensors, 5,648 parameters |
179
  | `iron_eval.py` | Comprehensive test suite |
180
  | `prune_weights.py` | Weight optimization tool |
181
 
 
17
  Every logic gate is a threshold neuron: `output = 1 if (Σ wᵢxᵢ + b) ≥ 0 else 0`
18
 
19
  ```
20
+ Tensors: 24,200
21
+ Parameters: 40,323
22
  ```
23
 
24
  ---
 
85
  | Arithmetic | 18 | Half/full adder, 2/4/8-bit ripple carry, comparators |
86
  | ALU | 3 | 8-bit ALU, control decoder, flag computation |
87
  | Combinational | 10 | MUX (2:1, 4:1, 8:1), DEMUX, encoders, decoders |
88
+ | Control Flow | 16 | JMP, conditional jumps, CALL, RET, PUSH, POP |
89
+ | Error Detection | 11 | Parity (XOR tree), checksum, CRC, Hamming |
90
+ | Modular | 11 | Divisibility by 2-12 (multi-layer for non-powers-of-2) |
91
+ | Threshold | 13 | k-of-n gates, majority, minority, exactly-k |
92
+ | Pattern | 10 | Popcount, leading/trailing ones, symmetry |
93
+ | Memory | 3 | 8-bit addr decoder, 256x8 read mux, write cell update |
94
 
95
  ---
96
 
97
+ ## Usage
98
 
99
  ```python
100
  import torch
 
113
  inp = torch.tensor([a, b_in], dtype=torch.float32)
114
  out = heaviside(inp @ w + b)
115
  print(f"AND({a}, {b_in}) = {int(out.item())}")
116
+ ```
117
+
118
+ ---
119
+
120
+ ## State Tensor Layout
121
+
122
+ All multi-bit fields are **MSB-first** (index 0 is the most-significant bit).
123
+
124
+ ```
125
+ [ PC[8] | IR[16] | R0[8] R1[8] R2[8] R3[8] | FLAGS[4] | SP[8] | CTRL[4] | MEM[256][8] ]
126
+ ```
127
+
128
+ Flags are ordered as: `Z, N, C, V`.
129
+
130
+ Control bits are ordered as: `HALT, MEM_WE, MEM_RE, RESERVED`.
131
+
132
+ Total state size: `2120` bits.
133
+
134
+ ---
135
+
136
+ ## Instruction Encoding (16-bit)
137
+
138
+ All instruction bits are **MSB-first**.
139
+
140
+ ```
141
+ 15..12 11..10 9..8 7..0
142
+ opcode rd rs imm8
143
+ ```
144
+
145
+ Interpretation:
146
+ - **R-type**: `rd = rd op rs` (imm8 ignored).
147
+ - **I-type**: `rd = op rd, imm8` (rs ignored).
148
+ - **Jumps/Calls**: `imm8` is the absolute target address.
149
+ - **LOAD/STORE**: `imm8` is the absolute memory address.
150
+
151
+ ---
152
+
153
+ ## Verification
154
 
155
  The model includes `iron_eval.py` which exhaustively tests all circuits:
156
 
 
209
 
210
  | File | Description |
211
  |------|-------------|
212
+ | `neural_computer.safetensors` | 24,200 tensors, 40,323 parameters |
213
  | `iron_eval.py` | Comprehensive test suite |
214
  | `prune_weights.py` | Weight optimization tool |
215
 
cpu/__pycache__/cycle.cpython-311.pyc ADDED
Binary file (5.07 kB). View file
 
cpu/__pycache__/state.cpython-311.pyc ADDED
Binary file (6.99 kB). View file
 
cpu/cycle.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Reference CPU cycle (software) for the threshold computer.
3
+ Implements fetch/decode/execute over the state layout.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import Tuple
9
+
10
+ from .state import CPUState
11
+
12
+
13
+ def decode_ir(ir: int) -> Tuple[int, int, int, int]:
14
+ opcode = (ir >> 12) & 0xF
15
+ rd = (ir >> 10) & 0x3
16
+ rs = (ir >> 8) & 0x3
17
+ imm8 = ir & 0xFF
18
+ return opcode, rd, rs, imm8
19
+
20
+
21
+ def _flags_from_result(result: int, carry: int, overflow: int) -> Tuple[int, int, int, int]:
22
+ z = 1 if result == 0 else 0
23
+ n = 1 if (result & 0x80) else 0
24
+ c = 1 if carry else 0
25
+ v = 1 if overflow else 0
26
+ return z, n, c, v
27
+
28
+
29
+ def _alu_add(a: int, b: int) -> Tuple[int, int, int]:
30
+ full = a + b
31
+ result = full & 0xFF
32
+ carry = 1 if full > 0xFF else 0
33
+ overflow = 1 if (((a ^ result) & (b ^ result)) & 0x80) else 0
34
+ return result, carry, overflow
35
+
36
+
37
+ def _alu_sub(a: int, b: int) -> Tuple[int, int, int]:
38
+ full = (a - b) & 0x1FF
39
+ result = full & 0xFF
40
+ carry = 1 if a >= b else 0
41
+ overflow = 1 if (((a ^ b) & (a ^ result)) & 0x80) else 0
42
+ return result, carry, overflow
43
+
44
+
45
+ def step(state: CPUState) -> CPUState:
46
+ if state.ctrl[0] == 1: # HALT
47
+ return state.copy()
48
+
49
+ s = state.copy()
50
+
51
+ # Fetch: two bytes, big-endian
52
+ hi = s.mem[s.pc]
53
+ lo = s.mem[(s.pc + 1) & 0xFF]
54
+ s.ir = ((hi & 0xFF) << 8) | (lo & 0xFF)
55
+ next_pc = (s.pc + 2) & 0xFF
56
+
57
+ opcode, rd, rs, imm8 = decode_ir(s.ir)
58
+ a = s.regs[rd]
59
+ b = s.regs[rs]
60
+
61
+ write_result = True
62
+ result = a
63
+ carry = 0
64
+ overflow = 0
65
+
66
+ if opcode == 0x0: # ADD
67
+ result, carry, overflow = _alu_add(a, b)
68
+ elif opcode == 0x1: # SUB
69
+ result, carry, overflow = _alu_sub(a, b)
70
+ elif opcode == 0x2: # AND
71
+ result = a & b
72
+ elif opcode == 0x3: # OR
73
+ result = a | b
74
+ elif opcode == 0x4: # XOR
75
+ result = a ^ b
76
+ elif opcode == 0x5: # SHL
77
+ carry = 1 if (a & 0x80) else 0
78
+ result = (a << 1) & 0xFF
79
+ elif opcode == 0x6: # SHR
80
+ carry = 1 if (a & 0x01) else 0
81
+ result = (a >> 1) & 0xFF
82
+ elif opcode == 0x7: # MUL
83
+ full = a * b
84
+ result = full & 0xFF
85
+ carry = 1 if full > 0xFF else 0
86
+ elif opcode == 0x8: # DIV
87
+ if b == 0:
88
+ result = 0
89
+ carry = 1
90
+ overflow = 1
91
+ else:
92
+ result = (a // b) & 0xFF
93
+ elif opcode == 0x9: # CMP
94
+ result, carry, overflow = _alu_sub(a, b)
95
+ write_result = False
96
+ elif opcode == 0xA: # LOAD
97
+ result = s.mem[imm8]
98
+ elif opcode == 0xB: # STORE
99
+ s.mem[imm8] = b & 0xFF
100
+ write_result = False
101
+ elif opcode == 0xC: # JMP
102
+ s.pc = imm8 & 0xFF
103
+ write_result = False
104
+ elif opcode == 0xD: # JZ
105
+ if s.flags[0] == 1:
106
+ s.pc = imm8 & 0xFF
107
+ else:
108
+ s.pc = next_pc
109
+ write_result = False
110
+ elif opcode == 0xE: # CALL
111
+ s.sp = (s.sp - 1) & 0xFF
112
+ s.mem[s.sp] = next_pc
113
+ s.pc = imm8 & 0xFF
114
+ write_result = False
115
+ elif opcode == 0xF: # HALT
116
+ s.ctrl[0] = 1
117
+ write_result = False
118
+
119
+ if opcode <= 0x9 or opcode in (0xA, 0x7, 0x8):
120
+ s.flags = list(_flags_from_result(result, carry, overflow))
121
+
122
+ if write_result:
123
+ s.regs[rd] = result & 0xFF
124
+
125
+ if opcode not in (0xC, 0xD, 0xE):
126
+ s.pc = next_pc
127
+
128
+ return s
129
+
130
+
131
+ def run_until_halt(state: CPUState, max_cycles: int = 256) -> Tuple[CPUState, int]:
132
+ s = state.copy()
133
+ for i in range(max_cycles):
134
+ if s.ctrl[0] == 1:
135
+ return s, i
136
+ s = step(s)
137
+ return s, max_cycles
cpu/state.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ State layout helpers for the 8-bit threshold computer.
3
+ All multi-bit fields are MSB-first.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from dataclasses import dataclass
9
+ from typing import List
10
+
11
+ FLAG_NAMES = ["Z", "N", "C", "V"]
12
+ CTRL_NAMES = ["HALT", "MEM_WE", "MEM_RE", "RESERVED"]
13
+
14
+ PC_BITS = 8
15
+ IR_BITS = 16
16
+ REG_BITS = 8
17
+ REG_COUNT = 4
18
+ FLAG_BITS = 4
19
+ SP_BITS = 8
20
+ CTRL_BITS = 4
21
+ MEM_BYTES = 256
22
+ MEM_BITS = MEM_BYTES * 8
23
+
24
+ STATE_BITS = PC_BITS + IR_BITS + (REG_BITS * REG_COUNT) + FLAG_BITS + SP_BITS + CTRL_BITS + MEM_BITS
25
+
26
+
27
+ def int_to_bits(value: int, width: int) -> List[int]:
28
+ return [(value >> (width - 1 - i)) & 1 for i in range(width)]
29
+
30
+
31
+ def bits_to_int(bits: List[int]) -> int:
32
+ value = 0
33
+ for bit in bits:
34
+ value = (value << 1) | int(bit)
35
+ return value
36
+
37
+
38
+ @dataclass
39
+ class CPUState:
40
+ pc: int
41
+ ir: int
42
+ regs: List[int]
43
+ flags: List[int]
44
+ sp: int
45
+ ctrl: List[int]
46
+ mem: List[int]
47
+
48
+ def copy(self) -> "CPUState":
49
+ return CPUState(
50
+ pc=int(self.pc),
51
+ ir=int(self.ir),
52
+ regs=[int(r) for r in self.regs],
53
+ flags=[int(f) for f in self.flags],
54
+ sp=int(self.sp),
55
+ ctrl=[int(c) for c in self.ctrl],
56
+ mem=[int(m) for m in self.mem],
57
+ )
58
+
59
+
60
+ def pack_state(state: CPUState) -> List[int]:
61
+ bits: List[int] = []
62
+ bits.extend(int_to_bits(state.pc, PC_BITS))
63
+ bits.extend(int_to_bits(state.ir, IR_BITS))
64
+ for reg in state.regs:
65
+ bits.extend(int_to_bits(reg, REG_BITS))
66
+ bits.extend([int(f) for f in state.flags])
67
+ bits.extend(int_to_bits(state.sp, SP_BITS))
68
+ bits.extend([int(c) for c in state.ctrl])
69
+ for byte in state.mem:
70
+ bits.extend(int_to_bits(byte, REG_BITS))
71
+ return bits
72
+
73
+
74
+ def unpack_state(bits: List[int]) -> CPUState:
75
+ if len(bits) != STATE_BITS:
76
+ raise ValueError(f"Expected {STATE_BITS} bits, got {len(bits)}")
77
+
78
+ idx = 0
79
+ pc = bits_to_int(bits[idx:idx + PC_BITS])
80
+ idx += PC_BITS
81
+ ir = bits_to_int(bits[idx:idx + IR_BITS])
82
+ idx += IR_BITS
83
+
84
+ regs = []
85
+ for _ in range(REG_COUNT):
86
+ regs.append(bits_to_int(bits[idx:idx + REG_BITS]))
87
+ idx += REG_BITS
88
+
89
+ flags = [int(b) for b in bits[idx:idx + FLAG_BITS]]
90
+ idx += FLAG_BITS
91
+
92
+ sp = bits_to_int(bits[idx:idx + SP_BITS])
93
+ idx += SP_BITS
94
+
95
+ ctrl = [int(b) for b in bits[idx:idx + CTRL_BITS]]
96
+ idx += CTRL_BITS
97
+
98
+ mem = []
99
+ for _ in range(MEM_BYTES):
100
+ mem.append(bits_to_int(bits[idx:idx + REG_BITS]))
101
+ idx += REG_BITS
102
+
103
+ return CPUState(pc=pc, ir=ir, regs=regs, flags=flags, sp=sp, ctrl=ctrl, mem=mem)
eval/build_memory.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Generate memory and fetch/load/store buffers for the 8-bit threshold computer.
3
+ Updates neural_computer.safetensors and tensors.txt in-place.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from pathlib import Path
9
+ from typing import Dict, Iterable, List
10
+
11
+ import torch
12
+ from safetensors import safe_open
13
+ from safetensors.torch import save_file
14
+
15
+
16
+ MODEL_PATH = Path(__file__).resolve().parent.parent / "neural_computer.safetensors"
17
+ MANIFEST_PATH = Path(__file__).resolve().parent.parent / "tensors.txt"
18
+
19
+
20
+ def load_tensors(path: Path) -> Dict[str, torch.Tensor]:
21
+ tensors: Dict[str, torch.Tensor] = {}
22
+ with safe_open(str(path), framework="pt") as f:
23
+ for name in f.keys():
24
+ tensors[name] = f.get_tensor(name).float().cpu().clone()
25
+ return tensors
26
+
27
+
28
+ def add_gate(tensors: Dict[str, torch.Tensor], name: str, weight: Iterable[float], bias: Iterable[float]) -> None:
29
+ w_key = f"{name}.weight"
30
+ b_key = f"{name}.bias"
31
+ if w_key in tensors or b_key in tensors:
32
+ raise ValueError(f"Gate already exists: {name}")
33
+ tensors[w_key] = torch.tensor(list(weight), dtype=torch.float32)
34
+ tensors[b_key] = torch.tensor(list(bias), dtype=torch.float32)
35
+
36
+
37
+ def add_decoder_8to256(tensors: Dict[str, torch.Tensor]) -> None:
38
+ for addr in range(256):
39
+ bits = [(addr >> (7 - i)) & 1 for i in range(8)] # MSB-first
40
+ weights = [1.0 if bit == 1 else -1.0 for bit in bits]
41
+ bias = -float(sum(bits))
42
+ add_gate(tensors, f"memory.addr_decode.out{addr}", weights, [bias])
43
+
44
+
45
+ def add_memory_read_mux(tensors: Dict[str, torch.Tensor]) -> None:
46
+ # AND each mem bit with its address select, then OR across all addresses.
47
+ for bit in range(8):
48
+ for addr in range(256):
49
+ add_gate(tensors, f"memory.read.bit{bit}.and{addr}", [1.0, 1.0], [-2.0])
50
+ add_gate(tensors, f"memory.read.bit{bit}.or", [1.0] * 256, [-1.0])
51
+
52
+
53
+ def add_memory_write_cells(tensors: Dict[str, torch.Tensor]) -> None:
54
+ # write_sel = addr_select AND write_enable
55
+ # new_bit = (NOT write_sel AND old_bit) OR (write_sel AND write_data_bit)
56
+ for addr in range(256):
57
+ add_gate(tensors, f"memory.write.sel.addr{addr}", [1.0, 1.0], [-2.0])
58
+ add_gate(tensors, f"memory.write.nsel.addr{addr}", [-1.0], [0.0])
59
+ for bit in range(8):
60
+ add_gate(tensors, f"memory.write.addr{addr}.bit{bit}.and_old", [1.0, 1.0], [-2.0])
61
+ add_gate(tensors, f"memory.write.addr{addr}.bit{bit}.and_new", [1.0, 1.0], [-2.0])
62
+ add_gate(tensors, f"memory.write.addr{addr}.bit{bit}.or", [1.0, 1.0], [-1.0])
63
+
64
+
65
+ def add_fetch_load_store_buffers(tensors: Dict[str, torch.Tensor]) -> None:
66
+ # Buffer gates: output = input (weight=1, bias=-1)
67
+ for bit in range(16):
68
+ add_gate(tensors, f"control.fetch.ir.bit{bit}", [1.0], [-1.0])
69
+ for bit in range(8):
70
+ add_gate(tensors, f"control.load.bit{bit}", [1.0], [-1.0])
71
+ add_gate(tensors, f"control.store.bit{bit}", [1.0], [-1.0])
72
+ add_gate(tensors, f"control.mem_addr.bit{bit}", [1.0], [-1.0])
73
+
74
+
75
+ def update_manifest(tensors: Dict[str, torch.Tensor]) -> None:
76
+ # Bump manifest version to reflect memory integration.
77
+ key = "manifest.version"
78
+ if key not in tensors:
79
+ tensors[key] = torch.tensor([2.0], dtype=torch.float32)
80
+ return
81
+ tensors[key] = torch.tensor([2.0], dtype=torch.float32)
82
+
83
+
84
+ def write_manifest(path: Path, tensors: Dict[str, torch.Tensor]) -> None:
85
+ lines: List[str] = []
86
+ lines.append("# Tensor Manifest")
87
+ lines.append(f"# Total: {len(tensors)} tensors")
88
+ for name in sorted(tensors.keys()):
89
+ t = tensors[name]
90
+ values = ", ".join(f"{v:.1f}" for v in t.flatten().tolist())
91
+ lines.append(f"{name}: shape={list(t.shape)}, values=[{values}]")
92
+ path.write_text("\n".join(lines) + "\n", encoding="utf-8")
93
+
94
+
95
+ def main() -> None:
96
+ tensors = load_tensors(MODEL_PATH)
97
+ add_decoder_8to256(tensors)
98
+ add_memory_read_mux(tensors)
99
+ add_memory_write_cells(tensors)
100
+ add_fetch_load_store_buffers(tensors)
101
+ update_manifest(tensors)
102
+ save_file(tensors, str(MODEL_PATH))
103
+ write_manifest(MANIFEST_PATH, tensors)
104
+
105
+
106
+ if __name__ == "__main__":
107
+ main()
eval/comprehensive_eval.py CHANGED
@@ -1896,17 +1896,17 @@ class CircuitEvaluator:
1896
 
1897
  def test_manifest(self) -> TestResult:
1898
  """Test manifest metadata tensors."""
1899
- manifest_tensors = [
1900
- ('manifest.alu_operations', 16),
1901
- ('manifest.flags', 4),
1902
- ('manifest.instruction_width', 16),
1903
- ('manifest.memory_bytes', 256),
1904
- ('manifest.pc_width', 8),
1905
- ('manifest.register_width', 8),
1906
- ('manifest.registers', 4),
1907
- ('manifest.turing_complete', 1),
1908
- ('manifest.version', 1),
1909
- ]
1910
 
1911
  failures = []
1912
  passed = 0
@@ -2182,9 +2182,9 @@ class CircuitEvaluator:
2182
 
2183
  return TestResult('control.negated_conditional_jumps', passed, passed, [])
2184
 
2185
- def test_control_parity_jumps(self) -> TestResult:
2186
- """Test parity-based conditional jumps (jp, jnp - jump positive/not positive)."""
2187
- passed = 0
2188
 
2189
  for jump_type in ['jp', 'jnp', 'jpe', 'jpo']: # parity even/odd variants
2190
  for bit in range(8):
@@ -2193,9 +2193,161 @@ class CircuitEvaluator:
2193
  self.reg.get(f'control.{jump_type}.bit{bit}.{comp}.weight')
2194
  self.reg.get(f'control.{jump_type}.bit{bit}.{comp}.bias')
2195
  passed += 2
2196
-
2197
- return TestResult('control.parity_jumps', passed, passed, [])
2198
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2199
  # =========================================================================
2200
  # ARITHMETIC - ADDITIONAL CIRCUITS
2201
  # =========================================================================
@@ -2850,14 +3002,22 @@ class ComprehensiveEvaluator:
2850
  self._run_test(self.evaluator.test_control_register_mux, verbose)
2851
  self._run_test(self.evaluator.test_control_halt, verbose)
2852
  self._run_test(self.evaluator.test_control_pc_load, verbose)
2853
- self._run_test(self.evaluator.test_control_nop, verbose)
2854
- self._run_test(self.evaluator.test_control_conditional_jumps, verbose)
2855
- self._run_test(self.evaluator.test_control_negated_conditional_jumps, verbose)
2856
- self._run_test(self.evaluator.test_control_parity_jumps, verbose)
2857
-
2858
- # Error detection
2859
- if verbose:
2860
- print("\n=== ERROR DETECTION ===")
 
 
 
 
 
 
 
 
2861
  self._run_test(self.evaluator.test_even_parity, verbose)
2862
  self._run_test(self.evaluator.test_odd_parity, verbose)
2863
  self._run_test(self.evaluator.test_checksum_8bit, verbose)
 
1896
 
1897
  def test_manifest(self) -> TestResult:
1898
  """Test manifest metadata tensors."""
1899
+ manifest_tensors = [
1900
+ ('manifest.alu_operations', 16),
1901
+ ('manifest.flags', 4),
1902
+ ('manifest.instruction_width', 16),
1903
+ ('manifest.memory_bytes', 256),
1904
+ ('manifest.pc_width', 8),
1905
+ ('manifest.register_width', 8),
1906
+ ('manifest.registers', 4),
1907
+ ('manifest.turing_complete', 1),
1908
+ ('manifest.version', 2),
1909
+ ]
1910
 
1911
  failures = []
1912
  passed = 0
 
2182
 
2183
  return TestResult('control.negated_conditional_jumps', passed, passed, [])
2184
 
2185
+ def test_control_parity_jumps(self) -> TestResult:
2186
+ """Test parity-based conditional jumps (jp, jnp - jump positive/not positive)."""
2187
+ passed = 0
2188
 
2189
  for jump_type in ['jp', 'jnp', 'jpe', 'jpo']: # parity even/odd variants
2190
  for bit in range(8):
 
2193
  self.reg.get(f'control.{jump_type}.bit{bit}.{comp}.weight')
2194
  self.reg.get(f'control.{jump_type}.bit{bit}.{comp}.bias')
2195
  passed += 2
2196
+
2197
+ return TestResult('control.parity_jumps', passed, passed, [])
2198
+
2199
+ # =========================================================================
2200
+ # MEMORY CIRCUITS
2201
+ # =========================================================================
2202
+
2203
+ def test_memory_decoder_8to256(self) -> TestResult:
2204
+ """Test 8-to-256 address decoder exhaustively."""
2205
+ failures = []
2206
+ passed = 0
2207
+ total = 256 * 256
2208
+
2209
+ for addr in range(256):
2210
+ addr_bits = torch.tensor([(addr >> (7 - i)) & 1 for i in range(8)],
2211
+ device=self.device, dtype=torch.float32)
2212
+
2213
+ for out_idx in range(256):
2214
+ w = self.reg.get(f'memory.addr_decode.out{out_idx}.weight')
2215
+ b = self.reg.get(f'memory.addr_decode.out{out_idx}.bias')
2216
+ output = heaviside((addr_bits * w).sum() + b).item()
2217
+ expected = 1.0 if out_idx == addr else 0.0
2218
+
2219
+ if output == expected:
2220
+ passed += 1
2221
+ elif len(failures) < 20:
2222
+ failures.append(((addr, out_idx), expected, output))
2223
+
2224
+ return TestResult('memory.addr_decode', passed, total, failures)
2225
+
2226
+ def test_memory_read_mux(self) -> TestResult:
2227
+ """Test 256-byte memory read mux for a few representative addresses."""
2228
+ failures = []
2229
+ passed = 0
2230
+ total = 0
2231
+
2232
+ mem = [(addr * 37) & 0xFF for addr in range(256)]
2233
+ test_addrs = [0, 1, 2, 127, 255]
2234
+
2235
+ for addr in test_addrs:
2236
+ addr_bits = torch.tensor([(addr >> (7 - i)) & 1 for i in range(8)],
2237
+ device=self.device, dtype=torch.float32)
2238
+
2239
+ selects = []
2240
+ for out_idx in range(256):
2241
+ w = self.reg.get(f'memory.addr_decode.out{out_idx}.weight')
2242
+ b = self.reg.get(f'memory.addr_decode.out{out_idx}.bias')
2243
+ selects.append(heaviside((addr_bits * w).sum() + b).item())
2244
+
2245
+ for bit in range(8):
2246
+ and_vals = []
2247
+ for out_idx in range(256):
2248
+ mem_bit = float((mem[out_idx] >> (7 - bit)) & 1)
2249
+ inp = torch.tensor([mem_bit, selects[out_idx]], device=self.device)
2250
+ w = self.reg.get(f'memory.read.bit{bit}.and{out_idx}.weight')
2251
+ b = self.reg.get(f'memory.read.bit{bit}.and{out_idx}.bias')
2252
+ and_vals.append(heaviside((inp * w).sum() + b).item())
2253
+
2254
+ or_inp = torch.tensor(and_vals, device=self.device)
2255
+ w_or = self.reg.get(f'memory.read.bit{bit}.or.weight')
2256
+ b_or = self.reg.get(f'memory.read.bit{bit}.or.bias')
2257
+ output = heaviside((or_inp * w_or).sum() + b_or).item()
2258
+ expected = float((mem[addr] >> (7 - bit)) & 1)
2259
+
2260
+ total += 1
2261
+ if output == expected:
2262
+ passed += 1
2263
+ elif len(failures) < 20:
2264
+ failures.append(((addr, bit), expected, output))
2265
+
2266
+ return TestResult('memory.read', passed, total, failures)
2267
+
2268
+ def test_memory_write_cells(self) -> TestResult:
2269
+ """Test memory cell update logic with and without write enable."""
2270
+ failures = []
2271
+ passed = 0
2272
+ total = 0
2273
+
2274
+ mem = [(addr * 13 + 7) & 0xFF for addr in range(256)]
2275
+ test_cases = [
2276
+ (0xA5, 42, 1.0),
2277
+ (0x3C, 200, 0.0),
2278
+ ]
2279
+
2280
+ for write_data, write_addr, write_en in test_cases:
2281
+ addr_bits = torch.tensor([(write_addr >> (7 - i)) & 1 for i in range(8)],
2282
+ device=self.device, dtype=torch.float32)
2283
+
2284
+ decodes = []
2285
+ for out_idx in range(256):
2286
+ w = self.reg.get(f'memory.addr_decode.out{out_idx}.weight')
2287
+ b = self.reg.get(f'memory.addr_decode.out{out_idx}.bias')
2288
+ decodes.append(heaviside((addr_bits * w).sum() + b).item())
2289
+
2290
+ for out_idx in range(256):
2291
+ sel_inp = torch.tensor([decodes[out_idx], write_en], device=self.device)
2292
+ w_sel = self.reg.get(f'memory.write.sel.addr{out_idx}.weight')
2293
+ b_sel = self.reg.get(f'memory.write.sel.addr{out_idx}.bias')
2294
+ sel = heaviside((sel_inp * w_sel).sum() + b_sel).item()
2295
+
2296
+ w_nsel = self.reg.get(f'memory.write.nsel.addr{out_idx}.weight')
2297
+ b_nsel = self.reg.get(f'memory.write.nsel.addr{out_idx}.bias')
2298
+ nsel = heaviside(sel * w_nsel + b_nsel).item()
2299
+
2300
+ for bit in range(8):
2301
+ old_bit = float((mem[out_idx] >> (7 - bit)) & 1)
2302
+ data_bit = float((write_data >> (7 - bit)) & 1)
2303
+
2304
+ inp_old = torch.tensor([old_bit, nsel], device=self.device)
2305
+ w_old = self.reg.get(f'memory.write.addr{out_idx}.bit{bit}.and_old.weight')
2306
+ b_old = self.reg.get(f'memory.write.addr{out_idx}.bit{bit}.and_old.bias')
2307
+ and_old = heaviside((inp_old * w_old).sum() + b_old).item()
2308
+
2309
+ inp_new = torch.tensor([data_bit, sel], device=self.device)
2310
+ w_new = self.reg.get(f'memory.write.addr{out_idx}.bit{bit}.and_new.weight')
2311
+ b_new = self.reg.get(f'memory.write.addr{out_idx}.bit{bit}.and_new.bias')
2312
+ and_new = heaviside((inp_new * w_new).sum() + b_new).item()
2313
+
2314
+ inp_or = torch.tensor([and_old, and_new], device=self.device)
2315
+ w_or = self.reg.get(f'memory.write.addr{out_idx}.bit{bit}.or.weight')
2316
+ b_or = self.reg.get(f'memory.write.addr{out_idx}.bit{bit}.or.bias')
2317
+ output = heaviside((inp_or * w_or).sum() + b_or).item()
2318
+
2319
+ expected = data_bit if (write_en == 1.0 and out_idx == write_addr) else old_bit
2320
+
2321
+ total += 1
2322
+ if output == expected:
2323
+ passed += 1
2324
+ elif len(failures) < 20:
2325
+ failures.append(((out_idx, bit), expected, output))
2326
+
2327
+ return TestResult('memory.write', passed, total, failures)
2328
+
2329
+ def test_control_fetch_load_store(self) -> TestResult:
2330
+ """Test fetch/load/store buffer gate existence."""
2331
+ passed = 0
2332
+ total = 0
2333
+
2334
+ for bit in range(16):
2335
+ total += 2
2336
+ if self.reg.has(f'control.fetch.ir.bit{bit}.weight'):
2337
+ self.reg.get(f'control.fetch.ir.bit{bit}.weight')
2338
+ self.reg.get(f'control.fetch.ir.bit{bit}.bias')
2339
+ passed += 2
2340
+
2341
+ for bit in range(8):
2342
+ for name in ['control.load', 'control.store', 'control.mem_addr']:
2343
+ total += 2
2344
+ if self.reg.has(f'{name}.bit{bit}.weight'):
2345
+ self.reg.get(f'{name}.bit{bit}.weight')
2346
+ self.reg.get(f'{name}.bit{bit}.bias')
2347
+ passed += 2
2348
+
2349
+ return TestResult('control.fetch_load_store', passed, total, [])
2350
+
2351
  # =========================================================================
2352
  # ARITHMETIC - ADDITIONAL CIRCUITS
2353
  # =========================================================================
 
3002
  self._run_test(self.evaluator.test_control_register_mux, verbose)
3003
  self._run_test(self.evaluator.test_control_halt, verbose)
3004
  self._run_test(self.evaluator.test_control_pc_load, verbose)
3005
+ self._run_test(self.evaluator.test_control_nop, verbose)
3006
+ self._run_test(self.evaluator.test_control_conditional_jumps, verbose)
3007
+ self._run_test(self.evaluator.test_control_negated_conditional_jumps, verbose)
3008
+ self._run_test(self.evaluator.test_control_parity_jumps, verbose)
3009
+
3010
+ # Memory
3011
+ if verbose:
3012
+ print("\n=== MEMORY ===")
3013
+ self._run_test(self.evaluator.test_memory_decoder_8to256, verbose)
3014
+ self._run_test(self.evaluator.test_memory_read_mux, verbose)
3015
+ self._run_test(self.evaluator.test_memory_write_cells, verbose)
3016
+ self._run_test(self.evaluator.test_control_fetch_load_store, verbose)
3017
+
3018
+ # Error detection
3019
+ if verbose:
3020
+ print("\n=== ERROR DETECTION ===")
3021
  self._run_test(self.evaluator.test_even_parity, verbose)
3022
  self._run_test(self.evaluator.test_odd_parity, verbose)
3023
  self._run_test(self.evaluator.test_checksum_8bit, verbose)
eval/cpu_cycle_test.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Basic CPU cycle smoke test.
3
+ """
4
+
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ sys.path.append(str(Path(__file__).resolve().parent.parent))
9
+
10
+ from cpu.cycle import run_until_halt
11
+ from cpu.state import CPUState
12
+
13
+
14
+ def encode(opcode: int, rd: int, rs: int, imm8: int) -> int:
15
+ return ((opcode & 0xF) << 12) | ((rd & 0x3) << 10) | ((rs & 0x3) << 8) | (imm8 & 0xFF)
16
+
17
+
18
+ def write_instr(mem, addr, instr):
19
+ mem[addr & 0xFF] = (instr >> 8) & 0xFF
20
+ mem[(addr + 1) & 0xFF] = instr & 0xFF
21
+
22
+
23
+ def main() -> None:
24
+ mem = [0] * 256
25
+
26
+ write_instr(mem, 0x00, encode(0xA, 0, 0, 0x10)) # LOAD R0, [0x10]
27
+ write_instr(mem, 0x02, encode(0xA, 1, 0, 0x11)) # LOAD R1, [0x11]
28
+ write_instr(mem, 0x04, encode(0x0, 0, 1, 0x00)) # ADD R0, R1
29
+ write_instr(mem, 0x06, encode(0xB, 0, 0, 0x12)) # STORE R0 -> [0x12]
30
+ write_instr(mem, 0x08, encode(0xF, 0, 0, 0x00)) # HALT
31
+
32
+ mem[0x10] = 5
33
+ mem[0x11] = 7
34
+
35
+ state = CPUState(
36
+ pc=0,
37
+ ir=0,
38
+ regs=[0, 0, 0, 0],
39
+ flags=[0, 0, 0, 0],
40
+ sp=0xFF,
41
+ ctrl=[0, 0, 0, 0],
42
+ mem=mem,
43
+ )
44
+
45
+ final, cycles = run_until_halt(state, max_cycles=20)
46
+
47
+ assert final.ctrl[0] == 1, "HALT flag not set"
48
+ assert final.regs[0] == 12, f"R0 expected 12, got {final.regs[0]}"
49
+ assert final.mem[0x12] == 12, f"MEM[0x12] expected 12, got {final.mem[0x12]}"
50
+ assert cycles <= 10, f"Unexpected cycle count: {cycles}"
51
+
52
+ print("cpu_cycle_test: ok")
53
+
54
+
55
+ if __name__ == "__main__":
56
+ main()
eval/iron_eval.py CHANGED
@@ -3154,17 +3154,17 @@ class BatchedFitnessEvaluator:
3154
  scores = torch.zeros(pop_size, device=self.device)
3155
  total_tests = 0
3156
 
3157
- manifest_tensors = [
3158
- ('manifest.alu_operations', 16),
3159
- ('manifest.flags', 4),
3160
- ('manifest.instruction_width', 16),
3161
- ('manifest.memory_bytes', 256),
3162
- ('manifest.pc_width', 8),
3163
- ('manifest.register_width', 8),
3164
- ('manifest.registers', 4),
3165
- ('manifest.turing_complete', 1),
3166
- ('manifest.version', 1),
3167
- ]
3168
 
3169
  for tensor_name, expected_value in manifest_tensors:
3170
  w = pop[tensor_name].view(pop_size, -1)
 
3154
  scores = torch.zeros(pop_size, device=self.device)
3155
  total_tests = 0
3156
 
3157
+ manifest_tensors = [
3158
+ ('manifest.alu_operations', 16),
3159
+ ('manifest.flags', 4),
3160
+ ('manifest.instruction_width', 16),
3161
+ ('manifest.memory_bytes', 256),
3162
+ ('manifest.pc_width', 8),
3163
+ ('manifest.register_width', 8),
3164
+ ('manifest.registers', 4),
3165
+ ('manifest.turing_complete', 1),
3166
+ ('manifest.version', 2),
3167
+ ]
3168
 
3169
  for tensor_name, expected_value in manifest_tensors:
3170
  w = pop[tensor_name].view(pop_size, -1)
llm/guide.md CHANGED
@@ -102,7 +102,7 @@ Total circuit depth: ~32 threshold layers (8 FAs × 4 layers each).
102
 
103
  ## 3. Circuit Inventory
104
 
105
- The `neural_computer.safetensors` contains 3,122 tensors / 5,648 parameters implementing:
106
 
107
  | Category | Circuits | Tensors |
108
  |----------|----------|---------|
@@ -459,7 +459,7 @@ Circuit computation adds ~5-10% overhead:
459
  - BitInjector: 1 linear layer (16→960)
460
  - Router: 2 linear layers
461
 
462
- The circuits have only 5,648 parameters total—negligible versus the 361M in the base model.
463
 
464
  ### 9.3 Generalization
465
 
@@ -547,7 +547,7 @@ Model: [extracts 127, 128] [routes to circuit] [gets 255]
547
 
548
  ```
549
  8bit-threshold-computer/
550
- ├── neural_computer.safetensors # Frozen circuits (3,122 tensors)
551
  ├── circuit_llm.py # Integration architecture
552
  ├── train_circuit_interface.py # Training loop
553
  ├── iron_eval.py # Circuit verification (6,590 tests)
 
102
 
103
  ## 3. Circuit Inventory
104
 
105
+ The `neural_computer.safetensors` contains 24,200 tensors / 40,323 parameters implementing:
106
 
107
  | Category | Circuits | Tensors |
108
  |----------|----------|---------|
 
459
  - BitInjector: 1 linear layer (16→960)
460
  - Router: 2 linear layers
461
 
462
+ The circuits have only 40,323 parameters total—negligible versus the 361M in the base model.
463
 
464
  ### 9.3 Generalization
465
 
 
547
 
548
  ```
549
  8bit-threshold-computer/
550
+ ├── neural_computer.safetensors # Frozen circuits (24,200 tensors)
551
  ├── circuit_llm.py # Integration architecture
552
  ├── train_circuit_interface.py # Training loop
553
  ├── iron_eval.py # Circuit verification (6,590 tests)
neural_computer.safetensors CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:6c5e11ab199ce8548681595ff4a417a6634f62c2189749ef0d6a4cdb4a384a59
3
- size 683804
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9c40d35edb9ed7d37c0454b3aacde3d8effc68dfbc707b68ae1feb585836581f
3
+ size 2525316
routing.json ADDED
The diff for this file is too large to render. See raw diff
 
routing/generate_routing.py CHANGED
@@ -417,19 +417,19 @@ def generate_pattern_routing():
417
  return routing
418
 
419
 
420
- def generate_manifest_routing():
421
- """Generate routing for manifest (constants, no actual routing)."""
422
- return {
423
- 'manifest.alu_operations': {'type': 'constant', 'value': 16},
424
- 'manifest.flags': {'type': 'constant', 'value': 4},
425
- 'manifest.instruction_width': {'type': 'constant', 'value': 16},
426
- 'manifest.memory_bytes': {'type': 'constant', 'value': 256},
427
- 'manifest.pc_width': {'type': 'constant', 'value': 8},
428
- 'manifest.register_width': {'type': 'constant', 'value': 8},
429
- 'manifest.registers': {'type': 'constant', 'value': 4},
430
- 'manifest.turing_complete': {'type': 'constant', 'value': 1},
431
- 'manifest.version': {'type': 'constant', 'value': 1}
432
- }
433
 
434
 
435
  def generate_multiplier8x8_routing():
@@ -870,9 +870,9 @@ def generate_combinational_routing():
870
  return routing
871
 
872
 
873
- def generate_control_routing():
874
- """Generate routing for control circuits."""
875
- routing = {}
876
 
877
  # Instruction Decoder (4-bit to 16 one-hot)
878
  internal_dec = {}
@@ -1004,18 +1004,99 @@ def generate_control_routing():
1004
  for flag in ['flag_c', 'flag_n', 'flag_v', 'flag_z']:
1005
  internal_nop[flag] = [f'${flag}']
1006
 
1007
- routing['control.nop'] = {
1008
- 'inputs': ['$x[0:7]', '$flag_c', '$flag_n', '$flag_v', '$flag_z'],
1009
- 'type': 'nop',
1010
- 'internal': internal_nop
1011
- }
1012
-
1013
- return routing
1014
-
1015
-
1016
- def generate_error_detection_routing():
1017
- """Generate routing for error detection circuits."""
1018
- routing = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1019
 
1020
  # Even Parity Checker
1021
  routing['error_detection.evenparitychecker'] = {
@@ -1338,14 +1419,38 @@ def main():
1338
  print(' Negation')
1339
  routing['circuits'].update(generate_neg8bit_routing())
1340
 
1341
- print(' 2x2 Multiplier')
1342
- routing['circuits'].update(generate_multiplier2x2_routing())
1343
-
1344
- print(' 8-bit Division')
1345
- routing['circuits'].update(generate_div8bit_routing())
1346
-
1347
- print(' Threshold gates')
1348
- routing['circuits'].update(generate_threshold_routing())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1349
 
1350
  print(' Modular arithmetic')
1351
  routing['circuits'].update(generate_modular_routing())
 
417
  return routing
418
 
419
 
420
+ def generate_manifest_routing():
421
+ """Generate routing for manifest (constants, no actual routing)."""
422
+ return {
423
+ 'manifest.alu_operations': {'type': 'constant', 'value': 16},
424
+ 'manifest.flags': {'type': 'constant', 'value': 4},
425
+ 'manifest.instruction_width': {'type': 'constant', 'value': 16},
426
+ 'manifest.memory_bytes': {'type': 'constant', 'value': 256},
427
+ 'manifest.pc_width': {'type': 'constant', 'value': 8},
428
+ 'manifest.register_width': {'type': 'constant', 'value': 8},
429
+ 'manifest.registers': {'type': 'constant', 'value': 4},
430
+ 'manifest.turing_complete': {'type': 'constant', 'value': 1},
431
+ 'manifest.version': {'type': 'constant', 'value': 2}
432
+ }
433
 
434
 
435
  def generate_multiplier8x8_routing():
 
870
  return routing
871
 
872
 
873
+ def generate_control_routing():
874
+ """Generate routing for control circuits."""
875
+ routing = {}
876
 
877
  # Instruction Decoder (4-bit to 16 one-hot)
878
  internal_dec = {}
 
1004
  for flag in ['flag_c', 'flag_n', 'flag_v', 'flag_z']:
1005
  internal_nop[flag] = [f'${flag}']
1006
 
1007
+ routing['control.nop'] = {
1008
+ 'inputs': ['$x[0:7]', '$flag_c', '$flag_n', '$flag_v', '$flag_z'],
1009
+ 'type': 'nop',
1010
+ 'internal': internal_nop
1011
+ }
1012
+
1013
+ # Fetch/load/store buffer gates
1014
+ internal_fetch = {f'bit{bit}': [f'$data[{bit}]'] for bit in range(16)}
1015
+ routing['control.fetch.ir'] = {
1016
+ 'inputs': ['$data[0:15]'],
1017
+ 'type': 'buffer',
1018
+ 'internal': internal_fetch
1019
+ }
1020
+
1021
+ internal_load = {f'bit{bit}': [f'$data[{bit}]'] for bit in range(8)}
1022
+ routing['control.load'] = {
1023
+ 'inputs': ['$data[0:7]'],
1024
+ 'type': 'buffer',
1025
+ 'internal': internal_load
1026
+ }
1027
+
1028
+ internal_store = {f'bit{bit}': [f'$data[{bit}]'] for bit in range(8)}
1029
+ routing['control.store'] = {
1030
+ 'inputs': ['$data[0:7]'],
1031
+ 'type': 'buffer',
1032
+ 'internal': internal_store
1033
+ }
1034
+
1035
+ internal_mem_addr = {f'bit{bit}': [f'$addr[{bit}]'] for bit in range(8)}
1036
+ routing['control.mem_addr'] = {
1037
+ 'inputs': ['$addr[0:7]'],
1038
+ 'type': 'buffer',
1039
+ 'internal': internal_mem_addr
1040
+ }
1041
+
1042
+ return routing
1043
+
1044
+
1045
+ def generate_memory_routing():
1046
+ """Generate routing for memory decoder, read mux, and write cell update."""
1047
+ routing = {}
1048
+
1049
+ addr_bits = [f'$addr[{i}]' for i in range(8)]
1050
+ internal_dec = {f'out{addr}': addr_bits for addr in range(256)}
1051
+ routing['memory.addr_decode'] = {
1052
+ 'inputs': ['$addr[0:7]'],
1053
+ 'type': 'decoder',
1054
+ 'internal': internal_dec
1055
+ }
1056
+
1057
+ internal_read = {}
1058
+ for bit in range(8):
1059
+ for addr in range(256):
1060
+ internal_read[f'bit{bit}.and{addr}'] = [f'$mem[{addr}][{bit}]', f'$sel[{addr}]']
1061
+ internal_read[f'bit{bit}.or'] = [f'bit{bit}.and{i}' for i in range(256)]
1062
+
1063
+ routing['memory.read'] = {
1064
+ 'inputs': ['$mem[0:255][0:7]', '$sel[0:255]'],
1065
+ 'type': 'read_mux',
1066
+ 'internal': internal_read,
1067
+ 'outputs': {f'bit{bit}': f'bit{bit}.or' for bit in range(8)}
1068
+ }
1069
+
1070
+ internal_write = {}
1071
+ for addr in range(256):
1072
+ internal_write[f'sel.addr{addr}'] = [f'$sel[{addr}]', '$we']
1073
+ internal_write[f'nsel.addr{addr}'] = [f'sel.addr{addr}']
1074
+ for bit in range(8):
1075
+ internal_write[f'addr{addr}.bit{bit}.and_old'] = [f'$mem[{addr}][{bit}]', f'nsel.addr{addr}']
1076
+ internal_write[f'addr{addr}.bit{bit}.and_new'] = [f'$write_data[{bit}]', f'sel.addr{addr}']
1077
+ internal_write[f'addr{addr}.bit{bit}.or'] = [
1078
+ f'addr{addr}.bit{bit}.and_old',
1079
+ f'addr{addr}.bit{bit}.and_new'
1080
+ ]
1081
+
1082
+ outputs = {
1083
+ f'mem[{addr}][{bit}]': f'addr{addr}.bit{bit}.or'
1084
+ for addr in range(256) for bit in range(8)
1085
+ }
1086
+
1087
+ routing['memory.write'] = {
1088
+ 'inputs': ['$mem[0:255][0:7]', '$write_data[0:7]', '$sel[0:255]', '$we'],
1089
+ 'type': 'write_mux',
1090
+ 'internal': internal_write,
1091
+ 'outputs': outputs
1092
+ }
1093
+
1094
+ return routing
1095
+
1096
+
1097
+ def generate_error_detection_routing():
1098
+ """Generate routing for error detection circuits."""
1099
+ routing = {}
1100
 
1101
  # Even Parity Checker
1102
  routing['error_detection.evenparitychecker'] = {
 
1419
  print(' Negation')
1420
  routing['circuits'].update(generate_neg8bit_routing())
1421
 
1422
+ print(' 2x2 Multiplier')
1423
+ routing['circuits'].update(generate_multiplier2x2_routing())
1424
+
1425
+ print(' 8x8 Multiplier')
1426
+ routing['circuits'].update(generate_multiplier8x8_routing())
1427
+
1428
+ print(' 8-bit Division')
1429
+ routing['circuits'].update(generate_div8bit_routing())
1430
+
1431
+ print(' ADC/SBC')
1432
+ routing['circuits'].update(generate_adc_sbc_routing())
1433
+
1434
+ print(' Rotate')
1435
+ routing['circuits'].update(generate_rotate_routing())
1436
+
1437
+ print(' Combinational')
1438
+ routing['circuits'].update(generate_combinational_routing())
1439
+
1440
+ print(' Control')
1441
+ routing['circuits'].update(generate_control_routing())
1442
+
1443
+ print(' Memory')
1444
+ routing['circuits'].update(generate_memory_routing())
1445
+
1446
+ print(' Error detection')
1447
+ routing['circuits'].update(generate_error_detection_routing())
1448
+
1449
+ print(' ALU')
1450
+ routing['circuits'].update(generate_alu_routing())
1451
+
1452
+ print(' Threshold gates')
1453
+ routing['circuits'].update(generate_threshold_routing())
1454
 
1455
  print(' Modular arithmetic')
1456
  routing['circuits'].update(generate_modular_routing())
routing/routing_schema.md CHANGED
@@ -32,8 +32,11 @@ The routing file (`routing.json`) defines how gates are interconnected. Each ent
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
  ## Circuit Types
39
 
 
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
  ## Circuit Types
42
 
tensors.txt CHANGED
The diff for this file is too large to render. See raw diff
 
todo.md CHANGED
@@ -151,7 +151,7 @@ The machine runs. Callers just provide initial state and collect results.
151
  - Comparators, threshold gates
152
  - Conditional jumps
153
 
154
- **Current: 6,184 tensors**
155
  **Projected: ~1.65M tensors (with 64KB memory)**
156
 
157
  ## Applications
 
151
  - Comparators, threshold gates
152
  - Conditional jumps
153
 
154
+ **Current: 24,200 tensors**
155
  **Projected: ~1.65M tensors (with 64KB memory)**
156
 
157
  ## Applications