def clk_physical(interconnect: Interconnect): for (x, y) in interconnect.tile_circuits: tile = interconnect.tile_circuits[(x, y)] tile_core = tile.core # We only want to do this on PE and memory tiles if isinstance(tile_core, IOCore) or tile_core is None: continue orig_in_port = tile.ports.clk orig_out_port = tile.ports.clk_out # Remove the pass through connection that already exists tile.remove_wire(orig_in_port, orig_out_port) # Create a new pass through clock input tile.add_port("clk_pass_through", magma.In(magma.Clock)) pass_through_input = tile.ports.clk_pass_through # Create new pass through output pass_through_output = pass_signal_through(tile, pass_through_input) # Connect new clk pass through input to old pass through output tile.wire(pass_through_input, orig_out_port) # For top row tiles, connect new clk input to global clock if y < 2: interconnect.wire(interconnect.ports.clk, pass_through_input) # For other tiles, connect new clk input to # pass through output of tile above. else: tile_above = interconnect.tile_circuits[(x, y-1)] interconnect.wire(tile_above.ports.clk_pass_through_out, pass_through_input)
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 stall_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 cgra stall assert "stall" in interconnect.ports stall_signal_width = interconnect.stall_signal_width # Currently stall signal is 1 bit assert stall_signal_width == 1 interconnect.remove_port("stall") interconnect.add_port( "stall", magma.In(magma.Bits[width * interconnect.stall_signal_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 stall column = [entry for entry in column if "stall" in entry.ports] # wire configuration ports to first tile in column interconnect.wire(interconnect.ports.stall[x_coor], column[0].ports.stall[0])
def tile_id_physical(interconnect: Interconnect): tile_id_width = interconnect.tile_id_width tie_hi_width = (tile_id_width // 2) + 1 if (tile_id_width % 2) == 0: tie_lo_width = tile_id_width // 2 else: tie_lo_width = (tile_id_width // 2) + 1 for (x, y) in interconnect.tile_circuits: tile = interconnect.tile_circuits[(x, y)] tile_core = tile.core if isinstance(tile_core, IOCoreValid) or tile_core is None: continue tile.add_ports(hi=magma.Out(magma.Bits[tie_hi_width]), lo=magma.Out(magma.Bits[tie_lo_width])) # wire all hi bits high tile.wire(tile.ports.hi, Const((2**tie_hi_width) - 1)) # wire all lo bits low tile.wire(tile.ports.lo, Const(0)) # Get the correct tile_id value x_bv = BitVector[tile_id_width / 2](x) y_bv = BitVector[tile_id_width / 2](y) tile_id_bv = BitVector.concat(y_bv, x_bv) # Disconnect existing constant from tile_id port tile_id_port = tile.ports.tile_id for wire in interconnect.wires: if tile_id_port in wire: interconnect.remove_wire(wire[0], wire[1]) break # Actually connect hi/lo outputs to tile_id at top level for i in range(tile_id_width): lo_index = i // 2 if (i % 2) == 0: hi_index = i // 2 else: hi_index = (i // 2) + 1 hi_port = tile.ports.hi[hi_index] lo_port = tile.ports.lo[lo_index] tie_port = hi_port if (tile_id_bv[i] == 1) else lo_port # Connect tile_id ports to hi/lo outputs instead of constant interconnect.wire(tile.ports.tile_id[i], tie_port)
def pipeline_config_signals(interconnect: Interconnect, interval): # Right now in canal, the width of config_addr and config_data # are hard-coded to be the same. This should be changed. config_data_width = interconnect.config_data_width config_addr_width = config_data_width for (x, y) in interconnect.tile_circuits: tile = interconnect.tile_circuits[(x, y)] tile_core = tile.core # We only want to do this on PE and memory tiles if tile_core is None or "config" not in tile_core.ports or y == 0: continue else: if interval != 0 and y % interval == 0 and ( (x, y + 1) in interconnect.tile_circuits): tile_below = interconnect.tile_circuits[(x, y + 1)] pipe_stage = PipelineStage(config_addr_width, config_data_width) # remove existing config wire interconnect.remove_wire(tile.ports.config_out, tile_below.ports.config) # Now, wire config through the pipe stage interconnect.wire(tile.ports.config_out, pipe_stage.ports.config) interconnect.wire(pipe_stage.ports.config_out, tile_below.ports.config) # Wire up pipe stage clock input to output clock interconnect.wire(tile.ports.clk_out, pipe_stage.ports.clk)
def chain_pass(interconnect: Interconnect): # pragma: nocover for (x, y) in interconnect.tile_circuits: tile = interconnect.tile_circuits[(x, y)] tile_core = tile.core if isinstance(tile_core, MemCore): # lift ports up lift_mem_ports(tile, tile_core) previous_tile = interconnect.tile_circuits[(x, y - 1)] if not isinstance(previous_tile.core, MemCore): interconnect.wire(Const(0), tile.ports.chain_valid_in) interconnect.wire(Const(0), tile.ports.chain_data_in) else: interconnect.wire(previous_tile.ports.chain_valid_out, tile.ports.chain_valid_in) interconnect.wire(previous_tile.ports.chain_data_out, tile.ports.chain_data_in)
def clk_physical(interconnect: Interconnect): for (x, y) in interconnect.tile_circuits: tile = interconnect.tile_circuits[(x, y)] tile_core = tile.core # We only want to do this on PE and memory tiles if tile_core is None or isinstance(tile_core, IOCoreBase): continue elif isinstance(tile_core, MemCore): if (x, y + 1) in interconnect.tile_circuits: tile_below = interconnect.tile_circuits[(x, y + 1)] if isinstance(tile_below.core, MemCore): interconnect.remove_wire(tile.ports.clk_out, tile_below.ports.clk) # Get the PE tile to the left of this mem tile tile_left = interconnect.tile_circuits[(x - 1, y)] # Connect the clk input of this mem tile to the right clk # output of the neighboring PE tile interconnect.wire(tile_left.ports.clk_pass_through_out_right, tile.ports.clk) else: orig_in_port = tile.ports.clk orig_out_port = tile.ports.clk_out # Remove the pass through connection that already exists tile.remove_wire(orig_in_port, orig_out_port) # Create a new pass through clock input tile.add_port("clk_pass_through", magma.In(magma.Bit)) pass_through_input = tile.ports.clk_pass_through pass_through_input_as_clk = tile.convert(pass_through_input, magma.clock) # Create 2 new clk pass through outputs (bottom and right) tile.add_port("clk_pass_through_out_bot", magma.Out(magma.Bit)) tile.add_port("clk_pass_through_out_right", magma.Out(magma.Clock)) tile.wire(tile.ports.clk_pass_through, tile.ports.clk_pass_through_out_bot) tile.wire(pass_through_input_as_clk, tile.ports.clk_pass_through_out_right) # Connect new clk pass through input to old pass through output tile.wire(pass_through_input_as_clk, orig_out_port) # For top row tiles, connect new clk input to global clock if y < 2: interconnect_clk_as_bit = interconnect.convert( interconnect.ports.clk, magma.bit) interconnect.wire(interconnect_clk_as_bit, pass_through_input) # For other tiles, connect new clk input to # pass through output of tile above. else: tile_above = interconnect.tile_circuits[(x, y - 1)] interconnect.wire(tile_above.ports.clk_pass_through_out_bot, pass_through_input)