Source code for jitx.controlpoint

from __future__ import annotations
from collections.abc import Sequence
from dataclasses import replace
from typing import overload

from jitx._structural import Ref, dispose
from jitx.copper import Copper
from jitx.decorators import identityclass
from jitx.inspect import Trace as _Trace
from jitx.query import transformer

from .placement import Positionable
from .shapes import Shape
from .net import (
    CoupledRouteConnectionEndpoint,
    Port,
    DiffPair,
    RouteConnectionEndpoint,
    TopologyNet,
    UncoupledConnectionEndpoint,
)


[docs] class ControlPoint[T: Port](Positionable, Ref): """Base class for control points. Do not subclass or use this directly, use one of :py:class:`RoutePoint`, :py:class:`PairInsertion` or :py:class:`PairPoint` instead.""" layer: int """The layer on which the control point should be placed.""" port: T """The port associated with this route point used for netting."""
[docs] @identityclass class Trace: """Computed output shape(s) of a control point. While a RoutePoint would likely have one shape, PairInsertion and PairPoint would have multiple.""" shapes: Sequence[Shape]
traces: Sequence[Trace] | None = None """Traces generated for this control point. Each entry holds the copper shapes belonging to one net (carried as a :class:`ComputedNet` property on the Trace). This will only be available once the design has been evaluated and captured by the runtime.""" def __init__(self, *, layer: int, bundle: type[T]): self.layer = layer self.port = bundle() def __rshift__(self, other: Port | ControlPoint | TopologyNet) -> TopologyNet: if isinstance(other, TopologyNet): dispose(other) return TopologyNet((self,) + other.sequence) elif isinstance(other, Port | ControlPoint): return TopologyNet(self, other) return NotImplemented
[docs] class RoutePoint[T: Port](ControlPoint): shape: Shape | None """The geometric shape of the control point.""" pad: RouteConnectionEndpoint[T] """The route connection end point associated to this control point, used for routing.""" @overload def __init__(self, *, layer: int, shape: Shape | None = None, bundle: type[T]): ... @overload def __init__(self: RoutePoint[Port], *, layer: int, shape: Shape | None = None): ... def __init__(self, *, layer: int, shape: Shape | None = None, bundle: type = Port): super().__init__(layer=layer, bundle=bundle) self.shape = shape self.pad = RouteConnectionEndpoint(bundle)
[docs] class PairInsertion[T: DiffPair](ControlPoint[T]): """Differential pair insertion point. Transitions two individual, uncoupled traces into a differential pair. Pair insertion points can also be netted directly with other objects using :py:attr:`~jitx.net.Net` or :py:attr:`~jitx.net.TopologyNet`. The coupled end of pair insertion can connect to the :py:attr:`~PairPoint.back` side of a pair point, and an inverted insertion can connect to the :py:attr:`~PairPoint.front` side of an pair point. The relationships are reversed if the pair point is inverted. Similarly an insertion point can only connect to an inverted insertion point. The diagram below shows why inverting one of the insertions is necessary. Note that specifying invert does _not_ imply that insertion point is rotated, to have insertion points face each other, one must be rotated 180 degrees. The default ``p`` and ``n`` sides are as shown below: .. code-block:: text uncoupled.p -- -- uncoupled.p \\ / == coupled == (inverted, rotated 180 degrees) / \\ uncoupled.n -- -- uncoupled.n This can be mirrored, (which, for example, would be needed to make a geometric connection possible on the other end, where the diff-pair route terminates in a second insertion point) by setting :py:attr:`~.invert=True`. When used with :py:attr:`~jitx.net.PortAttachment`, it needs an ordered pair of ports. The first port is connected to the ``p`` side and the second to the ``n`` side of the control point. Using PortAttachments for this should not be necessary for the vast majority of cases. >>> class MyCircuit(Circuit): ... c1 = MyComponent1() ... c2 = MyComponent2() ... def __init__(self): ... self.insertion1 = PairInsertion(layer=0).at(-2, 0) ... self.insertion2 = PairInsertion(layer=0, invert=True).at(2, 0, rotate=180) ... self.nets = [ ... Net([self.c1.p1, self.c2.p1]), ... Net([self.c1.p2, self.c2.p2]), ... ] ... self.attachments = [ ... PortAttachment([self.c1.p1, self.c1.p2], self.insertion1), ... PortAttachment([self.c2.p1, self.c2.p2], self.insertion2), ... ] ... self.routes = [ ... Route(self.insertion1.coupled, self.insertion2.coupled, 0) ... ] """ port: T """The differential pair port associated to this control point, used for connections.""" coupled: CoupledRouteConnectionEndpoint[T] "Coupled-side pads of the differential insertion point, used for connections and routing." uncoupled: UncoupledConnectionEndpoint[T] "Uncoupled-side pads of the differential insertion point, used for connections and routing." invert: bool """Mirror the chirality of the insertion point.""" @overload def __init__(self, *, layer: int, bundle: type[T], invert: bool = False): ... @overload def __init__( self: PairInsertion[DiffPair], *, layer: int, invert: bool = False ): ... def __init__(self, *, layer: int, bundle: type = DiffPair, invert: bool = False): super().__init__(layer=layer, bundle=bundle) self.coupled = CoupledRouteConnectionEndpoint(bundle) self.uncoupled = UncoupledConnectionEndpoint(bundle) self.invert = invert
[docs] class PairPoint[T: DiffPair](ControlPoint[T]): """A differential pair control point connects two segments of a differential pair, while still paired, allowing for each segment to be configured independently. A pair point can connect to another to other pairs, or insertion points. A chain of pair points would connect front -> back, as long as neither or both are inverted. To connect an inverted to non-inverted point you'd connect front to front or back to back. See the diagram in :py:class:`PairInsertion` for details. """ port: T """The differential pair port associated to this control point, used for connections.""" front: CoupledRouteConnectionEndpoint[T] "Front-side pads of the differential insertion point, used for routing." back: CoupledRouteConnectionEndpoint[T] "Back-side pads of the differential insertion point, used for routing." invert: bool """Mirror the chirality of the pair point.""" @overload def __init__(self, *, layer: int, bundle: type[T], invert: bool = False): ... @overload def __init__(self: PairPoint[DiffPair], *, layer: int, invert: bool = False): ... def __init__(self, *, layer: int, bundle: type = DiffPair, invert: bool = False): super().__init__(layer=layer, bundle=bundle) self.front = CoupledRouteConnectionEndpoint(bundle) self.back = CoupledRouteConnectionEndpoint(bundle) self.invert = invert
@transformer(ControlPoint, Copper) def _control_point_to_copper(trace: _Trace, control_point: ControlPoint): from jitx._translate.lookup import ComputedNet if not trace.transform or not control_point.transform: return trace = replace(trace, transform=trace.transform * control_point.transform) if control_point.traces: for cpt in control_point.traces: cnet = ComputedNet.get(cpt) or ComputedNet(None) for shape in cpt.shapes: yield trace, cnet.assign(Copper(shape, control_point.layer))