improvement: track opens (ie, unconnected same-named nets)

This commit is contained in:
Jan Petykiewicz 2022-03-29 21:23:27 -07:00
parent bef5421703
commit 718dc2a9ac
6 changed files with 99 additions and 42 deletions

BIN
connectivity.oas Normal file

Binary file not shown.

View File

@ -1,3 +1,5 @@
from pprint import pformat
from masque.file import gdsii, oasis from masque.file import gdsii, oasis
import snarl import snarl
@ -18,3 +20,12 @@ topcell = cells['top']
polys, labels = snarl.interfaces.masque.read_topcell(topcell, connectivity) polys, labels = snarl.interfaces.masque.read_topcell(topcell, connectivity)
nets_info = snarl.check_connectivity(polys, labels, connectivity) nets_info = snarl.check_connectivity(polys, labels, connectivity)
print('\nFinal nets:')
print([kk for kk in nets_info.nets if isinstance(kk.name, str)])
print('\nShorted net sets:')
for short in nets_info.get_shorted_nets():
print('(' + ','.join([repr(nn) for nn in sorted(list(short))]) + ')')
print('\nOpen nets:')
print(pformat(dict(nets_info.get_open_nets())))

View File

@ -37,8 +37,21 @@ def read_topcell(
metal_labels = defaultdict(list) metal_labels = defaultdict(list)
for label_layer, metal_layer in label_mapping.items(): for label_layer, metal_layer in label_mapping.items():
labels = [ll for ll in topcell.labels if ll.layer == label_layer] labels = []
metal_labels[metal_layer] += [(*ll.offset, ll.string) for ll in labels] for ll in topcell.labels:
if ll.layer != label_layer:
continue
if ll.repetition is None:
displacements = [(0, 0)]
else:
displacements = ll.repetition.displacements
for displacement in displacements:
offset = ll.offset + displacement
metal_labels[metal_layer].append(
(*offset, ll.string)
)
return polys, metal_labels return polys, metal_labels

View File

@ -7,10 +7,10 @@ import numpy
from numpy.typing import NDArray, ArrayLike from numpy.typing import NDArray, ArrayLike
from pyclipper import scale_to_clipper, scale_from_clipper, PyPolyNode from pyclipper import scale_to_clipper, scale_from_clipper, PyPolyNode
from .types import connectivity_t, layer_t, contour_t, net_name_t from .types import connectivity_t, layer_t, contour_t
from .poly import poly_contains_points from .poly import poly_contains_points
from .clipper import union_nonzero, union_evenodd, intersection_evenodd, hier2oriented from .clipper import union_nonzero, union_evenodd, intersection_evenodd, hier2oriented
from .tracker import NetsInfo from .tracker import NetsInfo, NetName
from .utils import connectivity2layers from .utils import connectivity2layers
@ -36,7 +36,7 @@ def check_connectivity(
nets_info = NetsInfo() nets_info = NetsInfo()
merge_groups: List[List[net_name_t]] = [] merge_groups: List[List[NetName]] = []
for layer, labels_for_layer in labels.items(): for layer, labels_for_layer in labels.items():
point_xys = [] point_xys = []
point_names = [] point_names = []
@ -47,18 +47,18 @@ def check_connectivity(
for poly in metal_polys[layer]: for poly in metal_polys[layer]:
found_nets = label_poly(poly, point_xys, point_names, clipper_scale_factor) found_nets = label_poly(poly, point_xys, point_names, clipper_scale_factor)
name: net_name_t name: Optional[str]
if found_nets: if found_nets:
name = found_nets[0] name = NetName(found_nets[0])
else: else:
name = object() # Anonymous net name = NetName() # Anonymous net
nets_info.get(name, layer).append(poly) nets_info.get(name, layer).append(poly)
if len(found_nets) > 1: if len(found_nets) > 1:
# Found a short # Found a short
logger.warning(f'Nets {found_nets} are shorted on layer {layer} in poly:\n {pformat(poly)}') logger.warning(f'Nets {found_nets} are shorted on layer {layer} in poly:\n {pformat(poly)}')
merge_groups.append(found_nets) # type: ignore merge_groups.append([name] + [NetName(nn) for nn in found_nets[1:]])
for group in merge_groups: for group in merge_groups:
first_net, *defunct_nets = group first_net, *defunct_nets = group
@ -82,17 +82,6 @@ def check_connectivity(
for net_a, net_b in merge_pairs: for net_a, net_b in merge_pairs:
nets_info.merge(net_a, net_b) nets_info.merge(net_a, net_b)
print('merged pairs')
print(pformat(merge_pairs))
print('\nFinal nets:')
print([kk for kk in nets_info.nets if isinstance(kk, str)])
print('\nNet sets:')
for short in nets_info.get_shorted_nets():
print('(' + ','.join(sorted(list(short))) + ')')
return nets_info return nets_info
@ -142,9 +131,9 @@ def label_poly(
def find_merge_pairs( def find_merge_pairs(
connectivity: connectivity_t, connectivity: connectivity_t,
nets: Mapping[net_name_t, Mapping[layer_t, Sequence[contour_t]]], nets: Mapping[NetName, Mapping[layer_t, Sequence[contour_t]]],
via_polys: Mapping[layer_t, Sequence[contour_t]], via_polys: Mapping[layer_t, Sequence[contour_t]],
) -> Set[Tuple[net_name_t, net_name_t]]: ) -> Set[Tuple[NetName, NetName]]:
# #
# Merge nets based on via connectivity # Merge nets based on via connectivity
# #
@ -155,8 +144,6 @@ def find_merge_pairs(
if not vias: if not vias:
continue continue
#TODO deal with polygons that have holes (loops?)
for top_name in nets.keys(): for top_name in nets.keys():
top_polys = nets[top_name][top_layer] top_polys = nets[top_name][top_layer]
if not top_polys: if not top_polys:
@ -165,7 +152,7 @@ def find_merge_pairs(
for bot_name in nets.keys(): for bot_name in nets.keys():
if bot_name == top_name: if bot_name == top_name:
continue continue
name_pair = tuple(sorted((top_name, bot_name), key=lambda s: id(s))) name_pair = tuple(sorted((top_name, bot_name)))
if name_pair in merge_pairs: if name_pair in merge_pairs:
continue continue

View File

@ -1,31 +1,61 @@
from typing import List, Set from typing import List, Set, ClassVar, Optional
from collections import defaultdict from collections import defaultdict
from dataclasses import dataclass
from .types import layer_t, net_name_t, contour_t from .types import layer_t, contour_t
class NetName:
name: Optional[str]
subname: int
count: ClassVar[defaultdict[Optional[str], int]] = defaultdict(int)
def __init__(self, name: Optional[str] = None) -> None:
self.name = name
self.subname = self.count[name]
NetName.count[name] += 1
def __lt__(self, other: 'NetName') -> bool:
if self.name == other.name:
return self.subname < other.subname
elif self.name is None:
return False
elif other.name is None:
return True
else:
return self.name < other.name
def __repr__(self) -> str:
if self.name is not None:
name = self.name
else:
name = '(None)'
if NetName.count[self.name] == 1:
return name
else:
return f'{name}__{self.subname}'
class NetsInfo: class NetsInfo:
nets: defaultdict[net_name_t, defaultdict[layer_t, List]] nets: defaultdict[NetName, defaultdict[layer_t, List]]
net_aliases: defaultdict[net_name_t, net_name_t] net_aliases: defaultdict[NetName, NetName]
def __init__(self) -> None: def __init__(self) -> None:
self.nets = defaultdict(lambda: defaultdict(list)) self.nets = defaultdict(lambda: defaultdict(list))
self.net_aliases = defaultdict(list) self.net_aliases = defaultdict(list)
def resolve_name(self, net_name: net_name_t) -> net_name_t: def resolve_name(self, net_name: NetName) -> NetName:
while net_name in self.net_aliases: while net_name in self.net_aliases:
net_name = self.net_aliases[net_name] net_name = self.net_aliases[net_name]
return net_name return net_name
def merge(self, net_a: net_name_t, net_b: net_name_t) -> None: def merge(self, net_a: NetName, net_b: NetName) -> None:
net_a = self.resolve_name(net_a) net_a = self.resolve_name(net_a)
net_b = self.resolve_name(net_b) net_b = self.resolve_name(net_b)
# Always keep named nets if the other is anonymous # Always keep named nets if the other is anonymous
if not isinstance(net_a, str) and isinstance(net_b, str): keep_net, old_net = sorted((net_a, net_b))
keep_net, old_net = net_b, net_a
else:
keep_net, old_net = net_a, net_b
#logger.info(f'merging {old_net} into {keep_net}') #logger.info(f'merging {old_net} into {keep_net}')
self.net_aliases[old_net] = keep_net self.net_aliases[old_net] = keep_net
@ -34,17 +64,34 @@ class NetsInfo:
self.nets[keep_net][layer] += self.nets[old_net][layer] self.nets[keep_net][layer] += self.nets[old_net][layer]
del self.nets[old_net] del self.nets[old_net]
def get(self, net: net_name_t, layer: layer_t) -> List[contour_t]: def get(self, net: NetName, layer: layer_t) -> List[contour_t]:
return self.nets[self.resolve_name(net)][layer] return self.nets[self.resolve_name(net)][layer]
def get_shorted_nets(self) -> List[Set[str]]: def get_shorted_nets(self) -> List[Set[NetName]]:
shorts = defaultdict(list) shorts = defaultdict(list)
for kk in self.net_aliases: for kk in self.net_aliases:
if isinstance(kk, str): if kk.name is None:
base_name = self.resolve_name(kk) continue
assert(isinstance(base_name, str))
shorts[base_name].append(kk) base_name = self.resolve_name(kk)
assert(base_name.name is not None)
shorts[base_name].append(kk)
shorted_sets = [set([kk] + others) shorted_sets = [set([kk] + others)
for kk, others in shorts.items()] for kk, others in shorts.items()]
return shorted_sets return shorted_sets
def get_open_nets(self) -> defaultdict[str, List[NetName]]:
opens = defaultdict(list)
seen_names = {}
for kk in self.nets:
if kk.name is None:
continue
if kk.name in seen_names:
if kk.name not in opens:
opens[kk.name].append(seen_names[kk.name])
opens[kk.name].append(kk)
else:
seen_names[kk.name] = kk
return opens

View File

@ -2,5 +2,4 @@ from typing import Union, Tuple, List, Sequence, Optional
layer_t = Tuple[int, int] layer_t = Tuple[int, int]
contour_t = List[Tuple[int, int]] contour_t = List[Tuple[int, int]]
net_name_t = Union[str, object]
connectivity_t = Sequence[Tuple[layer_t, Optional[layer_t], layer_t]] connectivity_t = Sequence[Tuple[layer_t, Optional[layer_t], layer_t]]