def populate_direct_connections(graph, connections, connection_loc_to_node): """ Populates all direct tile-to-tile connections. """ # Process connections bar = progressbar_utils.progressbar conns = [c for c in connections if is_direct(c)] for connection in bar(conns): # Get segment id and switch id if connection.src.pin.startswith("CLOCK"): switch_id = graph.get_delayless_switch_id() else: switch_id = graph.get_delayless_switch_id() # Get tile nodes src_tile_node = connection_loc_to_node.get(connection.src, None) dst_tile_node = connection_loc_to_node.get(connection.dst, None) # Couldn't find at least one endpoint node if src_tile_node is None or dst_tile_node is None: if src_tile_node is None: print("WARNING: No OPIN node for direct connection {}".format( connection)) if dst_tile_node is None: print("WARNING: No IPIN node for direct connection {}".format( connection)) continue # Add the edge add_edge(graph, src_tile_node.id, dst_tile_node.id, switch_id)
def create_column_clock_tracks(graph, clock_cells, quadrants): """ This function adds tracks for clock column routes. It returns a map of "assess points" to that tracks to be used by switchbox connections. """ CAND_RE = re.compile( r"^(?P<name>CAND[0-4])_(?P<quad>[A-Z]+)_(?P<col>[0-9]+)$") # Get segment id and switch id segment_id = graph.get_segment_id_from_name("clock") switch_id = graph.get_delayless_switch_id() # Process CAND cells cand_node_map = {} for cell in clock_cells.values(): # A clock column is defined by a CAND cell if cell.type != "CAND": continue # Get index and quadrant match = CAND_RE.match(cell.name) if not match: continue cand_name = match.group("name") cand_quad = match.group("quad") quadrant = quadrants[cand_quad] # Add track chains going upwards and downwards from the CAND cell up_entry_node, _, up_node_map = add_track_chain( graph, "Y", cell.loc.x, cell.loc.y, quadrant.y0, segment_id, switch_id) dn_entry_node, _, dn_node_map = add_track_chain( graph, "Y", cell.loc.x, cell.loc.y + 1, quadrant.y1, segment_id, switch_id) # Connect entry nodes cand_entry_node = up_entry_node add_edge(graph, cand_entry_node.id, dn_entry_node.id, switch_id) # Join node maps node_map = {**up_node_map, **dn_node_map} # Populate the global clock network to switchbox access map for y, node in node_map.items(): loc = Loc(x=cell.loc.x, y=y, z=0) if cand_name not in cand_node_map: cand_node_map[cand_name] = {} cand_node_map[cand_name][loc] = node return cand_node_map
def add_track_chain(graph, direction, u, v0, v1, segment_id, switch_id): """ Adds a chain of tracks that span the grid in the given direction. Returns the first and last node of the chain along with a map of coordinates to nodes. """ node_by_v = {} prev_node = None # Make range generator if v0 > v1: coords = range(v0, v1 - 1, -1) else: coords = range(v0, v1 + 1) # Add track chain for v in coords: # Add track (node) if direction == "X": track = tracks.Track( direction=direction, x_low=v, x_high=v, y_low=u, y_high=u, ) elif direction == "Y": track = tracks.Track( direction=direction, x_low=u, x_high=u, y_low=v, y_high=v, ) else: assert False, direction curr_node = add_track(graph, track, segment_id) # Add edge from the previous one if prev_node is not None: add_edge(graph, prev_node.id, curr_node.id, switch_id) # No previous one, this is the first one else: start_node = curr_node node_by_v[v] = curr_node prev_node = curr_node return start_node, curr_node, node_by_v
def add_tracks_for_const_network(graph, const, tile_grid): """ Builds a network of CHANX/CHANY and edges to propagate signal from a const source. The const network is purely artificial and does not correspond to any physical routing resources. Returns a map of const network nodes for each location. """ # Get the tilegrid span xs = set([loc.x for loc in tile_grid]) ys = set([loc.y for loc in tile_grid]) xmin, ymin = min(xs), min(ys) xmax, ymax = max(xs), max(ys) # Get segment id and switch id segment_id = graph.get_segment_id_from_name(const.lower()) switch_id = graph.get_delayless_switch_id() # Find the source tile src_loc = [ loc for loc, t in tile_grid.items() if t is not None and t.type == "SYN_{}".format(const) ] assert len(src_loc) == 1, const src_loc = src_loc[0] # Go down from the source to the edge of the tilegrid entry_node, col_node, _ = add_track_chain(graph, "Y", src_loc.x, src_loc.y, 1, segment_id, switch_id) # Connect the tile OPIN to the column pin_name = "TL-SYN_{const}.{const}0_{const}[0]".format(const=const) opin_node = graph.get_nodes_for_pin((src_loc[0], src_loc[1]), pin_name) assert len(opin_node) == 1, pin_name add_edge(graph, opin_node[0][0], entry_node.id, switch_id) # Got left and right from the source column over the bottommost row row_entry_node1, _, row_node_map1 = add_track_chain( graph, "X", 0, src_loc.x, 1, segment_id, switch_id) row_entry_node2, _, row_node_map2 = add_track_chain( graph, "X", 0, src_loc.x + 1, xmax - 1, segment_id, switch_id) # Connect rows to the column add_edge(graph, col_node.id, row_entry_node1.id, switch_id) add_edge(graph, col_node.id, row_entry_node2.id, switch_id) row_node_map = {**row_node_map1, **row_node_map2} row_node_map[0] = row_node_map[1] # For each column add one that spand over the entire grid height const_node_map = {} for x in range(xmin, xmax): # Add the column col_entry_node, _, col_node_map = add_track_chain( graph, "Y", x, ymin + 1, ymax - 1, segment_id, switch_id) # Add edge fom the horizontal row add_edge(graph, row_node_map[x].id, col_entry_node.id, switch_id) # Populate the const node map for y, node in col_node_map.items(): const_node_map[Loc(x=x, y=y, z=0)] = node return const_node_map
def add_l_track(graph, x0, y0, x1, y1, segment_id, switch_id): """ Add a "L"-shaped track consisting of two channel nodes and a switch between the given two grid coordinates. The (x0, y0) determines source location and (x1, y1) destination (sink) location. Returns a tuple with indices of the first and last node. """ dx = x1 - x0 dy = y1 - y0 assert dx != 0 or dy != 0, (x0, y0) nodes = [None, None] # Go vertically first if abs(dy) >= abs(dx): xc, yc = x0, y1 if abs(dy): track = tracks.Track( direction="Y", x_low=min(x0, xc), x_high=max(x0, xc), y_low=min(y0, yc), y_high=max(y0, yc), ) nodes[0] = add_track(graph, track, segment_id) if abs(dx): track = tracks.Track( direction="X", x_low=min(xc, x1), x_high=max(xc, x1), y_low=min(yc, y1), y_high=max(yc, y1), ) nodes[1] = add_track(graph, track, segment_id) # Go horizontally first else: xc, yc = x1, y0 if abs(dx): track = tracks.Track( direction="X", x_low=min(x0, xc), x_high=max(x0, xc), y_low=min(y0, yc), y_high=max(y0, yc), ) nodes[0] = add_track(graph, track, segment_id) if abs(dy): track = tracks.Track( direction="Y", x_low=min(xc, x1), x_high=max(xc, x1), y_low=min(yc, y1), y_high=max(yc, y1), ) nodes[1] = add_track(graph, track, segment_id) # In case of a horizontal or vertical only track make both nodes the same assert nodes[0] is not None or nodes[1] is not None if nodes[0] is None: nodes[0] = nodes[1] if nodes[1] is None: nodes[1] = nodes[0] # Add edge connecting the two nodes if needed if nodes[0].id != nodes[1].id: add_edge(graph, nodes[0].id, nodes[1].id, switch_id) return nodes