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
|
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())))
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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]]
|
||||||
|
Loading…
Reference in New Issue
Block a user