net module#

Net connectivity#

This module provides fundamental net connectivity classes including Port, Net, TopologyNet, and related functionality for net connections.

exception PortException[source]#

Bases: Exception

class Port[source]#

Bases: Structural

A Port is the fundamental connectivity element of JITX. It is used to represent everything from “pins” of a component, to the “virtual” pins of a circuit, to the abstract ports that make up a pin-assignment problem.

Ports can only be netted to other ports of the same concrete type, thus a subclass of Port can only be netted to the same subclass of port. For parameterized ports, they also need to match the same parameters. To create a multi-signal port, create a subclass of Port with a member field for each signal. These are sometimes also referred to as “bundle” ports.

Instantiating Port directly represents a single electrical signal, and is the only type that is allowed as a leaf in a complex Port structure.

Ports can be netted together using the + operator. This will create a Net object that represents the netted ports. If the ports should be part of a signal topology where the order of the ports matters, use the >> operator. This will create a TopologyNet object that represents the netted ports that will only allow the ports to be connected in this particular order while routing. Note that you must use a topology net even if there is only two ports in the net if you wish to apply a signal integrity constraint.

>>> class MyDualSignal(Port):
...     a = Port()
...     b = Port()
>>> class MyDualAndClockSignal(Port):
...     dual = MyDualSignal()
...     clock = Port()
is_single_pin()[source]#
no_connect()[source]#

Mark this port as no-connect.

>>> class MyComponent(Component):
...     port = Port()
...     # ...
...
>>> class MyCircuit(Circuit):
...     component = MyComponent()
...     def __init__(self):
...         self.component.port.no_connect()
is_no_connect()[source]#

Check if this port is marked as no-connect.

Return type:

bool

to(other)[source]#

Refer to a topology between these two ports. This does not construct the signal topology itself, but is used to create a topology object which is used as an argument to other functions operating on topologies. To construct a signal topology, use the >> operator to construct TopologyNet and bridge components using the BridgingPinModel, and terminate the topology using the TerminatingPinModel. The pin-models can be added after the fact in a circuit if needed, but ideally they’re already present in the component definition. After that, a signal constraint can applied to the end-to-end topology.

Return type:

Topology[Self]

Parameters:

other (Self)

>>> class MyCircuit(Circuit):
...     a = TransmitterComponent()
...     b = ReceiverComponent()
...     def __init__(self):
...         self.passive = Resistor(...)
...         p1, p2 = decompose(self.passive, Port)
...         self.signal = [
...             # Note: pin models are best added in the component
...             # definition, but can be added in the circuit after the
...             # fact if needed, as done here.
...             TerminatingPinModel(self.a.transmit, delay=2e-12, loss=0.1),
...             self.a.transmit >> p1,
...             BridgingPinModel(p1, p2, delay=10e-12, loss=0.1),
...             p2 >> self.b.receive,
...             TerminatingPinModel(self.b.receive, delay=2e-12, loss=0.1),
...         ]
...         self.constraint = Constrain(self.a.transmit.to(self.b.receive)).timing(50e-12)
port_array(cnt, /, ptype=<class 'jitx.net.Port'>)[source]#

Convenience function to construct a list of Port() instances

Note

This function simply constructs a list of Port() instances, it does not create a bundle that can be used for netting. More importantly, if we attempt to construct a net using the result of this function, indeed it will most likely silently add the list of ports together, and not create a net for each element.

Parameters:
  • cnt (int) – Number of ports in the constructed sequence

  • ptype (Callable[[], TypeVar(T, bound= Port)]) – Type or constructor of port for the constructed sequence. By default this functions uses Port

Return type:

list[TypeVar(T, bound= Port)]

Returns:

Sequence of port elements that make up a port array.

class PortArray(array, /, count=None)[source]#

Bases: Port, Generic

A port array is a primitive array of single-signal ports. It’s mainly intended as an example, and explicit bundles should be implemented instead.

Note

While this constructs a bundle that can be netted, it’s currenetly not possible to use it as a sequence of individual ports where a Sequence of ports is expected.

Parameters:
  • array (tuple[T, ...])

  • count (int | None)

array: tuple[T, ...]#
class provide(Bundle)[source]#

Bases: Generic

Decorator to help set up the pin-assignment problem. In the base form, the decorator is applied to a method of a circuit that will be given a bundle to map, and should return a list of possibilities how to map that bundle. Each returned options will be offered to requirements, so if 3 options are returned, three different instances of this bundle can be required, served by each option. This is commonly known as the “pin swapping” case, where all options can be used, but it’s not settled which is used where.

For other provisioning schemes, see the @provide.one_of and @provide.subset_of decorators.

Parameters:

Bundle (T | type[T]) – The type of the bundle to provide

