from __future__ import annotations
from typing import overload
from .placement import Positionable
from .shapes import Shape
from .net import Port, DiffPair
[docs]
class ControlPoint(Positionable):
"""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."""
def __init__(
self,
*,
layer: int,
):
self.layer = layer
[docs]
class RoutePoint[T: Port](ControlPoint):
shape: Shape | None
"""The geometric shape of the control point."""
port: T
"""The port 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)
self.shape = shape
self.port = bundle()
[docs]
class PairInsertion[T: DiffPair](ControlPoint):
"""Differential pair insertion point. Transitions two individual, uncoupled
traces into a differential pair. When used with :py:attr:`~jitx.net.PortAttachment`,
it needs an ordered pair of ports. The first port is connected to the left
side (looking from the uncoupled side of the pair insertion point), and
the second to the right side. Currently it cannot be used in a
:py:attr:`~jitx.net.Net` or :py:attr:`~jitx.net.TopologyNet` directly.
Currently, the left port is accessed using ``uncoupled.n``, and the
right port using ``uncoupled.p``, where :py:attr:`~.uncoupled` is a
member attribute of type :py:attr:`~jitx.net.DiffPair`. This is only
relevant when making code routes, since in that case the physical route
ends need to be referenced, and you may need to unintuitively connect ``n``
and ``p`` in order to make the geometry work. This a limitation in the
current implementation. This will be addressed to be more consistent
with logical netting in future releases. The ``coupled`` side is always routed
as a diffpair so ``coupled.p`` and ``coupled.n`` never need to be accessed
individually.
See the PortAttachment example below for how to set up two insertion
control points with correct geometry.
.. code-block:: text
uncoupled.n --
\\
== coupled
/
uncoupled.p --
>>> class MyCircuit(Circuit):
... c1 = MyComponent1()
... c2 = MyComponent2()
... def __init__(self):
... self.insertion1 = PairInsertion(layer=0).at(-2, 0)
... self.insertion2 = PairInsertion(layer=0).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.p2, self.c2.p1], self.insertion2), # note the flipped order for achieving opposite chirality
... ]
... self.routes = [
... Route(self.insertion1, self.insertion2, 0)
... ]
"""
coupled: T
"Coupled side of the differential insertion point."
uncoupled: T
"Uncoupled side of the differential insertion point."
@overload
def __init__(self, *, layer: int, bundle: type[T]): ...
@overload
def __init__(self: PairInsertion[DiffPair], *, layer: int): ...
def __init__(self, *, layer: int, bundle: type = DiffPair):
super().__init__(layer=layer)
self.coupled = bundle()
self.uncoupled = bundle()
[docs]
class PairPoint[T: DiffPair](ControlPoint):
"""A differential pair control point connects two segments of a
differential pair, while still paired, allowing for each segment to be
configured independently."""
pair: T
"""The differential pair port associated to this control point, used for routing."""
@overload
def __init__(self, *, layer: int, bundle: type[T]): ...
@overload
def __init__(self: PairPoint[DiffPair], *, layer: int): ...
def __init__(self, *, layer: int, bundle: type = DiffPair):
super().__init__(layer=layer)
self.pair = bundle()