Lots of doc updates
This commit is contained in:
parent
d79a0a6388
commit
169e5a1f12
@ -18,6 +18,12 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class Abstract(PortList):
|
class Abstract(PortList):
|
||||||
|
"""
|
||||||
|
An `Abstract` is a container for a name and associated ports.
|
||||||
|
|
||||||
|
When snapping a sub-component to an existing pattern, only the name (not contained
|
||||||
|
in a `Pattern` object) and port info is needed, and not the geometry itself.
|
||||||
|
"""
|
||||||
__slots__ = ('name', '_ports')
|
__slots__ = ('name', '_ports')
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
"""
|
||||||
|
Simplified Pattern assembly (`Builder`)
|
||||||
|
"""
|
||||||
from typing import Self, Sequence, Mapping, Literal, overload
|
from typing import Self, Sequence, Mapping, Literal, overload
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
@ -105,7 +108,15 @@ class Builder(PortList):
|
|||||||
name: str | None = None,
|
name: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
# TODO documentation for Builder() constructor
|
Args:
|
||||||
|
library: The library from which referenced patterns will be taken
|
||||||
|
pattern: The pattern which will be modified by subsequent operations.
|
||||||
|
If `None` (default), a new pattern is created.
|
||||||
|
ports: Allows specifying the initial set of ports, if `pattern` does
|
||||||
|
not already have any ports (or is not provided). May be a string,
|
||||||
|
in which case it is interpreted as a name in `library`.
|
||||||
|
Default `None` (no ports).
|
||||||
|
name: If specified, `library[name]` is set to `self.pattern`.
|
||||||
"""
|
"""
|
||||||
self._dead = False
|
self._dead = False
|
||||||
self.library = library
|
self.library = library
|
||||||
@ -263,32 +274,26 @@ class Builder(PortList):
|
|||||||
append: bool = False,
|
append: bool = False,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
"""
|
"""
|
||||||
Instantiate or append the device `other` into the current device, adding its
|
Wrapper around `Pattern.place` which allows a string for `other`.
|
||||||
ports to those of the current device (but not connecting any ports).
|
The `Builder`'s library is used to dereference the string (or `Abstract`, if
|
||||||
|
one is passed with `append=True`).
|
||||||
Mirroring is applied before rotation; translation (`offset`) is applied last.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
=========
|
|
||||||
- `my_device.place(pad, offset=(10, 10), rotation=pi / 2, port_map={'A': 'gnd'})`
|
|
||||||
instantiates `pad` at the specified (x, y) offset and with the specified
|
|
||||||
rotation, adding its ports to those of `my_device`. Port 'A' of `pad` is
|
|
||||||
renamed to 'gnd' so that further routing can use this signal or net name
|
|
||||||
rather than the port name on the original `pad` device.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
other: An `Abstract` describing the device to be instatiated.
|
other: An `Abstract`, string, or `Pattern` describing the device to be instatiated.
|
||||||
offset: Offset at which to place the instance. Default (0, 0).
|
offset: Offset at which to place the instance. Default (0, 0).
|
||||||
rotation: Rotation applied to the instance before placement. Default 0.
|
rotation: Rotation applied to the instance before placement. Default 0.
|
||||||
pivot: Rotation is applied around this pivot point (default (0, 0)).
|
pivot: Rotation is applied around this pivot point (default (0, 0)).
|
||||||
Rotation is applied prior to translation (`offset`).
|
Rotation is applied prior to translation (`offset`).
|
||||||
mirrored: Whether theinstance should be mirrored across the x and y axes.
|
mirrored: Whether theinstance should be mirrored across the x axis.
|
||||||
Mirroring is applied before translation and rotation.
|
Mirroring is applied before translation and rotation.
|
||||||
port_map: dict of `{'old_name': 'new_name'}` mappings, specifying
|
port_map: dict of `{'old_name': 'new_name'}` mappings, specifying
|
||||||
new names for ports in the instantiated device. New names can be
|
new names for ports in the instantiated device. New names can be
|
||||||
`None`, which will delete those ports.
|
`None`, which will delete those ports.
|
||||||
skip_port_check: Can be used to skip the internal call to `check_ports`,
|
skip_port_check: Can be used to skip the internal call to `check_ports`,
|
||||||
in case it has already been performed elsewhere.
|
in case it has already been performed elsewhere.
|
||||||
|
append: If `True`, `other` is appended instead of being referenced.
|
||||||
|
Note that this does not flatten `other`, so its refs will still
|
||||||
|
be refs (now inside `self`).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
self
|
self
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
"""
|
||||||
|
Manual wire/waveguide routing (`Pather`)
|
||||||
|
"""
|
||||||
from typing import Self, Sequence, MutableMapping, Mapping
|
from typing import Self, Sequence, MutableMapping, Mapping
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
@ -23,57 +26,87 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class Pather(Builder):
|
class Pather(Builder):
|
||||||
"""
|
"""
|
||||||
TODO DOCUMENT Builder
|
An extension of `Builder` which provides functionality for routing and attaching
|
||||||
A `Device` is a combination of a `Pattern` with a set of named `Port`s
|
single-use patterns (e.g. wires or waveguides) and bundles / buses of such patterns.
|
||||||
which can be used to "snap" devices together to make complex layouts.
|
|
||||||
|
|
||||||
`Device`s can be as simple as one or two ports (e.g. an electrical pad
|
`Pather` is mostly concerned with calculating how long each wire should be. It calls
|
||||||
or wire), but can also be used to build and represent a large routed
|
out to `Tool.path` functions provided by subclasses of `Tool` to build the actual patterns.
|
||||||
layout (e.g. a logical block with multiple I/O connections or even a
|
`Tool`s are assigned on a per-port basis and stored in `.tools`; a key of `None` represents
|
||||||
full chip).
|
a "default" `Tool` used for all ports which do not have a port-specific `Tool` assigned.
|
||||||
|
|
||||||
For convenience, ports can be read out using square brackets:
|
|
||||||
- `device['A'] == Port((0, 0), 0)`
|
|
||||||
- `device[['A', 'B']] == {'A': Port((0, 0), 0), 'B': Port((0, 0), pi)}`
|
|
||||||
|
|
||||||
Examples: Creating a Device
|
Examples: Creating a Pather
|
||||||
===========================
|
===========================
|
||||||
- `Device(pattern, ports={'A': port_a, 'C': port_c})` uses an existing
|
- `Pather(library, tools=my_tool)` makes an empty pattern with no ports. The pattern
|
||||||
pattern and defines some ports.
|
is not added into `library` and must later be added with e.g.
|
||||||
|
`library['mypat'] = pather.pattern`.
|
||||||
|
The default wire/waveguide generating tool for all ports is set to `my_tool`.
|
||||||
|
|
||||||
- `Device(ports=None)` makes a new empty pattern with
|
- `Pather(library, ports={'in': Port(...), 'out': ...}, name='mypat', tools=my_tool)`
|
||||||
default ports ('A' and 'B', in opposite directions, at (0, 0)).
|
makes an empty pattern, adds the given ports, and places it into `library`
|
||||||
|
under the name `'mypat'`. The default wire/waveguide generating tool
|
||||||
|
for all ports is set to `my_tool`
|
||||||
|
|
||||||
- `my_device.build('my_layout')` makes a new pattern and instantiates
|
- `Pather(..., tools={'in': top_metal_40um, 'out': bottom_metal_1um, None: my_tool})`
|
||||||
`my_device` in it with offset (0, 0) as a base for further building.
|
assigns specific tools to individual ports, and `my_tool` as a default for ports
|
||||||
|
which are not specified.
|
||||||
|
|
||||||
- `my_device.as_interface('my_component', port_map=['A', 'B'])` makes a new
|
- `Pather.interface(other_pat, port_map=['A', 'B'], library=library, tools=my_tool)`
|
||||||
(empty) pattern, copies over ports 'A' and 'B' from `my_device`, and
|
makes a new (empty) pattern, copies over ports 'A' and 'B' from
|
||||||
creates additional ports 'in_A' and 'in_B' facing in the opposite
|
`other_pat`, and creates additional ports 'in_A' and 'in_B' facing
|
||||||
directions. This can be used to build a device which can plug into
|
in the opposite directions. This can be used to build a device which
|
||||||
`my_device` (using the 'in_*' ports) but which does not itself include
|
can plug into `other_pat` (using the 'in_*' ports) but which does not
|
||||||
`my_device` as a subcomponent.
|
itself include `other_pat` as a subcomponent.
|
||||||
|
|
||||||
Examples: Adding to a Device
|
- `Pather.interface(other_pather, ...)` does the same thing as
|
||||||
============================
|
`Builder.interface(other_builder.pattern, ...)` but also uses
|
||||||
- `my_device.plug(subdevice, {'A': 'C', 'B': 'B'}, map_out={'D': 'myport'})`
|
`other_builder.library` as its library by default.
|
||||||
instantiates `subdevice` into `my_device`, plugging ports 'A' and 'B'
|
|
||||||
of `my_device` into ports 'C' and 'B' of `subdevice`. The connected ports
|
|
||||||
are removed and any unconnected ports from `subdevice` are added to
|
|
||||||
`my_device`. Port 'D' of `subdevice` (unconnected) is renamed to 'myport'.
|
|
||||||
|
|
||||||
- `my_device.plug(wire, {'myport': 'A'})` places port 'A' of `wire` at 'myport'
|
|
||||||
of `my_device`. If `wire` has only two ports (e.g. 'A' and 'B'), no `map_out`,
|
Examples: Adding to a pattern
|
||||||
|
=============================
|
||||||
|
- `pather.path('my_port', ccw=True, distance)` creates a "wire" for which the output
|
||||||
|
port is `distance` units away along the axis of `'my_port'` and rotated 90 degrees
|
||||||
|
counterclockwise (since `ccw=True`) relative to `'my_port'`. The wire is `plug`ged
|
||||||
|
into the existing `'my_port'`, causing the port to move to the wire's output.
|
||||||
|
|
||||||
|
There is no formal guarantee about how far off-axis the output will be located;
|
||||||
|
there may be a significant width to the bend that is used to accomplish the 90 degree
|
||||||
|
turn. However, an error is raised if `distance` is too small to fit the bend.
|
||||||
|
|
||||||
|
- `pather.path('my_port', ccw=None, distance)` creates a straight wire with a length
|
||||||
|
of `distance` and `plug`s it into `'my_port'`.
|
||||||
|
|
||||||
|
- `pather.path_to('my_port', ccw=False, position)` creates a wire which starts at
|
||||||
|
`'my_port'` and has its output at the specified `position`, pointing 90 degrees
|
||||||
|
clockwise relative to the input. Again, the off-axis position or distance to the
|
||||||
|
output is not specified, so `position` takes the form of a single coordinate. To
|
||||||
|
ease debugging, position may be specified as `x=position` or `y=position` and an
|
||||||
|
error will be raised if the wrong coordinate is given.
|
||||||
|
|
||||||
|
- `pather.mpath(['A', 'B', 'C'], ..., spacing=spacing)` is a superset of `path`
|
||||||
|
and `path_to` which can act on multiple ports simultaneously. Each port's wire is
|
||||||
|
generated using its own `Tool` (or the default tool if left unspecified).
|
||||||
|
The output ports are spaced out by `spacing` along the input ports' axis, unless
|
||||||
|
`ccw=None` is specified (i.e. no bends) in which case they all end at the same
|
||||||
|
destination coordinate.
|
||||||
|
|
||||||
|
- `pather.plug(wire, {'myport': 'A'})` places port 'A' of `wire` at 'myport'
|
||||||
|
of `pather.pattern`. If `wire` has only two ports (e.g. 'A' and 'B'), no `map_out`,
|
||||||
argument is provided, and the `inherit_name` argument is not explicitly
|
argument is provided, and the `inherit_name` argument is not explicitly
|
||||||
set to `False`, the unconnected port of `wire` is automatically renamed to
|
set to `False`, the unconnected port of `wire` is automatically renamed to
|
||||||
'myport'. This allows easy extension of existing ports without changing
|
'myport'. This allows easy extension of existing ports without changing
|
||||||
their names or having to provide `map_out` each time `plug` is called.
|
their names or having to provide `map_out` each time `plug` is called.
|
||||||
|
|
||||||
- `my_device.place(pad, offset=(10, 10), rotation=pi / 2, port_map={'A': 'gnd'})`
|
- `pather.place(pad, offset=(10, 10), rotation=pi / 2, port_map={'A': 'gnd'})`
|
||||||
instantiates `pad` at the specified (x, y) offset and with the specified
|
instantiates `pad` at the specified (x, y) offset and with the specified
|
||||||
rotation, adding its ports to those of `my_device`. Port 'A' of `pad` is
|
rotation, adding its ports to those of `pather.pattern`. Port 'A' of `pad` is
|
||||||
renamed to 'gnd' so that further routing can use this signal or net name
|
renamed to 'gnd' so that further routing can use this signal or net name
|
||||||
rather than the port name on the original `pad` device.
|
rather than the port name on the original `pad` device.
|
||||||
|
|
||||||
|
- `pather.retool(tool)` or `pather.retool(tool, ['in', 'out', None])` can change
|
||||||
|
which tool is used for the given ports (or as the default tool). Useful
|
||||||
|
when placing vias or using multiple waveguide types along a route.
|
||||||
"""
|
"""
|
||||||
__slots__ = ('tools',)
|
__slots__ = ('tools',)
|
||||||
|
|
||||||
@ -85,8 +118,9 @@ class Pather(Builder):
|
|||||||
|
|
||||||
tools: dict[str | None, Tool]
|
tools: dict[str | None, Tool]
|
||||||
"""
|
"""
|
||||||
Tool objects are used to dynamically generate new single-use Devices
|
Tool objects are used to dynamically generate new single-use `Pattern`s
|
||||||
(e.g wires or waveguides) to be plugged into this device.
|
(e.g wires or waveguides) to be plugged into this device. A key of `None`
|
||||||
|
indicates the default `Tool`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -99,13 +133,19 @@ class Pather(Builder):
|
|||||||
name: str | None = None,
|
name: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
# TODO documentation for Builder() constructor
|
Args:
|
||||||
|
library: The library from which referenced patterns will be taken,
|
||||||
# TODO MOVE THE BELOW DOCS to PortList
|
and where new patterns (e.g. generated by the `tools`) will be placed.
|
||||||
# If `ports` is `None`, two default ports ('A' and 'B') are created.
|
pattern: The pattern which will be modified by subsequent operations.
|
||||||
# Both are placed at (0, 0) and have default `ptype`, but 'A' has rotation 0
|
If `None` (default), a new pattern is created.
|
||||||
# (attached devices will be placed to the left) and 'B' has rotation
|
ports: Allows specifying the initial set of ports, if `pattern` does
|
||||||
# pi (attached devices will be placed to the right).
|
not already have any ports (or is not provided). May be a string,
|
||||||
|
in which case it is interpreted as a name in `library`.
|
||||||
|
Default `None` (no ports).
|
||||||
|
tools: A mapping of {port: tool} which specifies what `Tool` should be used
|
||||||
|
to generate waveguide or wire segments when `path`/`path_to`/`mpath`
|
||||||
|
are called. Relies on `Tool.path` implementations.
|
||||||
|
name: If specified, `library[name]` is set to `self.pattern`.
|
||||||
"""
|
"""
|
||||||
self._dead = False
|
self._dead = False
|
||||||
self.library = library
|
self.library = library
|
||||||
@ -165,7 +205,36 @@ class Pather(Builder):
|
|||||||
name: str | None = None,
|
name: str | None = None,
|
||||||
) -> 'Pather':
|
) -> 'Pather':
|
||||||
"""
|
"""
|
||||||
TODO doc pather.interface
|
Wrapper for `Pattern.interface()`, which returns a Pather instead.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
source: A collection of ports (e.g. Pattern, Builder, or dict)
|
||||||
|
from which to create the interface. May be a pattern name if
|
||||||
|
`library` is provided.
|
||||||
|
library: Library from which existing patterns should be referenced,
|
||||||
|
and to which the new one should be added (if named). If not provided,
|
||||||
|
`source.library` must exist and will be used.
|
||||||
|
tools: `Tool`s which will be used by the pather for generating new wires
|
||||||
|
or waveguides (via `path`/`path_to`/`mpath`).
|
||||||
|
in_prefix: Prepended to port names for newly-created ports with
|
||||||
|
reversed directions compared to the current device.
|
||||||
|
out_prefix: Prepended to port names for ports which are directly
|
||||||
|
copied from the current device.
|
||||||
|
port_map: Specification for ports to copy into the new device:
|
||||||
|
- If `None`, all ports are copied.
|
||||||
|
- If a sequence, only the listed ports are copied
|
||||||
|
- If a mapping, the listed ports (keys) are copied and
|
||||||
|
renamed (to the values).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The new pather, with an empty pattern and 2x as many ports as
|
||||||
|
listed in port_map.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
`PortError` if `port_map` contains port names not present in the
|
||||||
|
current device.
|
||||||
|
`PortError` if applying the prefixes results in duplicate port
|
||||||
|
names.
|
||||||
"""
|
"""
|
||||||
if library is None:
|
if library is None:
|
||||||
if hasattr(source, 'library') and isinstance(source.library, ILibrary):
|
if hasattr(source, 'library') and isinstance(source.library, ILibrary):
|
||||||
@ -192,6 +261,18 @@ class Pather(Builder):
|
|||||||
tool: Tool,
|
tool: Tool,
|
||||||
keys: str | Sequence[str | None] | None = None,
|
keys: str | Sequence[str | None] | None = None,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
|
"""
|
||||||
|
Update the `Tool` which will be used when generating `Pattern`s for the ports
|
||||||
|
given by `keys`.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tool: The new `Tool` to use for the given ports.
|
||||||
|
keys: Which ports the tool should apply to. `None` indicates the default tool,
|
||||||
|
used when there is no matching entry in `self.tools` for the port in question.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
self
|
||||||
|
"""
|
||||||
if keys is None or isinstance(keys, str):
|
if keys is None or isinstance(keys, str):
|
||||||
self.tools[keys] = tool
|
self.tools[keys] = tool
|
||||||
else:
|
else:
|
||||||
@ -209,6 +290,34 @@ class Pather(Builder):
|
|||||||
base_name: str = '_path',
|
base_name: str = '_path',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
|
"""
|
||||||
|
Create a "wire"/"waveguide" and `plug` it into the port `portspec`, with the aim
|
||||||
|
of traveling exactly `length` distance.
|
||||||
|
|
||||||
|
The wire will travel `length` distance along the port's axis, an an unspecified
|
||||||
|
(tool-dependent) distance in the perpendicular direction. The output port will
|
||||||
|
be rotated (or not) based on the `ccw` parameter.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
portspec: The name of the port into which the wire will be plugged.
|
||||||
|
ccw: If `None`, the output should be along the same axis as the input.
|
||||||
|
Otherwise, cast to bool and turn counterclockwise if True
|
||||||
|
and clockwise otherwise.
|
||||||
|
length: The total distance from input to output, along the input's axis only.
|
||||||
|
(There may be a tool-dependent offset along the other axis.)
|
||||||
|
tool_port_names: The names of the ports on the generated pattern. It is unlikely
|
||||||
|
that you will need to change these. The first port is the input (to be
|
||||||
|
connected to `portspec`).
|
||||||
|
base_name: Name to use for the generated `Pattern`. This will be passed through
|
||||||
|
`self.library.get_name()` to get a unique name for each new `Pattern`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
self
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BuildError if `distance` is too small to fit the bend (if a bend is present).
|
||||||
|
LibraryError if no valid name could be picked for the pattern.
|
||||||
|
"""
|
||||||
if self._dead:
|
if self._dead:
|
||||||
logger.error('Skipping path() since device is dead')
|
logger.error('Skipping path() since device is dead')
|
||||||
return self
|
return self
|
||||||
@ -232,6 +341,44 @@ class Pather(Builder):
|
|||||||
base_name: str = '_pathto',
|
base_name: str = '_pathto',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
|
"""
|
||||||
|
Create a "wire"/"waveguide" and `plug` it into the port `portspec`, with the aim
|
||||||
|
of ending exactly at a target position.
|
||||||
|
|
||||||
|
The wire will travel so that the output port will be placed at exactly the target
|
||||||
|
position along the input port's axis. There can be an unspecified (tool-dependent)
|
||||||
|
offset in the perpendicular direction. The output port will be rotated (or not)
|
||||||
|
based on the `ccw` parameter.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
portspec: The name of the port into which the wire will be plugged.
|
||||||
|
ccw: If `None`, the output should be along the same axis as the input.
|
||||||
|
Otherwise, cast to bool and turn counterclockwise if True
|
||||||
|
and clockwise otherwise.
|
||||||
|
position: The final port position, along the input's axis only.
|
||||||
|
(There may be a tool-dependent offset along the other axis.)
|
||||||
|
Only one of `position`, `x`, and `y` may be specified.
|
||||||
|
x: The final port position along the x axis.
|
||||||
|
`portspec` must refer to a horizontal port if `x` is passed, otherwise a
|
||||||
|
BuildError will be raised.
|
||||||
|
y: The final port position along the y axis.
|
||||||
|
`portspec` must refer to a vertical port if `y` is passed, otherwise a
|
||||||
|
BuildError will be raised.
|
||||||
|
tool_port_names: The names of the ports on the generated pattern. It is unlikely
|
||||||
|
that you will need to change these. The first port is the input (to be
|
||||||
|
connected to `portspec`).
|
||||||
|
base_name: Name to use for the generated `Pattern`. This will be passed through
|
||||||
|
`self.library.get_name()` to get a unique name for each new `Pattern`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
self
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BuildError if `position`, `x`, or `y` is too close to fit the bend (if a bend
|
||||||
|
is present).
|
||||||
|
BuildError if `x` or `y` is specified but does not match the axis of `portspec`.
|
||||||
|
BuildError if more than one of `x`, `y`, and `position` is specified.
|
||||||
|
"""
|
||||||
if self._dead:
|
if self._dead:
|
||||||
logger.error('Skipping path_to() since device is dead')
|
logger.error('Skipping path_to() since device is dead')
|
||||||
return self
|
return self
|
||||||
@ -285,6 +432,81 @@ class Pather(Builder):
|
|||||||
base_name: str = '_mpath',
|
base_name: str = '_mpath',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
|
"""
|
||||||
|
`mpath` is a superset of `path` and `path_to` which can act on bundles or buses
|
||||||
|
of "wires or "waveguides".
|
||||||
|
|
||||||
|
The wires will travel so that the output ports will be placed at well-defined
|
||||||
|
locations along the axis of their input ports, but may have arbitrary (tool-
|
||||||
|
dependent) offsets in the perpendicular direction.
|
||||||
|
|
||||||
|
If `ccw` is not `None`, the wire bundle will turn 90 degres in either the
|
||||||
|
clockwise (`ccw=False`) or counter-clockwise (`ccw=True`) direction. Within the
|
||||||
|
bundle, the center-to-center wire spacings after the turn are set by `spacing`,
|
||||||
|
which is required when `ccw` is not `None`. The final position of bundle as a
|
||||||
|
whole can be set in a number of ways:
|
||||||
|
|
||||||
|
=A>---------------------------V turn direction: `ccw=False`
|
||||||
|
=B>-------------V |
|
||||||
|
=C>-----------------------V |
|
||||||
|
=D=>----------------V |
|
||||||
|
|
|
||||||
|
|
||||||
|
x---x---x---x `spacing` (can be scalar or array)
|
||||||
|
|
||||||
|
<--------------> `emin=`
|
||||||
|
<------> `bound_type='min_past_furthest', bound=`
|
||||||
|
<--------------------------------> `emax=`
|
||||||
|
x `pmin=`
|
||||||
|
x `pmax=`
|
||||||
|
|
||||||
|
- `emin=`, equivalent to `bound_type='min_extension', bound=`
|
||||||
|
The total extension value for the furthest-out port (B in the diagram).
|
||||||
|
- `emax=`, equivalent to `bound_type='max_extension', bound=`:
|
||||||
|
The total extension value for the closest-in port (C in the diagram).
|
||||||
|
- `pmin=`, equivalent to `xmin=`, `ymin=`, or `bound_type='min_position', bound=`:
|
||||||
|
The coordinate of the innermost bend (D's bend).
|
||||||
|
The x/y versions throw an error if they do not match the port axis (for debug)
|
||||||
|
- `pmax=`, `xmax=`, `ymax=`, or `bound_type='max_position', bound=`:
|
||||||
|
The coordinate of the outermost bend (A's bend).
|
||||||
|
The x/y versions throw an error if they do not match the port axis (for debug)
|
||||||
|
- `bound_type='min_past_furthest', bound=`:
|
||||||
|
The distance between furthest out-port (B) and the innermost bend (D's bend).
|
||||||
|
|
||||||
|
If `ccw=None`, final output positions (along the input axis) of all wires will be
|
||||||
|
identical (i.e. wires will all be cut off evenly). In this case, `spacing=None` is
|
||||||
|
required. In this case, `emin=` and `emax=` are equivalent to each other, and
|
||||||
|
`pmin=`, `pmax=`, `xmin=`, etc. are also equivalent to each other.
|
||||||
|
|
||||||
|
|
||||||
|
Args:
|
||||||
|
portspec: The names of the ports which are to be routed.
|
||||||
|
ccw: If `None`, the outputs should be along the same axis as the inputs.
|
||||||
|
Otherwise, cast to bool and turn 90 degrees counterclockwise if `True`
|
||||||
|
and clockwise otherwise.
|
||||||
|
spacing: Center-to-center distance between output ports along the input port's axis.
|
||||||
|
Must be provided if (and only if) `ccw` is not `None`.
|
||||||
|
set_rotation: If the provided ports have `rotation=None`, this can be used
|
||||||
|
to set a rotation for them.
|
||||||
|
tool_port_names: The names of the ports on the generated pattern. It is unlikely
|
||||||
|
that you will need to change these. The first port is the input (to be
|
||||||
|
connected to `portspec`).
|
||||||
|
force_container: If `False` (default), and only a single port is provided, the
|
||||||
|
generated wire for that port will be referenced directly, rather than being
|
||||||
|
wrapped in an additonal `Pattern`.
|
||||||
|
base_name: Name to use for the generated `Pattern`. This will be passed through
|
||||||
|
`self.library.get_name()` to get a unique name for each new `Pattern`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
self
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BuildError if the implied length for any wire is too close to fit the bend
|
||||||
|
(if a bend is requested).
|
||||||
|
BuildError if `xmin`/`xmax` or `ymin`/`ymax` is specified but does not
|
||||||
|
match the axis of `portspec`.
|
||||||
|
BuildError if an incorrect bound type or spacing is specified.
|
||||||
|
"""
|
||||||
if self._dead:
|
if self._dead:
|
||||||
logger.error('Skipping mpath() since device is dead')
|
logger.error('Skipping mpath() since device is dead')
|
||||||
return self
|
return self
|
||||||
|
@ -24,6 +24,24 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class RenderPather(PortList):
|
class RenderPather(PortList):
|
||||||
|
"""
|
||||||
|
`RenderPather` is an alternative to `Pather` which uses the `path`/`path_to`/`mpath`
|
||||||
|
functions to plan out wire paths without incrementally generating the layout. Instead,
|
||||||
|
it waits until `render` is called, at which point it draws all the planned segments
|
||||||
|
simultaneously. This allows it to e.g. draw each wire using a single `Path` or
|
||||||
|
`Polygon` shape instead of multiple rectangles.
|
||||||
|
|
||||||
|
`RenderPather` calls out to `Tool.planL` and `Tool.render` to provide tool-specific
|
||||||
|
dimensions and build the final geometry for each wire. `Tool.planL` provides the
|
||||||
|
output port data (relative to the input) for each segment. The tool, input and output
|
||||||
|
ports are placed into a `RenderStep`, and a sequence of `RenderStep`s is stored for
|
||||||
|
each port. When `render` is called, it bundles `RenderStep`s into batches which use
|
||||||
|
the same `Tool`, and passes each batch to the relevant tool's `Tool.render` to build
|
||||||
|
the geometry.
|
||||||
|
|
||||||
|
See `Pather` for routing examples. After routing is complete, `render` must be called
|
||||||
|
to generate the final geometry.
|
||||||
|
"""
|
||||||
__slots__ = ('pattern', 'library', 'paths', 'tools', '_dead', )
|
__slots__ = ('pattern', 'library', 'paths', 'tools', '_dead', )
|
||||||
|
|
||||||
pattern: Pattern
|
pattern: Pattern
|
||||||
@ -36,6 +54,7 @@ class RenderPather(PortList):
|
|||||||
""" If True, plug()/place() are skipped (for debugging) """
|
""" If True, plug()/place() are skipped (for debugging) """
|
||||||
|
|
||||||
paths: defaultdict[str, list[RenderStep]]
|
paths: defaultdict[str, list[RenderStep]]
|
||||||
|
""" Per-port list of operations, to be used by `render` """
|
||||||
|
|
||||||
tools: dict[str | None, Tool]
|
tools: dict[str | None, Tool]
|
||||||
"""
|
"""
|
||||||
@ -61,8 +80,19 @@ class RenderPather(PortList):
|
|||||||
name: str | None = None,
|
name: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
# TODO documentation for Builder() constructor
|
Args:
|
||||||
|
library: The library from which referenced patterns will be taken,
|
||||||
|
and where new patterns (e.g. generated by the `tools`) will be placed.
|
||||||
|
pattern: The pattern which will be modified by subsequent operations.
|
||||||
|
If `None` (default), a new pattern is created.
|
||||||
|
ports: Allows specifying the initial set of ports, if `pattern` does
|
||||||
|
not already have any ports (or is not provided). May be a string,
|
||||||
|
in which case it is interpreted as a name in `library`.
|
||||||
|
Default `None` (no ports).
|
||||||
|
tools: A mapping of {port: tool} which specifies what `Tool` should be used
|
||||||
|
to generate waveguide or wire segments when `path`/`path_to`/`mpath`
|
||||||
|
are called. Relies on `Tool.planL` and `Tool.render` implementations.
|
||||||
|
name: If specified, `library[name]` is set to `self.pattern`.
|
||||||
"""
|
"""
|
||||||
self._dead = False
|
self._dead = False
|
||||||
self.paths = defaultdict(list)
|
self.paths = defaultdict(list)
|
||||||
@ -107,32 +137,17 @@ class RenderPather(PortList):
|
|||||||
name: str | None = None,
|
name: str | None = None,
|
||||||
) -> 'RenderPather':
|
) -> 'RenderPather':
|
||||||
"""
|
"""
|
||||||
Begin building a new device based on all or some of the ports in the
|
Wrapper for `Pattern.interface()`, which returns a RenderPather instead.
|
||||||
source device. Do not include the source device; instead use it
|
|
||||||
to define ports (the "interface") for the new device.
|
|
||||||
|
|
||||||
The ports specified by `port_map` (default: all ports) are copied to
|
|
||||||
new device, and additional (input) ports are created facing in the
|
|
||||||
opposite directions. The specified `in_prefix` and `out_prefix` are
|
|
||||||
prepended to the port names to differentiate them.
|
|
||||||
|
|
||||||
By default, the flipped ports are given an 'in_' prefix and unflipped
|
|
||||||
ports keep their original names, enabling intuitive construction of
|
|
||||||
a device that will "plug into" the current device; the 'in_*' ports
|
|
||||||
are used for plugging the devices together while the original port
|
|
||||||
names are used for building the new device.
|
|
||||||
|
|
||||||
Another use-case could be to build the new device using the 'in_'
|
|
||||||
ports, creating a new device which could be used in place of the
|
|
||||||
current device.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
source: A collection of ports (e.g. Pattern, Builder, or dict)
|
source: A collection of ports (e.g. Pattern, Builder, or dict)
|
||||||
from which to create the interface.
|
from which to create the interface. May be a pattern name if
|
||||||
library: Used for buildin functions; if not passed and the source
|
`library` is provided.
|
||||||
library: Library from which existing patterns should be referenced,
|
library: Library from which existing patterns should be referenced,
|
||||||
and to which new ones should be added. If not provided,
|
and to which the new one should be added (if named). If not provided,
|
||||||
the source's library will be used (if available).
|
`source.library` must exist and will be used.
|
||||||
|
tools: `Tool`s which will be used by the pather for generating new wires
|
||||||
|
or waveguides (via `path`/`path_to`/`mpath`).
|
||||||
in_prefix: Prepended to port names for newly-created ports with
|
in_prefix: Prepended to port names for newly-created ports with
|
||||||
reversed directions compared to the current device.
|
reversed directions compared to the current device.
|
||||||
out_prefix: Prepended to port names for ports which are directly
|
out_prefix: Prepended to port names for ports which are directly
|
||||||
@ -144,7 +159,7 @@ class RenderPather(PortList):
|
|||||||
renamed (to the values).
|
renamed (to the values).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The new builder, with an empty pattern and 2x as many ports as
|
The new `RenderPather`, with an empty pattern and 2x as many ports as
|
||||||
listed in port_map.
|
listed in port_map.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@ -180,6 +195,46 @@ class RenderPather(PortList):
|
|||||||
set_rotation: bool | None = None,
|
set_rotation: bool | None = None,
|
||||||
append: bool = False,
|
append: bool = False,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
|
"""
|
||||||
|
Wrapper for `Pattern.plug` which adds a `RenderStep` with opcode 'P'
|
||||||
|
for any affected ports. This separates any future `RenderStep`s on the
|
||||||
|
same port into a new batch, since the plugged device interferes with drawing.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
other: An `Abstract`, string, or `Pattern` describing the device to be instatiated.
|
||||||
|
map_in: dict of `{'self_port': 'other_port'}` mappings, specifying
|
||||||
|
port connections between the two devices.
|
||||||
|
map_out: dict of `{'old_name': 'new_name'}` mappings, specifying
|
||||||
|
new names for ports in `other`.
|
||||||
|
mirrored: Enables mirroring `other` across the x axis prior to
|
||||||
|
connecting any ports.
|
||||||
|
inherit_name: If `True`, and `map_in` specifies only a single port,
|
||||||
|
and `map_out` is `None`, and `other` has only two ports total,
|
||||||
|
then automatically renames the output port of `other` to the
|
||||||
|
name of the port from `self` that appears in `map_in`. This
|
||||||
|
makes it easy to extend a device with simple 2-port devices
|
||||||
|
(e.g. wires) without providing `map_out` each time `plug` is
|
||||||
|
called. See "Examples" above for more info. Default `True`.
|
||||||
|
set_rotation: If the necessary rotation cannot be determined from
|
||||||
|
the ports being connected (i.e. all pairs have at least one
|
||||||
|
port with `rotation=None`), `set_rotation` must be provided
|
||||||
|
to indicate how much `other` should be rotated. Otherwise,
|
||||||
|
`set_rotation` must remain `None`.
|
||||||
|
append: If `True`, `other` is appended instead of being referenced.
|
||||||
|
Note that this does not flatten `other`, so its refs will still
|
||||||
|
be refs (now inside `self`).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
self
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
`PortError` if any ports specified in `map_in` or `map_out` do not
|
||||||
|
exist in `self.ports` or `other_names`.
|
||||||
|
`PortError` if there are any duplicate names after `map_in` and `map_out`
|
||||||
|
are applied.
|
||||||
|
`PortError` if the specified port mapping is not achieveable (the ports
|
||||||
|
do not line up)
|
||||||
|
"""
|
||||||
if self._dead:
|
if self._dead:
|
||||||
logger.error('Skipping plug() since device is dead')
|
logger.error('Skipping plug() since device is dead')
|
||||||
return self
|
return self
|
||||||
@ -227,6 +282,39 @@ class RenderPather(PortList):
|
|||||||
skip_port_check: bool = False,
|
skip_port_check: bool = False,
|
||||||
append: bool = False,
|
append: bool = False,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
|
"""
|
||||||
|
Wrapper for `Pattern.place` which adds a `RenderStep` with opcode 'P'
|
||||||
|
for any affected ports. This separates any future `RenderStep`s on the
|
||||||
|
same port into a new batch, since the placed device interferes with drawing.
|
||||||
|
|
||||||
|
Note that mirroring is applied before rotation; translation (`offset`) is applied last.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
other: An `Abstract` or `Pattern` describing the device to be instatiated.
|
||||||
|
offset: Offset at which to place the instance. Default (0, 0).
|
||||||
|
rotation: Rotation applied to the instance before placement. Default 0.
|
||||||
|
pivot: Rotation is applied around this pivot point (default (0, 0)).
|
||||||
|
Rotation is applied prior to translation (`offset`).
|
||||||
|
mirrored: Whether theinstance should be mirrored across the x axis.
|
||||||
|
Mirroring is applied before translation and rotation.
|
||||||
|
port_map: dict of `{'old_name': 'new_name'}` mappings, specifying
|
||||||
|
new names for ports in the instantiated pattern. New names can be
|
||||||
|
`None`, which will delete those ports.
|
||||||
|
skip_port_check: Can be used to skip the internal call to `check_ports`,
|
||||||
|
in case it has already been performed elsewhere.
|
||||||
|
append: If `True`, `other` is appended instead of being referenced.
|
||||||
|
Note that this does not flatten `other`, so its refs will still
|
||||||
|
be refs (now inside `self`).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
self
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
`PortError` if any ports specified in `map_in` or `map_out` do not
|
||||||
|
exist in `self.ports` or `other.ports`.
|
||||||
|
`PortError` if there are any duplicate names after `map_in` and `map_out`
|
||||||
|
are applied.
|
||||||
|
"""
|
||||||
if self._dead:
|
if self._dead:
|
||||||
logger.error('Skipping place() since device is dead')
|
logger.error('Skipping place() since device is dead')
|
||||||
return self
|
return self
|
||||||
@ -260,6 +348,18 @@ class RenderPather(PortList):
|
|||||||
tool: Tool,
|
tool: Tool,
|
||||||
keys: str | Sequence[str | None] | None = None,
|
keys: str | Sequence[str | None] | None = None,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
|
"""
|
||||||
|
Update the `Tool` which will be used when generating `Pattern`s for the ports
|
||||||
|
given by `keys`.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tool: The new `Tool` to use for the given ports.
|
||||||
|
keys: Which ports the tool should apply to. `None` indicates the default tool,
|
||||||
|
used when there is no matching entry in `self.tools` for the port in question.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
self
|
||||||
|
"""
|
||||||
if keys is None or isinstance(keys, str):
|
if keys is None or isinstance(keys, str):
|
||||||
self.tools[keys] = tool
|
self.tools[keys] = tool
|
||||||
else:
|
else:
|
||||||
@ -274,6 +374,31 @@ class RenderPather(PortList):
|
|||||||
length: float,
|
length: float,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
|
"""
|
||||||
|
Plan a "wire"/"waveguide" extending from the port `portspec`, with the aim
|
||||||
|
of traveling exactly `length` distance.
|
||||||
|
|
||||||
|
The wire will travel `length` distance along the port's axis, an an unspecified
|
||||||
|
(tool-dependent) distance in the perpendicular direction. The output port will
|
||||||
|
be rotated (or not) based on the `ccw` parameter.
|
||||||
|
|
||||||
|
`RenderPather.render` must be called after all paths have been fully planned.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
portspec: The name of the port into which the wire will be plugged.
|
||||||
|
ccw: If `None`, the output should be along the same axis as the input.
|
||||||
|
Otherwise, cast to bool and turn counterclockwise if True
|
||||||
|
and clockwise otherwise.
|
||||||
|
length: The total distance from input to output, along the input's axis only.
|
||||||
|
(There may be a tool-dependent offset along the other axis.)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
self
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BuildError if `distance` is too small to fit the bend (if a bend is present).
|
||||||
|
LibraryError if no valid name could be picked for the pattern.
|
||||||
|
"""
|
||||||
if self._dead:
|
if self._dead:
|
||||||
logger.error('Skipping path() since device is dead')
|
logger.error('Skipping path() since device is dead')
|
||||||
return self
|
return self
|
||||||
@ -281,7 +406,7 @@ class RenderPather(PortList):
|
|||||||
port = self.pattern[portspec]
|
port = self.pattern[portspec]
|
||||||
in_ptype = port.ptype
|
in_ptype = port.ptype
|
||||||
port_rot = port.rotation
|
port_rot = port.rotation
|
||||||
assert port_rot is not None # TODO allow manually setting rotation?
|
assert port_rot is not None # TODO allow manually setting rotation for RenderPather.path()?
|
||||||
|
|
||||||
tool = self.tools.get(portspec, self.tools[None])
|
tool = self.tools.get(portspec, self.tools[None])
|
||||||
# ask the tool for bend size (fill missing dx or dy), check feasibility, and get out_ptype
|
# ask the tool for bend size (fill missing dx or dy), check feasibility, and get out_ptype
|
||||||
@ -308,6 +433,41 @@ class RenderPather(PortList):
|
|||||||
y: float | None = None,
|
y: float | None = None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
|
"""
|
||||||
|
Plan a "wire"/"waveguide" extending from the port `portspec`, with the aim
|
||||||
|
of ending exactly at a target position.
|
||||||
|
|
||||||
|
The wire will travel so that the output port will be placed at exactly the target
|
||||||
|
position along the input port's axis. There can be an unspecified (tool-dependent)
|
||||||
|
offset in the perpendicular direction. The output port will be rotated (or not)
|
||||||
|
based on the `ccw` parameter.
|
||||||
|
|
||||||
|
`RenderPather.render` must be called after all paths have been fully planned.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
portspec: The name of the port into which the wire will be plugged.
|
||||||
|
ccw: If `None`, the output should be along the same axis as the input.
|
||||||
|
Otherwise, cast to bool and turn counterclockwise if True
|
||||||
|
and clockwise otherwise.
|
||||||
|
position: The final port position, along the input's axis only.
|
||||||
|
(There may be a tool-dependent offset along the other axis.)
|
||||||
|
Only one of `position`, `x`, and `y` may be specified.
|
||||||
|
x: The final port position along the x axis.
|
||||||
|
`portspec` must refer to a horizontal port if `x` is passed, otherwise a
|
||||||
|
BuildError will be raised.
|
||||||
|
y: The final port position along the y axis.
|
||||||
|
`portspec` must refer to a vertical port if `y` is passed, otherwise a
|
||||||
|
BuildError will be raised.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
self
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BuildError if `position`, `x`, or `y` is too close to fit the bend (if a bend
|
||||||
|
is present).
|
||||||
|
BuildError if `x` or `y` is specified but does not match the axis of `portspec`.
|
||||||
|
BuildError if more than one of `x`, `y`, and `position` is specified.
|
||||||
|
"""
|
||||||
if self._dead:
|
if self._dead:
|
||||||
logger.error('Skipping path_to() since device is dead')
|
logger.error('Skipping path_to() since device is dead')
|
||||||
return self
|
return self
|
||||||
@ -358,6 +518,32 @@ class RenderPather(PortList):
|
|||||||
set_rotation: float | None = None,
|
set_rotation: float | None = None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
|
"""
|
||||||
|
`mpath` is a superset of `path` and `path_to` which can act on bundles or buses
|
||||||
|
of "wires or "waveguides".
|
||||||
|
|
||||||
|
See `Pather.mpath` for details.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
portspec: The names of the ports which are to be routed.
|
||||||
|
ccw: If `None`, the outputs should be along the same axis as the inputs.
|
||||||
|
Otherwise, cast to bool and turn 90 degrees counterclockwise if `True`
|
||||||
|
and clockwise otherwise.
|
||||||
|
spacing: Center-to-center distance between output ports along the input port's axis.
|
||||||
|
Must be provided if (and only if) `ccw` is not `None`.
|
||||||
|
set_rotation: If the provided ports have `rotation=None`, this can be used
|
||||||
|
to set a rotation for them.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
self
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
BuildError if the implied length for any wire is too close to fit the bend
|
||||||
|
(if a bend is requested).
|
||||||
|
BuildError if `xmin`/`xmax` or `ymin`/`ymax` is specified but does not
|
||||||
|
match the axis of `portspec`.
|
||||||
|
BuildError if an incorrect bound type or spacing is specified.
|
||||||
|
"""
|
||||||
if self._dead:
|
if self._dead:
|
||||||
logger.error('Skipping mpath() since device is dead')
|
logger.error('Skipping mpath() since device is dead')
|
||||||
return self
|
return self
|
||||||
|
@ -28,8 +28,61 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
||||||
"""
|
"""
|
||||||
2D layout consisting of some set of shapes, labels, and references to other Pattern objects
|
2D layout consisting of some set of shapes, labels, and references to other
|
||||||
(via Ref). Shapes are assumed to inherit from `masque.shapes.Shape` or provide equivalent functions.
|
Pattern objects (via Ref). Shapes are assumed to inherit from `masque.shapes.Shape`
|
||||||
|
or provide equivalent functions.
|
||||||
|
|
||||||
|
`Pattern` also stores a dict of `Port`s, which can be used to "snap" together points.
|
||||||
|
See `Pattern.plug()` and `Pattern.place()`, as well as the helper classes
|
||||||
|
`builder.Builder`, `builder.Pather`, `builder.RenderPather`, and `ports.PortsList`.
|
||||||
|
|
||||||
|
For convenience, ports can be read out using square brackets:
|
||||||
|
- `pattern['A'] == Port((0, 0), 0)`
|
||||||
|
- `pattern[['A', 'B']] == {'A': Port((0, 0), 0), 'B': Port((0, 0), pi)}`
|
||||||
|
|
||||||
|
|
||||||
|
Examples: Making a Pattern
|
||||||
|
==========================
|
||||||
|
- `pat = Pattern()` just creates an empty pattern, with no geometry or ports
|
||||||
|
|
||||||
|
- To immediately set some of the pattern's contents,
|
||||||
|
```
|
||||||
|
pat = Pattern(
|
||||||
|
shapes={'layer1': [shape0, ...], 'layer2': [shape,...], ...},
|
||||||
|
labels={'layer1': [...], ...},
|
||||||
|
refs={'name1': [ref0, ...], 'name2': [ref, ...], ...},
|
||||||
|
ports={'in': Port(...), 'out': Port(...)},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `Pattern.interface(other_pat, port_map=['A', 'B'])` makes a new
|
||||||
|
(empty) pattern, copies over ports 'A' and 'B' from `other_pat`, and
|
||||||
|
creates additional ports 'in_A' and 'in_B' facing in the opposite
|
||||||
|
directions. This can be used to build a device which can plug into
|
||||||
|
`other_pat` (using the 'in_*' ports) but which does not itself include
|
||||||
|
`other_pat` as a subcomponent.
|
||||||
|
|
||||||
|
|
||||||
|
Examples: Adding to a pattern
|
||||||
|
=============================
|
||||||
|
- `pat.plug(subdevice, {'A': 'C', 'B': 'B'}, map_out={'D': 'myport'})`
|
||||||
|
instantiates `subdevice` into `pat`, plugging ports 'A' and 'B'
|
||||||
|
of `pat` into ports 'C' and 'B' of `subdevice`. The connected ports
|
||||||
|
are removed and any unconnected ports from `subdevice` are added to
|
||||||
|
`pat`. Port 'D' of `subdevice` (unconnected) is renamed to 'myport'.
|
||||||
|
|
||||||
|
- `pat.plug(wire, {'myport': 'A'})` places port 'A' of `wire` at 'myport'
|
||||||
|
of `pat`. If `wire` has only two ports (e.g. 'A' and 'B'), since no `map_out`
|
||||||
|
argument is provided and the `inherit_name` argument is not explicitly
|
||||||
|
set to `False`, the unconnected port of `wire` is automatically renamed to
|
||||||
|
'myport'. This allows easy extension of existing ports without changing
|
||||||
|
their names or having to provide `map_out` each time `plug` is called.
|
||||||
|
|
||||||
|
- `pat.place(pad, offset=(10, 10), rotation=pi / 2, port_map={'A': 'gnd'})`
|
||||||
|
instantiates `pad` at the specified (x, y) offset and with the specified
|
||||||
|
rotation, adding its ports to those of `pat`. Port 'A' of `pad` is
|
||||||
|
renamed to 'gnd' so that further routing can use this signal or net name
|
||||||
|
rather than the port name on the original `pad` device.
|
||||||
"""
|
"""
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'shapes', 'labels', 'refs', '_ports',
|
'shapes', 'labels', 'refs', '_ports',
|
||||||
|
Loading…
Reference in New Issue
Block a user