net module#
Net connectivity#
This module provides fundamental net connectivity classes including Port, Net, TopologyNet, and related functionality for net connections.
- class Port[source]#
Bases:
StructuralA 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
Netobject 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 aTopologyNetobject 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()
- 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()
- 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 constructTopologyNetand bridge components using theBridgingPinModel, and terminate the topology using theTerminatingPinModel. 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.>>> 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()instancesNote
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:
- Return type:
- Returns:
Sequence of port elements that make up a port array.
- class PortArray(array, /, count=None)[source]#
-
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
Sequenceof ports is expected.- array: tuple[T, ...]#
- class provide(Bundle)[source]#
Bases:
GenericDecorator 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_ofand@provide.subset_ofdecorators.- 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
providedirectly 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 forninstances out of a list of options to be provided. This is equivalent to manually copying a@provide.one_ofdecorated methodntimes.- 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,GenericThis is the general structure for setting up a pin-assignment problem. It is recommended to use one of the convenience decorators in
provideinstead, 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_ofdecorator, 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 ofProvideobjects.- Return type:
Sequence[Provide]
- Parameters:
mapping (Provider)
- one_of(mapping)[source]#
Equivalent of the
@provide.one_ofdecorator, 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_ofdecorator, 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 ofProvideobjects.- 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 conveniencerequire()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 restrictions_for(port)[source]#
Get restrictions associated with a required bundle port.
- Provider = Provider#
- class Net(ports=(), *, name=None, symbol=None)[source]#
Bases:
Structural,GenericConstruct 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#
- 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:
>>> 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 TopologyNet(sequence, *args, name=None)[source]#
Bases:
Structural,Generic- name: str | None = None#
- sequence: tuple[T, ...]#
- class Topology(begin, end)[source]#
Bases:
GenericA 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 constructTopologyNetobjects as well as declaringTerminatingPinModelandBridgingPingModelto construct the actual signal path, from end to end, potentially across components.- Parameters:
begin (T)
end (T)
- class DiffPair[source]#
Bases:
PortBundles 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]#
-
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 ... ]
- class PortAttachment(port, attachment)[source]#
Bases:
Structural,RefIndicates 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)), ... ]
- port: Port#
- attachment: Copper | Via#