def test_mux_wrapper(height, width): """ Test that the mux wrapper circuit works as expected. Specifically, we initialize a mux with random height and width, and check that the output is as expected for select in range [0, height). Note that we do not check the behavior with sel >= height, because this is undefined behavior. """ mux = MuxWrapper(height, width) assert mux.height == height assert mux.width == width assert mux.name() == f"MuxWrapper_{height}_{width}" mux_circuit = mux.circuit() tester = fault.Tester(mux_circuit) inputs = [fault.random.random_bv(width) for _ in range(height)] for i, input_ in enumerate(inputs): tester.poke(mux_circuit.I[i], input_) for i in range(height): tester.poke(mux_circuit.S, BitVector(i, mux.sel_bits)) tester.eval() tester.expect(mux_circuit.O, inputs[i]) with tempfile.TemporaryDirectory() as tempdir: tester.compile_and_run(directory=tempdir, magma_output="coreir-verilog", flags=["-Wno-fatal"])
def __init__(self, num_tracks, width): super().__init__() if num_tracks <= 1: raise ValueError("num_tracks must be > 1") self.num_tracks = num_tracks self.width = width sel_bits = magma.bitutils.clog2(self.num_tracks) self.mux = MuxWrapper(self.num_tracks, self.width) T = magma.Bits(self.width) self.add_ports( I=magma.In(magma.Array(self.num_tracks, T)), O=magma.Out(T), clk=magma.In(magma.Clock), reset=magma.In(magma.AsyncReset), config=magma.In(ConfigurationType(8, 32)), read_config_data=magma.Out(magma.Bits(32)), ) self.add_configs(S=sel_bits) # read_config_data output num_config_reg = len(self.registers) if (num_config_reg > 1): self.read_config_data_mux = MuxWrapper(num_config_reg, 32) self.wire(self.ports.config.config_addr, self.read_config_data_mux.ports.S) self.wire(self.read_config_data_mux.ports.O, self.ports.read_config_data) for idx, reg in enumerate(self.registers.values()): self.wire(reg.ports.O, self.read_config_data_mux.ports.I[idx]) # Wire up config register resets self.wire(reg.ports.reset, self.ports.reset) # If we only have 1 config register, we don't need a mux # Wire sole config register directly to read_config_data_output else: reg = list(self.registers.values())[0] zext = ZextWrapper(reg.width, 32) self.wire(reg.ports.O, zext.ports.I) zext_out = zext.ports.O self.wire(zext_out, self.ports.read_config_data) self.wire(self.ports.I, self.mux.ports.I) self.wire(self.registers.S.ports.O, self.mux.ports.S) self.wire(self.mux.ports.O, self.ports.O) for idx, reg in enumerate(self.registers.values()): reg.set_addr(idx) reg.set_addr_width(8) reg.set_data_width(32) self.wire(self.ports.config.config_addr, reg.ports.config_addr) self.wire(self.ports.config.config_data, reg.ports.config_data) # Connect config_en for each config reg self.wire(reg.ports.config_en, self.ports.config.write[0])
def __init__(self, num_inputs, width, sel_bits, default): super().__init__() self.num_inputs = num_inputs self.width = width self.sel_bits = sel_bits self.default = default if 2**self.sel_bits <= self.num_inputs: raise ValueError(f"(2 ^ sel_bits) must be > num_inputs " f"(sel_bits={self.sel_bits}, " f"num_inputs={self.num_inputs})") self.data_mux = MuxWrapper(self.num_inputs, self.width) self.default_mux = MuxWrapper(2, self.width) lt = mantle.DefineULT(self.sel_bits) and_gate = mantle.DefineAnd(2) self.lt = FromMagma(lt) self.and_gate = FromMagma(and_gate) T = magma.Bits(self.width) self.add_ports( I=magma.In(magma.Array(self.num_inputs, T)), S=magma.In(magma.Bits(self.sel_bits)), EN=magma.In(magma.Bits(1)), O=magma.Out(T), ) # Wire data inputs to data mux. for i in range(self.num_inputs): self.wire(self.ports.I[i], self.data_mux.ports.I[i]) # Wire select input to select input of data_mux. Note that we only wire # the first clog2(num_inputs) bits of the select input. self.wire(self.ports.S[:self.data_mux.sel_bits], self.data_mux.ports.S) # Wire default value to first input of default mux, and output of # data_mux to second input of default mux. default_mux_inputs = [ Const(magma.bits(self.default, self.width)), self.data_mux.ports.O, ] for i, mux_in in enumerate(default_mux_inputs): self.wire(mux_in, self.default_mux.ports.I[i]) # Generate select logic for default mux: # sel = (S < num_inputs) & EN self.wire(self.ports.S, self.lt.ports.I0) self.wire(Const(magma.bits(self.num_inputs, self.sel_bits)), self.lt.ports.I1) self.wire(self.lt.ports.O, self.and_gate.ports.I0) self.wire(self.ports.EN[0], self.and_gate.ports.I1) self.wire(self.and_gate.ports.O, self.default_mux.ports.S[0]) self.wire(self.default_mux.ports.O, self.ports.O)
def __make_muxs(self, sides): height_per_layer = { 1: 3 + len(self.inputs[1]), 16: 3 + len(self.inputs[16]), } muxs = {} for side in sides: for layer, height in height_per_layer.items(): for track in range(5): muxs[(side, layer, track)] = MuxWrapper(height, layer) return muxs
def __make_register_buffer(self, unbuffered_mux): signal_in = unbuffered_mux.ports.O width = get_width(signal_in.type()) # register = Register(width) RegisterCls = DefineRegister(width) register = FromMagma(RegisterCls) mux = MuxWrapper(2, width) self.wire(signal_in, mux.ports.I[0]) self.wire(signal_in, register.ports.I) self.wire(register.ports.O, mux.ports.I[1]) return mux
def _setup_config(self): # sort the registers by it's name. this will be the order of config # addr index config_names = list(self.registers.keys()) config_names.sort() for idx, config_name in enumerate(config_names): reg = self.registers[config_name] # set the configuration registers reg.set_addr(idx) reg.set_addr_width(self.config_addr_width) reg.set_data_width(self.config_data_width) self.wire(self.ports.config.config_addr, reg.ports.config_addr) self.wire(self.ports.config.config_data, reg.ports.config_data) self.wire(self.ports.config.write[0], reg.ports.config_en) self.wire(self.ports.reset, reg.ports.reset) # read_config_data output num_config_reg = len(config_names) if num_config_reg > 1: self.read_config_data_mux = MuxWrapper(num_config_reg, self.config_data_width) sel_bits = self.read_config_data_mux.sel_bits # Wire up config_addr to select input of read_data MUX self.wire(self.ports.config.config_addr[:sel_bits], self.read_config_data_mux.ports.S) self.wire(self.read_config_data_mux.ports.O, self.ports.read_config_data) for idx, config_name in enumerate(config_names): reg = self.registers[config_name] zext = ZextWrapper(reg.width, self.config_data_width) self.wire(reg.ports.O, zext.ports.I) zext_out = zext.ports.O self.wire(zext_out, self.read_config_data_mux.ports.I[idx]) else: config_name = config_names[0] reg = self.registers[config_name] zext = ZextWrapper(reg.width, self.config_data_width) self.wire(reg.ports.O, zext.ports.I) zext_out = zext.ports.O self.wire(zext_out, self.ports.read_config_data)
def create_mux(node: Node): conn_in = node.get_conn_in() height = len(conn_in) mux = MuxWrapper(height, node.width) return mux
def __init__(self, inputs): super().__init__() self.all_inputs = inputs self.inputs = self.__organize_inputs(inputs) self.add_ports( north=SideType(5, (1, 16)), west=SideType(5, (1, 16)), south=SideType(5, (1, 16)), east=SideType(5, (1, 16)), clk=magma.In(magma.Clock), reset=magma.In(magma.AsyncReset), config=magma.In(ConfigurationType(8, 32)), read_config_data=magma.Out(magma.Bits(32)), ) # TODO(rsetaluri): Clean up this logic. for i, input_ in enumerate(self.all_inputs): assert input_.type().isoutput() port_name = f"{input_._name}" self.add_port(port_name, magma.In(input_.type())) sides = (self.ports.north, self.ports.west, self.ports.south, self.ports.east) self.muxs = self.__make_muxs(sides) for (side, layer, track), mux in self.muxs.items(): idx = 0 for side_in in sides: if side_in == side: continue mux_in = getattr(side_in.I, f"layer{layer}")[track] self.wire(mux_in, mux.ports.I[idx]) idx += 1 for input_ in self.inputs[layer]: port_name = input_._name self.wire(self.ports[port_name], mux.ports.I[idx]) idx += 1 buffered_mux = self.__make_register_buffer(mux) mux_out = getattr(side.O, f"layer{layer}")[track] self.wire(buffered_mux.ports.O, mux_out) # Add corresponding config register. config_name = f"mux_{side._name}_{layer}_{track}" config_name_mux = config_name + '_sel' config_name_buffer = config_name + '_buffer_sel' self.add_config(config_name_mux, mux.sel_bits) self.wire(self.registers[config_name_mux].ports.O, mux.ports.S) self.add_config(config_name_buffer, buffered_mux.sel_bits) self.wire(self.registers[config_name_buffer].ports.O, buffered_mux.ports.S) # NOTE(rsetaluri): We set the config register addresses explicitly and # in a well-defined order. This ordering can be considered a part of # the functional spec of this module. idx = 0 for side in sides: for layer in (1, 16): for track in range(5): reg_name = f"mux_{side._name}_{layer}_{track}" reg_name_mux = reg_name + '_sel' reg_name_buffer = reg_name + '_buffer_sel' self.registers[reg_name_mux].set_addr(idx) idx += 1 self.registers[reg_name_buffer].set_addr(idx) idx += 1 for idx, reg in enumerate(self.registers.values()): reg.set_addr_width(8) reg.set_data_width(32) self.wire(self.ports.config.config_addr, reg.ports.config_addr) self.wire(self.ports.config.config_data, reg.ports.config_data) self.wire(self.ports.config.write[0], reg.ports.config_en) self.wire(self.ports.reset, reg.ports.reset) # read_config_data output num_config_reg = len(self.registers) if(num_config_reg > 1): self.read_config_data_mux = MuxWrapper(num_config_reg, 32) sel_bits = self.read_config_data_mux.sel_bits # Wire up config_addr to select input of read_data MUX # TODO(rsetaluri): Make this a mux with default. self.wire(self.ports.config.config_addr[:sel_bits], self.read_config_data_mux.ports.S) self.wire(self.read_config_data_mux.ports.O, self.ports.read_config_data) for idx, reg in enumerate(self.registers.values()): zext = ZextWrapper(reg.width, 32) self.wire(reg.ports.O, zext.ports.I) zext_out = zext.ports.O self.wire(zext_out, self.read_config_data_mux.ports.I[idx]) # If we only have 1 config register, we don't need a mux # Wire sole config register directly to read_config_data_output else: self.wire(self.registers[0].ports.O, self.ports.read_config_data)