def _do_resolve_const(self, value, bits=None): if isinstance(value, int): value = bin(value)[2:] if "'" in value: lit_width, _, value = value.partition("'") assert int(lit_width) == bits assert value[0].lower() == 'b' value = value[1:] if any(x not in '10x' for x in value): value = bin(int(value))[2:] if value == '1' and bits in [1, None]: return ir.ASTPort('__const1_on', 1) if value == '0' and bits in [1, None]: return ir.ASTPort('__const1_off', 1) if value == "x" and bits in [1, None]: return ir.ASTPort('__const1_x', 1) my_sig = self.circuit.generate_internal_signal(bits) port = self.circuit.get_port(my_sig) value = '0' * (bits - len(value)) + value for i in range(bits): self.circuit.children.append( ir.ASTAssign( port[i], self.resolve_const(value[len(value) - 1 - i], bits=1))) return port
def __init__(self, name, inputs=None, outputs=None, internal=None): self.name = name self.inputs = [ir.ASTPort(name, size) for name, size in (inputs or [])] self.outputs = [ ir.ASTPort(name, size) for name, size in (outputs or []) ] self.circuit = ir.ASTCircuit(self.inputs, self.outputs) for name, size in (internal or []): self.circuit.internal_signals[name] = size
def _lower_edge(self, info: visit.AlwaysInfo): if info.clock_edge is None and info.reset_edge is None: return None raw_edges = [(str(info.clock_name[1]), info.clock_edge, info.clock_bit) ] if info.reset_edge: raw_edges.append( (str(info.reset_name[1]), info.reset_edge, info.reset_bit)) edges = [] for raw in raw_edges: name = raw[0] port = self.circuit.get_port(name) if port.bitsize != 1: name += '(' + str(raw[2]) + ')' my_port = ir.ASTPort(name, 1) if raw[1] != 'posedge': my_port = ir.ASTLogicGate('not', children=[my_port]) edges.append(my_port) if len(edges) == 2: return ir.ASTLogicGate('or', children=edges) return edges[0]
def __init__(self, module, terms, binddict, instances, other_modules): self.module = module self.terms = terms self.binddict = binddict self.instances = instances self.other_modules = other_modules terms = {t: terms[t] for t in terms if len(t) == 2} inputs = filter_terms(terms, lambda x: 'Input' in x) outputs = filter_terms(terms, lambda x: 'Output' in x) internal = filter_terms(terms, lambda x: not ('Input' in x or 'Output' in x)) self.circuit = ir.ASTCircuit([ir.ASTPort(n, s) for n, s in inputs], [ir.ASTPort(n, s) for n, s in outputs]) for name, size in internal: self.circuit.get_or_create(name, size) self.circuit.validate() self.resolved_consts = {} self.required_intrinsics = set([]) self.mem_definitions = {} self.mem_initial = {} for item in self.other_modules[self.module].items: if isinstance(item, Decl): for arr in item.list: if isinstance(arr, RegArray): self.mem_definitions[arr.name] = arr if isinstance(item, Initial): item = item.statement.statement if item.syscall == 'readmem_sim': with open(item.args[0].value) as f: self.mem_initial[item.args[1].name] = f.read() pass
def _emit_expr_intrinsic(self, v_name, children, bits=None): if v_name == 'Plus': assert type(bits) == int assert len(children) == 2 assert all(c.get_bitsize() == bits for c in children) result_sig = self.circuit.generate_internal_signal(bits) result_port = self.circuit.get_port(result_sig) self.required_intrinsics.add(('add', bits)) self.circuit.children.append( ir.ASTSubCircuit( '_add' + str(bits), { 'A': children[0], 'B': children[1], 'Cin': ir.ASTPort('__const1_off', 1), }, { 'X': result_port, })) return result_port raise ValueError
def generate_intrinsic(info): name = '[unknown]' circuit = None other_intrinsics = set([]) if info == ('add', 1): name = '_add1' circuit = ir.ASTCircuit( [ir.ASTPort('A', 1), ir.ASTPort('B', 1), ir.ASTPort('Cin', 1)], [ir.ASTPort('X', 1), ir.ASTPort('Cout', 1)]) circuit.internal_signals['AxorB'] = 1 circuit.children.append( ir.ASTAssign( circuit.get_port('AxorB'), ir.ASTLogicGate('xor', [circuit.get_port('A'), circuit.get_port('B')]))) circuit.children.append( ir.ASTAssign( circuit.get_port('X'), ir.ASTLogicGate( 'xor', [circuit.get_port('AxorB'), circuit.get_port('Cin')]))) circuit.children.append( ir.ASTAssign( circuit.get_port('Cout'), ir.ASTLogicGate('or', [ ir.ASTLogicGate( 'and', [circuit.get_port('A'), circuit.get_port('B')]), ir.ASTLogicGate( 'and', [circuit.get_port('AxorB'), circuit.get_port('Cin')]) ]))) elif info[0] == 'add': size = info[1] other_intrinsics.add(('add', 1)) name = '_add' + str(size) circuit = ir.ASTCircuit([ ir.ASTPort('A', size), ir.ASTPort('B', size), ir.ASTPort('Cin', 1) ], [ir.ASTPort('X', size), ir.ASTPort('Cout', 1)]) in_cin = 'Cin' for i in range(size): carry = 'c' + str(i) circuit.internal_signals[carry] = 1 circuit.children.append( ir.ASTSubCircuit( '_add1', { 'A': circuit.get_port('A')[i], 'B': circuit.get_port('B')[i], 'Cin': circuit.get_port(in_cin), }, { 'X': circuit.get_port('X')[i], 'Cout': circuit.get_port(carry), })) in_cin = carry circuit.children.append( ir.ASTAssign(circuit.get_port('Cout'), circuit.get_port(in_cin))) # (circuit, new intrinsics that are needed) return name, circuit, other_intrinsics
def _lower_expr(self, expr, ident=None, bits=None): # if isinstance(expr, ) if isinstance(expr, df.DFPartselect): return self.circuit.get_port(expr.var.name[1])[int(expr.lsb.value), int(expr.msb.value)] elif isinstance(expr, df.DFBranch): spec_mux = self._speculative_mux_folding(expr, ident=ident, bits=bits) if spec_mux: return spec_mux false_node = self._lower_expr( expr.falsenode, ident=ident, bits=bits) if expr.falsenode else ident true_node = self._lower_expr(expr.truenode, ident=ident, bits=bits) if expr.truenode else ident if isinstance(expr.condnode, df.DFIntConst): if expr.condnode.eval(): return true_node else: return false_node return ir.ASTMultiplexer(self._lower_expr(expr.condnode, bits=1), { 0: false_node, 1: true_node, }) elif isinstance(expr, df.DFTerminal): return self.circuit.get_port(str(expr.name[1])) elif isinstance(expr, df.DFOperator): assert len(expr.nextnodes) == 2 or (len(expr.nextnodes) == 1 and expr.operator in ['Unot']) if expr.operator == 'Eq' and isinstance(expr.nextnodes[1], df.DFIntConst): lhs = self._lower_expr(expr.nextnodes[0]) bitsize = lhs.get_bitsize() lhs_name = self.circuit.generate_internal_signal(bitsize) lhs_port = self.circuit.get_port(lhs_name) self.circuit.children.append(ir.ASTAssign(lhs_port, lhs)) const_str = expr.nextnodes[1].value if "'" in const_str: _, _, const_str = const_str.partition("'b") const_value = int(const_str) previous = None for i in range(bitsize): my_bit = lhs_port[i] if ((const_value >> i) % 2) == 0: my_bit = ir.ASTLogicGate('not', children=[my_bit]) if previous: previous = ir.ASTLogicGate('and', children=[previous, my_bit]) else: previous = my_bit return previous lookup = { 'Lor': 'or', 'Or': 'or', 'And': 'and', 'Xor': 'xor', 'Unot': 'not' } if expr.operator in ['Plus']: return self._emit_expr_intrinsic( expr.operator, [self._lower_expr(x, bits=bits) for x in expr.nextnodes], bits=bits) return ir.ASTLogicGate( lookup[expr.operator], children=[self._lower_expr(x) for x in expr.nextnodes]) elif isinstance(expr, df.DFIntConst): return self.resolve_const(expr.eval(), bits=bits) elif isinstance(expr, df.DFPointer): mem_name = str(expr.var.name[1]) mem_init = '' if mem_name in self.mem_initial: mem_init = self.mem_initial[mem_name] reg_array = self.mem_definitions[mem_name] def p(x): return df.DFIntConst(x.value).eval() assert p(reg_array.width.lsb) == 0 assert p(reg_array.length.msb) == 0 address_bits = math.ceil( math.log2( p(reg_array.length.lsb) - p(reg_array.length.msb) + 1)) data_bits = p(reg_array.width.msb) - p(reg_array.width.lsb) + 1 address = self._lower_expr(expr.ptr, bits=address_bits) data_name = self.circuit.generate_internal_signal(data_bits) data_port = ir.ASTPort(data_name, data_bits) self.circuit.children.append( ir.ASTSubCircuit('rom', {'Address': address}, {'Data': data_port}, data={ 'address_bits': address_bits, 'data_bits': data_bits, 'contents': mem_init, })) return data_port elif isinstance(expr, df.DFConcat): pieces = [self._lower_expr(node) for node in expr.nextnodes] total_bits = sum(x.get_bitsize() for x in pieces) out_name = self.circuit.generate_internal_signal(total_bits) out_port = ir.ASTPort(out_name, total_bits) high_bit = total_bits - 1 for piece in pieces: low = high_bit - piece.get_bitsize() + 1 self.circuit.children.append( ir.ASTAssign(ir.ASTSubPort(out_name, low, high_bit), piece)) high_bit = low - 1 return out_port else: raise ValueError
def main(): internal_signals = [('a', 2), ('ALU_func', 2), ('OpTest', 1)] # order should match signals starting at index 6 of control ROM control_signals = [ 'DrREG', 'DrMEM', 'DrALU', 'DrPC', 'DrOFF', 'LdPC', 'LdIR', 'LdMAR', 'LdA', 'LdB', 'LdCmp', 'WrREG', 'WrMEM' ] for sig in control_signals: internal_signals.append((sig, 1)) i_regs = [('iReg' + str(i), 32) for i in range(16)] with CircuitBuilder('DataPath', [('clk', 1), ('b', 2)], [('bus', 32), ('PC', 32), ('IR', 32), ('micro_state', 6)] + i_regs + [('IR_imm', 32)], internal_signals): # Ld/Dr PC port('PC').assign(port('bus'), write_enable=port('LdPC')) port('bus').assign(port('PC'), enabled=port('DrPC')) # ALU port_define('ALU_A', 32).assign(port('bus'), write_enable=port('LdA')) port_define('ALU_B', 32).assign(port('bus'), write_enable=port('LdB')) sub_circuit('ALU', A=port('ALU_A'), B=port('ALU_B'), Op=port('ALU_func'), Out=port_define('ALU_Out', 32)) port('bus').assign(port('ALU_Out'), enabled=port('DrALU')) # Registers sub_circuit('RegFile', clk=port('clk'), Write=port('WrREG'), Index=port_define('RegNo', 4), In=port('bus'), Out=port_define('Reg_Out', 32), **{k: port(k) for k, _ in i_regs}) port('bus').assign(port('Reg_Out'), enabled=port('DrREG')) # Memory port_define('MAR', 32).assign(port('bus'), write_enable=port('LdMAR')) sub_circuit('Memory', In=port('bus'), Out=port_define('mem_out', 32), Address=port('MAR', 0, 15), Clock=port('clk'), Write=port('WrMEM')) port('bus').assign(port('mem_out'), enabled=port('DrMEM')) # IR port('IR').assign(port('bus'), write_enable=port('LdIR')) # poor man's sign extend lmao imm = port('IR_imm') imm[0, 19] <= port('IR', 0, 19) for i in range(20, 32): imm[i] <= port('IR', 19) port('bus').assign(imm, enabled=port('DrOFF')) # Comparison Logic sub_circuit('ComparisonLogic', Data=port('bus'), Mode=port('IR', 24, 27), Out=port_define('CmpResult', 1)) # Misc port('RegNo') <= mux( port_define('RegSel', 2), { 0b00: port('IR', 24, 27), 0b01: port('IR', 20, 23), 0b10: port('IR', 0, 3), }) sub_circuit('ControlUnit', clk=port('clk'), RegSel=port('RegSel'), ALU_func=port('ALU_func'), Cmp=port('CmpResult'), OpCode=port('IR', 28, 31), OpTest=port('OpTest'), state=port('micro_state'), **{k: port(k) for k in control_signals}) # my_logic = g_and(port('a')[0], g_or(port('a', 0), port('a', 1))) # port('a', 1).assign(my_logic, rising_edge=port('b', 0), enabled=port('b', 0)) pass with CircuitBuilder('ControlUnit', [('clk', 1), ('OpCode', 4), ('Cmp', 1)], [(x, 1) for x in control_signals] + [('RegSel', 2), ('ALU_func', 2), ('OpTest', 1), ('ChkCmp', 1), ('state', 6)]): with open('circuitsim/rom_main.dat') as f: control_rom = f.read() with open('circuitsim/rom_seq.dat') as f: control_seq_rom = f.read() with open('circuitsim/rom_cond.dat') as f: control_cond_rom = f.read() sub_circuit('rom', Address=port('state'), Data=port_define('control_vector', 25), config={ 'address_bits': 6, 'data_bits': 25, 'contents': control_rom, }) sub_circuit('rom', Address=port('OpCode'), Data=port_define('next_seq_state', 6), config={ 'address_bits': 4, 'data_bits': 6, 'contents': control_seq_rom, }) sub_circuit('rom', Address=port('Cmp'), Data=port_define('next_cmp_state', 6), config={ 'address_bits': 1, 'data_bits': 6, 'contents': control_cond_rom, }) port_define('next_state', 6) <= mux( port_define('state_mux', 2), { 0b00: port('control_vector', 0, 5), 0b01: port('next_seq_state'), 0b10: port('next_cmp_state'), }) # every clock we transition state port('state').assign(port('next_state'), write_enable=port('__const1_on')) for i, v in enumerate(control_signals): port(v) <= port('control_vector', 6 + i, 6 + i) port('RegSel') <= port('control_vector', 19, 20) port('ALU_func') <= port('control_vector', 21, 22) port('OpTest') <= port('control_vector', 23) port('ChkCmp') <= port('control_vector', 24) port('state_mux', 0) <= port('OpTest') port('state_mux', 1) <= port('ChkCmp') pass with CircuitBuilder('RegFile', [('clk', 1), ('Write', 1), ('Index', 4), ('In', 32), ('__const32_off', 32)], [('Out', 32)] + i_regs): write_enables = {} outputs = {} do_write = g_and(port('clk'), port('Write')) for i in range(1, 16): write_enables[i] = port_define('WrReg{}'.format(i), 1) outputs[i] = port_define('OutReg{}'.format(i), 32) outputs[i].assign(port('In'), write_enable=g_and(write_enables[i], port('Write'))) outputs[0] = port('__const32_off') for i in range(16): port('iReg' + str(i)) <= outputs[i] decoder(port('Index'), write_enables) port('Out') <= mux(port('Index'), outputs) with CircuitBuilder('ALU', [('A', 32), ('B', 32), ('Op', 2), ('__const32_off', 32)], [('Out', 32)]): sub_circuit('ADD32', X=port_define('add_result', 32), A=port('A'), B=port('B'), Cin=port('__const1_off')) sub_circuit('ADD32', X=port_define('sub_result', 32), A=port('A'), B=g_not(port('B')), Cin=port('__const1_on')) port_define('nand_result', 32) <= g_not(g_and(port('A'), port('B'))) sub_circuit('ADD32', X=port_define('add1_result', 32), A=port('A'), B=port('__const32_off'), Cin=port('__const1_on')) port('Out') <= mux( port('Op'), { 0b00: port('add_result'), 0b01: port('sub_result'), 0b10: port('nand_result'), 0b11: port('add1_result'), }) with CircuitBuilder('ComparisonLogic', [('Data', 32), ('Mode', 4)], [('Out', 1)]): or_sigs = [port('Data', i, i) for i in range(32)] while len(or_sigs) != 1: copy = list(or_sigs) or_sigs = [] for i in range(0, len(copy), 2): or_sigs.append(g_or(copy[i], copy[i + 1])) port('Out') <= mux(port('Mode'), { 0b00: g_not(or_sigs[0]), 0b01: port('Data', 31) }) with CircuitBuilder('ADD32', [('A', 32), ('B', 32), ('Cin', 1)], [('X', 32), ('Cout', 1)]): in_cin = 'Cin' for i in range(32): carry = 'c' + str(i) sub_circuit('ADD1', A=port('A', i), B=port('B', i), X=port('X', i), Cin=port(in_cin), Cout=port_define(carry, 1)) in_cin = carry port('Cout') <= port(in_cin) with CircuitBuilder('ADD1', [('A', 1), ('B', 1), ('Cin', 1)], [('X', 1), ('Cout', 1)]): port_define('AxorB', 1) <= g_xor(port('A'), port('B')) port('X') <= g_xor(port('AxorB'), port('Cin')) port('Cout') <= g_or(g_and(port('A'), port('B')), g_and(port('AxorB'), port('Cin'))) input = ir.ASTPort('in', 2) clock = ir.ASTPort('clock', 1) output = ir.ASTPort('out', 2) root = ir.ASTCircuit([input, clock], [output]) meh = ir.ASTPort(root.generate_internal_signal(1), 1) root.children.append(ir.ASTAssign(meh, clock)) # root.children.append(ir.ASTSubCircuit('Bar', {'a': input[0]}, {'out': clock})) root.children.append( ir.ASTAssign(output, input, enabled=ir.ASTLogicGate('and', children=[clock, meh]))) # other_root = ir.ASTCircuit([ir.ASTPort('in', 1), ir.ASTPort('in2', 1)], [ir.ASTPort('out', 1)]) _root_builder.circuits['foo'] = root with open('circuitsim/intrinsics.sim', 'r') as f: native_circuits = json.load(f)['circuits'] native_defines = [('Memory', [('Address', 16), ('In', 32), ('Clock', 1), ('Write', 1)], [('Out', 32)])] ir_lowering.compile_circuits(_root_builder.circuits, filename='circuitsim/gen2.sim', native_circuits=native_circuits)