def chain_pass(interconnect: Interconnect): # pragma: nocover for (x, y) in interconnect.tile_circuits: tile = interconnect.tile_circuits[(x, y)] tile_core = tile.core if isinstance(tile_core, MemCore): # lift ports up lift_mem_ports(tile, tile_core) previous_tile = interconnect.tile_circuits[(x, y - 1)] if not isinstance(previous_tile.core, MemCore): interconnect.wire(Const(0), tile.ports.chain_valid_in) interconnect.wire(Const(0), tile.ports.chain_data_in) else: interconnect.wire(previous_tile.ports.chain_valid_out, tile.ports.chain_valid_in) interconnect.wire(previous_tile.ports.chain_data_out, tile.ports.chain_data_in)
def tile_id_physical(interconnect: Interconnect): tile_id_width = interconnect.tile_id_width tie_hi_width = (tile_id_width // 2) + 1 if (tile_id_width % 2) == 0: tie_lo_width = tile_id_width // 2 else: tie_lo_width = (tile_id_width // 2) + 1 for (x, y) in interconnect.tile_circuits: tile = interconnect.tile_circuits[(x, y)] tile_core = tile.core if isinstance(tile_core, IOCoreValid) or tile_core is None: continue tile.add_ports(hi=magma.Out(magma.Bits[tie_hi_width]), lo=magma.Out(magma.Bits[tie_lo_width])) # wire all hi bits high tile.wire(tile.ports.hi, Const((2**tie_hi_width) - 1)) # wire all lo bits low tile.wire(tile.ports.lo, Const(0)) # Get the correct tile_id value x_bv = BitVector[tile_id_width / 2](x) y_bv = BitVector[tile_id_width / 2](y) tile_id_bv = BitVector.concat(y_bv, x_bv) # Disconnect existing constant from tile_id port tile_id_port = tile.ports.tile_id for wire in interconnect.wires: if tile_id_port in wire: interconnect.remove_wire(wire[0], wire[1]) break # Actually connect hi/lo outputs to tile_id at top level for i in range(tile_id_width): lo_index = i // 2 if (i % 2) == 0: hi_index = i // 2 else: hi_index = (i // 2) + 1 hi_port = tile.ports.hi[hi_index] lo_port = tile.ports.lo[lo_index] tie_port = hi_port if (tile_id_bv[i] == 1) else lo_port # Connect tile_id ports to hi/lo outputs instead of constant interconnect.wire(tile.ports.tile_id[i], tie_port)
def __ground_ports(self): # this is a pass to ground every sb ports that's not connected for coord, tile_dict in self.__tiles.items(): for bit_width, tile in tile_dict.items(): ground = Const(magma.Bits[bit_width](0)) for sb in tile.switchbox.get_all_sbs(): if sb.io != SwitchBoxIO.SB_IN: continue if sb.get_conn_in(): continue # no connection to that sb port, ground it sb_name = create_name(str(sb)) sb_port = self.tile_circuits[coord].ports[sb_name] self.wire(ground, sb_port)
def __wire_config_ce(self): if len(self.regs) == 0: return # fanout the stall signals to registers # invert the stall signal to clk_en invert = FromMagma(mantle.DefineInvert(1)) # FIXME: use the low bits of stall signal to stall self.wire(invert.ports.I[0], self.ports.stall[0]) for (reg_node, reg) in self.regs.values(): rmux: RegisterMuxNode = list(reg_node)[0] # get rmux address config_name = get_mux_sel_name(rmux) config_reg = self.registers[config_name] index_val = rmux.get_conn_in().index(reg_node) eq_gate = FromMagma(mantle.DefineEQ(config_reg.width)) self.wire(eq_gate.ports.I0, Const(index_val)) self.wire(eq_gate.ports.I1, config_reg.ports.O) and_gate = FromMagma(mantle.DefineAnd(2, 1)) self.wire(and_gate.ports.I0[0], eq_gate.ports.O) self.wire(and_gate.ports.I1, invert.ports.O) self.wire(reg.ports.CE, self.convert(and_gate.ports.O[0], magma.enable))
def glb_interconnect_wiring(garnet, width: int, num_cfg: int): # parallel configuration ports wiring for i in range(num_cfg): garnet.wire(garnet.global_buffer.ports.glb_to_cgra_config[i], garnet.interconnect.ports.config[i]) # input/output stream ports wiring for x in range(width): io2glb_16_port = f"io2glb_16_X{x:02X}_Y{0:02X}" io2glb_1_port = f"io2glb_1_X{x:02X}_Y{0:02X}" glb2io_16_port = f"glb2io_16_X{x:02X}_Y{0:02X}" glb2io_1_port = f"glb2io_1_X{x:02X}_Y{0:02X}" i = int(x / 4) if x % 4 == 0: garnet.wire(garnet.global_buffer.ports.io_to_cgra_rd_data[i], garnet.interconnect.ports[glb2io_16_port]) garnet.wire(garnet.global_buffer.ports.io_to_cgra_rd_data_valid[i], garnet.interconnect.ports[glb2io_1_port][0]) garnet.wire(garnet.global_buffer.ports.cgra_to_io_rd_en[i], garnet.interconnect.ports[io2glb_1_port][0]) elif x % 4 == 1: garnet.wire(garnet.global_buffer.ports.cgra_to_io_wr_data[i], garnet.interconnect.ports[io2glb_16_port]) garnet.wire(garnet.global_buffer.ports.cgra_to_io_wr_en[i], garnet.interconnect.ports[io2glb_1_port][0]) garnet.wire(garnet.interconnect.ports[glb2io_16_port], Const(magma.bits(0, 16))) garnet.wire(garnet.interconnect.ports[glb2io_1_port], Const(magma.bits(0, 1))) elif x % 4 == 2: garnet.wire(garnet.global_buffer.ports.cgra_to_io_addr_high[i], garnet.interconnect.ports[io2glb_16_port]) garnet.wire(garnet.interconnect.ports[glb2io_16_port], Const(magma.bits(0, 16))) garnet.wire(garnet.interconnect.ports[glb2io_1_port], Const(magma.bits(0, 1))) else: garnet.wire(garnet.global_buffer.ports.cgra_to_io_addr_low[i], garnet.interconnect.ports[io2glb_16_port]) garnet.wire(garnet.interconnect.ports[glb2io_16_port], Const(magma.bits(0, 16))) if x != garnet.interconnect.x_max: garnet.wire(garnet.interconnect.ports[glb2io_1_port], Const(magma.bits(0, 1))) return garnet
def apply_global_meso_wiring(interconnect: Interconnect, io_sides: IOSide): # "river routing" for global signal global_ports = interconnect.globals width, height = interconnect.x_max + 1, interconnect.y_max + 1 x_min, x_max, y_min, y_max = get_array_size(width, height, io_sides) cgra_width = x_max - x_min + 1 interconnect_read_data_or = \ FromMagma(mantle.DefineOr(cgra_width, interconnect.config_data_width)) interconnect_read_data_or.instance_name = "read_config_data_or_final" # looping through on a per-column bases for x in range(x_min, x_max + 1): column = interconnect.get_column(x) # skip the margin column = [entry for entry in column if "config" in entry.ports] # wire global inputs to first tile in column for signal in global_ports: interconnect.wire(interconnect.ports[signal], column[0].ports[signal]) # first pass to make signals pass through # pre_ports keep track of ports created by pass_signal_through pre_ports = {} for signal in global_ports: pre_ports[signal] = [] for tile in column: # use the transform pass pre_port = pass_signal_through(tile, signal) pre_ports[signal].append(pre_port) # second pass to wire them up for i in range(len(column) - 1): next_tile = column[i + 1] for signal in global_ports: pre_port = pre_ports[signal][i] interconnect.wire(pre_port, next_tile.ports[signal]) # read_config_data # Call tile function that adds input for read_data, # along with OR gate to reduce input read_data with # that tile's read_data # ports_in keep track of new ports created by or_reduction ports_in = [] for tile in column: port_in = or_reduction(tile, "read_data_mux", "read_config_data", interconnect.config_data_width) ports_in.append(port_in) # Connect 0 to first tile's read_data input interconnect.wire(ports_in[0], Const(magma.bits(0, interconnect.config_data_width))) # connect each tile's read_data output to next tile's # read_data input for i, tile in enumerate(column[:-1]): interconnect.wire(tile.ports.read_config_data, ports_in[i + 1]) # Connect the last tile's read_data output to the global OR idx = x - x_min interconnect.wire(interconnect_read_data_or.ports[f"I{idx}"], column[-1].ports.read_config_data) # wiring the read_config_data interconnect.wire(interconnect.ports.read_config_data, interconnect_read_data_or.ports.O) return interconnect_read_data_or
def finalize(self): if self.finalized: raise Exception("Circuit already finalized") self.finalized = True # add stall and reset signal self.__add_stall() self.__add_reset() # see if we really need to add config or not if not self.__should_add_config(): return self.add_ports(config=magma.In( ConfigurationType(self.full_config_addr_width, self.config_data_width)), clk=magma.In(magma.Clock), read_config_data=magma.Out( magma.Bits[self.config_data_width])) # double buffer ports if self.double_buffer: self.add_ports(config_db=magma.In(magma.Bit), use_db=magma.In(magma.Bit)) features = self.features() num_features = len(features) self.read_data_mux = MuxWithDefaultWrapper(num_features, self.config_data_width, self.config_addr_width, 0) self.read_data_mux.instance_name = "read_data_mux" # most of the logic copied from tile_magma.py # remove all hardcoded values for feature in self.features(): if "config" not in feature.ports: continue self.wire(self.ports.config.config_addr[self.feature_config_slice], feature.ports.config.config_addr) self.wire(self.ports.config.config_data, feature.ports.config.config_data) self.wire(self.ports.config.read, feature.ports.config.read) if self.double_buffer and "config_db" in feature.ports: self.wire(self.ports.config_db, feature.ports.config_db) self.wire(self.ports.use_db, feature.ports.use_db) # Connect S input to config_addr.feature. self.wire(self.ports.config.config_addr[self.feature_addr_slice], self.read_data_mux.ports.S) self.wire(self.read_data_mux.ports.O, self.ports.read_config_data) # Logic to generate EN input for read_data_mux read_and_tile = FromMagma(mantle.DefineAnd(2)) eq_tile = FromMagma(mantle.DefineEQ(self.tile_id_width)) # config_addr.tile_id == self.tile_id? self.wire(self.ports.tile_id, eq_tile.ports.I0) self.wire(self.ports.config.config_addr[self.tile_id_slice], eq_tile.ports.I1) # (config_addr.tile_id == self.tile_id) & READ self.wire(read_and_tile.ports.I0, eq_tile.ports.O) self.wire(read_and_tile.ports.I1, self.ports.config.read[0]) # read_data_mux.EN = (config_addr.tile_id == self.tile_id) & READ self.wire(read_and_tile.ports.O, self.read_data_mux.ports.EN[0]) # Logic for writing to config registers # Config_en_tile = (config_addr.tile_id == self.tile_id & WRITE) write_and_tile = FromMagma(mantle.DefineAnd(2)) self.wire(write_and_tile.ports.I0, eq_tile.ports.O) self.wire(write_and_tile.ports.I1, self.ports.config.write[0]) decode_feat = [] feat_and_config_en_tile = [] for i, feat in enumerate(self.features()): # wire each feature's read_data output to # read_data_mux inputs if "read_config_data" in feat.ports: self.wire(feat.ports.read_config_data, self.read_data_mux.ports.I[i]) else: # wire constant self.wire(Const(0), self.read_data_mux.ports.I[i]) # for each feature, # config_en = (config_addr.feature == feature_num) & config_en_tile decode_feat.append( FromMagma(mantle.DefineDecode(i, self.config_addr_width))) decode_feat[-1].instance_name = f"DECODE_FEATURE_{i}" feat_and_config_en_tile.append(FromMagma(mantle.DefineAnd(2))) feat_and_config_en_tile[-1].instance_name = f"FEATURE_AND_{i}" self.wire(decode_feat[i].ports.I, self.ports.config.config_addr[self.feature_addr_slice]) self.wire(decode_feat[i].ports.O, feat_and_config_en_tile[i].ports.I0) self.wire(write_and_tile.ports.O, feat_and_config_en_tile[i].ports.I1) if "config" in feat.ports: self.wire(feat_and_config_en_tile[i].ports.O, feat.ports.config.write[0]) if "config_en" in feat.ports: self.wire(decode_feat[i].ports.O, feat.ports["config_en"])
def __init__(self, peak_generator): super().__init__(8, 32) self.ignored_ports = { "clk_en", "reset", "config_addr", "config_data", "config_en", "read_config_data" } self.wrapper = _PeakWrapper(peak_generator) # Generate core RTL (as magma). self.peak_circuit = FromMagma(self.wrapper.rtl()) # Add input/output ports and wire them. inputs = self.wrapper.inputs() outputs = self.wrapper.outputs() for ports, dir_ in ( (inputs, magma.In), (outputs, magma.Out), ): for i, (name, typ) in enumerate(ports.items()): if name in self.ignored_ports: continue magma_type = _convert_type(typ) self.add_port(name, dir_(magma_type)) my_port = self.ports[name] if magma_type is magma.Bits[1]: my_port = my_port[0] magma_name = name if dir_ is magma.In else f"O{i}" self.wire(my_port, self.peak_circuit.ports[magma_name]) self.add_ports(config=magma.In(ConfigurationType(8, 32)), stall=magma.In(magma.Bits[1])) # Set up configuration for PE instruction. Currently, we perform a naive # partitioning of the large instruction into 32-bit config registers. config_width = self.wrapper.instruction_width() num_config = math.ceil(config_width / 32) instr_name = self.wrapper.instruction_name() self.reg_width = {} for i in range(num_config): name = f"{instr_name}_{i}" self.add_config(name, 32) lb = i * 32 ub = min(i * 32 + 32, config_width) len_ = ub - lb self.reg_width[name] = len_ self.wire(self.registers[name].ports.O[:len_], self.peak_circuit.ports[instr_name][lb:ub]) # connecting the wires # TODO: connect this wire once lassen has async reset self.wire(self.ports.reset, self.peak_circuit.ports.ASYNCRESET) # wire the fake register to the actual lassen core ports = ["config_data", "config_addr"] for port in ports: self.wire(self.ports.config[port], self.peak_circuit.ports[port]) # self.wire(reg1.ports[reg_port], self.peak_circuit.ports[port]) # wire it to 0, since we'll never going to use it self.wire(Const(0), self.peak_circuit.ports.config_en) # PE core uses clk_en (essentially active low stall) self.stallInverter = FromMagma(mantle.DefineInvert(1)) self.wire(self.stallInverter.ports.I, self.ports.stall) self.wire(self.stallInverter.ports.O[0], self.peak_circuit.ports.clk_en) self._setup_config()
def __init__(self, data_width, word_width, data_depth, num_banks, use_sram_stub): super().__init__(8, 32) self.data_width = data_width self.data_depth = data_depth self.num_banks = num_banks self.word_width = word_width if use_sram_stub: self.use_sram_stub = 1 else: self.use_sram_stub = 0 TData = magma.Bits[self.word_width] TBit = magma.Bits[1] self.add_ports( data_in=magma.In(TData), addr_in=magma.In(TData), data_out=magma.Out(TData), flush=magma.In(TBit), wen_in=magma.In(TBit), ren_in=magma.In(TBit), stall=magma.In(magma.Bits[4]), valid_out=magma.Out(TBit), switch_db=magma.In(TBit) ) # Instead of a single read_config_data, we have multiple for each # "sub"-feature of this core. # self.ports.pop("read_config_data") if (data_width, word_width, data_depth, num_banks, use_sram_stub) not in \ MemCore.__circuit_cache: wrapper = memory_core_genesis2.memory_core_wrapper param_mapping = memory_core_genesis2.param_mapping generator = wrapper.generator(param_mapping, mode="declare") circ = generator(data_width=self.data_width, data_depth=self.data_depth, word_width=self.word_width, num_banks=self.num_banks, use_sram_stub=self.use_sram_stub) MemCore.__circuit_cache[(data_width, word_width, data_depth, num_banks, use_sram_stub)] = circ else: circ = MemCore.__circuit_cache[(data_width, word_width, data_depth, num_banks, use_sram_stub)] self.underlying = FromMagma(circ) # put a 1-bit register and a mux to select the control signals control_signals = ["wen_in", "ren_in", "flush", "switch_db"] for control_signal in control_signals: # TODO: consult with Ankita to see if we can use the normal # mux here mux = MuxWrapper(2, 1, name=f"{control_signal}_sel") reg_value_name = f"{control_signal}_reg_value" reg_sel_name = f"{control_signal}_reg_sel" self.add_config(reg_value_name, 1) self.add_config(reg_sel_name, 1) self.wire(mux.ports.I[0], self.ports[control_signal]) self.wire(mux.ports.I[1], self.registers[reg_value_name].ports.O) self.wire(mux.ports.S, self.registers[reg_sel_name].ports.O) # 0 is the default wire, which takes from the routing network self.wire(mux.ports.O[0], self.underlying.ports[control_signal]) self.wire(self.ports.data_in, self.underlying.ports.data_in) self.wire(self.ports.addr_in, self.underlying.ports.addr_in) self.wire(self.ports.data_out, self.underlying.ports.data_out) self.wire(self.ports.reset, self.underlying.ports.reset) self.wire(self.ports.clk, self.underlying.ports.clk) self.wire(self.ports.valid_out[0], self.underlying.ports.valid_out) # PE core uses clk_en (essentially active low stall) self.stallInverter = FromMagma(mantle.DefineInvert(1)) self.wire(self.stallInverter.ports.I, self.ports.stall[0:1]) self.wire(self.stallInverter.ports.O[0], self.underlying.ports.clk_en) zero_signals = ( ("chain_wen_in", 1), ("chain_in", self.word_width), ) one_signals = ( ("config_read", 1), ("config_write", 1) ) # enable read and write by default for name, width in zero_signals: val = magma.bits(0, width) if width > 1 else magma.bit(0) self.wire(Const(val), self.underlying.ports[name]) for name, width in one_signals: val = magma.bits(1, width) if width > 1 else magma.bit(1) self.wire(Const(val), self.underlying.ports[name]) self.wire(Const(magma.bits(0, 24)), self.underlying.ports.config_addr[0:24]) # we have five features in total # 0: TILE # 1-4: SMEM # Feature 0: Tile self.__features: List[CoreFeature] = [self] # Features 1-4: SRAM for sram_index in range(4): core_feature = CoreFeature(self, sram_index + 1) self.__features.append(core_feature) # Wire the config for idx, core_feature in enumerate(self.__features): if(idx > 0): self.add_port(f"config_{idx}", magma.In(ConfigurationType(8, 32))) # port aliasing core_feature.ports["config"] = self.ports[f"config_{idx}"] self.add_port("config", magma.In(ConfigurationType(8, 32))) # or the signal up t = ConfigurationType(8, 32) t_names = ["config_addr", "config_data"] or_gates = {} for t_name in t_names: port_type = t[t_name] or_gate = FromMagma(mantle.DefineOr(len(self.__features), len(port_type))) or_gate.instance_name = f"OR_{t_name}_FEATURE" for idx, core_feature in enumerate(self.__features): self.wire(or_gate.ports[f"I{idx}"], core_feature.ports.config[t_name]) or_gates[t_name] = or_gate self.wire(or_gates["config_addr"].ports.O, self.underlying.ports.config_addr[24:32]) self.wire(or_gates["config_data"].ports.O, self.underlying.ports.config_data) # only the first one has config_en # self.wire(self.__features[0].ports.config.write[0], # self.underlying.ports.config_en) # read data out for idx, core_feature in enumerate(self.__features): if(idx > 0): self.add_port(f"read_config_data_{idx}", magma.Out(magma.Bits[32])) # port aliasing core_feature.ports["read_config_data"] = \ self.ports[f"read_config_data_{idx}"] # MEM config # self.wire(self.ports.read_config_data, # self.underlying.ports.read_config_data) configurations = [ ("stencil_width", 32), ("read_mode", 1), ("arbitrary_addr", 1), ("starting_addr", 32), ("iter_cnt", 32), ("dimensionality", 32), ("circular_en", 1), ("almost_count", 4), ("enable_chain", 1), ("mode", 2), ("tile_en", 1), ("chain_idx", 4), ("depth", 13) ] # Do all the stuff for the main config main_feature = self.__features[0] for config_reg_name, width in configurations: main_feature.add_config(config_reg_name, width) if(width == 1): self.wire(main_feature.registers[config_reg_name].ports.O[0], self.underlying.ports[config_reg_name]) else: self.wire(main_feature.registers[config_reg_name].ports.O, self.underlying.ports[config_reg_name]) for idx in range(8): main_feature.add_config(f"stride_{idx}", 32) main_feature.add_config(f"range_{idx}", 32) self.wire(main_feature.registers[f"stride_{idx}"].ports.O, self.underlying.ports[f"stride_{idx}"]) self.wire(main_feature.registers[f"range_{idx}"].ports.O, self.underlying.ports[f"range_{idx}"]) # SRAM for sram_index in range(4): core_feature = self.__features[sram_index + 1] self.wire(core_feature.ports.read_config_data, self.underlying.ports[f"read_data_sram_{sram_index}"]) # also need to wire the sram signal self.wire(core_feature.ports.config.write[0], self.underlying.ports["config_en_sram"][sram_index]) self._setup_config() conf_names = list(self.registers.keys()) conf_names.sort() with open("mem_cfg.txt", "w+") as cfg_dump: for idx, reg in enumerate(conf_names): write_line = f"|{reg}|{idx}|{self.registers[reg].width}||\n" cfg_dump.write(write_line)
def __set_tile_id(self): for (x, y), tile in self.tile_circuits.items(): tile_id = self.get_tile_id(x, y) self.wire(tile.ports.tile_id, Const(magma.Bits[self.tile_id_width](tile_id)))
def __init__(self, data_width, data_depth): super().__init__(8, 32) self.data_width = data_width self.data_depth = data_depth TData = magma.Bits[self.data_width] TBit = magma.Bits[1] self.add_ports(data_in=magma.In(TData), addr_in=magma.In(TData), data_out=magma.Out(TData), flush=magma.In(TBit), wen_in=magma.In(TBit), ren_in=magma.In(TBit), stall=magma.In(magma.Bits[4])) # Instead of a single read_config_data, we have multiple for each # "sub"-feature of this core. self.ports.pop("read_config_data") wrapper = memory_core_genesis2.memory_core_wrapper param_mapping = memory_core_genesis2.param_mapping generator = wrapper.generator(param_mapping, mode="declare") circ = generator(data_width=self.data_width, data_depth=self.data_depth) self.underlying = FromMagma(circ) self.wire(self.ports.data_in, self.underlying.ports.data_in) self.wire(self.ports.addr_in, self.underlying.ports.addr_in) self.wire(self.ports.data_out, self.underlying.ports.data_out) self.wire(self.ports.reset, self.underlying.ports.reset) self.wire(self.ports.flush[0], self.underlying.ports.flush) self.wire(self.ports.wen_in[0], self.underlying.ports.wen_in) self.wire(self.ports.ren_in[0], self.underlying.ports.ren_in) # PE core uses clk_en (essentially active low stall) self.stallInverter = FromMagma(mantle.DefineInvert(1)) self.wire(self.stallInverter.ports.I, self.ports.stall[0:1]) self.wire(self.stallInverter.ports.O[0], self.underlying.ports.clk_en) # TODO(rsetaluri): Actually wire these inputs. zero_signals = ( ("config_en_linebuf", 1), ("chain_wen_in", 1), ("chain_in", self.data_width), ) one_signals = ( ("config_read", 1), ("config_write", 1), ) # enable read and write by default for name, width in zero_signals: val = magma.bits(0, width) if width > 1 else magma.bit(0) self.wire(Const(val), self.underlying.ports[name]) for name, width in one_signals: val = magma.bits(1, width) if width > 1 else magma.bit(1) self.wire(Const(val), self.underlying.ports[name]) self.wire(Const(magma.bits(0, 24)), self.underlying.ports.config_addr[0:24]) # we have five features in total # 0: LINEBUF # 1-4: SMEM # current setup is already in line buffer mode, so we pass self in # notice that config_en_linebuf is to change the address in the # line buffer mode, which is not used in practice self.__features: List[CoreFeature] = [CoreFeature(self, 0)] for sram_index in range(4): core_feature = CoreFeature(self, sram_index + 1) self.__features.append(core_feature) for idx, core_feature in enumerate(self.__features): self.add_port(f"config_{idx}", magma.In(ConfigurationType(8, 32))) # port aliasing core_feature.ports["config"] = self.ports[f"config_{idx}"] # or the signal up t = ConfigurationType(8, 32) t_names = ["config_addr", "config_data"] or_gates = {} for t_name in t_names: port_type = t[t_name] or_gate = FromMagma( mantle.DefineOr(len(self.__features), len(port_type))) or_gate.instance_name = f"OR_{t_name}_FEATURE" for idx, core_feature in enumerate(self.__features): self.wire(or_gate.ports[f"I{idx}"], core_feature.ports.config[t_name]) or_gates[t_name] = or_gate self.wire(or_gates["config_addr"].ports.O, self.underlying.ports.config_addr[24:32]) self.wire(or_gates["config_data"].ports.O, self.underlying.ports.config_data) # only the first one has config_en self.wire(self.__features[0].ports.config.write[0], self.underlying.ports.config_en) # read data out for idx, core_feature in enumerate(self.__features): self.add_port(f"read_config_data_{idx}", magma.Out(magma.Bits[32])) # port aliasing core_feature.ports["read_config_data"] = \ self.ports[f"read_config_data_{idx}"] # MEM config self.wire(self.ports.read_config_data_0, self.underlying.ports.read_data) # SRAM for sram_index in range(4): core_feature = self.__features[sram_index + 1] self.wire(core_feature.ports.read_config_data, self.underlying.ports[f"read_data_sram_{sram_index}"]) # also need to wire the sram signal self.add_port(f"config_en_{sram_index}", magma.In(magma.Bit)) # port aliasing core_feature.ports["config_en"] = \ self.ports[f"config_en_{sram_index}"] self.wire(self.underlying.ports["config_en_sram"][sram_index], self.ports[f"config_en_{sram_index}"])