Source code for jitx.virtual

"""
Virtual Connections
====================

User-declared assertions that two endpoints are electrically connected, without
JITX routing the connection itself.

.. warning::
    The API in this module is still experimental and may change
    significantly without notice.
"""

from __future__ import annotations

from jitx._structural import Structural
from jitx.landpattern import Pad
from jitx.net import DiffPair, Port, ConnectionEndpoint, CoupledRouteConnectionEndpoint
from jitx.si import PinModel
from jitx.via import Via


def _check_endpoint(target: object, role: str) -> None:
    if isinstance(target, Port):
        if isinstance(target, DiffPair):
            raise TypeError(
                f"VirtualConnection {role} cannot be a combined "
                "differential-pair port; connect a single leg instead "
                "(e.g. .p or .n)."
            )
        if not target.is_single_pin():
            raise TypeError(
                f"VirtualConnection {role} must be a single-pin Port, "
                f"not a bundle port ({type(target).__name__}); connect "
                "one of its leaf ports instead."
            )
    elif isinstance(target, ConnectionEndpoint):
        if isinstance(target, CoupledRouteConnectionEndpoint):
            raise TypeError(
                f"{role} must not be single connection endpoints, "
                f"not coupled connection endpoints ({type(target).__name__}); connect "
                "p/n sides instead."
            )
    elif not isinstance(target, (Pad, Via)):
        raise TypeError(
            f"VirtualConnection {role} must be a ConnectionEndpoint, Port, Pad, or Via, "
            f"not {type(target).__name__}."
        )


[docs] class VirtualConnection(Structural): """User-declared assertion that two endpoints are electrically connected. JITX does not route the connection; ratsnest visualization and unrouted-warning emission are suppressed for the affected endpoints. Use cases include :py:class:`~jitx.feature.OverlappableCopper` bridges, connections made manually post-fabrication (e.g., a soldered wire), and modeling electrically real structures invisible to the router. Each endpoint must be a single electrical target: a single-pin :class:`~jitx.net.Port`, a :class:`~jitx.landpattern.Pad`, or a :class:`~jitx.via.Via`. Bundle ports — including a combined differential pair — are rejected; name a single leg (``pair.p`` / ``pair.n``) instead. .. warning:: Declaring an VirtualConnection does not automatically bridge nets This allows VirtualConnections to be used for pin-assigned connections An external connection that isn't allowed will be dropped .. warning:: Declaring multiple external connections between the same endpoint pair is not supported. If there are multiple pin models which one is chosen may be arbitrary. .. note:: VirtualConnection can be used to override default models for other connections. e.g. If two features overlap and have an VirtualConnection defined, the VirtualConnection's pin model will be used instead of the generated model for touching features. This can be used to override route models in cases where VirtualConnection and route are allowed at the same time. Though routes will need to be forced since autorouter will consider the endpoints already connected. """ source: ConnectionEndpoint | Port | Pad | Via """One endpoint of the external connection. Must be a single-pin :class:`~jitx.net.Port`, :class:`~jitx.landpattern.Pad`, or :class:`~jitx.via.Via`.""" destination: ConnectionEndpoint | Port | Pad | Via """The other endpoint of the external connection. Same type constraints as :py:attr:`source`.""" source_layer: int """Copper layer at which the connection attaches to :py:attr:`source`. ``0`` is the top layer; negative indices count from the bottom (``-1`` is the bottom layer), like Python sequence indexing. The two endpoints may be on different layers.""" destination_layer: int """Copper layer at which the connection attaches to :py:attr:`destination`. Same conventions as :py:attr:`source_layer`.""" pin_model: PinModel | None """Optional electrical characterization of the connection. If omitted, the endpoints still count as connected (ratsnest and unrouted warnings are suppressed), but the connection cannot satisfy a constrained topology: length-matching, skew, or insertion-loss constraints routed through a model-less external connection surface may be reported as a missing-route error. If this connection sits inside a constrained topology, provide a pin model. Only the base :class:`~jitx.si.PinModel` delay/loss values are consumed; the port bindings of :class:`~jitx.si.BridgingPinModel` / :class:`~jitx.si.TerminatingPinModel` are ignored here, as the connection's own endpoints determine what the model bridges. """ def __init__( self, source: ConnectionEndpoint | Port | Pad | Via, destination: ConnectionEndpoint | Port | Pad | Via, *, source_layer: int, destination_layer: int, pin_model: PinModel | None = None, ): _check_endpoint(source, "source") _check_endpoint(destination, "destination") self.source = source self.destination = destination self.source_layer = source_layer self.destination_layer = destination_layer self.pin_model = pin_model