>>> class MyBundle(Port):
...     a = Port()
...     b = Port()
>>> class MyCircuit(Circuit):
...     # these are created here for demonstration purposes, we could just
...     # as easily refer to a component's ports.
...     internal_1 = Port(), Port()
...     internal_2 = Port(), Port()
...     @provide(MyBundle)
...     def provide_my_internals(self, bundle: MyBundle):
...         return [{
...             # first option
...             bundle.a: self.internal_1[0],
...             bundle.b: self.internal_1[1],
...         }, {
...             # second option
...             bundle.a: self.internal_2[0],
...             bundle.b: self.internal_2[1],
...         }]
>>> class MyOtherCircuit(Circuit):
...     subcircuit = MyCircuit()
...     interfaces = MyBundle(), MyBundle()
...     def __init__(self):
...         self.nets = [
...             interfaces[0] + subcircuit.require(MyBundle),
...             interfaces[1] + subcircuit.require(MyBundle),
...         ]
classmethod all_of(Bundle)[source]#

This is the default behavior and indeed an alias of using provide directly without specifying a provisioning scheme. Each returned option will be offered to requirements, so if 3 options are returned, three different instances of this bundle can be required, served by each option. This is commonly known as the “pin swapping” case, where all options can be used, but it’s not settled which is used where.

Parameters:

Bundle (T | type[T]) – The type of the bundle to provide

classmethod one_of(Bundle)[source]#

Provide a single instance of the given bundle type, chosen from a list of possible options. The options may overlap with other provided functions. This is the dynamic function use case, where a function can served by multiple different ports, but there’s only one instance of the function provided, e.g. a microcontroller has only one clock, but which pin it’s on is not settled.

Parameters:

Bundle (T | type[T]) – The type of the bundle to provide

classmethod subset_of(Bundle, n)[source]#

Similar to @provide.one_of, but allows for n instances out of a list of options to be provided. This is equivalent to manually copying a @provide.one_of decorated method n times.

Parameters:
  • Bundle (T | type[T]) – The type of the bundle to provide

  • n (int) – The number of instances to provide

Provider = Provider#
class Provide(Bundle, mapping=None)[source]#

Bases: Structural, Ref, Generic

This is the general structure for setting up a pin-assignment problem. It is recommended to use one of the convenience decorators in provide instead, but if the full power of the pin-assignment system is needed in a way that is not covered by the decorators, or if you need programmatic control over the pin-assignment problem, then directly instantiating this class the appropriate number of times and populating the options in each may be a way to achieve that.

The equivalent of the conenvience decorators are also available as methods on this class for use inside functions, such as __init__.

>>> class MyCircuit(Circuit):
...     def __init__(self, ports: int):
...         self.ports = [Port() for _ in range(ports)]
...         self.gpios = Provide(GPIO).all_of(lambda b: [{b.port: port} for port in self.ports])

Note

Be aware of pythons binding of loop-variables, if you create Provides in a loop, you should bind the loop variable if it’s used in the mapping function. Consider the functional equivalent of .all_of but using .one_of in the example below:

>>> class MyCircuit(Circuit):
...     def __init__(self, ports: int):
...         self.ports = [Port() for _ in range(ports)]
...         self.gpios = [Provide(GPIO).one_of(lambda b, port=port: [{b.port: port}]) for port in ports]

Note that the port argument is bound to the lambda function’s body using a parameter with a default argument, instead of using the loop variable directly, which could potentially be bound to a different value by the time the lambda is called. In the current implementation, the lambda is called immediately, so the result would still be correct here, but it’s a good practice to avoid this pitfall.

Parameters:
  • Bundle (T | type[T]) – The type of the bundle to provide

  • mapping (Provider | None) – The mapping function to use to populate the options for this Provide port. Note that this is the equivalent argument of calling one_of(), and it’s recommended to use that method instead for clarity.

options: Sequence[Sequence[tuple[Port, Port]]]#
bundle: T#
all_of(mapping)[source]#

Equivalent of the @provide.all_of decorator, but can be constructed inside a function, such as __init__. Useful when needing more parametric control over the pin-assignment problem. Note that this function will return a collection of Provide objects.

Return type:

Sequence[Provide]

Parameters:

mapping (Provider)

one_of(mapping)[source]#

Equivalent of the @provide.one_of decorator, but can be constructed inside a function, such as __init__. Useful when needing more parametric control over the pin-assignment problem.

Return type:

Provide

Parameters:

mapping (Provider)

subset_of(count, mapping)[source]#

Equivalent of the @provide.subset_of decorator, but can be constructed inside a function, such as __init__. Useful when needing more parametric control over the pin-assignment problem. Note that this function will return a collection of Provide objects.

Return type:

Sequence[Provide]

Parameters:
  • count (int)

  • mapping (Provider)

