Add Tree/TreeView and allow Builder to ingest them

This commit is contained in:
jan 2023-10-20 23:14:47 -07:00
parent b4d31903c1
commit 9a7a5583ed
4 changed files with 43 additions and 12 deletions

View File

@ -101,7 +101,7 @@ References are accomplished by listing the target's name, not its `Pattern` obje
## Glossary
- `Library`: A collection of named cells. OASIS or GDS "library" or file.
- "tree": Any Library which has only one topcell.
- `Tree`: Any `{name: pattern}` mapping which has only one topcell.
- `Pattern`: A collection of geometry, text labels, and reference to other patterns.
OASIS or GDS "Cell", DXF "Block".
- `Ref`: A reference to another pattern. GDS "AREF/SREF", OASIS "Placement".
@ -142,6 +142,11 @@ my_pattern.ref(new_name, ...) # instantiate the cell
# In practice, you may do lots of
my_pattern.ref(lib << make_tree(...), ...)
# With a `Builder` and `place()`/`plug()` the `lib <<` portion can be implicit:
my_builder = Builder(library=lib, ...)
...
my_builder.place(make_tree(...))
```
We can also use this shorthand to quickly add and reference a single flat (as yet un-named) pattern:

View File

@ -38,7 +38,7 @@ from .pattern import Pattern, map_layers, map_targets, chain_elements
from .library import (
ILibraryView, ILibrary,
LibraryView, Library, LazyLibrary,
AbstractView,
AbstractView, TreeView, Tree,
)
from .ports import Port, PortList
from .abstract import Abstract

View File

@ -8,7 +8,7 @@ import logging
from numpy.typing import ArrayLike
from ..pattern import Pattern
from ..library import ILibrary
from ..library import ILibrary, TreeView
from ..error import BuildError
from ..ports import PortList, Port
from ..abstract import Abstract
@ -190,7 +190,7 @@ class Builder(PortList):
def plug(
self,
other: Abstract | str | Pattern,
other: Abstract | str | Pattern | TreeView,
map_in: dict[str, str],
map_out: dict[str, str | None] | None = None,
*,
@ -201,11 +201,16 @@ class Builder(PortList):
) -> Self:
"""
Wrapper around `Pattern.plug` which allows a string for `other`.
The `Builder`'s library is used to dereference the string (or `Abstract`, if
one is passed with `append=True`).
one is passed with `append=True`). If a `TreeView` is passed, it is first
added into `self.library`.
Args:
other: An `Abstract`, string, or `Pattern` describing the device to be instatiated.
other: An `Abstract`, string, `Pattern`, or `TreeView` describing the
device to be instatiated. If it is a `TreeView`, it is first
added into `self.library`, after which the topcell is plugged;
an equivalent statement is `self.plug(self.library << other, ...)`.
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
@ -243,6 +248,10 @@ class Builder(PortList):
logger.error('Skipping plug() since device is dead')
return self
if not isinstance(other, (str, Abstract, Pattern)):
# We got a Tree; add it into self.library and grab an Abstract for it
other = self.library << other
if isinstance(other, str):
other = self.library.abstract(other)
if append and isinstance(other, Abstract):
@ -261,7 +270,7 @@ class Builder(PortList):
def place(
self,
other: Abstract | str | Pattern,
other: Abstract | str | Pattern | TreeView,
*,
offset: ArrayLike = (0, 0),
rotation: float = 0,
@ -272,12 +281,17 @@ class Builder(PortList):
append: bool = False,
) -> Self:
"""
Wrapper around `Pattern.place` which allows a string for `other`.
Wrapper around `Pattern.place` which allows a string or `TreeView` for `other`.
The `Builder`'s library is used to dereference the string (or `Abstract`, if
one is passed with `append=True`).
one is passed with `append=True`). If a `TreeView` is passed, it is first
added into `self.library`.
Args:
other: An `Abstract`, string, or `Pattern` describing the device to be instatiated.
other: An `Abstract`, string, `Pattern`, or `TreeView` describing the
device to be instatiated. If it is a `TreeView`, it is first
added into `self.library`, after which the topcell is plugged;
an equivalent statement is `self.plug(self.library << other, ...)`.
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)).
@ -306,6 +320,10 @@ class Builder(PortList):
logger.error('Skipping place() since device is dead')
return self
if not isinstance(other, (str, Abstract, Pattern)):
# We got a Tree; add it into self.library and grab an Abstract for it
other = self.library << other
if isinstance(other, str):
other = self.library.abstract(other)
if append and isinstance(other, Abstract):

View File

@ -14,7 +14,7 @@ Classes include:
- `AbstractView`: Provides a way to use []-indexing to generate abstracts for patterns in the linked
library. Generated with `ILibraryView.abstract_view()`.
"""
from typing import Callable, Self, Type, TYPE_CHECKING, cast
from typing import Callable, Self, Type, TYPE_CHECKING, cast, TypeAlias, Protocol, Literal
from typing import Iterator, Mapping, MutableMapping, Sequence
import logging
import base64
@ -44,6 +44,14 @@ logger = logging.getLogger(__name__)
visitor_function_t = Callable[..., 'Pattern']
TreeView: TypeAlias = Mapping[str, 'Pattern']
""" A name-to-`Pattern` mapping which is expected to have only one top-level cell """
Tree: TypeAlias = MutableMapping[str, 'Pattern']
""" A mutable name-to-`Pattern` mapping which is expected to have only one top-level cell """
SINGLE_USE_PREFIX = '_'
"""
Names starting with this prefix are assumed to refer to single-use patterns,
@ -674,7 +682,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
return rename_map
def __lshift__(self, other: Mapping[str, 'Pattern']) -> str:
def __lshift__(self, other: TreeView) -> str:
if len(other) == 1:
name = next(iter(other))
else: