CharlesCNorton commited on
Commit ·
b9fb5ce
1
Parent(s): c3e47db
8-bit computer decode and PC sequencing as threshold gates: a 4-to-16 opcode one-hot decoder and a next-PC network (PC+2/PC+4 increment chains plus a priority mux over PC+2, PC+4, the conditional-jump mux, and the direct target). The runtime dispatches on the gate one-hots and takes the next PC from the mux instead of Python slicing and PC+2. Full 10/10 CPU suite passes; all variants rebuilt. Completes decode/PC-as-gates across every runtime (rv32, subleq, 8-bit computer).
Browse files- README.md +6 -5
- neural_computer.safetensors +2 -2
- src/build.py +94 -1
- src/eval_all.py +82 -36
- todo.md +1 -2
- variants/neural_alu32.safetensors +1 -1
- variants/neural_alu8.safetensors +1 -1
- variants/neural_computer16.safetensors +2 -2
- variants/neural_computer16_reduced.safetensors +2 -2
- variants/neural_computer16_registers.safetensors +2 -2
- variants/neural_computer16_scratchpad.safetensors +2 -2
- variants/neural_computer16_small.safetensors +2 -2
- variants/neural_computer32.safetensors +2 -2
- variants/neural_computer32_reduced.safetensors +2 -2
- variants/neural_computer32_registers.safetensors +2 -2
- variants/neural_computer32_scratchpad.safetensors +2 -2
- variants/neural_computer32_small.safetensors +2 -2
- variants/neural_computer8.safetensors +2 -2
- variants/neural_computer8_reduced.safetensors +2 -2
- variants/neural_computer8_registers.safetensors +2 -2
- variants/neural_computer8_scratchpad.safetensors +2 -2
- variants/neural_computer8_small.safetensors +2 -2
README.md
CHANGED
|
@@ -123,9 +123,10 @@ Every datapath operation runs through threshold gates: ALU arithmetic
|
|
| 123 |
(ADD/SUB via ripple-carry, MUL via partial-product AND gates and shift-add,
|
| 124 |
DIV via per-stage bit-cascade comparators), bitwise logic, shifts,
|
| 125 |
comparisons, conditional-jump PC muxing, stack-pointer stepping, and all
|
| 126 |
-
memory reads and writes. Instruction decode (
|
| 127 |
-
|
| 128 |
-
|
|
|
|
| 129 |
|
| 130 |
```
|
| 131 |
┌─────────────────────────────┐
|
|
@@ -383,7 +384,7 @@ The most capable member: a RISC-V CPU whose entire datapath is ternary threshold
|
|
| 383 |
- **Dual issue**: two adjacent OP/OP-IMM/LUI/AUIPC instructions retire in one cycle when the gate-level hazard comparators (`rv32.hazard.*`) clear RAW and WAW dependences.
|
| 384 |
- **MMIO**: stores to `0xFF00` append a character to the console.
|
| 385 |
|
| 386 |
-
Signed comparisons ride the unsigned bit-cascade with sign bits complemented through NOT gates; SLL uses the barrel shifter, SRL is bit-reversal wiring over it, SRA a gate mux over the complement form. Instruction decode
|
| 387 |
|
| 388 |
```bash
|
| 389 |
python src/build.py --apply rv32 # build the file
|
|
@@ -604,7 +605,7 @@ Modern neuromorphic processors implement large arrays of configurable threshold-
|
|
| 604 |
|
| 605 |
Published work on these platforms has focused on neural network inference, sensory processing, and pattern recognition. A 2024 paper demonstrated basic logic gates, adders, and decoders on SpiNNaker and Dynap-SE1 and described that work as "a first step toward the construction of a spiking computer"; that implementation lacked instruction fetch, a program counter, memory, and control logic.
|
| 606 |
|
| 607 |
-
The weights in this repository implement a complete CPU: registers, ALU with 16 operations, status flags, conditional branching, subroutine calls, stack operations, and memory access. Every logic component is a threshold neuron with integer weights;
|
| 608 |
|
| 609 |
---
|
| 610 |
|
|
|
|
| 123 |
(ADD/SUB via ripple-carry, MUL via partial-product AND gates and shift-add,
|
| 124 |
DIV via per-stage bit-cascade comparators), bitwise logic, shifts,
|
| 125 |
comparisons, conditional-jump PC muxing, stack-pointer stepping, and all
|
| 126 |
+
memory reads and writes. Instruction decode (a 4-to-16 opcode one-hot
|
| 127 |
+
decoder) and PC sequencing (the PC+2/PC+4 increment chains and the next-PC
|
| 128 |
+
mux) are also threshold gates now; register-field indexing and the bit
|
| 129 |
+
complements feeding negated-condition selects remain structural routing.
|
| 130 |
|
| 131 |
```
|
| 132 |
┌─────────────────────────────┐
|
|
|
|
| 384 |
- **Dual issue**: two adjacent OP/OP-IMM/LUI/AUIPC instructions retire in one cycle when the gate-level hazard comparators (`rv32.hazard.*`) clear RAW and WAW dependences.
|
| 385 |
- **MMIO**: stores to `0xFF00` append a character to the console.
|
| 386 |
|
| 387 |
+
Signed comparisons ride the unsigned bit-cascade with sign bits complemented through NOT gates; SLL uses the barrel shifter, SRL is bit-reversal wiring over it, SRA a gate mux over the complement form. Instruction decode (opcode-class detectors and the I/S/B/U/J immediate mux) and PC sequencing (the next-PC mux over PC+4, PC+imm, and (rs1+imm)&~1) are threshold gates; register-field indexing remains fixed wiring.
|
| 388 |
|
| 389 |
```bash
|
| 390 |
python src/build.py --apply rv32 # build the file
|
|
|
|
| 605 |
|
| 606 |
Published work on these platforms has focused on neural network inference, sensory processing, and pattern recognition. A 2024 paper demonstrated basic logic gates, adders, and decoders on SpiNNaker and Dynap-SE1 and described that work as "a first step toward the construction of a spiking computer"; that implementation lacked instruction fetch, a program counter, memory, and control logic.
|
| 607 |
|
| 608 |
+
The weights in this repository implement a complete CPU: registers, ALU with 16 operations, status flags, conditional branching, subroutine calls, stack operations, and memory access. Every logic component is a threshold neuron with integer weights; opcode decode and PC sequencing run through threshold gates as well, leaving only register-field indexing as structural wiring.
|
| 609 |
|
| 610 |
---
|
| 611 |
|
neural_computer.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1555742a28aea9f00affacd90ee88b68760e09d9ca82dbeebc57ae746a99f1b8
|
| 3 |
+
size 46044315
|
src/build.py
CHANGED
|
@@ -674,6 +674,38 @@ def add_conditional_jumps(tensors: Dict[str, torch.Tensor], addr_bits: int) -> N
|
|
| 674 |
add_gate(tensors, f"{prefix}.or", [1.0, 1.0], [-1.0])
|
| 675 |
|
| 676 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 677 |
def add_status_flags(tensors: Dict[str, torch.Tensor], data_bits: int) -> None:
|
| 678 |
"""Add status flag computation circuits (Z, N, C, V).
|
| 679 |
|
|
@@ -5472,7 +5504,8 @@ def infer_inputs_for_gate(gate: str, reg: SignalRegistry, tensors: Dict[str, tor
|
|
| 5472 |
if gate.startswith('modular.'):
|
| 5473 |
return infer_modular_inputs(gate, reg)
|
| 5474 |
if gate.startswith('control.'):
|
| 5475 |
-
if
|
|
|
|
| 5476 |
prefix = gate.split('.bit')[0] if '.bit' in gate else gate.rsplit('.', 1)[0]
|
| 5477 |
return infer_control_jump_inputs(gate, prefix, reg)
|
| 5478 |
if any(b in gate for b in ['fetch', 'load', 'store', 'mem_addr']):
|
|
@@ -5506,6 +5539,58 @@ def infer_inputs_for_gate(gate: str, reg: SignalRegistry, tensors: Dict[str, tor
|
|
| 5506 |
bit = int(m.group(1))
|
| 5507 |
return [reg.register(f"$ret_addr[{bit}]")]
|
| 5508 |
return [reg.register(f"$ret_addr[{i}]") for i in range(16)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5509 |
return [reg.register("$ctrl")]
|
| 5510 |
if gate.startswith('memory.'):
|
| 5511 |
return infer_memory_inputs(gate, reg, tensors)
|
|
@@ -5671,6 +5756,14 @@ def cmd_memory(args) -> None:
|
|
| 5671 |
except ValueError as e:
|
| 5672 |
print(f" Conditional jumps already exist: {e}")
|
| 5673 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5674 |
print("\nGenerating status flag circuits...")
|
| 5675 |
try:
|
| 5676 |
add_status_flags(tensors, args.bits)
|
|
|
|
| 674 |
add_gate(tensors, f"{prefix}.or", [1.0, 1.0], [-1.0])
|
| 675 |
|
| 676 |
|
| 677 |
+
def add_computer_decode(tensors: Dict[str, torch.Tensor]) -> None:
|
| 678 |
+
"""Instruction decode for the 8-bit computer as threshold gates: a 4-to-16
|
| 679 |
+
opcode one-hot decoder (exact match on the instruction's opcode nibble). The
|
| 680 |
+
runtime reads these one-hots to dispatch instead of slicing the word."""
|
| 681 |
+
for opc in range(16):
|
| 682 |
+
w = [1.0 if (opc >> j) & 1 else -1.0 for j in range(4)]
|
| 683 |
+
add_gate(tensors, f"control.decode.op{opc}", w, [-float(bin(opc).count("1"))])
|
| 684 |
+
|
| 685 |
+
|
| 686 |
+
def add_computer_pcnext(tensors: Dict[str, torch.Tensor], addr_bits: int) -> None:
|
| 687 |
+
"""PC sequencing for the 8-bit computer as threshold gates: PC+2 / PC+4
|
| 688 |
+
increment chains and a priority mux selecting the next PC among PC+2 (ALU),
|
| 689 |
+
PC+4 (load/store), the conditional-jump mux, and the direct target
|
| 690 |
+
(JMP/CALL). Replaces the runtime's Python PC+2/PC+4 and jump wiring."""
|
| 691 |
+
for name, start in (("pc2", 1), ("pc4", 2)):
|
| 692 |
+
for bit in range(addr_bits):
|
| 693 |
+
if bit < start:
|
| 694 |
+
add_gate(tensors, f"control.pcnext.{name}.bit{bit}", [1.0], [-1.0])
|
| 695 |
+
else:
|
| 696 |
+
add_gate(tensors, f"control.pcnext.{name}.bit{bit}.xor.layer1.or", [1.0, 1.0], [-1.0])
|
| 697 |
+
add_gate(tensors, f"control.pcnext.{name}.bit{bit}.xor.layer1.nand", [-1.0, -1.0], [1.0])
|
| 698 |
+
add_gate(tensors, f"control.pcnext.{name}.bit{bit}.xor.layer2", [1.0, 1.0], [-2.0])
|
| 699 |
+
add_gate(tensors, f"control.pcnext.{name}.bit{bit}.carry", [1.0, 1.0], [-2.0])
|
| 700 |
+
# priority mux: next = use_addr ? addr : (use_jcc ? jcc : (use_pc4 ? pc4 : pc2))
|
| 701 |
+
for lvl in ("m_pc4", "m_jcc", "m_addr"):
|
| 702 |
+
for bit in range(addr_bits):
|
| 703 |
+
add_gate(tensors, f"control.pcnext.{lvl}.bit{bit}.not_sel", [-1.0], [0.0])
|
| 704 |
+
add_gate(tensors, f"control.pcnext.{lvl}.bit{bit}.and_a", [1.0, 1.0], [-2.0])
|
| 705 |
+
add_gate(tensors, f"control.pcnext.{lvl}.bit{bit}.and_b", [1.0, 1.0], [-2.0])
|
| 706 |
+
add_gate(tensors, f"control.pcnext.{lvl}.bit{bit}.or", [1.0, 1.0], [-1.0])
|
| 707 |
+
|
| 708 |
+
|
| 709 |
def add_status_flags(tensors: Dict[str, torch.Tensor], data_bits: int) -> None:
|
| 710 |
"""Add status flag computation circuits (Z, N, C, V).
|
| 711 |
|
|
|
|
| 5504 |
if gate.startswith('modular.'):
|
| 5505 |
return infer_modular_inputs(gate, reg)
|
| 5506 |
if gate.startswith('control.'):
|
| 5507 |
+
if ('pcnext' not in gate and 'decode' not in gate
|
| 5508 |
+
and any(j in gate for j in ['jz', 'jc', 'jn', 'jv', 'jp', 'jnz', 'jnc', 'jnv', 'conditionaljump'])):
|
| 5509 |
prefix = gate.split('.bit')[0] if '.bit' in gate else gate.rsplit('.', 1)[0]
|
| 5510 |
return infer_control_jump_inputs(gate, prefix, reg)
|
| 5511 |
if any(b in gate for b in ['fetch', 'load', 'store', 'mem_addr']):
|
|
|
|
| 5539 |
bit = int(m.group(1))
|
| 5540 |
return [reg.register(f"$ret_addr[{bit}]")]
|
| 5541 |
return [reg.register(f"$ret_addr[{i}]") for i in range(16)]
|
| 5542 |
+
if 'decode.op' in gate:
|
| 5543 |
+
for i in range(4):
|
| 5544 |
+
reg.register(f"$comp_op[{i}]")
|
| 5545 |
+
return [reg.get_id(f"$comp_op[{i}]") for i in range(4)]
|
| 5546 |
+
if 'pcnext.' in gate:
|
| 5547 |
+
for i in range(16):
|
| 5548 |
+
reg.register(f"$comp_pc[{i}]")
|
| 5549 |
+
reg.register(f"$comp_addr[{i}]")
|
| 5550 |
+
reg.register(f"$comp_jcc[{i}]")
|
| 5551 |
+
for nm in ("$comp_use_pc4", "$comp_use_jcc", "$comp_use_addr"):
|
| 5552 |
+
reg.register(nm)
|
| 5553 |
+
|
| 5554 |
+
def _inc_out(name, k):
|
| 5555 |
+
start = 1 if name == "pc2" else 2
|
| 5556 |
+
return reg.register(f"control.pcnext.{name}.bit{k}"
|
| 5557 |
+
if k < start else f"control.pcnext.{name}.bit{k}.xor.layer2")
|
| 5558 |
+
|
| 5559 |
+
mm = re.match(r"^control\.pcnext\.(pc2|pc4)\.bit(\d+)(?:\.(.+))?$", gate)
|
| 5560 |
+
if mm:
|
| 5561 |
+
name, k, sub = mm.group(1), int(mm.group(2)), mm.group(3)
|
| 5562 |
+
start = 1 if name == "pc2" else 2
|
| 5563 |
+
if sub is None:
|
| 5564 |
+
return [reg.get_id(f"$comp_pc[{k}]")]
|
| 5565 |
+
cin = reg.get_id("#1") if k == start else reg.register(f"control.pcnext.{name}.bit{k - 1}.carry")
|
| 5566 |
+
pcb = reg.get_id(f"$comp_pc[{k}]")
|
| 5567 |
+
if sub in ("xor.layer1.or", "xor.layer1.nand", "carry"):
|
| 5568 |
+
return [pcb, cin]
|
| 5569 |
+
if sub == "xor.layer2":
|
| 5570 |
+
return [reg.register(f"control.pcnext.{name}.bit{k}.xor.layer1.or"),
|
| 5571 |
+
reg.register(f"control.pcnext.{name}.bit{k}.xor.layer1.nand")]
|
| 5572 |
+
mm = re.match(r"^control\.pcnext\.(m_pc4|m_jcc|m_addr)\.bit(\d+)\.(not_sel|and_a|and_b|or)$", gate)
|
| 5573 |
+
if mm:
|
| 5574 |
+
lvl, k, kind = mm.group(1), int(mm.group(2)), mm.group(3)
|
| 5575 |
+
sel = {"m_pc4": "$comp_use_pc4", "m_jcc": "$comp_use_jcc", "m_addr": "$comp_use_addr"}[lvl]
|
| 5576 |
+
if kind == "not_sel":
|
| 5577 |
+
return [reg.get_id(sel)]
|
| 5578 |
+
if kind == "and_a":
|
| 5579 |
+
if lvl == "m_pc4":
|
| 5580 |
+
a_src = _inc_out("pc2", k)
|
| 5581 |
+
else:
|
| 5582 |
+
a_src = reg.register(f"control.pcnext.{'m_pc4' if lvl == 'm_jcc' else 'm_jcc'}.bit{k}.or")
|
| 5583 |
+
return [a_src, reg.register(f"control.pcnext.{lvl}.bit{k}.not_sel")]
|
| 5584 |
+
if kind == "and_b":
|
| 5585 |
+
if lvl == "m_pc4":
|
| 5586 |
+
b_src = _inc_out("pc4", k)
|
| 5587 |
+
elif lvl == "m_jcc":
|
| 5588 |
+
b_src = reg.get_id(f"$comp_jcc[{k}]")
|
| 5589 |
+
else:
|
| 5590 |
+
b_src = reg.get_id(f"$comp_addr[{k}]")
|
| 5591 |
+
return [b_src, reg.get_id(sel)]
|
| 5592 |
+
return [reg.register(f"control.pcnext.{lvl}.bit{k}.and_a"),
|
| 5593 |
+
reg.register(f"control.pcnext.{lvl}.bit{k}.and_b")]
|
| 5594 |
return [reg.register("$ctrl")]
|
| 5595 |
if gate.startswith('memory.'):
|
| 5596 |
return infer_memory_inputs(gate, reg, tensors)
|
|
|
|
| 5756 |
except ValueError as e:
|
| 5757 |
print(f" Conditional jumps already exist: {e}")
|
| 5758 |
|
| 5759 |
+
print("\nGenerating instruction-decode and PC-sequencing circuits...")
|
| 5760 |
+
try:
|
| 5761 |
+
add_computer_decode(tensors)
|
| 5762 |
+
add_computer_pcnext(tensors, addr_bits)
|
| 5763 |
+
print(f" Added opcode decoder (4->16) and next-PC mux ({addr_bits}-bit)")
|
| 5764 |
+
except ValueError as e:
|
| 5765 |
+
print(f" Decode/PC circuits already exist: {e}")
|
| 5766 |
+
|
| 5767 |
print("\nGenerating status flag circuits...")
|
| 5768 |
try:
|
| 5769 |
add_status_flags(tensors, args.bits)
|
src/eval_all.py
CHANGED
|
@@ -414,6 +414,52 @@ class GenericThresholdCPU:
|
|
| 414 |
borrow = self.alu._g(f"{bp}.borrow", [float(1 - bits[bit]), float(borrow)])
|
| 415 |
return bits_to_int(out)
|
| 416 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 417 |
def step(self, state):
|
| 418 |
if state["halted"]:
|
| 419 |
return state
|
|
@@ -427,88 +473,88 @@ class GenericThresholdCPU:
|
|
| 427 |
lo = self.mem_read(s["mem"], (pc + 1) & addr_mask)
|
| 428 |
ir = ((hi & 0xFF) << 8) | (lo & 0xFF)
|
| 429 |
opcode = (ir >> 12) & 0xF
|
|
|
|
| 430 |
rd = (ir >> 10) & 0x3
|
| 431 |
rs = (ir >> 8) & 0x3
|
| 432 |
imm = ir & 0xFF
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
addr = (addr_full & addr_mask) if addr_full is not None else None
|
| 441 |
a = s["regs"][rd]
|
| 442 |
b = s["regs"][rs]
|
| 443 |
result = a
|
| 444 |
carry = 0
|
| 445 |
overflow = 0
|
| 446 |
write_result = True
|
| 447 |
-
|
|
|
|
| 448 |
result, carry = self.alu.add8(a, b)
|
| 449 |
overflow = 1 if (((a ^ result) & (b ^ result)) & 0x80) else 0
|
| 450 |
-
elif
|
| 451 |
result, carry = self.alu.sub8(a, b)
|
| 452 |
overflow = 1 if (((a ^ b) & (a ^ result)) & 0x80) else 0
|
| 453 |
-
elif
|
| 454 |
result = self.alu.and8(a, b)
|
| 455 |
-
elif
|
| 456 |
result = self.alu.or8(a, b)
|
| 457 |
-
elif
|
| 458 |
result = self.alu.xor8(a, b)
|
| 459 |
-
elif
|
| 460 |
result = self.alu.shl8(a)
|
| 461 |
-
elif
|
| 462 |
result = self.alu.shr8(a)
|
| 463 |
-
elif
|
| 464 |
result = self.alu.mul8(a, b)
|
| 465 |
-
elif
|
| 466 |
result, _ = self.alu.div8(a, b)
|
| 467 |
-
elif
|
| 468 |
r2, carry = self.alu.sub8(a, b)
|
| 469 |
z = 1 if r2 == 0 else 0
|
| 470 |
n = 1 if (r2 & 0x80) else 0
|
| 471 |
v = 1 if (((a ^ b) & (a ^ r2)) & 0x80) else 0
|
| 472 |
s["flags"] = [z, n, carry, v]
|
| 473 |
write_result = False
|
| 474 |
-
elif
|
| 475 |
result = self.mem_read(s["mem"], addr)
|
| 476 |
-
elif
|
| 477 |
s["mem"] = self.mem_write(s["mem"], addr, b & 0xFF)
|
| 478 |
write_result = False
|
| 479 |
-
elif
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
elif opcode == 0xD:
|
| 483 |
cond = imm & 0x7
|
| 484 |
circuit, flag_idx = self._JCC[cond]
|
| 485 |
flag = s["flags"][flag_idx]
|
| 486 |
sel = flag if cond % 2 == 0 else 1 - flag
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
elif
|
| 490 |
-
ret_addr =
|
| 491 |
sp = s.get("sp", addr_mask)
|
| 492 |
sp = self._sp_dec(sp)
|
| 493 |
s["mem"] = self.mem_write(s["mem"], sp, (ret_addr >> 8) & 0xFF)
|
| 494 |
sp = self._sp_dec(sp)
|
| 495 |
s["mem"] = self.mem_write(s["mem"], sp, ret_addr & 0xFF)
|
| 496 |
s["sp"] = sp
|
| 497 |
-
|
| 498 |
-
|
| 499 |
-
elif opcode == 0xF:
|
| 500 |
s["halted"] = True
|
| 501 |
return s
|
| 502 |
|
| 503 |
-
if write_result
|
| 504 |
s["regs"][rd] = result & 0xFF
|
| 505 |
-
|
| 506 |
-
# Z/N/C/V; MUL clears C and V. All other opcodes leave FLAGS unchanged.
|
| 507 |
-
if opcode in (0x0, 0x1, 0x7):
|
| 508 |
z = 1 if (result & 0xFF) == 0 else 0
|
| 509 |
n = 1 if (result & 0x80) else 0
|
| 510 |
s["flags"] = [z, n, carry, overflow]
|
| 511 |
-
|
|
|
|
|
|
|
|
|
|
| 512 |
return s
|
| 513 |
|
| 514 |
def run(self, state, max_cycles=200):
|
|
|
|
| 414 |
borrow = self.alu._g(f"{bp}.borrow", [float(1 - bits[bit]), float(borrow)])
|
| 415 |
return bits_to_int(out)
|
| 416 |
|
| 417 |
+
def _decode_op(self, opcode):
|
| 418 |
+
"""4-to-16 opcode one-hot through the gate decoder."""
|
| 419 |
+
ob = [float((opcode >> j) & 1) for j in range(4)]
|
| 420 |
+
return [self.alu._g(f"control.decode.op{n}", ob) for n in range(16)]
|
| 421 |
+
|
| 422 |
+
def _inc(self, pc, name, start):
|
| 423 |
+
"""PC + 2^start through the gate increment chain (LSB-first)."""
|
| 424 |
+
ab = self.addr_bits
|
| 425 |
+
pcb = [(pc >> k) & 1 for k in range(ab)]
|
| 426 |
+
out = [0] * ab
|
| 427 |
+
carry = 0
|
| 428 |
+
for k in range(ab):
|
| 429 |
+
if k < start:
|
| 430 |
+
out[k] = self.alu._g(f"control.pcnext.{name}.bit{k}", [float(pcb[k])])
|
| 431 |
+
else:
|
| 432 |
+
cin = 1 if k == start else carry
|
| 433 |
+
o = self.alu._g(f"control.pcnext.{name}.bit{k}.xor.layer1.or", [float(pcb[k]), float(cin)])
|
| 434 |
+
nd = self.alu._g(f"control.pcnext.{name}.bit{k}.xor.layer1.nand", [float(pcb[k]), float(cin)])
|
| 435 |
+
out[k] = self.alu._g(f"control.pcnext.{name}.bit{k}.xor.layer2", [o, nd])
|
| 436 |
+
carry = self.alu._g(f"control.pcnext.{name}.bit{k}.carry", [float(pcb[k]), float(cin)])
|
| 437 |
+
return sum(out[k] << k for k in range(ab))
|
| 438 |
+
|
| 439 |
+
def _pcnext(self, pc2, pc4, addr, jcc, use_pc4, use_addr, use_jcc):
|
| 440 |
+
"""Next-PC priority mux through the gates: use_addr ? addr :
|
| 441 |
+
(use_jcc ? jcc : (use_pc4 ? pc4 : pc2))."""
|
| 442 |
+
ab = self.addr_bits
|
| 443 |
+
p2 = [(pc2 >> k) & 1 for k in range(ab)]
|
| 444 |
+
p4 = [(pc4 >> k) & 1 for k in range(ab)]
|
| 445 |
+
aB = [(addr >> k) & 1 for k in range(ab)]
|
| 446 |
+
jB = [(jcc >> k) & 1 for k in range(ab)]
|
| 447 |
+
out = [0] * ab
|
| 448 |
+
for k in range(ab):
|
| 449 |
+
ns = self.alu._g(f"control.pcnext.m_pc4.bit{k}.not_sel", [float(use_pc4)])
|
| 450 |
+
aa = self.alu._g(f"control.pcnext.m_pc4.bit{k}.and_a", [float(p2[k]), ns])
|
| 451 |
+
bb = self.alu._g(f"control.pcnext.m_pc4.bit{k}.and_b", [float(p4[k]), float(use_pc4)])
|
| 452 |
+
m1 = self.alu._g(f"control.pcnext.m_pc4.bit{k}.or", [aa, bb])
|
| 453 |
+
ns = self.alu._g(f"control.pcnext.m_jcc.bit{k}.not_sel", [float(use_jcc)])
|
| 454 |
+
aa = self.alu._g(f"control.pcnext.m_jcc.bit{k}.and_a", [float(m1), ns])
|
| 455 |
+
bb = self.alu._g(f"control.pcnext.m_jcc.bit{k}.and_b", [float(jB[k]), float(use_jcc)])
|
| 456 |
+
m2 = self.alu._g(f"control.pcnext.m_jcc.bit{k}.or", [aa, bb])
|
| 457 |
+
ns = self.alu._g(f"control.pcnext.m_addr.bit{k}.not_sel", [float(use_addr)])
|
| 458 |
+
aa = self.alu._g(f"control.pcnext.m_addr.bit{k}.and_a", [float(m2), ns])
|
| 459 |
+
bb = self.alu._g(f"control.pcnext.m_addr.bit{k}.and_b", [float(aB[k]), float(use_addr)])
|
| 460 |
+
out[k] = self.alu._g(f"control.pcnext.m_addr.bit{k}.or", [aa, bb])
|
| 461 |
+
return sum(out[k] << k for k in range(ab))
|
| 462 |
+
|
| 463 |
def step(self, state):
|
| 464 |
if state["halted"]:
|
| 465 |
return state
|
|
|
|
| 473 |
lo = self.mem_read(s["mem"], (pc + 1) & addr_mask)
|
| 474 |
ir = ((hi & 0xFF) << 8) | (lo & 0xFF)
|
| 475 |
opcode = (ir >> 12) & 0xF
|
| 476 |
+
oh = self._decode_op(opcode) # gate opcode decode (one-hots)
|
| 477 |
rd = (ir >> 10) & 0x3
|
| 478 |
rs = (ir >> 8) & 0x3
|
| 479 |
imm = ir & 0xFF
|
| 480 |
+
addr = None
|
| 481 |
+
if oh[0xA] or oh[0xB] or oh[0xC] or oh[0xD] or oh[0xE]:
|
| 482 |
+
ah = self.mem_read(s["mem"], (pc + 2) & addr_mask)
|
| 483 |
+
al = self.mem_read(s["mem"], (pc + 3) & addr_mask)
|
| 484 |
+
addr = (((ah & 0xFF) << 8) | (al & 0xFF)) & addr_mask
|
| 485 |
+
pc2 = self._inc(pc, "pc2", 1) # PC+2 through the gate chain
|
| 486 |
+
pc4 = self._inc(pc, "pc4", 2) # PC+4 through the gate chain
|
|
|
|
| 487 |
a = s["regs"][rd]
|
| 488 |
b = s["regs"][rs]
|
| 489 |
result = a
|
| 490 |
carry = 0
|
| 491 |
overflow = 0
|
| 492 |
write_result = True
|
| 493 |
+
jcc_val = 0
|
| 494 |
+
if oh[0x0]:
|
| 495 |
result, carry = self.alu.add8(a, b)
|
| 496 |
overflow = 1 if (((a ^ result) & (b ^ result)) & 0x80) else 0
|
| 497 |
+
elif oh[0x1]:
|
| 498 |
result, carry = self.alu.sub8(a, b)
|
| 499 |
overflow = 1 if (((a ^ b) & (a ^ result)) & 0x80) else 0
|
| 500 |
+
elif oh[0x2]:
|
| 501 |
result = self.alu.and8(a, b)
|
| 502 |
+
elif oh[0x3]:
|
| 503 |
result = self.alu.or8(a, b)
|
| 504 |
+
elif oh[0x4]:
|
| 505 |
result = self.alu.xor8(a, b)
|
| 506 |
+
elif oh[0x5]:
|
| 507 |
result = self.alu.shl8(a)
|
| 508 |
+
elif oh[0x6]:
|
| 509 |
result = self.alu.shr8(a)
|
| 510 |
+
elif oh[0x7]:
|
| 511 |
result = self.alu.mul8(a, b)
|
| 512 |
+
elif oh[0x8]:
|
| 513 |
result, _ = self.alu.div8(a, b)
|
| 514 |
+
elif oh[0x9]: # CMP: set flags only
|
| 515 |
r2, carry = self.alu.sub8(a, b)
|
| 516 |
z = 1 if r2 == 0 else 0
|
| 517 |
n = 1 if (r2 & 0x80) else 0
|
| 518 |
v = 1 if (((a ^ b) & (a ^ r2)) & 0x80) else 0
|
| 519 |
s["flags"] = [z, n, carry, v]
|
| 520 |
write_result = False
|
| 521 |
+
elif oh[0xA]: # LOAD
|
| 522 |
result = self.mem_read(s["mem"], addr)
|
| 523 |
+
elif oh[0xB]: # STORE
|
| 524 |
s["mem"] = self.mem_write(s["mem"], addr, b & 0xFF)
|
| 525 |
write_result = False
|
| 526 |
+
elif oh[0xC]: # JMP (PC = addr via pcnext)
|
| 527 |
+
write_result = False
|
| 528 |
+
elif oh[0xD]: # JCC (PC via jcc mux + pcnext)
|
|
|
|
| 529 |
cond = imm & 0x7
|
| 530 |
circuit, flag_idx = self._JCC[cond]
|
| 531 |
flag = s["flags"][flag_idx]
|
| 532 |
sel = flag if cond % 2 == 0 else 1 - flag
|
| 533 |
+
jcc_val = self._jcc_pc(circuit, pc4, addr, sel)
|
| 534 |
+
write_result = False
|
| 535 |
+
elif oh[0xE]: # CALL: push PC+4, PC = addr
|
| 536 |
+
ret_addr = pc4 & 0xFFFF
|
| 537 |
sp = s.get("sp", addr_mask)
|
| 538 |
sp = self._sp_dec(sp)
|
| 539 |
s["mem"] = self.mem_write(s["mem"], sp, (ret_addr >> 8) & 0xFF)
|
| 540 |
sp = self._sp_dec(sp)
|
| 541 |
s["mem"] = self.mem_write(s["mem"], sp, ret_addr & 0xFF)
|
| 542 |
s["sp"] = sp
|
| 543 |
+
write_result = False
|
| 544 |
+
elif oh[0xF]: # HALT
|
|
|
|
| 545 |
s["halted"] = True
|
| 546 |
return s
|
| 547 |
|
| 548 |
+
if write_result:
|
| 549 |
s["regs"][rd] = result & 0xFF
|
| 550 |
+
if oh[0x0] or oh[0x1] or oh[0x7]:
|
|
|
|
|
|
|
| 551 |
z = 1 if (result & 0xFF) == 0 else 0
|
| 552 |
n = 1 if (result & 0x80) else 0
|
| 553 |
s["flags"] = [z, n, carry, overflow]
|
| 554 |
+
# PC sequencing through the gate mux.
|
| 555 |
+
use_pc4 = 1 if (oh[0xA] or oh[0xB]) else 0
|
| 556 |
+
use_addr = 1 if (oh[0xC] or oh[0xE]) else 0
|
| 557 |
+
s["pc"] = self._pcnext(pc2, pc4, addr or 0, jcc_val, use_pc4, use_addr, oh[0xD])
|
| 558 |
return s
|
| 559 |
|
| 560 |
def run(self, state, max_cycles=200):
|
todo.md
CHANGED
|
@@ -2,5 +2,4 @@
|
|
| 2 |
|
| 3 |
Unfinished work.
|
| 4 |
|
| 5 |
-
1.
|
| 6 |
-
2. A machine-checked correctness proof relates the shipped weights to the ISA.
|
|
|
|
| 2 |
|
| 3 |
Unfinished work.
|
| 4 |
|
| 5 |
+
1. A machine-checked correctness proof relates the shipped weights to the ISA.
|
|
|
variants/neural_alu32.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
size 37387833
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:17dde713d8ab1ccf4c93912d3d3d28ab5361f048cfe16f4a4a3964554b8966f9
|
| 3 |
size 37387833
|
variants/neural_alu8.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
size 37387833
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b7f54f9dbfdf2c713ea97763732c49ab4bcf1aff212f297f2f8ae8924ac48488
|
| 3 |
size 37387833
|
variants/neural_computer16.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bb914df8eeda349bcb483558386f31b35255097c1f89111c813ef2f7982560db
|
| 3 |
+
size 47686556
|
variants/neural_computer16_reduced.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:216855c3b7f0b96aad2ee0e3c9d9ba670027dc3db6a8bd9c726593d0d8c0d1bd
|
| 3 |
+
size 39842806
|
variants/neural_computer16_registers.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3a7eb1245ae20a37b82299f1f1a2e5daa6bd021c47f66dacfb8da26b8600a583
|
| 3 |
+
size 39170726
|
variants/neural_computer16_scratchpad.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:200e170fe0437540e4e34257ee249f2b6cb274d290772ac5374dc73f1738bc1c
|
| 3 |
+
size 39286906
|
variants/neural_computer16_small.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ed8a41811c069f7f15860318cff70a0827f3d4475c822ba3ebe3e678a6b08f67
|
| 3 |
+
size 39422016
|
variants/neural_computer32.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1555742a28aea9f00affacd90ee88b68760e09d9ca82dbeebc57ae746a99f1b8
|
| 3 |
+
size 46044315
|
variants/neural_computer32_reduced.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:44e39d99463ca8e20a9008561cb56d8f9406401fcd4afa54a9ad0330e2ad2ed1
|
| 3 |
+
size 38200743
|
variants/neural_computer32_registers.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0c91523cd10d97b719ec5a6824aa50de6d7fa44f22fa8106731c88a2d6f46507
|
| 3 |
+
size 37529867
|
variants/neural_computer32_scratchpad.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1c9e72848d76b00068b9d9ed0e411e18de72506314e686719b6791586a8bb9f5
|
| 3 |
+
size 37645803
|
variants/neural_computer32_small.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8a3ba9c1bfe8d1abca140748a7a86183d88eeaf0b72f41b5df234f361e5a44a8
|
| 3 |
+
size 37780985
|
variants/neural_computer8.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:212bd531fde5b5696d2845c57b0344c4ee08ac677b938eef9182a0bf8b93e6df
|
| 3 |
+
size 46015407
|
variants/neural_computer8_reduced.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c890abf1ed131efc4b25215ab4910e098b6f35e5d9017af157e731f72cd32a16
|
| 3 |
+
size 38171807
|
variants/neural_computer8_registers.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:73ebf0222baa86bc298055a350122f9ba32b7851c5489f3585b38f4c98173d38
|
| 3 |
+
size 37500943
|
variants/neural_computer8_scratchpad.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:88885bebf8fca7dddf3f99c38664103a191b3f9dcdea96ea99a5246c902a28b5
|
| 3 |
+
size 37616843
|
variants/neural_computer8_small.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b86d9f3b95378390bdb9bf7ec967d18fc0d097560e9ab525292d7961769868cf
|
| 3 |
+
size 37752037
|