Пример #1
0
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)
Пример #2
0
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)
Пример #3
0
 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)
Пример #4
0
 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))
Пример #5
0
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
Пример #6
0
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
Пример #7
0
    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"])
Пример #8
0
    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()
Пример #9
0
    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)
Пример #10
0
 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)))
Пример #11
0
    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}"])