CharlesCNorton commited on
Commit ·
c3e47db
1
Parent(s): 886dfed
rv32 decode and PC sequencing as threshold gates: an opcode-class detector network (exact 7-bit match per class), sign-extended immediate generation muxed over the I/S/B/U/J formats, and a next-PC mux (PC+4 / PC+imm / (rs1+imm)&~1). The threshold CPU reads these gate outputs for dispatch, immediates, and the next PC instead of slicing the instruction word in Python; verified in isolation and by the full RV32 lockstep suite plus randomized programs.
Browse files- src/build.py +124 -0
- src/machines.py +60 -35
- variants/neural_rv32.safetensors +2 -2
src/build.py
CHANGED
|
@@ -5166,6 +5166,48 @@ def infer_rv32_inputs(gate: str, reg: SignalRegistry) -> Optional[List[int]]:
|
|
| 5166 |
pair = m.group(1)
|
| 5167 |
return [R(f"rv32.hazard.{pair}.bit{k}.eq") for k in range(5)]
|
| 5168 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5169 |
return None
|
| 5170 |
|
| 5171 |
|
|
@@ -6056,6 +6098,86 @@ def add_rv32_extras(tensors: Dict[str, torch.Tensor]) -> None:
|
|
| 6056 |
add_gate(tensors, f"rv32.hazard.{pair}.all", [1.0] * 5, [-5.0])
|
| 6057 |
|
| 6058 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6059 |
def cmd_rv32(args) -> None:
|
| 6060 |
"""Build the standalone RV32 threshold processor file from scratch:
|
| 6061 |
64 KB packed memory, the shared 32-bit circuit families, the composed
|
|
@@ -6083,6 +6205,8 @@ def cmd_rv32(args) -> None:
|
|
| 6083 |
add_float_fma(tensors, "float32", 8, 23)
|
| 6084 |
add_float32_cmp(tensors)
|
| 6085 |
add_rv32_extras(tensors)
|
|
|
|
|
|
|
| 6086 |
tensors["manifest.data_bits"] = torch.tensor([32.0], dtype=torch.float32)
|
| 6087 |
tensors["manifest.addr_bits"] = torch.tensor([16.0], dtype=torch.float32)
|
| 6088 |
tensors["manifest.memory_bytes"] = torch.tensor([65536.0], dtype=torch.float32)
|
|
|
|
| 5166 |
pair = m.group(1)
|
| 5167 |
return [R(f"rv32.hazard.{pair}.bit{k}.eq") for k in range(5)]
|
| 5168 |
|
| 5169 |
+
# Instruction decode: opcode-class detectors, format-select, active immediate.
|
| 5170 |
+
m = re.match(r"^rv32\.decode\.is_(\w+)$", gate)
|
| 5171 |
+
if m:
|
| 5172 |
+
name = m.group(1)
|
| 5173 |
+
if name == "fma":
|
| 5174 |
+
return [R(f"$rv32_instr[{j}]") for j in (0, 1, 4, 5, 6)]
|
| 5175 |
+
return [R(f"$rv32_instr[{j}]") for j in range(7)]
|
| 5176 |
+
m = re.match(r"^rv32\.decode\.use_([ISBUJ])$", gate)
|
| 5177 |
+
if m:
|
| 5178 |
+
return [R(f"rv32.decode.is_{cls}") for cls in RV32_USE[m.group(1)]]
|
| 5179 |
+
m = re.match(r"^rv32\.decode\.imm\.bit(\d+)\.and_([ISBUJ])$", gate)
|
| 5180 |
+
if m:
|
| 5181 |
+
k, fmt = int(m.group(1)), m.group(2)
|
| 5182 |
+
return [R(f"rv32.decode.use_{fmt}"), R(f"$rv32_instr[{_rv32_imm_src(fmt, k)}]")]
|
| 5183 |
+
m = re.match(r"^rv32\.decode\.imm\.bit(\d+)$", gate)
|
| 5184 |
+
if m:
|
| 5185 |
+
k = int(m.group(1))
|
| 5186 |
+
terms = [fmt for fmt in RV32_IMM_FMTS if _rv32_imm_src(fmt, k) is not None]
|
| 5187 |
+
return [R(f"rv32.decode.imm.bit{k}.and_{fmt}") for fmt in terms] if terms else [zero]
|
| 5188 |
+
|
| 5189 |
+
# PC sequencing: next-PC mux (pc4 / pc+imm / (rs1+imm)&~1).
|
| 5190 |
+
m = re.match(r"^rv32\.pcnext\.m1\.bit(\d+)\.(not_sel|and_a|and_b|or)$", gate)
|
| 5191 |
+
if m:
|
| 5192 |
+
k, kind = int(m.group(1)), m.group(2)
|
| 5193 |
+
if kind == "not_sel":
|
| 5194 |
+
return [R("$rv32_sel_target")]
|
| 5195 |
+
if kind == "and_a":
|
| 5196 |
+
return [R(f"$rv32_pc4[{k}]"), R(f"rv32.pcnext.m1.bit{k}.not_sel")]
|
| 5197 |
+
if kind == "and_b":
|
| 5198 |
+
return [R(f"$rv32_pcimm[{k}]"), R("$rv32_sel_target")]
|
| 5199 |
+
return [R(f"rv32.pcnext.m1.bit{k}.and_a"), R(f"rv32.pcnext.m1.bit{k}.and_b")]
|
| 5200 |
+
m = re.match(r"^rv32\.pcnext\.bit(\d+)\.(not_sel|and_a|and_b|or)$", gate)
|
| 5201 |
+
if m:
|
| 5202 |
+
k, kind = int(m.group(1)), m.group(2)
|
| 5203 |
+
if kind == "not_sel":
|
| 5204 |
+
return [R("$rv32_sel_jalr")]
|
| 5205 |
+
if kind == "and_a":
|
| 5206 |
+
return [R(f"rv32.pcnext.m1.bit{k}.or"), R(f"rv32.pcnext.bit{k}.not_sel")]
|
| 5207 |
+
if kind == "and_b":
|
| 5208 |
+
return [R(f"$rv32_jalr[{k}]"), R("$rv32_sel_jalr")]
|
| 5209 |
+
return [R(f"rv32.pcnext.bit{k}.and_a"), R(f"rv32.pcnext.bit{k}.and_b")]
|
| 5210 |
+
|
| 5211 |
return None
|
| 5212 |
|
| 5213 |
|
|
|
|
| 6098 |
add_gate(tensors, f"rv32.hazard.{pair}.all", [1.0] * 5, [-5.0])
|
| 6099 |
|
| 6100 |
|
| 6101 |
+
# Opcode -> class name for the instruction-decode network.
|
| 6102 |
+
RV32_OPCLASS = {0x33: "op", 0x13: "op_imm", 0x03: "load", 0x23: "store",
|
| 6103 |
+
0x63: "branch", 0x37: "lui", 0x17: "auipc", 0x6F: "jal",
|
| 6104 |
+
0x67: "jalr", 0x73: "system", 0x53: "fp", 0x07: "flw", 0x27: "fsw"}
|
| 6105 |
+
# Which opcode classes select each immediate format.
|
| 6106 |
+
RV32_USE = {"I": ["op_imm", "load", "jalr", "flw"], "S": ["store", "fsw"],
|
| 6107 |
+
"B": ["branch"], "U": ["lui", "auipc"], "J": ["jal"]}
|
| 6108 |
+
RV32_IMM_FMTS = ("I", "S", "B", "U", "J")
|
| 6109 |
+
|
| 6110 |
+
|
| 6111 |
+
def _rv32_imm_src(fmt: str, k: int):
|
| 6112 |
+
"""Instruction bit that supplies immediate bit k for a format, or None (0)."""
|
| 6113 |
+
if fmt == "I":
|
| 6114 |
+
return (20 + k) if k <= 11 else 31
|
| 6115 |
+
if fmt == "S":
|
| 6116 |
+
if k <= 4:
|
| 6117 |
+
return 7 + k
|
| 6118 |
+
return (20 + k) if k <= 11 else 31
|
| 6119 |
+
if fmt == "B":
|
| 6120 |
+
if k == 0:
|
| 6121 |
+
return None
|
| 6122 |
+
if k <= 4:
|
| 6123 |
+
return 7 + k
|
| 6124 |
+
if k <= 10:
|
| 6125 |
+
return 20 + k
|
| 6126 |
+
if k == 11:
|
| 6127 |
+
return 7
|
| 6128 |
+
return 31
|
| 6129 |
+
if fmt == "U":
|
| 6130 |
+
return None if k < 12 else k
|
| 6131 |
+
if fmt == "J":
|
| 6132 |
+
if k == 0:
|
| 6133 |
+
return None
|
| 6134 |
+
if k <= 10:
|
| 6135 |
+
return 20 + k
|
| 6136 |
+
if k == 11:
|
| 6137 |
+
return 20
|
| 6138 |
+
if k <= 19:
|
| 6139 |
+
return k
|
| 6140 |
+
return 31
|
| 6141 |
+
return None
|
| 6142 |
+
|
| 6143 |
+
|
| 6144 |
+
def add_rv32_decode(tensors: Dict[str, torch.Tensor]) -> None:
|
| 6145 |
+
"""Instruction decode as threshold gates: opcode-class one-hots (an exact
|
| 6146 |
+
7-bit match on the instruction's opcode field) and the sign-extended
|
| 6147 |
+
immediate for the active format, muxed from the raw instruction word. The
|
| 6148 |
+
runtime reads these gate outputs instead of slicing the word in Python."""
|
| 6149 |
+
for opc, name in RV32_OPCLASS.items():
|
| 6150 |
+
w = [1.0 if (opc >> j) & 1 else -1.0 for j in range(7)]
|
| 6151 |
+
add_gate(tensors, f"rv32.decode.is_{name}", w, [-float(bin(opc).count("1"))])
|
| 6152 |
+
# FMADD/FMSUB/FNMADD/FNMSUB share the mask 100xx11 (instr bits 6,1,0 set; 5,4 clear).
|
| 6153 |
+
add_gate(tensors, "rv32.decode.is_fma", [1.0, 1.0, -1.0, -1.0, 1.0], [-3.0])
|
| 6154 |
+
for u, cls in RV32_USE.items():
|
| 6155 |
+
add_gate(tensors, f"rv32.decode.use_{u}", [1.0] * len(cls), [-1.0])
|
| 6156 |
+
for k in range(32):
|
| 6157 |
+
terms = [fmt for fmt in RV32_IMM_FMTS if _rv32_imm_src(fmt, k) is not None]
|
| 6158 |
+
for fmt in terms:
|
| 6159 |
+
add_gate(tensors, f"rv32.decode.imm.bit{k}.and_{fmt}", [1.0, 1.0], [-2.0])
|
| 6160 |
+
add_gate(tensors, f"rv32.decode.imm.bit{k}", [1.0] * max(1, len(terms)), [-1.0])
|
| 6161 |
+
|
| 6162 |
+
|
| 6163 |
+
def add_rv32_pcnext(tensors: Dict[str, torch.Tensor]) -> None:
|
| 6164 |
+
"""PC sequencing as threshold gates: a two-level mux selecting the next PC
|
| 6165 |
+
among PC+4, PC+imm (branch/jal), and (rs1+imm)&~1 (jalr). The three
|
| 6166 |
+
candidates are the gate adder's outputs; this network chooses between them
|
| 6167 |
+
from the decode's control signals rather than a Python if/elif."""
|
| 6168 |
+
for k in range(32):
|
| 6169 |
+
# m1 = sel_target ? pcimm : pc4
|
| 6170 |
+
add_gate(tensors, f"rv32.pcnext.m1.bit{k}.not_sel", [-1.0], [0.0])
|
| 6171 |
+
add_gate(tensors, f"rv32.pcnext.m1.bit{k}.and_a", [1.0, 1.0], [-2.0])
|
| 6172 |
+
add_gate(tensors, f"rv32.pcnext.m1.bit{k}.and_b", [1.0, 1.0], [-2.0])
|
| 6173 |
+
add_gate(tensors, f"rv32.pcnext.m1.bit{k}.or", [1.0, 1.0], [-1.0])
|
| 6174 |
+
# pcnext = sel_jalr ? jalr : m1
|
| 6175 |
+
add_gate(tensors, f"rv32.pcnext.bit{k}.not_sel", [-1.0], [0.0])
|
| 6176 |
+
add_gate(tensors, f"rv32.pcnext.bit{k}.and_a", [1.0, 1.0], [-2.0])
|
| 6177 |
+
add_gate(tensors, f"rv32.pcnext.bit{k}.and_b", [1.0, 1.0], [-2.0])
|
| 6178 |
+
add_gate(tensors, f"rv32.pcnext.bit{k}.or", [1.0, 1.0], [-1.0])
|
| 6179 |
+
|
| 6180 |
+
|
| 6181 |
def cmd_rv32(args) -> None:
|
| 6182 |
"""Build the standalone RV32 threshold processor file from scratch:
|
| 6183 |
64 KB packed memory, the shared 32-bit circuit families, the composed
|
|
|
|
| 6205 |
add_float_fma(tensors, "float32", 8, 23)
|
| 6206 |
add_float32_cmp(tensors)
|
| 6207 |
add_rv32_extras(tensors)
|
| 6208 |
+
add_rv32_decode(tensors)
|
| 6209 |
+
add_rv32_pcnext(tensors)
|
| 6210 |
tensors["manifest.data_bits"] = torch.tensor([32.0], dtype=torch.float32)
|
| 6211 |
tensors["manifest.addr_bits"] = torch.tensor([16.0], dtype=torch.float32)
|
| 6212 |
tensors["manifest.memory_bytes"] = torch.tensor([65536.0], dtype=torch.float32)
|
src/machines.py
CHANGED
|
@@ -573,6 +573,8 @@ def rv_state(mem):
|
|
| 573 |
# =============================================================================
|
| 574 |
|
| 575 |
PAIRABLE = (0x33, 0x13, 0x37, 0x17)
|
|
|
|
|
|
|
| 576 |
|
| 577 |
|
| 578 |
class Rv32ThresholdCPU:
|
|
@@ -585,6 +587,8 @@ class Rv32ThresholdCPU:
|
|
| 585 |
self.alu = GenericThresholdALU(self.T, 32)
|
| 586 |
self.fpu = {op: NetlistEvaluator(self.T, self.registry, f"float32.{op}")
|
| 587 |
for op in ("add", "mul", "div", "cmp", "fma")}
|
|
|
|
|
|
|
| 588 |
self.pairs_issued = 0
|
| 589 |
|
| 590 |
# ---- gate helpers -------------------------------------------------
|
|
@@ -926,13 +930,15 @@ class Rv32ThresholdCPU:
|
|
| 926 |
f7 = w >> 25
|
| 927 |
R = s["regs"]
|
| 928 |
a = R[rs1]
|
|
|
|
|
|
|
| 929 |
if op == 0x37:
|
| 930 |
-
return rd,
|
| 931 |
if op == 0x17:
|
| 932 |
-
return rd, self.add32(pc,
|
| 933 |
if op == 0x13:
|
| 934 |
-
b =
|
| 935 |
-
sh =
|
| 936 |
imm_mode = True
|
| 937 |
else:
|
| 938 |
b = R[rs2]
|
|
@@ -1001,7 +1007,9 @@ class Rv32ThresholdCPU:
|
|
| 1001 |
R = s["regs"]
|
| 1002 |
FR = s["fregs"]
|
| 1003 |
a, b = R[rs1], R[rs2]
|
| 1004 |
-
|
|
|
|
|
|
|
| 1005 |
|
| 1006 |
def wr(v):
|
| 1007 |
if rd:
|
|
@@ -1011,7 +1019,7 @@ class Rv32ThresholdCPU:
|
|
| 1011 |
d, v = self._exec_alu_class(s, pc, w)
|
| 1012 |
if d:
|
| 1013 |
R[d] = u32(v)
|
| 1014 |
-
if op
|
| 1015 |
if f3 == 0:
|
| 1016 |
wr(self.alu.mul_n(a, b, 32))
|
| 1017 |
elif f3 == 1:
|
|
@@ -1028,40 +1036,29 @@ class Rv32ThresholdCPU:
|
|
| 1028 |
wr(self.divs(a, b)[1])
|
| 1029 |
elif f3 == 7:
|
| 1030 |
wr(self.divu(a, b)[1])
|
| 1031 |
-
elif
|
| 1032 |
-
|
| 1033 |
-
|
| 1034 |
-
wr(
|
| 1035 |
-
|
| 1036 |
-
elif op == 0x67:
|
| 1037 |
-
t = self.add32(a, u32(sext(w >> 20, 12))) & ~1
|
| 1038 |
-
wr(nxt)
|
| 1039 |
-
nxt = t
|
| 1040 |
-
elif op == 0x63:
|
| 1041 |
-
imm = sext((((w >> 31) & 1) << 12) | (((w >> 7) & 1) << 11)
|
| 1042 |
-
| (((w >> 25) & 0x3F) << 5) | (((w >> 8) & 0xF) << 1), 13)
|
| 1043 |
eq = self.ucmp(a, b, "eq")
|
| 1044 |
sl = self.scmp_lt(a, b)
|
| 1045 |
ul = self.ucmp(a, b, "lessthan")
|
| 1046 |
-
|
| 1047 |
-
|
| 1048 |
-
|
| 1049 |
-
elif op == 0x03:
|
| 1050 |
-
addr = self.add32(a, u32(sext(w >> 20, 12))) & 0xFFFF
|
| 1051 |
width = [1, 2, 4, 0, 1, 2][f3]
|
| 1052 |
wr(self._load(s, addr, width, f3 < 3))
|
| 1053 |
-
elif
|
| 1054 |
-
|
| 1055 |
-
addr = self.add32(a, u32(imm)) & 0xFFFF
|
| 1056 |
self._store(s, addr, b, [1, 2, 4][f3])
|
| 1057 |
-
elif
|
| 1058 |
-
addr = self.add32(a,
|
| 1059 |
FR[rd] = self._load(s, addr, 4, False)
|
| 1060 |
-
elif
|
| 1061 |
-
|
| 1062 |
-
addr = self.add32(a, u32(imm)) & 0xFFFF
|
| 1063 |
self._store(s, addr, FR[rs2], 4)
|
| 1064 |
-
elif
|
| 1065 |
fa, fb = FR[rs1], FR[rs2]
|
| 1066 |
if f7 in (0x00, 0x04, 0x08, 0x0C):
|
| 1067 |
circ = {0x00: "add", 0x04: "add", 0x08: "mul", 0x0C: "div"}[f7]
|
|
@@ -1090,7 +1087,7 @@ class Rv32ThresholdCPU:
|
|
| 1090 |
FR[rd] = a
|
| 1091 |
elif f7 == 0x70:
|
| 1092 |
wr(FR[rs1])
|
| 1093 |
-
elif
|
| 1094 |
rs3 = (w >> 27) & 0x1F
|
| 1095 |
fa, fb, fc = FR[rs1], FR[rs2], FR[rs3]
|
| 1096 |
if op in (0x4b, 0x4f): # negate the product
|
|
@@ -1100,13 +1097,17 @@ class Rv32ThresholdCPU:
|
|
| 1100 |
FR[rd] = self._fpu_word3(fa, fb, fc)
|
| 1101 |
elif op == 0x0B:
|
| 1102 |
wr(self.neur(a, b))
|
| 1103 |
-
elif
|
| 1104 |
s["halted"] = True
|
| 1105 |
elif op == 0x0F:
|
| 1106 |
pass
|
| 1107 |
elif op not in PAIRABLE:
|
| 1108 |
raise ValueError(f"illegal instruction {w:#010x} at {pc:#06x}")
|
| 1109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1110 |
return s
|
| 1111 |
|
| 1112 |
def _fpu_ext(self, fa, fb):
|
|
@@ -1144,6 +1145,30 @@ class Rv32ThresholdCPU:
|
|
| 1144 |
v |= int(out[f"{p}.frac_out.bit{k}"][0, 0].item()) << k
|
| 1145 |
return v
|
| 1146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1147 |
def run(self, state, max_cycles=400):
|
| 1148 |
s = dict(state)
|
| 1149 |
s["_mem_bits"] = torch.tensor(
|
|
|
|
| 573 |
# =============================================================================
|
| 574 |
|
| 575 |
PAIRABLE = (0x33, 0x13, 0x37, 0x17)
|
| 576 |
+
DECODE_CLASSES = ("op", "op_imm", "load", "store", "branch", "lui", "auipc",
|
| 577 |
+
"jal", "jalr", "system", "fp", "flw", "fsw", "fma")
|
| 578 |
|
| 579 |
|
| 580 |
class Rv32ThresholdCPU:
|
|
|
|
| 587 |
self.alu = GenericThresholdALU(self.T, 32)
|
| 588 |
self.fpu = {op: NetlistEvaluator(self.T, self.registry, f"float32.{op}")
|
| 589 |
for op in ("add", "mul", "div", "cmp", "fma")}
|
| 590 |
+
self.decode = NetlistEvaluator(self.T, self.registry, "rv32.decode")
|
| 591 |
+
self.pcseq = NetlistEvaluator(self.T, self.registry, "rv32.pcnext")
|
| 592 |
self.pairs_issued = 0
|
| 593 |
|
| 594 |
# ---- gate helpers -------------------------------------------------
|
|
|
|
| 930 |
f7 = w >> 25
|
| 931 |
R = s["regs"]
|
| 932 |
a = R[rs1]
|
| 933 |
+
if op in (0x37, 0x17, 0x13):
|
| 934 |
+
_, imm_g = self._decode(w) # gate-decoded immediate
|
| 935 |
if op == 0x37:
|
| 936 |
+
return rd, imm_g
|
| 937 |
if op == 0x17:
|
| 938 |
+
return rd, self.add32(pc, imm_g)
|
| 939 |
if op == 0x13:
|
| 940 |
+
b = imm_g
|
| 941 |
+
sh = imm_g & 31
|
| 942 |
imm_mode = True
|
| 943 |
else:
|
| 944 |
b = R[rs2]
|
|
|
|
| 1007 |
R = s["regs"]
|
| 1008 |
FR = s["fregs"]
|
| 1009 |
a, b = R[rs1], R[rs2]
|
| 1010 |
+
cls, imm_g = self._decode(w) # gate decode: classes + immediate
|
| 1011 |
+
pc4 = self.add32(pc, 4) # PC+4 through the gate adder
|
| 1012 |
+
branch_taken = 0
|
| 1013 |
|
| 1014 |
def wr(v):
|
| 1015 |
if rd:
|
|
|
|
| 1019 |
d, v = self._exec_alu_class(s, pc, w)
|
| 1020 |
if d:
|
| 1021 |
R[d] = u32(v)
|
| 1022 |
+
if cls["op"] and f7 == 1:
|
| 1023 |
if f3 == 0:
|
| 1024 |
wr(self.alu.mul_n(a, b, 32))
|
| 1025 |
elif f3 == 1:
|
|
|
|
| 1036 |
wr(self.divs(a, b)[1])
|
| 1037 |
elif f3 == 7:
|
| 1038 |
wr(self.divu(a, b)[1])
|
| 1039 |
+
elif cls["jal"]:
|
| 1040 |
+
wr(pc4) # return address
|
| 1041 |
+
elif cls["jalr"]:
|
| 1042 |
+
wr(pc4)
|
| 1043 |
+
elif cls["branch"]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1044 |
eq = self.ucmp(a, b, "eq")
|
| 1045 |
sl = self.scmp_lt(a, b)
|
| 1046 |
ul = self.ucmp(a, b, "lessthan")
|
| 1047 |
+
branch_taken = [eq, 1 - eq, 0, 0, sl, 1 - sl, ul, 1 - ul][f3]
|
| 1048 |
+
elif cls["load"]:
|
| 1049 |
+
addr = self.add32(a, imm_g) & 0xFFFF
|
|
|
|
|
|
|
| 1050 |
width = [1, 2, 4, 0, 1, 2][f3]
|
| 1051 |
wr(self._load(s, addr, width, f3 < 3))
|
| 1052 |
+
elif cls["store"]:
|
| 1053 |
+
addr = self.add32(a, imm_g) & 0xFFFF
|
|
|
|
| 1054 |
self._store(s, addr, b, [1, 2, 4][f3])
|
| 1055 |
+
elif cls["flw"]:
|
| 1056 |
+
addr = self.add32(a, imm_g) & 0xFFFF
|
| 1057 |
FR[rd] = self._load(s, addr, 4, False)
|
| 1058 |
+
elif cls["fsw"]:
|
| 1059 |
+
addr = self.add32(a, imm_g) & 0xFFFF
|
|
|
|
| 1060 |
self._store(s, addr, FR[rs2], 4)
|
| 1061 |
+
elif cls["fp"]:
|
| 1062 |
fa, fb = FR[rs1], FR[rs2]
|
| 1063 |
if f7 in (0x00, 0x04, 0x08, 0x0C):
|
| 1064 |
circ = {0x00: "add", 0x04: "add", 0x08: "mul", 0x0C: "div"}[f7]
|
|
|
|
| 1087 |
FR[rd] = a
|
| 1088 |
elif f7 == 0x70:
|
| 1089 |
wr(FR[rs1])
|
| 1090 |
+
elif cls["fma"]: # FMADD/FMSUB/FNMSUB/FNMADD.S
|
| 1091 |
rs3 = (w >> 27) & 0x1F
|
| 1092 |
fa, fb, fc = FR[rs1], FR[rs2], FR[rs3]
|
| 1093 |
if op in (0x4b, 0x4f): # negate the product
|
|
|
|
| 1097 |
FR[rd] = self._fpu_word3(fa, fb, fc)
|
| 1098 |
elif op == 0x0B:
|
| 1099 |
wr(self.neur(a, b))
|
| 1100 |
+
elif cls["system"]:
|
| 1101 |
s["halted"] = True
|
| 1102 |
elif op == 0x0F:
|
| 1103 |
pass
|
| 1104 |
elif op not in PAIRABLE:
|
| 1105 |
raise ValueError(f"illegal instruction {w:#010x} at {pc:#06x}")
|
| 1106 |
+
# PC sequencing through the gate mux: pc4 / pc+imm (branch,jal) / (rs1+imm)&~1 (jalr).
|
| 1107 |
+
pcimm = self.add32(pc, imm_g)
|
| 1108 |
+
jalr_t = self.add32(a, imm_g) & ~1
|
| 1109 |
+
sel_target = 1 if (branch_taken or cls["jal"]) else 0
|
| 1110 |
+
s["pc"] = self._pcnext(pc4, pcimm, jalr_t, sel_target, cls["jalr"])
|
| 1111 |
return s
|
| 1112 |
|
| 1113 |
def _fpu_ext(self, fa, fb):
|
|
|
|
| 1145 |
v |= int(out[f"{p}.frac_out.bit{k}"][0, 0].item()) << k
|
| 1146 |
return v
|
| 1147 |
|
| 1148 |
+
def _decode(self, w):
|
| 1149 |
+
"""Instruction decode through the gate network: opcode-class one-hots
|
| 1150 |
+
and the sign-extended immediate for the active format."""
|
| 1151 |
+
ext = {f"$rv32_instr[{k}]": (w >> k) & 1 for k in range(32)}
|
| 1152 |
+
out = self.decode.run(ext)
|
| 1153 |
+
cls = {n: int(out[f"rv32.decode.is_{n}"][0, 0].item()) for n in DECODE_CLASSES}
|
| 1154 |
+
imm = 0
|
| 1155 |
+
for k in range(32):
|
| 1156 |
+
imm |= int(out[f"rv32.decode.imm.bit{k}"][0, 0].item()) << k
|
| 1157 |
+
return cls, imm
|
| 1158 |
+
|
| 1159 |
+
def _pcnext(self, pc4, pcimm, jalr, sel_target, sel_jalr):
|
| 1160 |
+
"""Next-PC selection through the gate mux (pc4 / pc+imm / (rs1+imm)&~1)."""
|
| 1161 |
+
ext = {"$rv32_sel_target": sel_target, "$rv32_sel_jalr": sel_jalr}
|
| 1162 |
+
for k in range(32):
|
| 1163 |
+
ext[f"$rv32_pc4[{k}]"] = (pc4 >> k) & 1
|
| 1164 |
+
ext[f"$rv32_pcimm[{k}]"] = (pcimm >> k) & 1
|
| 1165 |
+
ext[f"$rv32_jalr[{k}]"] = (jalr >> k) & 1
|
| 1166 |
+
out = self.pcseq.run(ext)
|
| 1167 |
+
v = 0
|
| 1168 |
+
for k in range(32):
|
| 1169 |
+
v |= int(out[f"rv32.pcnext.bit{k}.or"][0, 0].item()) << k
|
| 1170 |
+
return v
|
| 1171 |
+
|
| 1172 |
def run(self, state, max_cycles=400):
|
| 1173 |
s = dict(state)
|
| 1174 |
s["_mem_bits"] = torch.tensor(
|
variants/neural_rv32.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:1947dab2cf586de70c3f799538eda4f129106d77ead404944df060349ff1380e
|
| 3 |
+
size 43429657
|