diff --git a/lclayout/router.py b/lclayout/router.py index ad48bbb21656bf587b3c74444462296c2eed6fd4..ca38f90b6190ae1815f7c50b484c647a8769c30c 100644 --- a/lclayout/router.py +++ b/lclayout/router.py @@ -462,6 +462,9 @@ class DefaultRouter(): else: assert False, 'Routing graph is not connected.' + # Set via weights based on terminal costs. + _set_terminal_costs(graph, shapes, terminals_by_net, tech) + self._routing_graph = graph # TODO: SPLIT HERE @@ -614,6 +617,38 @@ class DefaultRouter(): # Merge the polygons on all layers. # _merge_all_layers(shapes) + +def _set_terminal_costs( + graph: nx.Graph, + shapes: Dict[str, db.Shape], + terminals_by_net: Dict[str, List] +): + """ + Set via weights based on terminal costs. + """ + regions_by_layer = { + # Convert Shapes objects into db.Region for fast 'inside' checks. + layer: db.Region(s) for layer, s in shapes.items() + } + for net, ts in terminals_by_net: + for terminal_node in ts: + layer, (x, y) = terminal_node + possible_via_layers = (data['layer'] for _, _, data in via_layers.edges(layer, data=True)) + + for via_layer in possible_via_layers: + via_cost = compute_terminal_cost( + via_layer, + layer, + (x, y), + regions_by_layer[layer], + tech + ) + + other_node = via_layer, (x, y) # Node on the other side of the via. + + if terminal_node in graph and other_node in graph: + # Scale the via cost + graph[terminal_node][other_node]['weight'] *= via_cost + 1 def _report_track_usage(xs: List[int], ys: List[int], routing_trees: Dict[Any, nx.Graph]): """ diff --git a/lclayout/routing_graph.py b/lclayout/routing_graph.py index 819f7495fabb7a74fdd43f46945899f2d3f6420d..f6bed27e9f55a10dc875483a5582e50d88fedc35 100644 --- a/lclayout/routing_graph.py +++ b/lclayout/routing_graph.py @@ -405,6 +405,36 @@ def _extract_terminal_nodes_from_shape(routing_nodes: Dict[Any, Set[Tuple[int, i logger.debug(f"routing_terminals: {routing_terminals}") return routing_terminals,weights +def compute_terminal_cost( + via_layer: str, + layer: str, + coord: Tuple[int, int], + layer_region: db.Region, + tech + ) -> int: + """ + Compute the cost of placing a via on `via_layer` which connects to `layer` at `(x, y)`. + The cost is derived from the existing geometries on the layer. + + TODO: Move to new module 'pin_access_analysis' + + Returns an unscaled cost. 0 for vias which can be placed without extending the pin shape, + 1 for vias which would extend the pin shape. + """ + + enc = tech.minimum_enclosure.get((layer, via_layer), 0) + via_size = tech.via_size[via_layer] + + d = enc + via_size // 2 + + # Test if the via would be completely enclosed in existing shapes. + is_enclosed = is_inside(coord, layer_region, d) + + if is_enclosed: + # Via can be placed without extending the pin shape. + return 0 + else: + return 1 def extract_terminal_nodes_by_lvs(graph: nx.Graph, pin_shapes_by_net: Dict[str, List[List[Tuple[str, db.Polygon]]]],