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 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 (bit-field selection), PC
127
- sequencing, and the bit complements feeding negated-condition selects are
128
- fixed wiring in the runtimes structural routing, not gates.
 
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, immediate extraction, register-file indexing, and PC sequencing are fixed wiring, per the family convention.
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; instruction decode and PC sequencing are fixed wiring between them.
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:9257c5519edac4fb5f76724389d6950590c83b8fa6e5a5e48bb2eda41e208c08
3
- size 45921927
 
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 any(j in gate for j in ['jz', 'jc', 'jn', 'jv', 'jp', 'jnz', 'jnc', 'jnv', 'conditionaljump']):
 
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
- next_pc = (pc + 2) & addr_mask
434
- addr_full = None
435
- if opcode in (0xA, 0xB, 0xC, 0xD, 0xE):
436
- ah = self.mem_read(s["mem"], next_pc)
437
- al = self.mem_read(s["mem"], (next_pc + 1) & addr_mask)
438
- addr_full = ((ah & 0xFF) << 8) | (al & 0xFF)
439
- next_pc = (next_pc + 2) & addr_mask
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
- if opcode == 0x0:
 
448
  result, carry = self.alu.add8(a, b)
449
  overflow = 1 if (((a ^ result) & (b ^ result)) & 0x80) else 0
450
- elif opcode == 0x1:
451
  result, carry = self.alu.sub8(a, b)
452
  overflow = 1 if (((a ^ b) & (a ^ result)) & 0x80) else 0
453
- elif opcode == 0x2: # AND
454
  result = self.alu.and8(a, b)
455
- elif opcode == 0x3: # OR
456
  result = self.alu.or8(a, b)
457
- elif opcode == 0x4: # XOR
458
  result = self.alu.xor8(a, b)
459
- elif opcode == 0x5: # SHL by 1 (8-bit)
460
  result = self.alu.shl8(a)
461
- elif opcode == 0x6: # SHR by 1
462
  result = self.alu.shr8(a)
463
- elif opcode == 0x7:
464
  result = self.alu.mul8(a, b)
465
- elif opcode == 0x8: # DIV (sets R[d] = R[d] / R[s]; 0xFF on divide by zero)
466
  result, _ = self.alu.div8(a, b)
467
- elif opcode == 0x9:
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 opcode == 0xA:
475
  result = self.mem_read(s["mem"], addr)
476
- elif opcode == 0xB:
477
  s["mem"] = self.mem_write(s["mem"], addr, b & 0xFF)
478
  write_result = False
479
- elif opcode == 0xC:
480
- s["pc"] = addr
481
- return s
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
- s["pc"] = self._jcc_pc(circuit, next_pc, addr, sel)
488
- return s
489
- elif opcode == 0xE: # CALL: push return address (next_pc), set PC = addr
490
- ret_addr = next_pc & 0xFFFF
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
- s["pc"] = addr
498
- return s
499
- elif opcode == 0xF:
500
  s["halted"] = True
501
  return s
502
 
503
- if write_result and opcode != 0x9:
504
  s["regs"][rd] = result & 0xFF
505
- # Flag policy: ADD, SUB, MUL (and CMP, handled in its branch) write
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
- s["pc"] = next_pc
 
 
 
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. Program-counter sequencing and instruction decode are computed by threshold gates rather than by fixed wiring in the runtimes.
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:1dc11ac5b06736a2d16a2904d1ed64dcc719960033f49c5caa50d033f22a7710
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:ad56db6b70e3c49a5b6f475f990190a30505ac2658d61a5bb2e5d14ed03f943c
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:3879d0b35eba828e26eba8195984c36220467411955a0256b2254459b30c1714
3
- size 47563034
 
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:40ac54ccac39c0c5beb9ed5d2f56a3caa70cd5dc6632505d7e42e2b9f5191370
3
- size 39749446
 
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:ace072b14bd6724d564e7411043aa009ca739dde4c78f17b9728d3fbe0f7c09a
3
- size 39136706
 
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:bc65c083aa1f822bc7493e2c695919db716589570f602e8d099b6213a37bcf05
3
- size 39224462
 
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:204dc61509b20cbfd5ff4f7eaa722df30b6d3cc90a60bfa6a210dc9b0e39edaf
3
- size 39344766
 
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:9257c5519edac4fb5f76724389d6950590c83b8fa6e5a5e48bb2eda41e208c08
3
- size 45921927
 
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:612b7246735ac6c27d7b39b49a8c884230d5824219995e03712009fa546c6f1f
3
- size 38108343
 
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:cf7e8aa2593a4dd87ba61bba28ef88d5f587188882d61a75bc143a2a8f54ce88
3
- size 37497223
 
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:b6bb2bea3bde7ac5912c81d4ca32acd82b42bf146aac7089b0d9816bda7ae3a3
3
- size 37583499
 
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:b188500ed25064a13079b09a2430c75863eb5283ea31cdea17933422e1b83228
3
- size 37703667
 
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:0b7aad3aa9c64dbebd8022299c3b64ed561cfb00321ce586a30c40df4ac84aa4
3
- size 45892907
 
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:1387f4646dc73723e3fed45810901242e5c61fb8eea52b10995e037b0fcad11c
3
- size 38079415
 
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:35e4667440cd56046f1709bda0ed4ddfa24e8df6f0df448fc3ddb0ec18c280e3
3
- size 37468231
 
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:426fe58eeb1022aebcac43203ee8e5484ae4a3dba9ed820faacd1a23a608bf37
3
- size 37554631
 
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:631606d3d79f09abf7fad511c68b55bfeeb908348da7309680ef4a593c07c9a3
3
- size 37674799
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b86d9f3b95378390bdb9bf7ec967d18fc0d097560e9ab525292d7961769868cf
3
+ size 37752037