classmethod require(Bundle, provider, restrictions=None)[source]#

This is the underlying framework function for requiring a bundle provided by a different circuit. Please use Circuit’s convenience require() method instead.

Parameters:
  • Bundle (T | type[T]) – The bundle type to require

  • provider (Circuit | Component) – The provider instance

  • restrictions (Callable[[T], _Restrictions] | None) – Optional function mapping single ports within a bundle to a restriction function

Return type:

T

classmethod provided_by(port)[source]#
Return type:

None | Circuit | Component

Parameters:

port (Port)

classmethod required_through(port)[source]#
Return type:

None | Port

Parameters:

port (Port)

classmethod restrictions_for(port)[source]#

Get restrictions associated with a required bundle port.

Return type:

TypeAliasType | None

Parameters:

port (Port)

Provider = Provider#
class Net(ports=(), *, name=None, symbol=None)[source]#

Bases: Structural, Generic

Construct a net by connecting ports or other nets. Note that nets are constructed implicitly by adding ports and other nets together, and constructing a net explicitly only has the main benefit of declaring the net up front, and give it a name and/or symbol.

Parameters:
  • ports (Iterable[T | Net[T] | Copper | Via]) – Optional list of ports to initially add to the net

  • name (str | None) – Optional name of this net. If there are multiple named nets connected together, a net naming heuristic will pick one of them.

  • symbol (Symbol | None) – Optional net symbol for this net. Only applicable to simple, non-bundle, nets.

>>> Net(name="P5V5")
Net(name="P5V5")
>>> a, b = Port(), Port()
>>> isinstance(a + b, Net)
True
name: str | None = None#
connected: list[T | Net[T] | Copper | Via]#
property port: T#
property symbol: Symbol | None#
classmethod zip(first, second, *more)[source]#

Zip multiple sequences of ports into a sequence of nets, where each net contains the corresponding ports from each sequence. For example, the first net in the returned sequence will contain the first port from each input sequence, the second net will contain the second port, and so on. This is particularly useful when dealing with arrays of ports that need to be connected in parallel.

Return type:

Sequence[Net[T]]

Parameters:
  • first (Sequence[T | Net[T]])

  • second (Sequence[T | Net[T]])

  • more (Sequence[T | Net[T]])

>>> a, b, c = Port(), Port(), Port()
>>> x, y, z = Port(), Port(), Port()
>>> u, v, w = Port(), Port(), Port()
>>> n1, n2, n3 = Net.zip([a, b, c], [x, y, z], [u, v, w])
>>> n1.connected == [a, x, u]
True
class SubNet(port, base)[source]#

Bases: Net, Ref, Generic

Parameters:
  • port (T)

  • base (Net)

class TopologyNet(sequence, *args, name=None)[source]#

Bases: Structural, Generic

Parameters:
  • sequence (tuple[T, ...])

  • args (T)

  • name (str | None)

name: str | None = None#
sequence: tuple[T, ...]#
class Topology(begin, end)[source]#

Bases: Generic

A topology is a description of a connectivity path, identified by it’s start and end ports. This object does not construct the topology itself, it merely identifies the path and is used as an argument to other functions such as signal integrity constraints. To construct a topology, use the >> operator to construct TopologyNet objects as well as declaring TerminatingPinModel and BridgingPingModel to construct the actual signal path, from end to end, potentially across components.

Parameters:
  • begin (T)

  • end (T)

begin: TypeVar(T, bound= Port)#
end: TypeVar(T, bound= Port)#
class DiffPair[source]#

Bases: Port

Bundles two pins p and n. The DiffPair base class bears special meaning and standardizes naming, any diff-pair type signal should inherit this base class.

>>> class Transmission(DiffPair):
...     pass
>>> class MDI:
...     tx = Transmission()
...     rx = Transmission()
p = Port()#
n = Port()#
class ShortTrace(p1, p2)[source]#

Bases: Net[Port]

Indicates that the distance between two ports should be minimized. A ShortTrace is a type of net, and can be used as such.

>>> class MyCircuit(Circuit):
...     a = MyComponent()
...     inp = Port()
...     out = Port()
...     def __init__(self):
...         self.nets = [
...             ShortTrace(a.p1, inp),
...             a.p2 + outp
...         ]
Parameters:
class PortAttachment(port, attachment)[source]#

Bases: Structural, Ref

Indicates that a port should be connected to a copper or via.

>>> class MyCircuit(Circuit):
...     c = MyComponent()
...     attachments = [
...         PortAttachment(c.p[0], Copper(0, Circle(diameter=1.0)).at(-5, 0)),
...         PortAttachment(c.p[1], SampleSubstrate.MicroVia().at(5, 0)),
...     ]
Parameters:
port: Port#
attachment: Copper | Via#