def __init__(self, config_addr_width: int, config_data_width: int): super().__init__() self.config_addr_width = config_addr_width self.config_data_width = config_data_width config_type = ConfigurationType(config_addr_width, config_data_width) self.add_ports(clk=magma.In(magma.Clock), config=magma.In(config_type), config_out=magma.Out(config_type)) # Pipeline registers config_addr_reg = FromMagma(DefineRegister(config_addr_width)) config_data_reg = FromMagma(DefineRegister(config_data_width)) config_read_reg = FromMagma(DefineRegister(1)) config_write_reg = FromMagma(DefineRegister(1)) # Wire pipeline reg inputs self.wire(self.ports.config.config_addr, config_addr_reg.ports.I) self.wire(self.ports.config.config_data, config_data_reg.ports.I) self.wire(self.ports.config.read, config_read_reg.ports.I) self.wire(self.ports.config.write, config_write_reg.ports.I) # Wire pipeline reg outputs self.wire(config_addr_reg.ports.O, self.ports.config_out.config_addr) self.wire(config_data_reg.ports.O, self.ports.config_out.config_data) self.wire(config_read_reg.ports.O, self.ports.config_out.read) self.wire(config_write_reg.ports.O, self.ports.config_out.write)
def config_port_pass(interconnect: Interconnect): # x coordinate of garnet x_min = interconnect.x_min x_max = interconnect.x_max width = x_max - x_min + 1 # Add parallel configuration ports to interconnect # interconnect must have config port assert "config" in interconnect.ports interconnect.remove_port("config") config_data_width = interconnect.config_data_width # config_addr_width is same as config_data_width interconnect.add_port( "config", magma.In(magma.Array[ width, ConfigurationType(config_data_width, config_data_width)])) # looping through on a per-column bases for x_coor in range(x_min, x_min + width): column = interconnect.get_column(x_coor) # skip tiles with no config column = [entry for entry in column if "config" in entry.ports] # wire configuration ports to first tile in column interconnect.wire(interconnect.ports.config[x_coor], column[0].ports.config)
def __init__(self, config_addr_width: int, config_data_width: int): super().__init__(config_addr_width, config_data_width) self.config = PDCGRAConfig() # ps self.add_config(self.config.ps_config_name, config_data_width) self.add_ports(config=magma.In( ConfigurationType(config_addr_width, config_data_width)), ) self._setup_config()
def __init__(self, config_addr_width: int, config_data_width: int): super().__init__(config_addr_width, config_data_width) ps_config_name = "ps_en" # ps self.add_config(ps_config_name, 1) self.add_ports(config=magma.In( ConfigurationType(config_addr_width, config_data_width)), ) self.add_port("ps_en_out", magma.Out(magma.Bits[1])) self.wire(self.ports.ps_en_out, self.registers.ps_en.ports.O) self._setup_config()
def _lift_interconnect_ports(self, config_data_width): for name in self.interconnect.interface(): self.add_port(name, self.interconnect.ports[name].type()) self.wire(self.ports[name], self.interconnect.ports[name]) self.add_ports( clk=magma.In(magma.Clock), reset=magma.In(magma.AsyncReset), config=magma.In( ConfigurationType(self.interconnect.config_data_width, self.interconnect.config_data_width)), stall=magma.In(magma.Bits[self.interconnect.stall_signal_width]), read_config_data=magma.Out(magma.Bits[config_data_width])) self.wire(self.ports.clk, self.interconnect.ports.clk) self.wire(self.ports.reset, self.interconnect.ports.reset) self.wire(self.ports.config, self.interconnect.ports.config) self.wire(self.ports.stall, self.interconnect.ports.stall) self.wire(self.interconnect.ports.read_config_data, self.ports.read_config_data)
def __init__(self, peak_generator): super().__init__(8, 32) 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()): 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)), ) # TODO(rsetaluri): Figure out stall signals. # 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() 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.wire(self.registers[name].ports.O[:len_], self.peak_circuit.ports[instr_name][lb:ub]) self._setup_config()
def __add_global_ports(self, stall_signal_width: int): self.add_ports(config=magma.In( ConfigurationType(self.config_data_width, self.config_data_width)), clk=magma.In(magma.Clock), reset=magma.In(magma.AsyncReset), stall=magma.In(magma.Bits[stall_signal_width])) if self.double_buffer: self.add_ports(config_db=magma.In(magma.Bit), use_db=magma.In(magma.Bit)) return (self.ports.config.qualified_name(), self.ports.clk.qualified_name(), self.ports.reset.qualified_name(), self.ports.stall.qualified_name(), self.ports.config_db.qualified_name(), self.ports.use_db.qualified_name()) return (self.ports.config.qualified_name(), self.ports.clk.qualified_name(), self.ports.reset.qualified_name(), self.ports.stall.qualified_name())
def __init__(self, node: PortNode, config_addr_width: int, config_data_width: int, double_buffer: bool = False): if not isinstance(node, PortNode): raise ValueError(node, PortNode.__name__) self.node: PortNode = node super().__init__(config_addr_width, config_data_width, double_buffer=double_buffer) self.mux = create_mux(self.node) # lift the port to the top level self.add_ports(I=self.mux.ports.I.base_type(), O=self.mux.ports.O.base_type()) self.wire(self.ports.I, self.mux.ports.I) self.wire(self.ports.O, self.mux.ports.O) if self.mux.height > 1: self.add_ports(config=magma.In( ConfigurationType(config_addr_width, config_data_width)), ) config_name = get_mux_sel_name(self.node) self.add_config(config_name, self.mux.sel_bits) self.wire(self.registers[config_name].ports.O, self.mux.ports.S) else: # remove clk and reset ports from the base class since it's going # to be a pass through wire anyway self.ports.pop("clk") self.ports.pop("reset") self.ports.pop("read_config_data") if self.double_buffer: self.ports.pop("use_db") self.ports.pop("config_db") self._setup_config() self.instance_name = self.name()
def apply_global_parallel_meso_wiring(interconnect: Interconnect, io_sides: IOSide, num_cfg: int = 1): interconnect_read_data_or = apply_global_meso_wiring(interconnect, io_sides) # interconnect must have config port assert "config" in interconnect.ports # there must be at least one configuration path assert num_cfg >= 1 interconnect.remove_port("config") # this is not a typo. Total number of bits in configuration address # is same as config_data config_data_width = interconnect.config_data_width interconnect.add_port( "config", magma.In(magma.Array[num_cfg, ConfigurationType(config_data_width, config_data_width)])) cgra_width = interconnect.x_max - interconnect.x_min + 1 # number of CGRA columns one configuration controller is in charge of col_per_config = math.ceil(cgra_width / num_cfg) # looping through on a per-column bases for x_coor in range(interconnect.x_min, interconnect.x_max + 1): column = interconnect.get_column(x_coor) # skip tiles with no config column = [entry for entry in column if "config" in entry.ports] # select which configuration controller is connected to that column config_sel = int(x_coor/col_per_config) # wire configuration ports to first tile in column interconnect.wire(interconnect.ports.config[config_sel], column[0].ports.config) return interconnect_read_data_or
def __init__(self, width, height, add_pd, interconnect_only: bool = False, use_sram_stub: bool = True, standalone: bool = False, add_pond: bool = True, use_io_valid: bool = False, pipeline_config_interval: int = 8, glb_params: GlobalBufferParams = GlobalBufferParams(), pe_fc=lassen_fc): super().__init__() # Check consistency of @standalone and @interconnect_only parameters. If # @standalone is True, then interconnect_only must also be True. if standalone: assert interconnect_only # configuration parameters self.glb_params = glb_params config_addr_width = 32 config_data_width = 32 self.config_addr_width = config_addr_width self.config_data_width = config_data_width axi_addr_width = 13 axi_data_width = 32 # axi_data_width must be same as cgra config_data_width assert axi_data_width == config_data_width tile_id_width = 16 config_addr_reg_width = 8 num_tracks = 5 # size self.width = width self.height = height # only north side has IO if standalone: io_side = IOSide.None_ else: io_side = IOSide.North self.pe_fc = pe_fc if not interconnect_only: # width must be even number assert (self.width % 2) == 0 # Bank should be larger than or equal to 1KB assert glb_params.bank_addr_width >= 10 glb_tile_mem_size = 2 ** (glb_params.bank_addr_width - 10) + \ math.ceil(math.log(glb_params.banks_per_tile, 2)) wiring = GlobalSignalWiring.ParallelMeso self.global_controller = GlobalController(addr_width=config_addr_width, data_width=config_data_width, axi_addr_width=axi_addr_width, axi_data_width=axi_data_width, num_glb_tiles=glb_params.num_glb_tiles, glb_addr_width=glb_params.glb_addr_width, glb_tile_mem_size=glb_tile_mem_size, block_axi_addr_width=glb_params.axi_addr_width) self.global_buffer = GlobalBufferMagma(glb_params) else: wiring = GlobalSignalWiring.Meso interconnect = create_cgra(width, height, io_side, reg_addr_width=config_addr_reg_width, config_data_width=config_data_width, tile_id_width=tile_id_width, num_tracks=num_tracks, add_pd=add_pd, add_pond=add_pond, use_io_valid=use_io_valid, use_sram_stub=use_sram_stub, global_signal_wiring=wiring, pipeline_config_interval=pipeline_config_interval, mem_ratio=(1, 4), standalone=standalone, pe_fc=pe_fc) self.interconnect = interconnect # make multiple stall ports stall_port_pass(self.interconnect) # make multiple configuration ports config_port_pass(self.interconnect) if not interconnect_only: self.add_ports( jtag=JTAGType, clk_in=magma.In(magma.Clock), reset_in=magma.In(magma.AsyncReset), proc_packet=ProcPacketIfc( glb_params.glb_addr_width, glb_params.bank_data_width).slave, axi4_slave=AXI4LiteIfc(axi_addr_width, axi_data_width).slave, interrupt=magma.Out(magma.Bit), cgra_running_clk_out=magma.Out(magma.Clock), ) # top <-> global controller ports connection self.wire(self.ports.clk_in, self.global_controller.ports.clk_in) self.wire(self.ports.reset_in, self.global_controller.ports.reset_in) self.wire(self.ports.jtag, self.global_controller.ports.jtag) self.wire(self.ports.axi4_slave, self.global_controller.ports.axi4_slave) self.wire(self.ports.interrupt, self.global_controller.ports.interrupt) self.wire(self.ports.cgra_running_clk_out, self.global_controller.ports.clk_out) # top <-> global buffer ports connection self.wire(self.ports.clk_in, self.global_buffer.ports.clk) self.wire(self.ports.proc_packet.wr_en, self.global_buffer.ports.proc_wr_en[0]) self.wire(self.ports.proc_packet.wr_strb, self.global_buffer.ports.proc_wr_strb) self.wire(self.ports.proc_packet.wr_addr, self.global_buffer.ports.proc_wr_addr) self.wire(self.ports.proc_packet.wr_data, self.global_buffer.ports.proc_wr_data) self.wire(self.ports.proc_packet.rd_en, self.global_buffer.ports.proc_rd_en[0]) self.wire(self.ports.proc_packet.rd_addr, self.global_buffer.ports.proc_rd_addr) self.wire(self.ports.proc_packet.rd_data, self.global_buffer.ports.proc_rd_data) self.wire(self.ports.proc_packet.rd_data_valid, self.global_buffer.ports.proc_rd_data_valid[0]) # Top -> Interconnect clock port connection self.wire(self.ports.clk_in, self.interconnect.ports.clk) glb_glc_wiring(self) glb_interconnect_wiring(self) glc_interconnect_wiring(self) else: # lift all the interconnect ports up for name in self.interconnect.interface(): self.add_port(name, self.interconnect.ports[name].type()) self.wire(self.ports[name], self.interconnect.ports[name]) self.add_ports( clk=magma.In(magma.Clock), reset=magma.In(magma.AsyncReset), config=magma.In(magma.Array[width, ConfigurationType(config_data_width, config_data_width)]), stall=magma.In( magma.Bits[self.width * self.interconnect.stall_signal_width]), read_config_data=magma.Out(magma.Bits[config_data_width]) ) self.wire(self.ports.clk, self.interconnect.ports.clk) self.wire(self.ports.reset, self.interconnect.ports.reset) self.wire(self.ports.config, self.interconnect.ports.config) self.wire(self.ports.stall, self.interconnect.ports.stall) self.wire(self.interconnect.ports.read_config_data, self.ports.read_config_data)
def __init__( self, data_width=16, # CGRA Params mem_width=16, mem_depth=256, banks=1, input_iterator_support=6, # Addr Controllers output_iterator_support=6, input_config_width=16, output_config_width=16, interconnect_input_ports=1, # Connection to int interconnect_output_ports=1, mem_input_ports=1, mem_output_ports=1, use_sram_stub=True, sram_macro_info=SRAMMacroInfo(), read_delay=1, # Cycle delay in read (SRAM vs Register File) rw_same_cycle=True, # Does the memory allow r+w in same cycle? agg_height=4, tb_sched_max=16, config_data_width=32, config_addr_width=8, num_tiles=1, remove_tb=False, fifo_mode=False, add_clk_enable=True, add_flush=True, override_name=None): # name if override_name: self.__name = override_name + "Core" lake_name = override_name else: self.__name = "MemCore" lake_name = "LakeTop" super().__init__(config_addr_width, config_data_width) # Capture everything to the tile object self.data_width = data_width self.mem_width = mem_width self.mem_depth = mem_depth self.banks = banks self.fw_int = int(self.mem_width / self.data_width) self.input_iterator_support = input_iterator_support self.output_iterator_support = output_iterator_support self.input_config_width = input_config_width self.output_config_width = output_config_width self.interconnect_input_ports = interconnect_input_ports self.interconnect_output_ports = interconnect_output_ports self.mem_input_ports = mem_input_ports self.mem_output_ports = mem_output_ports self.use_sram_stub = use_sram_stub self.sram_macro_info = sram_macro_info self.read_delay = read_delay self.rw_same_cycle = rw_same_cycle self.agg_height = agg_height self.config_data_width = config_data_width self.config_addr_width = config_addr_width self.num_tiles = num_tiles self.remove_tb = remove_tb self.fifo_mode = fifo_mode self.add_clk_enable = add_clk_enable self.add_flush = add_flush # self.app_ctrl_depth_width = app_ctrl_depth_width # self.stcl_valid_iter = stcl_valid_iter # Typedefs for ease TData = magma.Bits[self.data_width] TBit = magma.Bits[1] self.__inputs = [] self.__outputs = [] # cache_key = (self.data_width, self.mem_width, self.mem_depth, self.banks, # self.input_iterator_support, self.output_iterator_support, # self.interconnect_input_ports, self.interconnect_output_ports, # self.use_sram_stub, self.sram_macro_info, self.read_delay, # self.rw_same_cycle, self.agg_height, self.max_agg_schedule, # self.input_max_port_sched, self.output_max_port_sched, # self.align_input, self.max_line_length, self.max_tb_height, # self.tb_range_max, self.tb_sched_max, self.max_tb_stride, # self.num_tb, self.tb_iterator_support, self.multiwrite, # self.max_prefetch, self.config_data_width, self.config_addr_width, # self.num_tiles, self.remove_tb, self.fifo_mode, self.stcl_valid_iter, # self.add_clk_enable, self.add_flush, self.app_ctrl_depth_width) cache_key = (self.data_width, self.mem_width, self.mem_depth, self.banks, self.input_iterator_support, self.output_iterator_support, self.interconnect_input_ports, self.interconnect_output_ports, self.use_sram_stub, self.sram_macro_info, self.read_delay, self.rw_same_cycle, self.agg_height, self.config_data_width, self.config_addr_width, self.num_tiles, self.remove_tb, self.fifo_mode, self.add_clk_enable, self.add_flush) # Check for circuit caching if cache_key not in MemCore.__circuit_cache: # Instantiate core object here - will only use the object representation to # query for information. The circuit representation will be cached and retrieved # in the following steps. # lt_dut = LakeTop(data_width=self.data_width, # mem_width=self.mem_width, # mem_depth=self.mem_depth, # banks=self.banks, # input_iterator_support=self.input_iterator_support, # output_iterator_support=self.output_iterator_support, # input_config_width=self.input_config_width, # output_config_width=self.output_config_width, # interconnect_input_ports=self.interconnect_input_ports, # interconnect_output_ports=self.interconnect_output_ports, # use_sram_stub=self.use_sram_stub, # sram_macro_info=self.sram_macro_info, # read_delay=self.read_delay, # rw_same_cycle=self.rw_same_cycle, # agg_height=self.agg_height, # max_agg_schedule=self.max_agg_schedule, # input_max_port_sched=self.input_max_port_sched, # output_max_port_sched=self.output_max_port_sched, # align_input=self.align_input, # max_line_length=self.max_line_length, # max_tb_height=self.max_tb_height, # tb_range_max=self.tb_range_max, # tb_range_inner_max=self.tb_range_inner_max, # tb_sched_max=self.tb_sched_max, # max_tb_stride=self.max_tb_stride, # num_tb=self.num_tb, # tb_iterator_support=self.tb_iterator_support, # multiwrite=self.multiwrite, # max_prefetch=self.max_prefetch, # config_data_width=self.config_data_width, # config_addr_width=self.config_addr_width, # num_tiles=self.num_tiles, # app_ctrl_depth_width=self.app_ctrl_depth_width, # remove_tb=self.remove_tb, # fifo_mode=self.fifo_mode, # add_clk_enable=self.add_clk_enable, # add_flush=self.add_flush, # stcl_valid_iter=self.stcl_valid_iter) lt_dut = LakeTop( data_width=self.data_width, mem_width=self.mem_width, mem_depth=self.mem_depth, banks=self.banks, input_iterator_support=self.input_iterator_support, output_iterator_support=self.output_iterator_support, input_config_width=self.input_config_width, output_config_width=self.output_config_width, interconnect_input_ports=self.interconnect_input_ports, interconnect_output_ports=self.interconnect_output_ports, use_sram_stub=self.use_sram_stub, sram_macro_info=self.sram_macro_info, read_delay=self.read_delay, rw_same_cycle=self.rw_same_cycle, agg_height=self.agg_height, config_data_width=self.config_data_width, config_addr_width=self.config_addr_width, num_tiles=self.num_tiles, remove_tb=self.remove_tb, fifo_mode=self.fifo_mode, add_clk_enable=self.add_clk_enable, add_flush=self.add_flush, name=lake_name, gen_addr=False) change_sram_port_pass = change_sram_port_names( use_sram_stub, sram_macro_info) circ = kts.util.to_magma( lt_dut, flatten_array=True, check_multiple_driver=False, optimize_if=False, check_flip_flop_always_ff=False, additional_passes={"change_sram_port": change_sram_port_pass}) MemCore.__circuit_cache[cache_key] = (circ, lt_dut) else: circ, lt_dut = MemCore.__circuit_cache[cache_key] # Save as underlying circuit object self.underlying = FromMagma(circ) # Enumerate input and output ports # (clk and reset are assumed) core_interface = get_interface(lt_dut) cfgs = extract_top_config(lt_dut) assert len(cfgs) > 0, "No configs?" # We basically add in the configuration bus differently # than the other ports... skip_names = [ "config_data_in", "config_write", "config_addr_in", "config_data_out", "config_read", "config_en", "clk_en" ] # Create a list of signals that will be able to be # hardwired to a constant at runtime... control_signals = [] # The rest of the signals to wire to the underlying representation... other_signals = [] # for port_name, port_size, port_width, is_ctrl, port_dir, explicit_array in core_interface: for io_info in core_interface: if io_info.port_name in skip_names: continue ind_ports = io_info.port_width intf_type = TBit # For our purposes, an explicit array means the inner data HAS to be 16 bits if io_info.expl_arr: ind_ports = io_info.port_size[0] intf_type = TData dir_type = magma.In app_list = self.__inputs if io_info.port_dir == "PortDirection.Out": dir_type = magma.Out app_list = self.__outputs if ind_ports > 1: for i in range(ind_ports): self.add_port(f"{io_info.port_name}_{i}", dir_type(intf_type)) app_list.append(self.ports[f"{io_info.port_name}_{i}"]) else: self.add_port(io_info.port_name, dir_type(intf_type)) app_list.append(self.ports[io_info.port_name]) # classify each signal for wiring to underlying representation... if io_info.is_ctrl: control_signals.append((io_info.port_name, io_info.port_width)) else: if ind_ports > 1: for i in range(ind_ports): other_signals.append( (f"{io_info.port_name}_{i}", io_info.port_dir, io_info.expl_arr, i, io_info.port_name)) else: other_signals.append( (io_info.port_name, io_info.port_dir, io_info.expl_arr, 0, io_info.port_name)) assert (len(self.__outputs) > 0) # We call clk_en stall at this level for legacy reasons???? self.add_ports(stall=magma.In(TBit), ) self.chain_idx_bits = max(1, kts.clog2(self.num_tiles)) # put a 1-bit register and a mux to select the control signals for control_signal, width in control_signals: if width == 1: 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][0]) else: for i in range(width): mux = MuxWrapper(2, 1, name=f"{control_signal}_{i}_sel") reg_value_name = f"{control_signal}_{i}_reg_value" reg_sel_name = f"{control_signal}_{i}_reg_sel" self.add_config(reg_value_name, 1) self.add_config(reg_sel_name, 1) self.wire(mux.ports.I[0], self.ports[f"{control_signal}_{i}"]) 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][i]) # Wire the other signals up... for pname, pdir, expl_arr, ind, uname in other_signals: # If we are in an explicit array moment, use the given wire name... if expl_arr is False: # And if not, use the index self.wire(self.ports[pname][0], self.underlying.ports[uname][ind]) else: self.wire(self.ports[pname], self.underlying.ports[pname]) # CLK, RESET, and STALL PER STANDARD PROCEDURE # Need to invert this self.resetInverter = FromMagma(mantle.DefineInvert(1)) self.wire(self.resetInverter.ports.I[0], self.ports.reset) self.wire(self.resetInverter.ports.O[0], self.underlying.ports.rst_n) self.wire(self.ports.clk, self.underlying.ports.clk) # Mem 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.underlying.ports.clk_en[0]) # we have six? features in total # 0: TILE # 1: TILE # 1-4: SMEM # Feature 0: Tile self.__features: List[CoreFeature] = [self] # Features 1-4: SRAM self.num_sram_features = lt_dut.total_sets for sram_index in range(self.num_sram_features): 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(self.config_addr_width, self.config_data_width))) # port aliasing core_feature.ports["config"] = self.ports[f"config_{idx}"] self.add_port( "config", magma.In( ConfigurationType(self.config_addr_width, self.config_data_width))) # or the signal up t = ConfigurationType(self.config_addr_width, self.config_data_width) 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_in[0:self.config_addr_width]) self.wire(or_gates["config_data"].ports.O, self.underlying.ports.config_data_in) # read data out for idx, core_feature in enumerate(self.__features): if (idx > 0): # self.add_port(f"read_config_data_{idx}", self.add_port(f"read_config_data_{idx}", magma.Out(magma.Bits[self.config_data_width])) # port aliasing core_feature.ports["read_config_data"] = \ self.ports[f"read_config_data_{idx}"] # MEM Config configurations = [] # merged_configs = [] skip_cfgs = [] for cfg_info in cfgs: if cfg_info.port_name in skip_cfgs: continue if cfg_info.expl_arr: if cfg_info.port_size[0] > 1: for i in range(cfg_info.port_size[0]): configurations.append( (f"{cfg_info.port_name}_{i}", cfg_info.port_width)) else: configurations.append( (cfg_info.port_name, cfg_info.port_width)) else: configurations.append( (cfg_info.port_name, cfg_info.port_width)) # 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][0]) else: self.wire(main_feature.registers[config_reg_name].ports.O, self.underlying.ports[config_reg_name]) # SRAM # These should also account for num features # or_all_cfg_rd = FromMagma(mantle.DefineOr(4, 1)) or_all_cfg_rd = FromMagma(mantle.DefineOr(self.num_sram_features, 1)) or_all_cfg_rd.instance_name = f"OR_CONFIG_WR_SRAM" or_all_cfg_wr = FromMagma(mantle.DefineOr(self.num_sram_features, 1)) or_all_cfg_wr.instance_name = f"OR_CONFIG_RD_SRAM" for sram_index in range(self.num_sram_features): core_feature = self.__features[sram_index + 1] 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}"] # Sort of a temp hack - the name is just config_data_out if self.num_sram_features == 1: self.wire(core_feature.ports.read_config_data, self.underlying.ports["config_data_out"]) else: self.wire( core_feature.ports.read_config_data, self.underlying.ports[f"config_data_out_{sram_index}"]) # also need to wire the sram signal # the config enable is the OR of the rd+wr or_gate_en = FromMagma(mantle.DefineOr(2, 1)) or_gate_en.instance_name = f"OR_CONFIG_EN_SRAM_{sram_index}" self.wire(or_gate_en.ports.I0, core_feature.ports.config.write) self.wire(or_gate_en.ports.I1, core_feature.ports.config.read) self.wire(core_feature.ports.config_en, self.underlying.ports["config_en"][sram_index]) # Still connect to the OR of all the config rd/wr self.wire(core_feature.ports.config.write, or_all_cfg_wr.ports[f"I{sram_index}"]) self.wire(core_feature.ports.config.read, or_all_cfg_rd.ports[f"I{sram_index}"]) self.wire(or_all_cfg_rd.ports.O[0], self.underlying.ports.config_read[0]) self.wire(or_all_cfg_wr.ports.O[0], self.underlying.ports.config_write[0]) 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}\", 0), # {self.registers[reg].width}\n" cfg_dump.write(write_line) with open("mem_synth.txt", "w+") as cfg_dump: for idx, reg in enumerate(conf_names): write_line = f"{reg}\n" cfg_dump.write(write_line)
def __init__(self, num_banks, num_io, num_cfg, bank_addr_width, glb_addr_width=32, cfg_addr_width=32, cfg_data_width=32, axi_addr_width=12): super().__init__() self.num_banks = num_banks self.bank_addr_width = bank_addr_width self.glb_addr_width = glb_addr_width self.num_io = num_io self.num_cfg = num_cfg self.bank_data = 64 self.cgra_data = 16 self.cfg_addr_width = cfg_addr_width self.cfg_data_width = cfg_data_width self.axi_addr_width = axi_addr_width self.config_type = ConfigurationType(self.cfg_addr_width, self.cfg_data_width) self.axi_config_type = ConfigurationType(self.axi_addr_width, self.cfg_data_width) self.add_ports( clk=magma.In(magma.Clock), reset=magma.In(magma.AsyncReset), soc_data=SoCDataType(self.glb_addr_width, self.bank_data), cgra_to_io_wr_en=magma.In(magma.Array[self.num_io, magma.Bit]), cgra_to_io_rd_en=magma.In(magma.Array[self.num_io, magma.Bit]), io_to_cgra_rd_data_valid=magma.Out(magma.Array[self.num_io, magma.Bit]), cgra_to_io_wr_data=magma.In( magma.Array[self.num_io, magma.Bits[self.cgra_data]]), io_to_cgra_rd_data=magma.Out( magma.Array[self.num_io, magma.Bits[self.cgra_data]]), cgra_to_io_addr_high=magma.In( magma.Array[self.num_io, magma.Bits[self.cgra_data]]), cgra_to_io_addr_low=magma.In( magma.Array[self.num_io, magma.Bits[self.cgra_data]]), glc_to_io_stall=magma.In(magma.Bit), cgra_start_pulse=magma.In(magma.Bit), cgra_done_pulse=magma.Out(magma.Bit), config_start_pulse=magma.In(magma.Bit), config_done_pulse=magma.Out(magma.Bit), cgra_config=magma.In(self.config_type), glb_to_cgra_config=magma.Out(magma.Array[self.num_cfg, self.config_type]), glb_config=magma.In(self.axi_config_type), glb_config_rd_data=magma.Out(magma.Bits[self.cfg_data_width]), glb_sram_config=magma.In(self.config_type), glb_sram_config_rd_data=magma.Out(magma.Bits[self.cfg_data_width])) wrapper = global_buffer_genesis2.glb_wrapper param_mapping = global_buffer_genesis2.param_mapping generator = wrapper.generator(param_mapping, mode="declare") circ = generator(num_banks=self.num_banks, num_io=self.num_io, num_cfg=self.num_cfg, bank_addr_width=self.bank_addr_width, cfg_addr_width=self.cfg_addr_width, cfg_data=self.cfg_data_width) self.underlying = FromMagma(circ) self.wire(self.ports.clk, self.underlying.ports.clk) self.wire(self.ports.reset, self.underlying.ports.reset) self.wire(self.ports.soc_data.wr_strb, self.underlying.ports.host_wr_strb) self.wire(self.ports.soc_data.wr_addr, self.underlying.ports.host_wr_addr) self.wire(self.ports.soc_data.wr_data, self.underlying.ports.host_wr_data) self.wire(self.ports.soc_data.rd_en, self.underlying.ports.host_rd_en) self.wire(self.ports.soc_data.rd_addr, self.underlying.ports.host_rd_addr) self.wire(self.ports.soc_data.rd_data, self.underlying.ports.host_rd_data) for i in range(self.num_io): self.wire(self.ports.cgra_to_io_wr_en[i], self.underlying.ports.cgra_to_io_wr_en[i]) self.wire(self.ports.cgra_to_io_rd_en[i], self.underlying.ports.cgra_to_io_rd_en[i]) self.wire(self.ports.io_to_cgra_rd_data_valid[i], self.underlying.ports.io_to_cgra_rd_data_valid[i]) self.wire( self.ports.cgra_to_io_wr_data[i], self.underlying.ports.cgra_to_io_wr_data[i * self.cgra_data:(i + 1) * self.cgra_data]) self.wire( self.ports.io_to_cgra_rd_data[i], self.underlying.ports.io_to_cgra_rd_data[i * self.cgra_data:(i + 1) * self.cgra_data]) self.wire( self.ports.cgra_to_io_addr_high[i], self.underlying.ports.cgra_to_io_addr_high[i * self.cgra_data:(i + 1) * self.cgra_data]) self.wire( self.ports.cgra_to_io_addr_low[i], self.underlying.ports.cgra_to_io_addr_low[i * self.cgra_data:(i + 1) * self.cgra_data]) for i in range(self.num_cfg): self.wire(self.ports.glb_to_cgra_config[i].write[0], self.underlying.ports.glb_to_cgra_cfg_wr[i]) self.wire(self.ports.glb_to_cgra_config[i].read[0], self.underlying.ports.glb_to_cgra_cfg_rd[i]) self.wire( self.ports.glb_to_cgra_config[i].config_addr, self.underlying. ports.glb_to_cgra_cfg_addr[i * self.cfg_addr_width:(i + 1) * self.cfg_addr_width]) self.wire( self.ports.glb_to_cgra_config[i].config_data, self.underlying. ports.glb_to_cgra_cfg_data[i * self.cfg_data_width:(i + 1) * self.cfg_data_width]) self.wire(self.ports.glc_to_io_stall, self.underlying.ports.glc_to_io_stall) self.wire(self.ports.cgra_config.write[0], self.underlying.ports.glc_to_cgra_cfg_wr) self.wire(self.ports.cgra_config.read[0], self.underlying.ports.glc_to_cgra_cfg_rd) self.wire(self.ports.cgra_config.config_addr, self.underlying.ports.glc_to_cgra_cfg_addr) self.wire(self.ports.cgra_config.config_data, self.underlying.ports.glc_to_cgra_cfg_data) self.wire(self.ports.cgra_start_pulse, self.underlying.ports.cgra_start_pulse) self.wire(self.ports.cgra_done_pulse, self.underlying.ports.cgra_done_pulse) self.wire(self.ports.config_start_pulse, self.underlying.ports.config_start_pulse) self.wire(self.ports.config_done_pulse, self.underlying.ports.config_done_pulse) self.wire(self.ports.glb_config.write[0], self.underlying.ports.glb_config_wr) self.wire(self.ports.glb_config.read[0], self.underlying.ports.glb_config_rd) self.wire(self.ports.glb_config.config_data, self.underlying.ports.glb_config_wr_data) self.wire(self.ports.glb_config.config_addr, self.underlying.ports.glb_config_addr) self.wire(self.ports.glb_config_rd_data, self.underlying.ports.glb_config_rd_data) self.wire(self.ports.glb_sram_config.write[0], self.underlying.ports.glb_sram_config_wr) self.wire(self.ports.glb_sram_config.read[0], self.underlying.ports.glb_sram_config_rd) self.wire(self.ports.glb_sram_config.config_data, self.underlying.ports.glb_sram_config_wr_data) self.wire(self.ports.glb_sram_config.config_addr, self.underlying.ports.glb_sram_config_addr) self.wire(self.ports.glb_sram_config_rd_data, self.underlying.ports.glb_sram_config_rd_data)
def wrap_lake_core(self): # Typedefs for ease if self.data_width: TData = magma.Bits[self.data_width] else: TData = magma.Bits[ 16] # This shouldn't be used if the data_width was None TBit = magma.Bits[1] # Enumerate input and output ports # (clk and reset are assumed) core_interface = get_interface(self.dut) cfgs = extract_top_config(self.dut) assert len(cfgs) > 0, "No configs?" # We basically add in the configuration bus differently # than the other ports... skip_names = [ "config_data_in", "config_write", "config_addr_in", "config_data_out", "config_read", "config_en", "clk_en" ] # Create a list of signals that will be able to be # hardwired to a constant at runtime... control_signals = [] # The rest of the signals to wire to the underlying representation... other_signals = [] # for port_name, port_size, port_width, is_ctrl, port_dir, explicit_array in core_interface: for io_info in core_interface: if io_info.port_name in skip_names: continue ind_ports = io_info.port_width intf_type = TBit # For our purposes, an explicit array means the inner data HAS to be 16 bits if io_info.expl_arr: ind_ports = io_info.port_size[0] intf_type = TData dir_type = magma.In app_list = self.__inputs if io_info.port_dir == "PortDirection.Out": dir_type = magma.Out app_list = self.__outputs if ind_ports > 1: for i in range(ind_ports): self.add_port(f"{io_info.port_name}_{i}", dir_type(intf_type)) app_list.append(self.ports[f"{io_info.port_name}_{i}"]) else: self.add_port(io_info.port_name, dir_type(intf_type)) app_list.append(self.ports[io_info.port_name]) # classify each signal for wiring to underlying representation... if io_info.is_ctrl: control_signals.append((io_info.port_name, io_info.port_width)) else: if ind_ports > 1: for i in range(ind_ports): other_signals.append( (f"{io_info.port_name}_{i}", io_info.port_dir, io_info.expl_arr, i, io_info.port_name)) else: other_signals.append( (io_info.port_name, io_info.port_dir, io_info.expl_arr, 0, io_info.port_name)) assert (len(self.__outputs) > 0) # We call clk_en stall at this level for legacy reasons???? self.add_ports(stall=magma.In(TBit), ) # put a 1-bit register and a mux to select the control signals for control_signal, width in control_signals: if width == 1: 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][0]) else: for i in range(width): mux = MuxWrapper(2, 1, name=f"{control_signal}_{i}_sel") reg_value_name = f"{control_signal}_{i}_reg_value" reg_sel_name = f"{control_signal}_{i}_reg_sel" self.add_config(reg_value_name, 1) self.add_config(reg_sel_name, 1) self.wire(mux.ports.I[0], self.ports[f"{control_signal}_{i}"]) 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][i]) # Wire the other signals up... for pname, pdir, expl_arr, ind, uname in other_signals: # If we are in an explicit array moment, use the given wire name... if expl_arr is False: # And if not, use the index self.wire(self.ports[pname][0], self.underlying.ports[uname][ind]) else: self.wire(self.ports[pname], self.underlying.ports[pname]) # CLK, RESET, and STALL PER STANDARD PROCEDURE # Need to invert this self.resetInverter = FromMagma(mantle.DefineInvert(1)) self.wire(self.resetInverter.ports.I[0], self.ports.reset) self.wire( self.convert(self.resetInverter.ports.O[0], magma.asyncreset), self.underlying.ports.rst_n) self.wire(self.ports.clk, self.underlying.ports.clk) # Mem 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.underlying.ports.clk_en[0]) # we have six? features in total # 0: TILE # 1: TILE # 1-4: SMEM # Feature 0: Tile self.__features: List[CoreFeature] = [self] # Features 1-4: SRAM self.num_sram_features = self.dut.total_sets for sram_index in range(self.num_sram_features): core_feature = CoreFeature(self, sram_index + 1) core_feature.skip_compression = True 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(self.config_addr_width, self.config_data_width))) # port aliasing core_feature.ports["config"] = self.ports[f"config_{idx}"] self.add_port( "config", magma.In( ConfigurationType(self.config_addr_width, self.config_data_width))) if self.num_sram_features > 0: # or the signal up t = ConfigurationType(self.config_addr_width, self.config_data_width) 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_in[0:self.config_addr_width]) self.wire(or_gates["config_data"].ports.O, self.underlying.ports.config_data_in) # read data out for idx, core_feature in enumerate(self.__features): if (idx > 0): # self.add_port(f"read_config_data_{idx}", self.add_port(f"read_config_data_{idx}", magma.Out(magma.Bits[self.config_data_width])) # port aliasing core_feature.ports["read_config_data"] = \ self.ports[f"read_config_data_{idx}"] # MEM Config configurations = [] # merged_configs = [] skip_cfgs = [] for cfg_info in cfgs: if cfg_info.port_name in skip_cfgs: continue if cfg_info.expl_arr: if cfg_info.port_size[0] > 1: for i in range(cfg_info.port_size[0]): configurations.append( (f"{cfg_info.port_name}_{i}", cfg_info.port_width)) else: configurations.append( (cfg_info.port_name, cfg_info.port_width)) else: configurations.append( (cfg_info.port_name, cfg_info.port_width)) # 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][0]) else: self.wire(main_feature.registers[config_reg_name].ports.O, self.underlying.ports[config_reg_name]) # SRAM # These should also account for num features # or_all_cfg_rd = FromMagma(mantle.DefineOr(4, 1)) if self.num_sram_features > 0: or_all_cfg_rd = FromMagma( mantle.DefineOr(self.num_sram_features, 1)) or_all_cfg_rd.instance_name = f"OR_CONFIG_WR_SRAM" or_all_cfg_wr = FromMagma( mantle.DefineOr(self.num_sram_features, 1)) or_all_cfg_wr.instance_name = f"OR_CONFIG_RD_SRAM" for sram_index in range(self.num_sram_features): core_feature = self.__features[sram_index + 1] 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}"] # Sort of a temp hack - the name is just config_data_out if self.num_sram_features == 1: self.wire(core_feature.ports.read_config_data, self.underlying.ports["config_data_out"]) else: self.wire( core_feature.ports.read_config_data, self.underlying.ports[f"config_data_out_{sram_index}"]) and_gate_en = FromMagma(mantle.DefineAnd(2, 1)) and_gate_en.instance_name = f"AND_CONFIG_EN_SRAM_{sram_index}" # also need to wire the sram signal # the config enable is the OR of the rd+wr or_gate_en = FromMagma(mantle.DefineOr(2, 1)) or_gate_en.instance_name = f"OR_CONFIG_EN_SRAM_{sram_index}" self.wire(or_gate_en.ports.I0, core_feature.ports.config.write) self.wire(or_gate_en.ports.I1, core_feature.ports.config.read) self.wire(and_gate_en.ports.I0, or_gate_en.ports.O) self.wire(and_gate_en.ports.I1[0], core_feature.ports.config_en) self.wire(and_gate_en.ports.O[0], self.underlying.ports["config_en"][sram_index]) # Still connect to the OR of all the config rd/wr self.wire(core_feature.ports.config.write, or_all_cfg_wr.ports[f"I{sram_index}"]) self.wire(core_feature.ports.config.read, or_all_cfg_rd.ports[f"I{sram_index}"]) self.wire(or_all_cfg_rd.ports.O[0], self.underlying.ports.config_read[0]) self.wire(or_all_cfg_wr.ports.O[0], self.underlying.ports.config_write[0]) self._setup_config()
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, 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}"])
def __init__(self, num_glb_tiles, num_cgra_cols, banks_per_tile=2, bank_addr_width=17, bank_data_width=64, cgra_data_width=16, axi_addr_width=12, axi_data_width=32, cfg_addr_width=32, cfg_data_width=32, parameter_only: bool = False): super().__init__() self.num_glb_tiles = num_glb_tiles self.num_cgra_cols = num_cgra_cols # the number of glb tiles is half the number of cgra columns assert 2 * self.num_glb_tiles == self.num_cgra_cols self.col_per_tile = num_cgra_cols // num_glb_tiles self.banks_per_tile = banks_per_tile self.bank_addr_width = bank_addr_width self.bank_data_width = bank_data_width self.bank_byte_offset = magma.bitutils.clog2(self.bank_data_width // 8) self.cgra_data_width = cgra_data_width self.cgra_byte_offset = magma.bitutils.clog2(self.cgra_data_width // 8) self.axi_addr_width = axi_addr_width self.axi_data_width = axi_data_width self.axi_strb_width = self.axi_data_width // 8 self.axi_byte_offset = magma.bitutils.clog2(self.axi_data_width // 8) self.cfg_addr_width = cfg_addr_width self.cfg_data_width = cfg_data_width self.glb_addr_width = (self.bank_addr_width + magma.bitutils.clog2(self.banks_per_tile) + magma.bitutils.clog2(self.num_glb_tiles)) self.tile_sel_addr_width = m.bitutils.clog2(self.num_glb_tiles) self.cgra_per_glb = self.num_cgra_cols // self.num_glb_tiles self.bank_sel_addr_width = m.bitutils.clog2(self.banks_per_tile) self.cgra_cfg_type = ConfigurationType(self.cfg_addr_width, self.cfg_data_width) self.max_num_words_width = ( self.glb_addr_width - self.bank_byte_offset + magma.bitutils.clog2(bank_data_width // cgra_data_width)) self.max_stride_width = self.axi_data_width - self.max_num_words_width self.max_num_cfgs_width = self.glb_addr_width - self.bank_byte_offset self.queue_depth = 4 self.loop_level = 4 self.latency_width = 1 + magma.bitutils.clog2(self.num_glb_tiles) self.add_ports( clk=magma.In(magma.Clock), reset=magma.In(magma.AsyncReset), stall=magma.In(magma.Bit), cgra_stall_in=magma.In(magma.Bit), cgra_soft_reset=magma.In(magma.Bit), proc_packet=ProcPacketIfc(self.glb_addr_width, self.bank_data_width).slave, glb_cfg=GlbCfgIfc(self.axi_addr_width, self.axi_data_width).slave, sram_cfg=GlbCfgIfc(self.glb_addr_width, self.axi_data_width).slave, stream_data_f2g=magma.In( magma.Array[self.num_cgra_cols, magma.Bits[self.cgra_data_width]]), stream_data_valid_f2g=magma.In(magma.Array[self.num_cgra_cols, magma.Bits[1]]), stream_data_g2f=magma.Out( magma.Array[self.num_cgra_cols, magma.Bits[self.cgra_data_width]]), stream_data_valid_g2f=magma.Out(magma.Array[self.num_cgra_cols, magma.Bits[1]]), cgra_cfg_jtag=magma.In(self.cgra_cfg_type), cgra_cfg_g2f=magma.Out(magma.Array[self.num_cgra_cols, self.cgra_cfg_type]), cgra_stall=magma.Out(magma.Bits[self.num_cgra_cols]), strm_start_pulse=magma.In(magma.Bits[self.num_glb_tiles]), pc_start_pulse=magma.In(magma.Bits[self.num_glb_tiles]), strm_f2g_interrupt_pulse=magma.Out(magma.Bits[self.num_glb_tiles]), strm_g2f_interrupt_pulse=magma.Out(magma.Bits[self.num_glb_tiles]), pcfg_g2f_interrupt_pulse=magma.Out(magma.Bits[self.num_glb_tiles])) # parameter self.param = GlobalBufferParams( NUM_GLB_TILES=self.num_glb_tiles, TILE_SEL_ADDR_WIDTH=(self.tile_sel_addr_width), NUM_CGRA_TILES=self.num_cgra_cols, CGRA_PER_GLB=self.cgra_per_glb, BANKS_PER_TILE=self.banks_per_tile, BANK_SEL_ADDR_WIDTH=(self.bank_sel_addr_width), BANK_DATA_WIDTH=self.bank_data_width, BANK_ADDR_WIDTH=self.bank_addr_width, BANK_BYTE_OFFSET=self.bank_byte_offset, GLB_ADDR_WIDTH=self.glb_addr_width, CGRA_DATA_WIDTH=self.cgra_data_width, CGRA_BYTE_OFFSET=self.cgra_byte_offset, AXI_ADDR_WIDTH=self.axi_addr_width, AXI_DATA_WIDTH=self.axi_data_width, AXI_STRB_WIDTH=self.axi_strb_width, AXI_BYTE_OFFSET=self.axi_byte_offset, MAX_NUM_WORDS_WIDTH=(self.max_num_words_width), MAX_STRIDE_WIDTH=(self.max_stride_width), MAX_NUM_CFGS_WIDTH=(self.max_num_cfgs_width), CGRA_CFG_ADDR_WIDTH=self.cfg_addr_width, CGRA_CFG_DATA_WIDTH=self.cfg_data_width, QUEUE_DEPTH=self.queue_depth, LOOP_LEVEL=self.loop_level, LATENCY_WIDTH=self.latency_width) if parameter_only: gen_param_files(self.param) return self.underlying = FromMagma( GlobalBufferDeclarationGenerator(self.param)) # wiring self.wire(self.ports.clk, self.underlying.ports.clk) self.wire(self.ports.stall, self.underlying.ports.stall) self.wire(self.ports.cgra_stall_in, self.underlying.ports.cgra_stall_in) self.wire(self.ports.reset, self.underlying.ports.reset) self.wire(self.ports.cgra_soft_reset, self.underlying.ports.cgra_soft_reset) self.wire(self.ports.proc_packet.wr_en, self.underlying.ports.proc_wr_en) self.wire(self.ports.proc_packet.wr_strb, self.underlying.ports.proc_wr_strb) self.wire(self.ports.proc_packet.wr_addr, self.underlying.ports.proc_wr_addr) self.wire(self.ports.proc_packet.wr_data, self.underlying.ports.proc_wr_data) self.wire(self.ports.proc_packet.rd_en, self.underlying.ports.proc_rd_en) self.wire(self.ports.proc_packet.rd_addr, self.underlying.ports.proc_rd_addr) self.wire(self.ports.proc_packet.rd_data, self.underlying.ports.proc_rd_data) self.wire(self.ports.proc_packet.rd_data_valid, self.underlying.ports.proc_rd_data_valid) self.wire(self.ports.glb_cfg.wr_en, self.underlying.ports.if_cfg_wr_en) self.wire(self.ports.glb_cfg.wr_clk_en, self.underlying.ports.if_cfg_wr_clk_en) self.wire(self.ports.glb_cfg.wr_addr, self.underlying.ports.if_cfg_wr_addr) self.wire(self.ports.glb_cfg.wr_data, self.underlying.ports.if_cfg_wr_data) self.wire(self.ports.glb_cfg.rd_en, self.underlying.ports.if_cfg_rd_en) self.wire(self.ports.glb_cfg.rd_clk_en, self.underlying.ports.if_cfg_rd_clk_en) self.wire(self.ports.glb_cfg.rd_addr, self.underlying.ports.if_cfg_rd_addr) self.wire(self.ports.glb_cfg.rd_data, self.underlying.ports.if_cfg_rd_data) self.wire(self.ports.glb_cfg.rd_data_valid, self.underlying.ports.if_cfg_rd_data_valid) self.wire(self.ports.sram_cfg.wr_en, self.underlying.ports.if_sram_cfg_wr_en) self.wire(self.ports.sram_cfg.wr_clk_en, self.underlying.ports.if_sram_cfg_wr_clk_en) self.wire(self.ports.sram_cfg.wr_addr, self.underlying.ports.if_sram_cfg_wr_addr) self.wire(self.ports.sram_cfg.wr_data, self.underlying.ports.if_sram_cfg_wr_data) self.wire(self.ports.sram_cfg.rd_en, self.underlying.ports.if_sram_cfg_rd_en) self.wire(self.ports.sram_cfg.rd_clk_en, self.underlying.ports.if_sram_cfg_rd_clk_en) self.wire(self.ports.sram_cfg.rd_addr, self.underlying.ports.if_sram_cfg_rd_addr) self.wire(self.ports.sram_cfg.rd_data, self.underlying.ports.if_sram_cfg_rd_data) self.wire(self.ports.sram_cfg.rd_data_valid, self.underlying.ports.if_sram_cfg_rd_data_valid) for i in range(self.num_cgra_cols): self.wire( self.ports.stream_data_f2g[i], self.underlying.ports.stream_data_f2g[i * self.cgra_data_width: (i + 1) * self.cgra_data_width]) self.wire(self.ports.stream_data_valid_f2g[i][0], self.underlying.ports.stream_data_valid_f2g[i]) self.wire( self.ports.stream_data_g2f[i], self.underlying.ports.stream_data_g2f[i * self.cgra_data_width: (i + 1) * self.cgra_data_width]) self.wire(self.ports.stream_data_valid_g2f[i][0], self.underlying.ports.stream_data_valid_g2f[i]) self.wire(self.ports.cgra_stall, self.underlying.ports.cgra_stall) self.wire(self.ports.cgra_cfg_jtag.write, self.underlying.ports.cgra_cfg_jtag_gc2glb_wr_en) self.wire(self.ports.cgra_cfg_jtag.read, self.underlying.ports.cgra_cfg_jtag_gc2glb_rd_en) self.wire(self.ports.cgra_cfg_jtag.config_addr, self.underlying.ports.cgra_cfg_jtag_gc2glb_addr) self.wire(self.ports.cgra_cfg_jtag.config_data, self.underlying.ports.cgra_cfg_jtag_gc2glb_data) for i in range(self.num_cgra_cols): self.wire(self.ports.cgra_cfg_g2f[i].write[0], self.underlying.ports.cgra_cfg_g2f_cfg_wr_en[i]) self.wire(self.ports.cgra_cfg_g2f[i].read[0], self.underlying.ports.cgra_cfg_g2f_cfg_rd_en[i]) self.wire( self.ports.cgra_cfg_g2f[i].config_addr, self.underlying.ports. cgra_cfg_g2f_cfg_addr[i * self.cfg_addr_width:(i + 1) * self.cfg_addr_width]) self.wire( self.ports.cgra_cfg_g2f[i].config_data, self.underlying.ports. cgra_cfg_g2f_cfg_data[i * self.cfg_data_width:(i + 1) * self.cfg_data_width]) self.wire(self.ports.strm_start_pulse, self.underlying.ports.strm_start_pulse) self.wire(self.ports.pc_start_pulse, self.underlying.ports.pc_start_pulse) self.wire(self.ports.strm_f2g_interrupt_pulse, self.underlying.ports.strm_f2g_interrupt_pulse) self.wire(self.ports.strm_g2f_interrupt_pulse, self.underlying.ports.strm_g2f_interrupt_pulse) self.wire(self.ports.pcfg_g2f_interrupt_pulse, self.underlying.ports.pcfg_g2f_interrupt_pulse)
def __init__(self, addr_width, data_width, axi_addr_width): super().__init__() self.addr_width = addr_width self.data_width = data_width self.axi_addr_width = axi_addr_width self.config_type = ConfigurationType(self.addr_width, self.data_width) self.axi_config_type = ConfigurationType(self.axi_addr_width, self.data_width) self.add_ports( clk_in=magma.In(magma.Clock), reset_in=magma.In(magma.AsyncReset), clk_out=magma.Out(magma.Clock), reset_out=magma.Out(magma.AsyncReset), stall=magma.Out(magma.Bits[1]), glb_stall=magma.Out(magma.Bit), cgra_start_pulse=magma.Out(magma.Bit), cgra_done_pulse=magma.In(magma.Bit), cgra_soft_reset=magma.Out(magma.Bit), config_start_pulse=magma.Out(magma.Bit), config_done_pulse=magma.In(magma.Bit), glb_config=magma.Out(self.axi_config_type), glb_read_data_in=magma.In(magma.Bits[self.data_width]), glb_sram_config=magma.Out(self.config_type), glb_sram_read_data_in=magma.In(magma.Bits[self.data_width]), config=magma.Out(self.config_type), read_data_in=magma.In(magma.Bits[self.data_width]), jtag=JTAGType, axi4_ctrl=AXI4SlaveType(self.axi_addr_width, self.data_width), ) wrapper = global_controller_genesis2.gc_wrapper generator = wrapper.generator(mode="declare") self.underlying = FromMagma(generator()) # wire clk and reset self.wire(self.ports.clk_in, self.underlying.ports.clk_in) self.wire(self.ports.reset_in, self.underlying.ports.reset_in) # cgra control signals self.wire(self.underlying.ports.clk_out, self.ports.clk_out) self.wire(self.underlying.ports.reset_out, self.ports.reset_out) self.wire(self.underlying.ports.cgra_stalled, self.ports.stall) self.wire(self.underlying.ports.glb_stall, self.ports.glb_stall) self.wire(self.ports.cgra_start_pulse, self.underlying.ports.cgra_start_pulse) self.wire(self.ports.cgra_done_pulse, self.underlying.ports.cgra_done_pulse) self.wire(self.ports.cgra_soft_reset, self.underlying.ports.cgra_soft_reset) # fast reconfiguration interface self.wire(self.ports.config_start_pulse, self.underlying.ports.config_start_pulse) self.wire(self.ports.config_done_pulse, self.underlying.ports.config_done_pulse) # glb configuration interface self.wire(self.underlying.ports.glb_config_addr_out, self.ports.glb_config.config_addr) self.wire(self.underlying.ports.glb_config_data_out, self.ports.glb_config.config_data) self.wire(self.underlying.ports.glb_read, self.ports.glb_config.read[0]) self.wire(self.underlying.ports.glb_write, self.ports.glb_config.write[0]) self.wire(self.ports.glb_read_data_in, self.underlying.ports.glb_config_data_in) # glb sram configuration interface self.wire(self.underlying.ports.glb_sram_config_addr_out, self.ports.glb_sram_config.config_addr) self.wire(self.underlying.ports.glb_sram_config_data_out, self.ports.glb_sram_config.config_data) self.wire(self.underlying.ports.glb_sram_read, self.ports.glb_sram_config.read[0]) self.wire(self.underlying.ports.glb_sram_write, self.ports.glb_sram_config.write[0]) self.wire(self.ports.glb_sram_read_data_in, self.underlying.ports.glb_sram_config_data_in) # cgra configuration interface self.wire(self.underlying.ports.config_addr_out, self.ports.config.config_addr) self.wire(self.underlying.ports.config_data_out, self.ports.config.config_data) self.wire(self.underlying.ports.read, self.ports.config.read[0]) self.wire(self.underlying.ports.write, self.ports.config.write[0]) self.wire(self.ports.read_data_in, self.underlying.ports.config_data_in) # axi4-lite slave interface self.wire(self.ports.axi4_ctrl.awaddr, self.underlying.ports.AWADDR) self.wire(self.ports.axi4_ctrl.awvalid, self.underlying.ports.AWVALID) self.wire(self.ports.axi4_ctrl.awready, self.underlying.ports.AWREADY) self.wire(self.ports.axi4_ctrl.wdata, self.underlying.ports.WDATA) self.wire(self.ports.axi4_ctrl.wvalid, self.underlying.ports.WVALID) self.wire(self.ports.axi4_ctrl.wready, self.underlying.ports.WREADY) self.wire(self.ports.axi4_ctrl.araddr, self.underlying.ports.ARADDR) self.wire(self.ports.axi4_ctrl.arvalid, self.underlying.ports.ARVALID) self.wire(self.ports.axi4_ctrl.arready, self.underlying.ports.ARREADY) self.wire(self.ports.axi4_ctrl.rdata, self.underlying.ports.RDATA) self.wire(self.ports.axi4_ctrl.rresp, self.underlying.ports.RRESP) self.wire(self.ports.axi4_ctrl.rvalid, self.underlying.ports.RVALID) self.wire(self.ports.axi4_ctrl.rready, self.underlying.ports.RREADY) self.wire(self.ports.axi4_ctrl.interrupt, self.underlying.ports.interrupt) # jtag interface signals self.wire(self.ports.jtag.tdi, self.underlying.ports.tdi) self.wire(self.ports.jtag.tdo, self.underlying.ports.tdo) self.wire(self.ports.jtag.tms, self.underlying.ports.tms) self.wire(self.ports.jtag.tck, self.underlying.ports.tck) self.wire(self.ports.jtag.trst_n, self.underlying.ports.trst_n)
def __init__( self, data_width=16, # CGRA Params mem_width=64, mem_depth=512, banks=1, input_iterator_support=6, # Addr Controllers output_iterator_support=6, input_config_width=16, output_config_width=16, interconnect_input_ports=2, # Connection to int interconnect_output_ports=2, mem_input_ports=1, mem_output_ports=1, use_sram_stub=1, sram_macro_info=SRAMMacroInfo("TS1N16FFCLLSBLVTC512X32M4S"), read_delay=1, # Cycle delay in read (SRAM vs Register File) rw_same_cycle=False, # Does the memory allow r+w in same cycle? agg_height=4, max_agg_schedule=16, input_max_port_sched=16, output_max_port_sched=16, align_input=1, max_line_length=128, max_tb_height=1, tb_range_max=1024, tb_range_inner_max=64, tb_sched_max=16, max_tb_stride=15, num_tb=1, tb_iterator_support=2, multiwrite=1, max_prefetch=8, config_data_width=32, config_addr_width=8, num_tiles=2, app_ctrl_depth_width=16, remove_tb=False, fifo_mode=True, add_clk_enable=True, add_flush=True, core_reset_pos=False, stcl_valid_iter=4): super().__init__(config_addr_width, config_data_width) # Capture everything to the tile object self.data_width = data_width self.mem_width = mem_width self.mem_depth = mem_depth self.banks = banks self.fw_int = int(self.mem_width / self.data_width) self.input_iterator_support = input_iterator_support self.output_iterator_support = output_iterator_support self.input_config_width = input_config_width self.output_config_width = output_config_width self.interconnect_input_ports = interconnect_input_ports self.interconnect_output_ports = interconnect_output_ports self.mem_input_ports = mem_input_ports self.mem_output_ports = mem_output_ports self.use_sram_stub = use_sram_stub self.sram_macro_info = sram_macro_info self.read_delay = read_delay self.rw_same_cycle = rw_same_cycle self.agg_height = agg_height self.max_agg_schedule = max_agg_schedule self.input_max_port_sched = input_max_port_sched self.output_max_port_sched = output_max_port_sched self.align_input = align_input self.max_line_length = max_line_length self.max_tb_height = max_tb_height self.tb_range_max = tb_range_max self.tb_range_inner_max = tb_range_inner_max self.tb_sched_max = tb_sched_max self.max_tb_stride = max_tb_stride self.num_tb = num_tb self.tb_iterator_support = tb_iterator_support self.multiwrite = multiwrite self.max_prefetch = max_prefetch self.config_data_width = config_data_width self.config_addr_width = config_addr_width self.num_tiles = num_tiles self.remove_tb = remove_tb self.fifo_mode = fifo_mode self.add_clk_enable = add_clk_enable self.add_flush = add_flush self.core_reset_pos = core_reset_pos self.app_ctrl_depth_width = app_ctrl_depth_width self.stcl_valid_iter = stcl_valid_iter # Typedefs for ease TData = magma.Bits[self.data_width] TBit = magma.Bits[1] self.__inputs = [] self.__outputs = [] # Enumerate input and output ports # (clk and reset are assumed) if self.interconnect_input_ports > 1: for i in range(self.interconnect_input_ports): self.add_port(f"addr_in_{i}", magma.In(TData)) self.__inputs.append(self.ports[f"addr_in_{i}"]) self.add_port(f"data_in_{i}", magma.In(TData)) self.__inputs.append(self.ports[f"data_in_{i}"]) self.add_port(f"wen_in_{i}", magma.In(TBit)) self.__inputs.append(self.ports[f"wen_in_{i}"]) else: self.add_port("addr_in", magma.In(TData)) self.__inputs.append(self.ports[f"addr_in"]) self.add_port("data_in", magma.In(TData)) self.__inputs.append(self.ports[f"data_in"]) self.add_port("wen_in", magma.In(TBit)) self.__inputs.append(self.ports.wen_in) if self.interconnect_output_ports > 1: for i in range(self.interconnect_output_ports): self.add_port(f"data_out_{i}", magma.Out(TData)) self.__outputs.append(self.ports[f"data_out_{i}"]) self.add_port(f"ren_in_{i}", magma.In(TBit)) self.__inputs.append(self.ports[f"ren_in_{i}"]) self.add_port(f"valid_out_{i}", magma.Out(TBit)) self.__outputs.append(self.ports[f"valid_out_{i}"]) # Chaining self.add_port(f"chain_valid_in_{i}", magma.In(TBit)) self.__inputs.append(self.ports[f"chain_valid_in_{i}"]) self.add_port(f"chain_data_in_{i}", magma.In(TData)) self.__inputs.append(self.ports[f"chain_data_in_{i}"]) self.add_port(f"chain_data_out_{i}", magma.Out(TData)) self.__outputs.append(self.ports[f"chain_data_out_{i}"]) self.add_port(f"chain_valid_out_{i}", magma.Out(TBit)) self.__outputs.append(self.ports[f"chain_valid_out_{i}"]) else: self.add_port("data_out", magma.Out(TData)) self.__outputs.append(self.ports[f"data_out"]) self.add_port(f"ren_in", magma.In(TBit)) self.__inputs.append(self.ports[f"ren_in"]) self.add_port(f"valid_out", magma.Out(TBit)) self.__outputs.append(self.ports[f"valid_out"]) self.add_port(f"chain_valid_in", magma.In(TBit)) self.__inputs.append(self.ports[f"chain_valid_in"]) self.add_port(f"chain_data_in", magma.In(TData)) self.__inputs.append(self.ports[f"chain_data_in"]) self.add_port(f"chain_data_out", magma.Out(TData)) self.__outputs.append(self.ports[f"chain_data_out"]) self.add_port(f"chain_valid_out", magma.Out(TBit)) self.__outputs.append(self.ports[f"chain_valid_out"]) self.add_ports(flush=magma.In(TBit), full=magma.Out(TBit), empty=magma.Out(TBit), stall=magma.In(TBit), sram_ready_out=magma.Out(TBit)) self.__inputs.append(self.ports.flush) # self.__inputs.append(self.ports.stall) self.__outputs.append(self.ports.full) self.__outputs.append(self.ports.empty) self.__outputs.append(self.ports.sram_ready_out) cache_key = (self.data_width, self.mem_width, self.mem_depth, self.banks, self.input_iterator_support, self.output_iterator_support, self.interconnect_input_ports, self.interconnect_output_ports, self.use_sram_stub, self.sram_macro_info, self.read_delay, self.rw_same_cycle, self.agg_height, self.max_agg_schedule, self.input_max_port_sched, self.output_max_port_sched, self.align_input, self.max_line_length, self.max_tb_height, self.tb_range_max, self.tb_sched_max, self.max_tb_stride, self.num_tb, self.tb_iterator_support, self.multiwrite, self.max_prefetch, self.config_data_width, self.config_addr_width, self.num_tiles, self.remove_tb, self.fifo_mode, self.stcl_valid_iter, self.add_clk_enable, self.add_flush, self.app_ctrl_depth_width) # Check for circuit caching if cache_key not in MemCore.__circuit_cache: # Instantiate core object here - will only use the object representation to # query for information. The circuit representation will be cached and retrieved # in the following steps. lt_dut = LakeTop( data_width=self.data_width, mem_width=self.mem_width, mem_depth=self.mem_depth, banks=self.banks, input_iterator_support=self.input_iterator_support, output_iterator_support=self.output_iterator_support, input_config_width=self.input_config_width, output_config_width=self.output_config_width, interconnect_input_ports=self.interconnect_input_ports, interconnect_output_ports=self.interconnect_output_ports, use_sram_stub=self.use_sram_stub, sram_macro_info=self.sram_macro_info, read_delay=self.read_delay, rw_same_cycle=self.rw_same_cycle, agg_height=self.agg_height, max_agg_schedule=self.max_agg_schedule, input_max_port_sched=self.input_max_port_sched, output_max_port_sched=self.output_max_port_sched, align_input=self.align_input, max_line_length=self.max_line_length, max_tb_height=self.max_tb_height, tb_range_max=self.tb_range_max, tb_range_inner_max=self.tb_range_inner_max, tb_sched_max=self.tb_sched_max, max_tb_stride=self.max_tb_stride, num_tb=self.num_tb, tb_iterator_support=self.tb_iterator_support, multiwrite=self.multiwrite, max_prefetch=self.max_prefetch, config_data_width=self.config_data_width, config_addr_width=self.config_addr_width, num_tiles=self.num_tiles, app_ctrl_depth_width=self.app_ctrl_depth_width, remove_tb=self.remove_tb, fifo_mode=self.fifo_mode, add_clk_enable=self.add_clk_enable, add_flush=self.add_flush, stcl_valid_iter=self.stcl_valid_iter) change_sram_port_pass = change_sram_port_names( use_sram_stub, sram_macro_info) circ = kts.util.to_magma( lt_dut, flatten_array=True, check_multiple_driver=False, optimize_if=False, check_flip_flop_always_ff=False, additional_passes={"change_sram_port": change_sram_port_pass}) MemCore.__circuit_cache[cache_key] = (circ, lt_dut) else: circ, lt_dut = MemCore.__circuit_cache[cache_key] # Save as underlying circuit object self.underlying = FromMagma(circ) self.chain_idx_bits = max(1, kts.clog2(self.num_tiles)) # put a 1-bit register and a mux to select the control signals # TODO: check if enable_chain_output needs to be here? I don't think so? control_signals = [("wen_in", self.interconnect_input_ports), ("ren_in", self.interconnect_output_ports), ("flush", 1), ("chain_valid_in", self.interconnect_output_ports)] for control_signal, width in control_signals: # TODO: consult with Ankita to see if we can use the normal # mux here if width == 1: 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][0]) else: for i in range(width): mux = MuxWrapper(2, 1, name=f"{control_signal}_{i}_sel") reg_value_name = f"{control_signal}_{i}_reg_value" reg_sel_name = f"{control_signal}_{i}_reg_sel" self.add_config(reg_value_name, 1) self.add_config(reg_sel_name, 1) self.wire(mux.ports.I[0], self.ports[f"{control_signal}_{i}"]) 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][i]) if self.interconnect_input_ports > 1: for i in range(self.interconnect_input_ports): self.wire(self.ports[f"data_in_{i}"], self.underlying.ports[f"data_in_{i}"]) self.wire(self.ports[f"addr_in_{i}"], self.underlying.ports[f"addr_in_{i}"]) else: self.wire(self.ports.addr_in, self.underlying.ports.addr_in) self.wire(self.ports.data_in, self.underlying.ports.data_in) if self.interconnect_output_ports > 1: for i in range(self.interconnect_output_ports): self.wire(self.ports[f"data_out_{i}"], self.underlying.ports[f"data_out_{i}"]) self.wire(self.ports[f"chain_data_in_{i}"], self.underlying.ports[f"chain_data_in_{i}"]) self.wire(self.ports[f"chain_data_out_{i}"], self.underlying.ports[f"chain_data_out_{i}"]) else: self.wire(self.ports.data_out, self.underlying.ports.data_out) self.wire(self.ports.chain_data_in, self.underlying.ports.chain_data_in) self.wire(self.ports.chain_data_out, self.underlying.ports.chain_data_out) # Need to invert this self.resetInverter = FromMagma(mantle.DefineInvert(1)) self.wire(self.resetInverter.ports.I[0], self.ports.reset) self.wire(self.resetInverter.ports.O[0], self.underlying.ports.rst_n) self.wire(self.ports.clk, self.underlying.ports.clk) if self.interconnect_output_ports == 1: self.wire(self.ports.valid_out[0], self.underlying.ports.valid_out[0]) self.wire(self.ports.chain_valid_out[0], self.underlying.ports.chain_valid_out[0]) else: for j in range(self.interconnect_output_ports): self.wire(self.ports[f"valid_out_{j}"][0], self.underlying.ports.valid_out[j]) self.wire(self.ports[f"chain_valid_out_{j}"][0], self.underlying.ports.chain_valid_out[j]) self.wire(self.ports.empty[0], self.underlying.ports.empty[0]) self.wire(self.ports.full[0], self.underlying.ports.full[0]) # 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.underlying.ports.clk_en[0]) self.wire(self.ports.sram_ready_out[0], self.underlying.ports.sram_ready_out[0]) # we have six? features in total # 0: TILE # 1: TILE # 1-4: SMEM # Feature 0: Tile self.__features: List[CoreFeature] = [self] # Features 1-4: SRAM self.num_sram_features = lt_dut.total_sets for sram_index in range(self.num_sram_features): 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_in[0:8]) self.wire(or_gates["config_data"].ports.O, self.underlying.ports.config_data_in) # read data out for idx, core_feature in enumerate(self.__features): if (idx > 0): # self.add_port(f"read_config_data_{idx}", 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 configurations = [("tile_en", 1), ("fifo_ctrl_fifo_depth", 16), ("mode", 2), ("enable_chain_output", 1), ("enable_chain_input", 1)] # ("stencil_width", 16), NOT YET merged_configs = [] merged_in_sched = [] merged_out_sched = [] # Add config registers to configurations # TODO: Have lake spit this information out automatically from the wrapper configurations.append((f"chain_idx_input", self.chain_idx_bits)) configurations.append((f"chain_idx_output", self.chain_idx_bits)) for i in range(self.interconnect_input_ports): configurations.append((f"strg_ub_agg_align_{i}_line_length", kts.clog2(self.max_line_length))) configurations.append((f"strg_ub_agg_in_{i}_in_period", kts.clog2(self.input_max_port_sched))) # num_bits_in_sched = kts.clog2(self.agg_height) # sched_per_feat = math.floor(self.config_data_width / num_bits_in_sched) # new_width = num_bits_in_sched * sched_per_feat # feat_num = 0 # num_feats_merge = math.ceil(self.input_max_port_sched / sched_per_feat) # for k in range(num_feats_merge): # num_here = sched_per_feat # if self.input_max_port_sched - (k * sched_per_feat) < sched_per_feat: # num_here = self.input_max_port_sched - (k * sched_per_feat) # merged_configs.append((f"strg_ub_agg_in_{i}_in_sched_merged_{k * sched_per_feat}", # num_here * num_bits_in_sched, num_here)) for j in range(self.input_max_port_sched): configurations.append((f"strg_ub_agg_in_{i}_in_sched_{j}", kts.clog2(self.agg_height))) configurations.append((f"strg_ub_agg_in_{i}_out_period", kts.clog2(self.input_max_port_sched))) for j in range(self.output_max_port_sched): configurations.append((f"strg_ub_agg_in_{i}_out_sched_{j}", kts.clog2(self.agg_height))) configurations.append((f"strg_ub_app_ctrl_write_depth_wo_{i}", self.app_ctrl_depth_width)) configurations.append((f"strg_ub_app_ctrl_write_depth_ss_{i}", self.app_ctrl_depth_width)) configurations.append( (f"strg_ub_app_ctrl_coarse_write_depth_wo_{i}", self.app_ctrl_depth_width)) configurations.append( (f"strg_ub_app_ctrl_coarse_write_depth_ss_{i}", self.app_ctrl_depth_width)) configurations.append( (f"strg_ub_input_addr_ctrl_address_gen_{i}_dimensionality", 1 + kts.clog2(self.input_iterator_support))) configurations.append( (f"strg_ub_input_addr_ctrl_address_gen_{i}_starting_addr", self.input_config_width)) for j in range(self.input_iterator_support): configurations.append( (f"strg_ub_input_addr_ctrl_address_gen_{i}_ranges_{j}", self.input_config_width)) configurations.append( (f"strg_ub_input_addr_ctrl_address_gen_{i}_strides_{j}", self.input_config_width)) configurations.append( (f"strg_ub_app_ctrl_prefill", self.interconnect_output_ports)) configurations.append((f"strg_ub_app_ctrl_coarse_prefill", self.interconnect_output_ports)) for i in range(self.stcl_valid_iter): configurations.append((f"strg_ub_app_ctrl_ranges_{i}", 16)) configurations.append((f"strg_ub_app_ctrl_threshold_{i}", 16)) for i in range(self.interconnect_output_ports): configurations.append((f"strg_ub_app_ctrl_input_port_{i}", kts.clog2(self.interconnect_input_ports))) configurations.append((f"strg_ub_app_ctrl_read_depth_{i}", self.app_ctrl_depth_width)) configurations.append((f"strg_ub_app_ctrl_coarse_input_port_{i}", kts.clog2(self.interconnect_input_ports))) configurations.append((f"strg_ub_app_ctrl_coarse_read_depth_{i}", self.app_ctrl_depth_width)) configurations.append( (f"strg_ub_output_addr_ctrl_address_gen_{i}_dimensionality", 1 + kts.clog2(self.output_iterator_support))) configurations.append( (f"strg_ub_output_addr_ctrl_address_gen_{i}_starting_addr", self.output_config_width)) for j in range(self.output_iterator_support): configurations.append( (f"strg_ub_output_addr_ctrl_address_gen_{i}_ranges_{j}", self.output_config_width)) configurations.append( (f"strg_ub_output_addr_ctrl_address_gen_{i}_strides_{j}", self.output_config_width)) configurations.append((f"strg_ub_pre_fetch_{i}_input_latency", kts.clog2(self.max_prefetch) + 1)) configurations.append((f"strg_ub_sync_grp_sync_group_{i}", self.interconnect_output_ports)) configurations.append( (f"strg_ub_rate_matched_{i}", 1 + kts.clog2(self.interconnect_input_ports))) for j in range(self.num_tb): configurations.append( (f"strg_ub_tba_{i}_tb_{j}_dimensionality", 2)) num_indices_bits = 1 + kts.clog2(self.fw_int) indices_per_feat = math.floor(self.config_data_width / num_indices_bits) new_width = num_indices_bits * indices_per_feat feat_num = 0 num_feats_merge = math.ceil(self.tb_range_inner_max / indices_per_feat) for k in range(num_feats_merge): num_idx = indices_per_feat if (self.tb_range_inner_max - (k * indices_per_feat)) < indices_per_feat: num_idx = self.tb_range_inner_max - (k * indices_per_feat) merged_configs.append(( f"strg_ub_tba_{i}_tb_{j}_indices_merged_{k * indices_per_feat}", num_idx * num_indices_bits, num_idx)) # for k in range(self.tb_range_inner_max): # configurations.append((f"strg_ub_tba_{i}_tb_{j}_indices_{k}", kts.clog2(self.fw_int) + 1)) configurations.append((f"strg_ub_tba_{i}_tb_{j}_range_inner", kts.clog2(self.tb_range_inner_max))) configurations.append((f"strg_ub_tba_{i}_tb_{j}_range_outer", kts.clog2(self.tb_range_max))) configurations.append((f"strg_ub_tba_{i}_tb_{j}_stride", kts.clog2(self.max_tb_stride))) configurations.append((f"strg_ub_tba_{i}_tb_{j}_tb_height", max(1, kts.clog2(self.num_tb)))) configurations.append((f"strg_ub_tba_{i}_tb_{j}_starting_addr", max(1, kts.clog2(self.fw_int)))) # 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][0]) else: self.wire(main_feature.registers[config_reg_name].ports.O, self.underlying.ports[config_reg_name]) for config_reg_name, width, num_merged in merged_configs: main_feature.add_config(config_reg_name, width) token_under = config_reg_name.split("_") base_name = config_reg_name.split("_merged")[0] base_indices = int(config_reg_name.split("_merged_")[1]) num_bits = width // num_merged for i in range(num_merged): self.wire( main_feature.registers[config_reg_name].ports. O[i * num_bits:(i + 1) * num_bits], self.underlying.ports[f"{base_name}_{base_indices + i}"]) # SRAM # These should also account for num features # or_all_cfg_rd = FromMagma(mantle.DefineOr(4, 1)) or_all_cfg_rd = FromMagma(mantle.DefineOr(self.num_sram_features, 1)) or_all_cfg_rd.instance_name = f"OR_CONFIG_WR_SRAM" or_all_cfg_wr = FromMagma(mantle.DefineOr(self.num_sram_features, 1)) or_all_cfg_wr.instance_name = f"OR_CONFIG_RD_SRAM" for sram_index in range(self.num_sram_features): core_feature = self.__features[sram_index + 1] 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(core_feature.ports.read_config_data, self.underlying.ports[f"config_data_out_{sram_index}"]) # also need to wire the sram signal # the config enable is the OR of the rd+wr or_gate_en = FromMagma(mantle.DefineOr(2, 1)) or_gate_en.instance_name = f"OR_CONFIG_EN_SRAM_{sram_index}" self.wire(or_gate_en.ports.I0, core_feature.ports.config.write) self.wire(or_gate_en.ports.I1, core_feature.ports.config.read) self.wire(core_feature.ports.config_en, self.underlying.ports["config_en"][sram_index]) # Still connect to the OR of all the config rd/wr self.wire(core_feature.ports.config.write, or_all_cfg_wr.ports[f"I{sram_index}"]) self.wire(core_feature.ports.config.read, or_all_cfg_rd.ports[f"I{sram_index}"]) self.wire(or_all_cfg_rd.ports.O[0], self.underlying.ports.config_read[0]) self.wire(or_all_cfg_wr.ports.O[0], self.underlying.ports.config_write[0]) 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) with open("mem_synth.txt", "w+") as cfg_dump: for idx, reg in enumerate(conf_names): write_line = f"{reg}\n" cfg_dump.write(write_line)
def glb_interconnect_wiring(garnet): # width of garnet width = garnet.width start_idx = garnet.interconnect.x_min # Add parallel configuration ports to interconnect # interconnect must have config port assert "config" in garnet.interconnect.ports garnet.interconnect.remove_port("config") config_addr_width = garnet.config_addr_width config_data_width = garnet.config_data_width garnet.interconnect.add_port( "config", magma.In(magma.Array[ width, ConfigurationType(config_addr_width, config_data_width)])) # looping through on a per-column bases for x_coor in range(start_idx, start_idx + width): column = garnet.interconnect.get_column(x_coor) # skip tiles with no config column = [entry for entry in column if "config" in entry.ports] # wire configuration ports to first tile in column garnet.interconnect.wire(garnet.interconnect.ports.config[x_coor], column[0].ports.config) # parallel configuration ports wiring for i in range(width): garnet.wire(garnet.global_buffer.ports.cgra_cfg_g2f[i], garnet.interconnect.ports.config[i]) # Add cgra stall assert "stall" in garnet.interconnect.ports stall_signal_width = garnet.interconnect.stall_signal_width garnet.interconnect.remove_port("stall") garnet.interconnect.add_port( "stall", magma.In(magma.Array[width, magma.Bits[stall_signal_width]])) # looping through on a per-column bases for x_coor in range(start_idx, start_idx + width): column = garnet.interconnect.get_column(x_coor) # skip tiles with no stall column = [entry for entry in column if "stall" in entry.ports] # wire configuration ports to first tile in column garnet.interconnect.wire(garnet.interconnect.ports.stall[x_coor], column[0].ports.stall) # Currently stall signal is 1 bit assert garnet.interconnect.stall_signal_width == 1 for i in range(width): garnet.wire(garnet.global_buffer.ports.cgra_stall[i], garnet.interconnect.ports.stall[i][0]) # 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}" garnet.wire(garnet.global_buffer.ports.stream_data_f2g[x], garnet.interconnect.ports[io2glb_16_port]) garnet.wire(garnet.global_buffer.ports.stream_data_valid_f2g[x], garnet.interconnect.ports[io2glb_1_port]) garnet.wire(garnet.global_buffer.ports.stream_data_g2f[x], garnet.interconnect.ports[glb2io_16_port]) garnet.wire(garnet.global_buffer.ports.stream_data_valid_g2f[x], garnet.interconnect.ports[glb2io_1_port]) return garnet
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 __init__(self, addr_width=32, data_width=32, axi_addr_width=12, axi_data_width=32, num_glb_tiles=16, glb_addr_width=22, block_axi_addr_width=12, glb_tile_mem_size=256): super().__init__() self.addr_width = addr_width self.data_width = data_width self.axi_addr_width = axi_addr_width self.axi_data_width = axi_data_width self.num_glb_tiles = num_glb_tiles self.glb_addr_width = glb_addr_width self.glb_tile_mem_size = glb_tile_mem_size self.block_axi_addr_width = block_axi_addr_width # Control logic assumes cgra config_data_width is same as axi_data_width assert self.axi_data_width == self.data_width self.config_type = ConfigurationType(self.addr_width, self.data_width) self.add_ports( clk_in=magma.In(magma.Clock), reset_in=magma.In(magma.AsyncReset), clk_out=magma.Out(magma.Clock), reset_out=magma.Out(magma.AsyncReset), cgra_stall=magma.Out(magma.Bits[self.num_glb_tiles]), glb_stall=magma.Out(magma.Bits[self.num_glb_tiles]), soft_reset=magma.Out(magma.Bit), glb_cfg=GlbCfgIfc(self.block_axi_addr_width, self.axi_data_width).master, sram_cfg=GlbCfgIfc(self.glb_addr_width, self.axi_data_width).master, strm_g2f_start_pulse=magma.Out(magma.Bits[self.num_glb_tiles]), strm_f2g_start_pulse=magma.Out(magma.Bits[self.num_glb_tiles]), pc_start_pulse=magma.Out(magma.Bits[self.num_glb_tiles]), strm_g2f_interrupt_pulse=magma.In(magma.Bits[self.num_glb_tiles]), strm_f2g_interrupt_pulse=magma.In(magma.Bits[self.num_glb_tiles]), pcfg_g2f_interrupt_pulse=magma.In(magma.Bits[self.num_glb_tiles]), cgra_config=magma.Out(self.config_type), read_data_in=magma.In(magma.Bits[self.data_width]), jtag=JTAGType, axi4_slave=AXI4LiteIfc(self.axi_addr_width, self.data_width).slave, interrupt=magma.Out(magma.Bit)) if not self.__class__.cache: self.__class__.cache = 1 self.run_glc_systemrdl() params = GlobalControllerParams( cfg_data_width=self.data_width, cfg_addr_width=self.addr_width, axi_addr_width=self.axi_addr_width, axi_data_width=self.axi_data_width, num_glb_tiles=self.num_glb_tiles, glb_tile_mem_size=self.glb_tile_mem_size, block_axi_addr_width=(self.block_axi_addr_width)) wrapper = gen_wrapper(params) generator = wrapper.generator(mode="declare") self.underlying = FromMagma(generator()) # wire clk and reset self.wire(self.ports.clk_in, self.underlying.ports.clk_in) self.wire(self.ports.reset_in, self.underlying.ports.reset_in) # cgra control signals self.wire(self.underlying.ports.clk_out, self.ports.clk_out) self.wire(self.underlying.ports.reset_out, self.ports.reset_out) self.wire(self.underlying.ports.cgra_stall, self.ports.cgra_stall) self.wire(self.underlying.ports.glb_stall, self.ports.glb_stall) self.wire(self.underlying.ports.soft_reset, self.ports.soft_reset) # global buffer configuration self.wire(self.ports.glb_cfg.wr_en, self.underlying.ports.glb_cfg_wr_en) self.wire(self.ports.glb_cfg.wr_clk_en, self.underlying.ports.glb_cfg_wr_clk_en) self.wire(self.ports.glb_cfg.wr_addr, self.underlying.ports.glb_cfg_wr_addr) self.wire(self.ports.glb_cfg.wr_data, self.underlying.ports.glb_cfg_wr_data) self.wire(self.ports.glb_cfg.rd_en, self.underlying.ports.glb_cfg_rd_en) self.wire(self.ports.glb_cfg.rd_clk_en, self.underlying.ports.glb_cfg_rd_clk_en) self.wire(self.ports.glb_cfg.rd_addr, self.underlying.ports.glb_cfg_rd_addr) self.wire(self.underlying.ports.glb_cfg_rd_data, self.ports.glb_cfg.rd_data) self.wire(self.underlying.ports.glb_cfg_rd_data_valid, self.ports.glb_cfg.rd_data_valid) # global buffer sram configuration self.wire(self.ports.sram_cfg.wr_en, self.underlying.ports.sram_cfg_wr_en) self.wire(self.ports.sram_cfg.wr_clk_en, self.underlying.ports.sram_cfg_wr_clk_en) self.wire(self.ports.sram_cfg.wr_addr, self.underlying.ports.sram_cfg_wr_addr) self.wire(self.ports.sram_cfg.wr_data, self.underlying.ports.sram_cfg_wr_data) self.wire(self.ports.sram_cfg.rd_en, self.underlying.ports.sram_cfg_rd_en) self.wire(self.ports.sram_cfg.rd_clk_en, self.underlying.ports.sram_cfg_rd_clk_en) self.wire(self.ports.sram_cfg.rd_addr, self.underlying.ports.sram_cfg_rd_addr) self.wire(self.underlying.ports.sram_cfg_rd_data, self.ports.sram_cfg.rd_data) self.wire(self.underlying.ports.sram_cfg_rd_data_valid, self.ports.sram_cfg.rd_data_valid) # start/done pulse self.wire(self.underlying.ports.strm_f2g_interrupt_pulse, self.ports.strm_f2g_interrupt_pulse) self.wire(self.underlying.ports.strm_g2f_interrupt_pulse, self.ports.strm_g2f_interrupt_pulse) self.wire(self.underlying.ports.pcfg_g2f_interrupt_pulse, self.ports.pcfg_g2f_interrupt_pulse) self.wire(self.ports.strm_g2f_start_pulse, self.underlying.ports.strm_g2f_start_pulse) self.wire(self.ports.strm_f2g_start_pulse, self.underlying.ports.strm_f2g_start_pulse) self.wire(self.ports.pc_start_pulse, self.underlying.ports.pc_start_pulse) # cgra configuration interface self.wire(self.underlying.ports.cgra_cfg_addr, self.ports.cgra_config.config_addr) self.wire(self.underlying.ports.cgra_cfg_wr_data, self.ports.cgra_config.config_data) self.wire(self.underlying.ports.cgra_cfg_read, self.ports.cgra_config.read[0]) self.wire(self.underlying.ports.cgra_cfg_write, self.ports.cgra_config.write[0]) self.wire(self.ports.read_data_in, self.underlying.ports.cgra_cfg_rd_data) # axi4-lite slave interface self.wire(self.ports.axi4_slave.awaddr, self.underlying.ports.axi_awaddr) self.wire(self.ports.axi4_slave.awvalid, self.underlying.ports.axi_awvalid) self.wire(self.ports.axi4_slave.awready, self.underlying.ports.axi_awready) self.wire(self.ports.axi4_slave.wdata, self.underlying.ports.axi_wdata) self.wire(self.ports.axi4_slave.wvalid, self.underlying.ports.axi_wvalid) self.wire(self.ports.axi4_slave.wready, self.underlying.ports.axi_wready) self.wire(self.ports.axi4_slave.bready, self.underlying.ports.axi_bready) self.wire(self.ports.axi4_slave.bvalid, self.underlying.ports.axi_bvalid) self.wire(self.ports.axi4_slave.bresp, self.underlying.ports.axi_bresp) self.wire(self.ports.axi4_slave.araddr, self.underlying.ports.axi_araddr) self.wire(self.ports.axi4_slave.arvalid, self.underlying.ports.axi_arvalid) self.wire(self.ports.axi4_slave.arready, self.underlying.ports.axi_arready) self.wire(self.ports.axi4_slave.rdata, self.underlying.ports.axi_rdata) self.wire(self.ports.axi4_slave.rresp, self.underlying.ports.axi_rresp) self.wire(self.ports.axi4_slave.rvalid, self.underlying.ports.axi_rvalid) self.wire(self.ports.axi4_slave.rready, self.underlying.ports.axi_rready) # interrupt self.wire(self.ports.interrupt, self.underlying.ports.interrupt) # jtag interface signals self.wire(self.ports.jtag.tdi, self.underlying.ports.tdi) self.wire(self.ports.jtag.tdo, self.underlying.ports.tdo) self.wire(self.ports.jtag.tms, self.underlying.ports.tms) self.wire(self.ports.jtag.tck, self.underlying.ports.tck) self.wire(self.ports.jtag.trst_n, self.underlying.ports.trst_n)
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, width, height, add_pd, interconnect_only: bool = False, use_sram_stub: bool = True, standalone: bool = False): super().__init__() # Check consistency of @standalone and @interconnect_only parameters. If # @standalone is True, then interconnect_only must also be True. if standalone: assert interconnect_only # configuration parameters config_addr_width = 32 config_data_width = 32 self.config_addr_width = config_addr_width self.config_data_width = config_data_width axi_addr_width = 13 glb_axi_addr_width = 12 axi_data_width = 32 # axi_data_width must be same as cgra config_data_width assert axi_data_width == config_data_width tile_id_width = 16 config_addr_reg_width = 8 num_tracks = 5 # size self.width = width self.height = height # only north side has IO if standalone: io_side = IOSide.None_ else: io_side = IOSide.North if not interconnect_only: # global buffer parameters # width must be even number assert (self.width % 2) == 0 num_glb_tiles = self.width // 2 bank_addr_width = 17 bank_data_width = 64 banks_per_tile = 2 glb_addr_width = (bank_addr_width + magma.bitutils.clog2(banks_per_tile) + magma.bitutils.clog2(num_glb_tiles)) # bank_data_width must be the size of bitstream assert bank_data_width == config_addr_width + config_data_width wiring = GlobalSignalWiring.ParallelMeso self.global_controller = GlobalController( addr_width=config_addr_width, data_width=config_data_width, axi_addr_width=axi_addr_width, axi_data_width=axi_data_width, num_glb_tiles=num_glb_tiles, glb_addr_width=glb_addr_width, block_axi_addr_width=glb_axi_addr_width) self.global_buffer = GlobalBuffer( num_glb_tiles=num_glb_tiles, num_cgra_cols=width, bank_addr_width=bank_addr_width, bank_data_width=bank_data_width, cfg_addr_width=config_addr_width, cfg_data_width=config_data_width, axi_addr_width=glb_axi_addr_width, axi_data_width=axi_data_width) else: wiring = GlobalSignalWiring.Meso interconnect = create_cgra(width, height, io_side, reg_addr_width=config_addr_reg_width, config_data_width=config_data_width, tile_id_width=tile_id_width, num_tracks=num_tracks, add_pd=add_pd, use_sram_stub=use_sram_stub, global_signal_wiring=wiring, mem_ratio=(1, 4), standalone=standalone) self.interconnect = interconnect if not interconnect_only: self.add_ports( jtag=JTAGType, clk_in=magma.In(magma.Clock), reset_in=magma.In(magma.AsyncReset), proc_packet=ProcPacketIfc(glb_addr_width, bank_data_width).slave, axi4_slave=AXI4LiteIfc(axi_addr_width, axi_data_width).slave, interrupt=magma.Out(magma.Bit), cgra_running_clk_out=magma.Out(magma.Clock), ) # top <-> global controller ports connection self.wire(self.ports.clk_in, self.global_controller.ports.clk_in) self.wire(self.ports.reset_in, self.global_controller.ports.reset_in) self.wire(self.ports.jtag, self.global_controller.ports.jtag) self.wire(self.ports.axi4_slave, self.global_controller.ports.axi4_slave) self.wire(self.ports.interrupt, self.global_controller.ports.interrupt) self.wire(self.ports.cgra_running_clk_out, self.global_controller.ports.clk_out) # top <-> global buffer ports connection self.wire(self.ports.proc_packet, self.global_buffer.ports.proc_packet) glb_glc_wiring(self) glb_interconnect_wiring(self) glc_interconnect_wiring(self) else: # lift all the interconnect ports up for name in self.interconnect.interface(): self.add_port(name, self.interconnect.ports[name].type()) self.wire(self.ports[name], self.interconnect.ports[name]) self.add_ports( clk=magma.In(magma.Clock), reset=magma.In(magma.AsyncReset), config=magma.In( ConfigurationType(self.interconnect.config_data_width, self.interconnect.config_data_width)), stall=magma.In( magma.Bits[self.interconnect.stall_signal_width]), read_config_data=magma.Out(magma.Bits[config_data_width])) self.wire(self.ports.clk, self.interconnect.ports.clk) self.wire(self.ports.reset, self.interconnect.ports.reset) self.wire(self.ports.config, self.interconnect.ports.config) self.wire(self.ports.stall, self.interconnect.ports.stall) self.wire(self.interconnect.ports.read_config_data, self.ports.read_config_data)
def __init__(self, switchbox: SwitchBox, config_addr_width: int, config_data_width: int, core_name: str = "", stall_signal_width: int = 4, double_buffer: bool = False): self.switchbox = switchbox self.__core_name = core_name self.stall_signal_width = stall_signal_width self.sb_muxs: Dict[str, Tuple[SwitchBoxNode, MuxWrapper]] = {} self.reg_muxs: Dict[str, Tuple[RegisterMuxNode, MuxWrapper]] = {} self.regs: Dict[str, Tuple[RegisterNode, FromMagma]] = {} self.mux_name_to_node: Dict[str:, Node] = {} super().__init__(config_addr_width, config_data_width, double_buffer=double_buffer) # turn off hashing because we control the hash by ourselves self.set_skip_hash(True) # first pass to create the mux and register circuit self.__create_reg() self.__create_sb_mux() self.__create_reg_mux() # second pass to lift the ports and wire them for sb_name, (sb, mux) in self.sb_muxs.items(): # only lift them if the ports are connect to the outside world port_name = create_name(sb_name) if sb.io == SwitchBoxIO.SB_IN: self.add_port(port_name, magma.In(mux.ports.I.base_type())) self.wire(self.ports[port_name], mux.ports.I) else: self.add_port(port_name, magma.Out(mux.ports.O.base_type())) # to see if we have a register mux here # if so , we need to lift the reg_mux output instead if sb_name in self.reg_muxs: # override the mux value node, mux = self.reg_muxs[sb_name] assert isinstance(node, RegisterMuxNode) assert node in sb self.wire(self.ports[port_name], mux.ports.O) # connect internal sbs self.__connect_sbs() # connect regs and reg muxs # we need to make three connections in total # REG # 1 / \ 3 # SB ______ MUX # 2 self.__connect_sb_out() self.__connect_regs() # set up the configuration registers, if needed if len(self.sb_muxs) > 0: self.add_ports(config=magma.In( ConfigurationType(config_addr_width, config_data_width)), ) else: # remove added ports since it's a empty switchbox self.ports.pop("clk") self.ports.pop("reset") self.ports.pop("read_config_data") for _, (sb, mux) in self.sb_muxs.items(): config_name = get_mux_sel_name(sb) if mux.height > 1: assert mux.sel_bits > 0 self.add_config_node(sb, config_name, mux.sel_bits) self.wire(self.registers[config_name].ports.O, mux.ports.S) for _, (reg_mux, mux) in self.reg_muxs.items(): config_name = get_mux_sel_name(reg_mux) assert mux.height == 2 assert mux.sel_bits > 0 self.add_config_node(reg_mux, config_name, mux.sel_bits) self.wire(self.registers[config_name].ports.O, mux.ports.S) self._setup_config() # clock gate the pipeline registers if not used self.__wire_config_ce() # name self.instance_name = self.name() # extra hashing because we don't represent it in the module name _hash = hash(self) # ordering doesn't mater here for reg_name in self.regs: _hash ^= hash(reg_name) # also hash the internal wires based on switch box id _hash ^= hash(switchbox.id) self.set_hash(_hash) self.set_skip_hash(False)