improvement: track opens (ie, unconnected same-named nets)
This commit is contained in:
parent
bef5421703
commit
718dc2a9ac
BIN
connectivity.oas
Normal file
BIN
connectivity.oas
Normal file
Binary file not shown.
11
example.py
11
example.py
@ -1,3 +1,5 @@
|
||||
from pprint import pformat
|
||||
|
||||
from masque.file import gdsii, oasis
|
||||
|
||||
import snarl
|
||||
@ -18,3 +20,12 @@ topcell = cells['top']
|
||||
polys, labels = snarl.interfaces.masque.read_topcell(topcell, 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())))
|
||||
|
@ -37,8 +37,21 @@ def read_topcell(
|
||||
|
||||
metal_labels = defaultdict(list)
|
||||
for label_layer, metal_layer in label_mapping.items():
|
||||
labels = [ll for ll in topcell.labels if ll.layer == label_layer]
|
||||
metal_labels[metal_layer] += [(*ll.offset, ll.string) for ll in labels]
|
||||
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
|
||||
|
||||
|
@ -7,10 +7,10 @@ import numpy
|
||||
from numpy.typing import NDArray, ArrayLike
|
||||
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 .clipper import union_nonzero, union_evenodd, intersection_evenodd, hier2oriented
|
||||
from .tracker import NetsInfo
|
||||
from .tracker import NetsInfo, NetName
|
||||
from .utils import connectivity2layers
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ def check_connectivity(
|
||||
|
||||
nets_info = NetsInfo()
|
||||
|
||||
merge_groups: List[List[net_name_t]] = []
|
||||
merge_groups: List[List[NetName]] = []
|
||||
for layer, labels_for_layer in labels.items():
|
||||
point_xys = []
|
||||
point_names = []
|
||||
@ -47,18 +47,18 @@ def check_connectivity(
|
||||
for poly in metal_polys[layer]:
|
||||
found_nets = label_poly(poly, point_xys, point_names, clipper_scale_factor)
|
||||
|
||||
name: net_name_t
|
||||
name: Optional[str]
|
||||
if found_nets:
|
||||
name = found_nets[0]
|
||||
name = NetName(found_nets[0])
|
||||
else:
|
||||
name = object() # Anonymous net
|
||||
name = NetName() # Anonymous net
|
||||
|
||||
nets_info.get(name, layer).append(poly)
|
||||
|
||||
if len(found_nets) > 1:
|
||||
# Found a short
|
||||
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:
|
||||
first_net, *defunct_nets = group
|
||||
@ -82,17 +82,6 @@ def check_connectivity(
|
||||
for net_a, net_b in merge_pairs:
|
||||
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
|
||||
|
||||
|
||||
@ -142,9 +131,9 @@ def label_poly(
|
||||
|
||||
def find_merge_pairs(
|
||||
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]],
|
||||
) -> Set[Tuple[net_name_t, net_name_t]]:
|
||||
) -> Set[Tuple[NetName, NetName]]:
|
||||
#
|
||||
# Merge nets based on via connectivity
|
||||
#
|
||||
@ -155,8 +144,6 @@ def find_merge_pairs(
|
||||
if not vias:
|
||||
continue
|
||||
|
||||
#TODO deal with polygons that have holes (loops?)
|
||||
|
||||
for top_name in nets.keys():
|
||||
top_polys = nets[top_name][top_layer]
|
||||
if not top_polys:
|
||||
@ -165,7 +152,7 @@ def find_merge_pairs(
|
||||
for bot_name in nets.keys():
|
||||
if bot_name == top_name:
|
||||
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:
|
||||
continue
|
||||
|
||||
|
@ -1,31 +1,61 @@
|
||||
from typing import List, Set
|
||||
from typing import List, Set, ClassVar, Optional
|
||||
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:
|
||||
nets: defaultdict[net_name_t, defaultdict[layer_t, List]]
|
||||
net_aliases: defaultdict[net_name_t, net_name_t]
|
||||
nets: defaultdict[NetName, defaultdict[layer_t, List]]
|
||||
net_aliases: defaultdict[NetName, NetName]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.nets = defaultdict(lambda: 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:
|
||||
net_name = self.net_aliases[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_b = self.resolve_name(net_b)
|
||||
|
||||
# Always keep named nets if the other is anonymous
|
||||
if not isinstance(net_a, str) and isinstance(net_b, str):
|
||||
keep_net, old_net = net_b, net_a
|
||||
else:
|
||||
keep_net, old_net = net_a, net_b
|
||||
keep_net, old_net = sorted((net_a, net_b))
|
||||
|
||||
#logger.info(f'merging {old_net} into {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]
|
||||
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]
|
||||
|
||||
def get_shorted_nets(self) -> List[Set[str]]:
|
||||
def get_shorted_nets(self) -> List[Set[NetName]]:
|
||||
shorts = defaultdict(list)
|
||||
for kk in self.net_aliases:
|
||||
if isinstance(kk, str):
|
||||
base_name = self.resolve_name(kk)
|
||||
assert(isinstance(base_name, str))
|
||||
shorts[base_name].append(kk)
|
||||
if kk.name is None:
|
||||
continue
|
||||
|
||||
base_name = self.resolve_name(kk)
|
||||
assert(base_name.name is not None)
|
||||
shorts[base_name].append(kk)
|
||||
|
||||
shorted_sets = [set([kk] + others)
|
||||
for kk, others in shorts.items()]
|
||||
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
|
||||
|
@ -2,5 +2,4 @@ from typing import Union, Tuple, List, Sequence, Optional
|
||||
|
||||
layer_t = 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]]
|
||||
|
Loading…
Reference in New Issue
Block a user