The grid is laid out for the default width; a wider trace also needs the cells its extra half width reaches to be free, and claims a wider halo. A connection that does not fit at its class width (typically at a fine-pitch pin) is routed at the default instead and reported. A net is written at its class width when all of its connections went in at it, and the exact check verifies it at that width; nets that shove or the diagonal pass reshaped go out at the default.
87 lines
2.7 KiB
Python
87 lines
2.7 KiB
Python
"""In-memory routing result — the input the SES writer serializes.
|
|
|
|
The maze router (a later stage) populates a :class:`RoutingResult`; the SES
|
|
writer turns it into the ``(routes (network_out ...))`` scope. Coordinates and
|
|
widths are in DSN units (the same units the DSN was parsed in); the writer
|
|
rounds them to integers, matching FreeRouting's ``SesWriter`` which emits
|
|
integer session coordinates.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
|
|
__all__ = ["RoutedWire", "RoutedVia", "RoutedNet", "RoutingResult"]
|
|
|
|
|
|
@dataclass
|
|
class RoutedWire:
|
|
"""A routed trace segment on one layer.
|
|
|
|
``coords`` is a flat ``[x1, y1, x2, y2, ...]`` list (DSN units); ``width`` is
|
|
the full trace width (DSN units). ``fixed`` maps to an emitted
|
|
``(type fix)`` marker when set.
|
|
"""
|
|
|
|
layer: str
|
|
width: float
|
|
coords: list[float]
|
|
fixed: bool = False
|
|
|
|
|
|
@dataclass
|
|
class RoutedVia:
|
|
"""A routed via at ``(x, y)`` (DSN units) using a named padstack."""
|
|
|
|
padstack: str
|
|
x: float
|
|
y: float
|
|
fixed: bool = False
|
|
|
|
|
|
@dataclass
|
|
class RoutedNet:
|
|
"""The routed wires and vias belonging to one net."""
|
|
|
|
name: str
|
|
wires: list[RoutedWire] = field(default_factory=list)
|
|
vias: list[RoutedVia] = field(default_factory=list)
|
|
|
|
@property
|
|
def has_items(self) -> bool:
|
|
return bool(self.wires or self.vias)
|
|
|
|
|
|
@dataclass
|
|
class RoutingResult:
|
|
"""The complete routing output for a board, grouped by net."""
|
|
|
|
nets: list[RoutedNet] = field(default_factory=list)
|
|
#: connections the router could not make: (net name, one end, other end,
|
|
#: the zone both ends sit in or "") -- a zone's fill may join the latter
|
|
unrouted: list[tuple[str, str, str, str]] = field(default_factory=list)
|
|
#: connections of a wider net class that only fitted at the default width
|
|
#: (net name, one end, other end); their net is written at the default
|
|
narrowed: list[tuple[str, str, str]] = field(default_factory=list)
|
|
|
|
def net(self, name: str, *, create: bool = False) -> RoutedNet | None:
|
|
"""Return the :class:`RoutedNet` with ``name`` (creating it if asked)."""
|
|
for n in self.nets:
|
|
if n.name == name:
|
|
return n
|
|
if create:
|
|
n = RoutedNet(name=name)
|
|
self.nets.append(n)
|
|
return n
|
|
return None
|
|
|
|
def add_wire(self, net_name: str, wire: RoutedWire) -> None:
|
|
self.net(net_name, create=True).wires.append(wire)
|
|
|
|
def add_via(self, net_name: str, via: RoutedVia) -> None:
|
|
self.net(net_name, create=True).vias.append(via)
|
|
|
|
def routed_nets(self) -> list[RoutedNet]:
|
|
"""Nets that actually carry routed geometry (in insertion order)."""
|
|
return [n for n in self.nets if n.has_items]
|