Turn Builder into a subset of Pather
This commit is contained in:
parent
83c710a85f
commit
4a2c4c5220
@ -42,7 +42,7 @@ from .library import (
|
|||||||
)
|
)
|
||||||
from .ports import Port, PortList
|
from .ports import Port, PortList
|
||||||
from .abstract import Abstract
|
from .abstract import Abstract
|
||||||
from .builder import Builder, Tool, FlatBuilder
|
from .builder import Builder, Tool, FlatBuilder, Pather
|
||||||
from .utils import ports2data
|
from .utils import ports2data
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from .builder import Builder
|
from .builder import Builder, Pather
|
||||||
from .flatbuilder import FlatBuilder
|
from .flatbuilder import FlatBuilder
|
||||||
from .utils import ell
|
from .utils import ell
|
||||||
from .tools import Tool
|
from .tools import Tool
|
||||||
|
@ -22,6 +22,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
BB = TypeVar('BB', bound='Builder')
|
BB = TypeVar('BB', bound='Builder')
|
||||||
|
PP = TypeVar('PP', bound='Pather')
|
||||||
|
|
||||||
|
|
||||||
class Builder(PortList):
|
class Builder(PortList):
|
||||||
@ -78,23 +79,17 @@ class Builder(PortList):
|
|||||||
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.
|
||||||
"""
|
"""
|
||||||
__slots__ = ('pattern', 'library', 'tools', '_dead')
|
__slots__ = ('pattern', 'library', '_dead')
|
||||||
|
|
||||||
pattern: Pattern
|
pattern: Pattern
|
||||||
""" Layout of this device """
|
""" Layout of this device """
|
||||||
|
|
||||||
library: MutableLibrary
|
library: Optional[MutableLibrary]
|
||||||
"""
|
"""
|
||||||
Library from which existing patterns should be referenced, and to which
|
Library from which existing patterns should be referenced, and to which
|
||||||
new ones should be added
|
new ones should be added
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tools: Dict[Optional[str], Tool]
|
|
||||||
"""
|
|
||||||
Tool objects are used to dynamically generate new single-use Devices
|
|
||||||
(e.g wires or waveguides) to be plugged into this device.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_dead: bool
|
_dead: bool
|
||||||
""" If True, plug()/place() are skipped (for debugging)"""
|
""" If True, plug()/place() are skipped (for debugging)"""
|
||||||
|
|
||||||
@ -108,11 +103,10 @@ class Builder(PortList):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
library: MutableLibrary,
|
library: Optional[MutableLibrary],
|
||||||
*,
|
*,
|
||||||
pattern: Optional[Pattern] = None,
|
pattern: Optional[Pattern] = None,
|
||||||
ports: Union[None, str, Mapping[str, Port]] = None,
|
ports: Union[None, str, Mapping[str, Port]] = None,
|
||||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
# TODO documentation for Builder() constructor
|
# TODO documentation for Builder() constructor
|
||||||
@ -133,17 +127,12 @@ class Builder(PortList):
|
|||||||
if self.pattern.ports:
|
if self.pattern.ports:
|
||||||
raise BuildError('Ports supplied for pattern with pre-existing ports!')
|
raise BuildError('Ports supplied for pattern with pre-existing ports!')
|
||||||
if isinstance(ports, str):
|
if isinstance(ports, str):
|
||||||
|
if library is None:
|
||||||
|
raise BuildError('Ports given as a string, but `library` was `None`!')
|
||||||
ports = library.abstract(ports).ports
|
ports = library.abstract(ports).ports
|
||||||
|
|
||||||
self.pattern.ports.update(copy.deepcopy(dict(ports)))
|
self.pattern.ports.update(copy.deepcopy(dict(ports)))
|
||||||
|
|
||||||
if tools is None:
|
|
||||||
self.tools = {}
|
|
||||||
elif isinstance(tools, Tool):
|
|
||||||
self.tools = {None: tools}
|
|
||||||
else:
|
|
||||||
self.tools = dict(tools)
|
|
||||||
|
|
||||||
self._dead = False
|
self._dead = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -152,7 +141,6 @@ class Builder(PortList):
|
|||||||
source: Union[PortList, Mapping[str, Port], str],
|
source: Union[PortList, Mapping[str, Port], str],
|
||||||
*,
|
*,
|
||||||
library: Optional[MutableLibrary] = None,
|
library: Optional[MutableLibrary] = None,
|
||||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
|
||||||
in_prefix: str = 'in_',
|
in_prefix: str = 'in_',
|
||||||
out_prefix: str = '',
|
out_prefix: str = '',
|
||||||
port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None,
|
port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None,
|
||||||
@ -184,9 +172,6 @@ class Builder(PortList):
|
|||||||
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 new ones should be added. If not provided,
|
||||||
the source's library will be used (if available).
|
the source's library will be used (if available).
|
||||||
tools: Tool objects are used to dynamically generate new single-use
|
|
||||||
patterns (e.g wires or waveguides) while building. If not provided,
|
|
||||||
the source's tools will be reused (if available).
|
|
||||||
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
|
||||||
@ -210,13 +195,10 @@ class Builder(PortList):
|
|||||||
if library is None:
|
if library is None:
|
||||||
if hasattr(source, 'library') and isinstance(source.library, MutableLibrary):
|
if hasattr(source, 'library') and isinstance(source.library, MutableLibrary):
|
||||||
library = source.library
|
library = source.library
|
||||||
else:
|
|
||||||
raise BuildError('No library provided (and not present in `source.library`')
|
|
||||||
|
|
||||||
if tools is None and hasattr(source, 'tools') and isinstance(source.tools, dict):
|
|
||||||
tools = source.tools
|
|
||||||
|
|
||||||
if isinstance(source, str):
|
if isinstance(source, str):
|
||||||
|
if library is None:
|
||||||
|
raise BuildError('Source given as a string, but `library` was `None`!')
|
||||||
orig_ports = library.abstract(source).ports
|
orig_ports = library.abstract(source).ports
|
||||||
elif isinstance(source, PortList):
|
elif isinstance(source, PortList):
|
||||||
orig_ports = source.ports
|
orig_ports = source.ports
|
||||||
@ -248,7 +230,7 @@ class Builder(PortList):
|
|||||||
if duplicates:
|
if duplicates:
|
||||||
raise PortError(f'Duplicate keys after prefixing, try a different prefix: {duplicates}')
|
raise PortError(f'Duplicate keys after prefixing, try a different prefix: {duplicates}')
|
||||||
|
|
||||||
new = Builder(library=library, ports={**ports_in, **ports_out}, tools=tools)
|
new = Builder(library=library, ports={**ports_in, **ports_out})
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def plug(
|
def plug(
|
||||||
@ -319,6 +301,8 @@ class Builder(PortList):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
if isinstance(other, str):
|
if isinstance(other, str):
|
||||||
|
if self.library is None:
|
||||||
|
raise BuildError('No library available, but `other` was a string!')
|
||||||
other = self.library.abstract(other)
|
other = self.library.abstract(other)
|
||||||
|
|
||||||
# If asked to inherit a name, check that all conditions are met
|
# If asked to inherit a name, check that all conditions are met
|
||||||
@ -403,6 +387,8 @@ class Builder(PortList):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
if isinstance(other, str):
|
if isinstance(other, str):
|
||||||
|
if self.library is None:
|
||||||
|
raise BuildError('No library available, but `other` was a string!')
|
||||||
other = self.library.abstract(other)
|
other = self.library.abstract(other)
|
||||||
|
|
||||||
if port_map is None:
|
if port_map is None:
|
||||||
@ -499,11 +485,172 @@ class Builder(PortList):
|
|||||||
s = f'<Builder {self.pattern} >' # TODO maybe show lib and tools? in builder repr?
|
s = f'<Builder {self.pattern} >' # TODO maybe show lib and tools? in builder repr?
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Pather(Builder):
|
||||||
|
"""
|
||||||
|
TODO DOCUMENT Builder
|
||||||
|
A `Device` is a combination of a `Pattern` with a set of named `Port`s
|
||||||
|
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
|
||||||
|
or wire), but can also be used to build and represent a large routed
|
||||||
|
layout (e.g. a logical block with multiple I/O connections or even a
|
||||||
|
full chip).
|
||||||
|
|
||||||
|
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
|
||||||
|
===========================
|
||||||
|
- `Device(pattern, ports={'A': port_a, 'C': port_c})` uses an existing
|
||||||
|
pattern and defines some ports.
|
||||||
|
|
||||||
|
- `Device(ports=None)` makes a new empty pattern with
|
||||||
|
default ports ('A' and 'B', in opposite directions, at (0, 0)).
|
||||||
|
|
||||||
|
- `my_device.build('my_layout')` makes a new pattern and instantiates
|
||||||
|
`my_device` in it with offset (0, 0) as a base for further building.
|
||||||
|
|
||||||
|
- `my_device.as_interface('my_component', port_map=['A', 'B'])` makes a new
|
||||||
|
(empty) pattern, copies over ports 'A' and 'B' from `my_device`, 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
|
||||||
|
`my_device` (using the 'in_*' ports) but which does not itself include
|
||||||
|
`my_device` as a subcomponent.
|
||||||
|
|
||||||
|
Examples: Adding to a Device
|
||||||
|
============================
|
||||||
|
- `my_device.plug(subdevice, {'A': 'C', 'B': 'B'}, map_out={'D': 'myport'})`
|
||||||
|
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`,
|
||||||
|
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.
|
||||||
|
|
||||||
|
- `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.
|
||||||
|
"""
|
||||||
|
__slots__ = ('tools',)
|
||||||
|
|
||||||
|
library: MutableLibrary
|
||||||
|
"""
|
||||||
|
Library from which existing patterns should be referenced, and to which
|
||||||
|
new ones should be added
|
||||||
|
"""
|
||||||
|
|
||||||
|
tools: Dict[Optional[str], Tool]
|
||||||
|
"""
|
||||||
|
Tool objects are used to dynamically generate new single-use Devices
|
||||||
|
(e.g wires or waveguides) to be plugged into this device.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
library: MutableLibrary,
|
||||||
|
*,
|
||||||
|
pattern: Optional[Pattern] = None,
|
||||||
|
ports: Union[None, str, Mapping[str, Port]] = None,
|
||||||
|
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
# TODO documentation for Builder() constructor
|
||||||
|
|
||||||
|
# TODO MOVE THE BELOW DOCS to PortList
|
||||||
|
# If `ports` is `None`, two default ports ('A' and 'B') are created.
|
||||||
|
# Both are placed at (0, 0) and have default `ptype`, but 'A' has rotation 0
|
||||||
|
# (attached devices will be placed to the left) and 'B' has rotation
|
||||||
|
# pi (attached devices will be placed to the right).
|
||||||
|
"""
|
||||||
|
self.library = library
|
||||||
|
if pattern is not None:
|
||||||
|
self.pattern = pattern
|
||||||
|
else:
|
||||||
|
self.pattern = Pattern()
|
||||||
|
|
||||||
|
if ports is not None:
|
||||||
|
if self.pattern.ports:
|
||||||
|
raise BuildError('Ports supplied for pattern with pre-existing ports!')
|
||||||
|
if isinstance(ports, str):
|
||||||
|
ports = library.abstract(ports).ports
|
||||||
|
|
||||||
|
self.pattern.ports.update(copy.deepcopy(dict(ports)))
|
||||||
|
|
||||||
|
if tools is None:
|
||||||
|
self.tools = {}
|
||||||
|
elif isinstance(tools, Tool):
|
||||||
|
self.tools = {None: tools}
|
||||||
|
else:
|
||||||
|
self.tools = dict(tools)
|
||||||
|
|
||||||
|
self._dead = False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_builder(
|
||||||
|
cls,
|
||||||
|
builder: Builder,
|
||||||
|
*,
|
||||||
|
library: Optional[MutableLibrary] = None,
|
||||||
|
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
||||||
|
) -> 'Pather':
|
||||||
|
"""TODO from_builder docs"""
|
||||||
|
library = library if library is not None else builder.library
|
||||||
|
if library is None:
|
||||||
|
raise BuildError('No library available for Pather!')
|
||||||
|
new = Pather(library=library, tools=tools, pattern=builder.pattern)
|
||||||
|
return new
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def interface(
|
||||||
|
cls,
|
||||||
|
source: Union[PortList, Mapping[str, Port], str],
|
||||||
|
*,
|
||||||
|
library: Optional[MutableLibrary] = None,
|
||||||
|
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
||||||
|
in_prefix: str = 'in_',
|
||||||
|
out_prefix: str = '',
|
||||||
|
port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None,
|
||||||
|
) -> 'Pather':
|
||||||
|
"""
|
||||||
|
TODO doc pather.interface
|
||||||
|
"""
|
||||||
|
if library is None:
|
||||||
|
if hasattr(source, 'library') and isinstance(source.library, MutableLibrary):
|
||||||
|
library = source.library
|
||||||
|
else:
|
||||||
|
raise BuildError('No library provided (and not present in `source.library`')
|
||||||
|
|
||||||
|
if tools is None and hasattr(source, 'tools') and isinstance(source.tools, dict):
|
||||||
|
tools = source.tools
|
||||||
|
|
||||||
|
new = Pather.from_builder(Builder.interface(
|
||||||
|
source=source,
|
||||||
|
library=library,
|
||||||
|
in_prefix=in_prefix,
|
||||||
|
out_prefix=out_prefix,
|
||||||
|
port_map=port_map,
|
||||||
|
))
|
||||||
|
return new
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
s = f'<Builder {self.pattern} >' # TODO maybe show lib and tools? in builder repr?
|
||||||
|
return s
|
||||||
|
|
||||||
def retool(
|
def retool(
|
||||||
self: BB,
|
self: PP,
|
||||||
tool: Tool,
|
tool: Tool,
|
||||||
keys: Union[Optional[str], Sequence[Optional[str]]] = None,
|
keys: Union[Optional[str], Sequence[Optional[str]]] = None,
|
||||||
) -> BB:
|
) -> PP:
|
||||||
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:
|
||||||
@ -512,7 +659,7 @@ class Builder(PortList):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def path(
|
def path(
|
||||||
self: BB,
|
self: PP,
|
||||||
portspec: str,
|
portspec: str,
|
||||||
ccw: Optional[SupportsBool],
|
ccw: Optional[SupportsBool],
|
||||||
length: float,
|
length: float,
|
||||||
@ -520,7 +667,7 @@ class Builder(PortList):
|
|||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||||
base_name: str = '_path',
|
base_name: str = '_path',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> BB:
|
) -> PP:
|
||||||
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
|
||||||
@ -533,7 +680,7 @@ class Builder(PortList):
|
|||||||
return self.plug(Abstract(name, pat.ports), {portspec: tool_port_names[0]})
|
return self.plug(Abstract(name, pat.ports), {portspec: tool_port_names[0]})
|
||||||
|
|
||||||
def path_to(
|
def path_to(
|
||||||
self: BB,
|
self: PP,
|
||||||
portspec: str,
|
portspec: str,
|
||||||
ccw: Optional[SupportsBool],
|
ccw: Optional[SupportsBool],
|
||||||
position: float,
|
position: float,
|
||||||
@ -541,7 +688,7 @@ class Builder(PortList):
|
|||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||||
base_name: str = '_pathto',
|
base_name: str = '_pathto',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> BB:
|
) -> PP:
|
||||||
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
|
||||||
@ -567,7 +714,7 @@ class Builder(PortList):
|
|||||||
return self.path(portspec, ccw, length, tool_port_names=tool_port_names, base_name=base_name, **kwargs)
|
return self.path(portspec, ccw, length, tool_port_names=tool_port_names, base_name=base_name, **kwargs)
|
||||||
|
|
||||||
def mpath(
|
def mpath(
|
||||||
self: BB,
|
self: PP,
|
||||||
portspec: Union[str, Sequence[str]],
|
portspec: Union[str, Sequence[str]],
|
||||||
ccw: Optional[SupportsBool],
|
ccw: Optional[SupportsBool],
|
||||||
*,
|
*,
|
||||||
@ -577,7 +724,7 @@ class Builder(PortList):
|
|||||||
force_container: bool = False,
|
force_container: bool = False,
|
||||||
base_name: str = '_mpath',
|
base_name: str = '_mpath',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> BB:
|
) -> PP:
|
||||||
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
|
||||||
@ -608,7 +755,7 @@ class Builder(PortList):
|
|||||||
port_name = tuple(portspec)[0]
|
port_name = tuple(portspec)[0]
|
||||||
return self.path(port_name, ccw, extensions[port_name], tool_port_names=tool_port_names)
|
return self.path(port_name, ccw, extensions[port_name], tool_port_names=tool_port_names)
|
||||||
else:
|
else:
|
||||||
bld = Builder.interface(source=ports, library=self.library, tools=self.tools)
|
bld = Pather.interface(source=ports, library=self.library, tools=self.tools)
|
||||||
for port_name, length in extensions.items():
|
for port_name, length in extensions.items():
|
||||||
bld.path(port_name, ccw, length, tool_port_names=tool_port_names)
|
bld.path(port_name, ccw, length, tool_port_names=tool_port_names)
|
||||||
name = self.library.get_name(base_name)
|
name = self.library.get_name(base_name)
|
||||||
@ -617,7 +764,7 @@ class Builder(PortList):
|
|||||||
|
|
||||||
# TODO def path_join() and def bus_join()?
|
# TODO def path_join() and def bus_join()?
|
||||||
|
|
||||||
def flatten(self: BB) -> BB:
|
def flatten(self: PP) -> PP:
|
||||||
"""
|
"""
|
||||||
Flatten the contained pattern, using the contained library to resolve references.
|
Flatten the contained pattern, using the contained library to resolve references.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user