OR-1 dataflow CPU sketch
at ba08ffded3d3b2badb2a7e22816feafaacea5ded 247 lines 8.2 kB view raw
1"""Opcode mnemonic mapping and arity classification for OR1 assembly. 2 3This module provides: 4- MNEMONIC_TO_OP: Maps assembly mnemonic strings to ALUOp or MemOp enum values 5- OP_TO_MNEMONIC: Reverse mapping for serialization (handles IntEnum value collisions) 6- MONADIC_OPS: Set of opcodes that are always monadic 7- is_monadic() and is_dyadic(): Functions to check operand arity 8""" 9 10from typing import Optional, Union 11from cm_inst import ArithOp, LogicOp, MemOp, RoutingOp, is_monadic_alu 12 13 14# Build mnemonic to opcode mapping 15MNEMONIC_TO_OP: dict[str, Union[ArithOp, LogicOp, RoutingOp, MemOp]] = { 16 # Arithmetic operations 17 "add": ArithOp.ADD, 18 "sub": ArithOp.SUB, 19 "inc": ArithOp.INC, 20 "dec": ArithOp.DEC, 21 "shiftl": ArithOp.SHIFT_L, 22 "shiftr": ArithOp.SHIFT_R, 23 "ashiftr": ArithOp.ASHFT_R, 24 # Logic operations 25 "and": LogicOp.AND, 26 "or": LogicOp.OR, 27 "xor": LogicOp.XOR, 28 "not": LogicOp.NOT, 29 "eq": LogicOp.EQ, 30 "lt": LogicOp.LT, 31 "lte": LogicOp.LTE, 32 "gt": LogicOp.GT, 33 "gte": LogicOp.GTE, 34 # Routing/branch operations 35 "breq": RoutingOp.BREQ, 36 "brgt": RoutingOp.BRGT, 37 "brge": RoutingOp.BRGE, 38 "brof": RoutingOp.BROF, 39 "sweq": RoutingOp.SWEQ, 40 "swgt": RoutingOp.SWGT, 41 "swge": RoutingOp.SWGE, 42 "swof": RoutingOp.SWOF, 43 "gate": RoutingOp.GATE, 44 "sel": RoutingOp.SEL, 45 "merge": RoutingOp.MRGE, 46 "pass": RoutingOp.PASS, 47 "const": RoutingOp.CONST, 48 "free_ctx": RoutingOp.FREE_CTX, # ALU free (deallocate context slot) 49 # Memory operations 50 "read": MemOp.READ, 51 "write": MemOp.WRITE, 52 "clear": MemOp.CLEAR, 53 "alloc": MemOp.ALLOC, 54 "free": MemOp.FREE, # SM free 55 "rd_inc": MemOp.RD_INC, 56 "rd_dec": MemOp.RD_DEC, 57 "cmp_sw": MemOp.CMP_SW, 58 "exec": MemOp.EXEC, 59 "raw_read": MemOp.RAW_READ, 60 "set_page": MemOp.SET_PAGE, 61 "write_imm": MemOp.WRITE_IMM, 62 "ext": MemOp.EXT, 63} 64 65 66# Build reverse mapping with type information to avoid IntEnum collisions 67_reverse_mapping: dict[tuple[type, int], str] = {} 68for mnemonic, op in MNEMONIC_TO_OP.items(): 69 _reverse_mapping[(type(op), int(op))] = mnemonic 70 71 72class TypeAwareOpToMnemonicDict: 73 """Collision-free reverse mapping from opcodes to mnemonics. 74 75 Handles IntEnum cross-type equality by using (type, value) tuples internally. 76 Supports dict-like access: OP_TO_MNEMONIC[ArithOp.ADD] returns "add", 77 OP_TO_MNEMONIC[MemOp.READ] returns "read", etc. 78 """ 79 80 def __init__(self, mapping: dict[tuple[type, int], str]): 81 """Initialize with a type-indexed mapping. 82 83 Args: 84 mapping: dict from (type, value) tuples to mnemonic strings 85 """ 86 self._mapping = mapping 87 88 def __getitem__(self, op: Union[ArithOp, LogicOp, RoutingOp, MemOp]) -> str: 89 """Get the mnemonic for an opcode. 90 91 Args: 92 op: The opcode enum value 93 94 Returns: 95 The mnemonic string 96 97 Raises: 98 KeyError: If the opcode is not in the mapping 99 """ 100 key = (type(op), int(op)) 101 if key not in self._mapping: 102 raise KeyError(f"Opcode {op} ({type(op).__name__}) not found in mapping") 103 return self._mapping[key] 104 105 def __contains__(self, op: Union[ArithOp, LogicOp, RoutingOp, MemOp]) -> bool: 106 """Check if an opcode is in the mapping.""" 107 return (type(op), int(op)) in self._mapping 108 109 def __iter__(self): 110 """Iterate over mnemonic strings.""" 111 return iter(self._mapping.values()) 112 113 def __len__(self) -> int: 114 """Return the number of opcode-mnemonic pairs.""" 115 return len(self._mapping) 116 117 def items(self): 118 """Return an iterator of (opcode_type, mnemonic) pairs for testing. 119 120 This reconstructs the original enum instances from the stored types/values. 121 """ 122 result = [] 123 for (op_type, op_val), mnemonic in self._mapping.items(): 124 result.append((op_type(op_val), mnemonic)) 125 return result 126 127 128OP_TO_MNEMONIC: TypeAwareOpToMnemonicDict = TypeAwareOpToMnemonicDict(_reverse_mapping) 129 130 131# Set of opcodes that are always monadic (single input operand). 132# We use a frozenset of (type, value) tuples to avoid IntEnum collisions. 133_MONADIC_OPS_TUPLES: frozenset[tuple[type, int]] = frozenset([ 134 # ALU ops: duplicated from cm_inst.is_monadic_alu() for MONADIC_OPS membership testing. 135 # is_monadic() short-circuits to is_monadic_alu() for ALU ops, so these entries 136 # are only reached via `op in MONADIC_OPS` (TypeAwareMonadicOpsSet). 137 (ArithOp, int(ArithOp.INC)), 138 (ArithOp, int(ArithOp.DEC)), 139 (ArithOp, int(ArithOp.SHIFT_L)), 140 (ArithOp, int(ArithOp.SHIFT_R)), 141 (ArithOp, int(ArithOp.ASHFT_R)), 142 # Logic: single input 143 (LogicOp, int(LogicOp.NOT)), 144 # Routing: single input or no ALU involvement 145 (RoutingOp, int(RoutingOp.PASS)), 146 (RoutingOp, int(RoutingOp.CONST)), 147 (RoutingOp, int(RoutingOp.FREE_CTX)), 148 # Memory: single input (monadic SM operations) 149 (MemOp, int(MemOp.READ)), 150 (MemOp, int(MemOp.ALLOC)), 151 (MemOp, int(MemOp.FREE)), 152 (MemOp, int(MemOp.CLEAR)), 153 (MemOp, int(MemOp.RD_INC)), 154 (MemOp, int(MemOp.RD_DEC)), 155 (MemOp, int(MemOp.EXEC)), 156 (MemOp, int(MemOp.RAW_READ)), 157 (MemOp, int(MemOp.SET_PAGE)), 158 (MemOp, int(MemOp.WRITE_IMM)), 159 (MemOp, int(MemOp.EXT)), 160]) 161 162 163class TypeAwareMonadicOpsSet: 164 """Collision-free set of monadic opcodes. 165 166 Handles IntEnum cross-type equality by using (type, value) tuples internally. 167 Supports membership testing: ArithOp.INC in MONADIC_OPS returns True, 168 but ArithOp.ADD in MONADIC_OPS returns False (collision-free). 169 """ 170 171 def __init__(self, tuples: frozenset[tuple[type, int]]): 172 """Initialize with type-indexed tuples. 173 174 Args: 175 tuples: frozenset of (type, value) tuples 176 """ 177 self._tuples = tuples 178 179 def __contains__(self, op: Union[ArithOp, LogicOp, RoutingOp, MemOp]) -> bool: 180 """Check if an opcode is in the set, handling IntEnum collisions. 181 182 Args: 183 op: The opcode enum value 184 185 Returns: 186 True if the opcode is monadic, False otherwise 187 """ 188 return (type(op), int(op)) in self._tuples 189 190 def __iter__(self): 191 """Iterate over opcode instances in the set.""" 192 for op_type, op_val in self._tuples: 193 yield op_type(op_val) 194 195 def __len__(self) -> int: 196 """Return the number of monadic opcodes.""" 197 return len(self._tuples) 198 199 def __repr__(self) -> str: 200 """Return a string representation of the set.""" 201 ops = list(self) 202 return f"TypeAwareMonadicOpsSet({ops})" 203 204 205MONADIC_OPS: TypeAwareMonadicOpsSet = TypeAwareMonadicOpsSet(_MONADIC_OPS_TUPLES) 206 207 208def is_monadic(op: Union[ArithOp, LogicOp, RoutingOp, MemOp], const: Optional[int] = None) -> bool: 209 """Check if an opcode is monadic (single input operand). 210 211 Args: 212 op: The ALUOp or MemOp enum value 213 const: Optional const value. Used to determine monadic form of WRITE. 214 If const is not None, WRITE is monadic (cell_addr from const). 215 If const is None, WRITE is dyadic (cell_addr from left operand). 216 217 Returns: 218 True if the opcode is always monadic, or if it's WRITE with const set. 219 False for CMP_SW (always dyadic) and WRITE with const=None (dyadic). 220 """ 221 # Use canonical is_monadic_alu for ALU operations 222 if isinstance(op, (ArithOp, LogicOp, RoutingOp)): 223 return is_monadic_alu(op) 224 225 # Handle MemOp operations 226 op_tuple = (type(op), int(op)) 227 if op_tuple in _MONADIC_OPS_TUPLES: 228 return True 229 230 # Special case: WRITE can be monadic (const given) or dyadic (const not given) 231 if type(op) is MemOp and op == MemOp.WRITE: 232 return const is not None 233 234 return False 235 236 237def is_dyadic(op: Union[ArithOp, LogicOp, RoutingOp, MemOp], const: Optional[int] = None) -> bool: 238 """Check if an opcode is dyadic (two input operands). 239 240 Args: 241 op: The ALUOp or MemOp enum value 242 const: Optional const value. Used for context-dependent operations like WRITE. 243 244 Returns: 245 True if the opcode is dyadic, False otherwise. 246 """ 247 return not is_monadic(op, const)