Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
2cb3862
refactor arm mcus
ducky64 May 25, 2026
e59e10f
Update nRF52840.py
ducky64 May 26, 2026
2f1d24a
Update nRF52840.py
ducky64 May 26, 2026
c5ef561
wip
ducky64 May 26, 2026
14892ac
not making sense
ducky64 May 26, 2026
cf1f173
wip
ducky64 May 26, 2026
e9044f0
generate resource names for peripheralfixedpin
ducky64 May 26, 2026
87d6598
it kinda builds
ducky64 May 26, 2026
7d7bb2e
seemed to work?
ducky64 May 26, 2026
b6819ed
Update Rp2040.py
ducky64 May 26, 2026
7cf5f3a
fix pinmap test with remap test
ducky64 May 26, 2026
4aebbe0
update netlists
ducky64 May 26, 2026
f92b2d0
wip dev board refactor
ducky64 May 26, 2026
0ca074a
Merge branch 'master' into micro-wrapper-next
ducky64 May 27, 2026
9dfe78f
wip
ducky64 May 28, 2026
8208949
Update nRF52840.py
ducky64 May 28, 2026
520a9bd
cleaning
ducky64 May 28, 2026
7f23b3f
refactoring
ducky64 May 28, 2026
edfb214
update references
ducky64 May 28, 2026
c4f97a8
cleaning structure
ducky64 May 28, 2026
114c62e
refactoring
ducky64 May 28, 2026
1742166
Update PinMappable.py
ducky64 May 28, 2026
0dd1800
pin filtering
ducky64 May 28, 2026
841fb91
use allowed_pins
ducky64 May 28, 2026
1280c7f
Create test_mcu_wrapper.py
ducky64 May 29, 2026
64fefbc
wip
ducky64 May 29, 2026
f7b6c52
update tp2040 power net style, update examples
ducky64 May 29, 2026
82b8aed
wip
ducky64 May 29, 2026
6e77acf
wip utility
ducky64 May 29, 2026
550f855
wip bundle remap test
ducky64 May 30, 2026
e457b5c
Update Rp2040.py
ducky64 May 30, 2026
5feb02a
clean iocontroller wrapper infrastructure
ducky64 May 30, 2026
60084b8
cleaning
ducky64 May 30, 2026
fc6112d
refactoring wip
ducky64 May 30, 2026
d51b0d0
bad types but more building
ducky64 May 30, 2026
2724f6c
refactor wrapper side logic
ducky64 May 30, 2026
b6f64c3
Update BaseIoControllerWrapped.py
ducky64 May 30, 2026
8b2c4d5
cleaning
ducky64 May 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 226 additions & 0 deletions edg/abstract_parts/BaseIoControllerWrapped.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
from typing import *

from ..electronics_interfaces import *
from .IoController import BaseIoController


class BaseIoControllerWrapped(BaseIoController):
"""Base class for IoController wrapped blocks, particularly footprints that are used
with an outer WrapperSubboardBlock to implement e.g. a dev board or module around a modeling subcircuit.

Provides some utility functions to remap pin assignments from the model to the footprint.

In this class, pin_assigns is treated as the model's pin assigns and internally remapped.
"""

@staticmethod
def _remap_model_pin_assigns(
remapping: Dict[str, str], pin_assigns: List[str]
) -> Dict[str, Tuple[Optional[str], Optional[str]]]:
"""Given a remapping dict and a list of pin assigns, returns the mapping as a dict with the remapping applied.
The output is (pin name, pin number), with both being optional.

Assigns not present in the remapping dict are passed unchanged, eg for non-pin-assigns like bundle containers.

Internal utility.
"""
remapped_assigns: Dict[str, Tuple[Optional[str], Optional[str]]] = {}
for assign in pin_assigns:
name, pindef = assign.split("=")
name = name.strip()
split = pindef.split(",")
split = [s.strip() for s in split]
if len(split) == 1: # should be a bundle name, since pins should have two parts
assert name not in remapping
remapped_assigns[name] = (split[0], None)
elif len(split) == 2:
pinname, pinnum = split[0], split[1]
assert pinname in remapping
remapped_assigns[name] = (pinname, remapping[pinname])
Comment on lines +27 to +39
else:
raise ValueError(f"unable to parse assign {assign}")

return remapped_assigns

def _generator_pin_dict(self) -> Dict[str, Port]:
"""Returns a dict of pin name to port for all IO ports, recursing into bundles Ports.
This includes both the bundle container Port and their (recursive) contents.
Users of this may want to filter by the port type.

For Vector-typed IO ports, this generates the subports and must be authoritative.
This cannot be used with anything else that generates vector sub-ports.
This must be a GeneratorBlock with requested() declared as generator params.

Internal utility.
"""
assert isinstance(self, GeneratorBlock)

pin_dict: Dict[str, Port] = {}

def recurse_port(port: Port, prefix: str) -> None:
assert prefix not in pin_dict, f"duplicate pin name {prefix}"
pin_dict[prefix] = port

for subport_name, subport in port._ports.items():
recurse_port(subport, f"{prefix}.{subport_name}")

for io_port in self._io_ports:
if isinstance(io_port, Vector):
io_port.defined()
for subport_name in self.get(io_port.requested()):
subport = io_port.append_elt(io_port._tpe.empty(), subport_name)
recurse_port(subport, subport_name)
elif isinstance(io_port, Port):
if self.get(io_port.is_connected()):
port_name = io_port._name_from(self)
recurse_port(io_port, port_name)
else:
raise NotImplementedError(f"unknown port type {io_port}")

return pin_dict

def _remap_to_footprint_pinning(
self, pin_assigns: Dict[str, Tuple[Optional[str], Optional[str]]], pin_dict: Dict[str, Port]
) -> Dict[str, HasPassivePort]:
"""Generates pinning that can be passed into a footprint, given the pin assign dict from _remap_pin_assigns_list
and pin dict from _generator_pin_dict.

This requires all pins to be assigned.

Internal utility.
"""
pinning: Dict[str, HasPassivePort] = {}

for name, assign in pin_assigns.items():
assert name in pin_dict
port = pin_dict[name]
if not isinstance(port, HasPassivePort):
continue # ignore non-leaf ports
assert assign[1] is not None, f"pin {name} missing pin number assignment"
pinning[assign[1]] = port

return pinning

@staticmethod
def _remap_assigns_to_value(assigns: Dict[str, Tuple[Optional[str], Optional[str]]]) -> List[str]:
"""Given a dict of pin assigns from _remap_pinning_assigns, returns a list of assign strings
for use in self.actual_pin_assigns.

Internal utility.
"""
pin_assigns: List[str] = []
for name, assign in assigns.items():
if assign[0] is not None and assign[1] is not None:
pin_assigns.append(f"{name}={assign[0]}, {assign[1]}")
elif assign[0] is not None:
pin_assigns.append(f"{name}={assign[0]}")
elif assign[1] is not None:
pin_assigns.append(f"{name}={assign[1]}")
else:
raise ValueError(f"invalid assign for {name}: {assign}")
return pin_assigns

def _make_pinning(
self, fixed_pinning: Dict[str, Union[Passive, HasPassivePort]], remapping: Dict[str, str]
) -> Dict[str, Union[Passive, HasPassivePort]]:
"""Creates the footprint pinning dict for the wrapped footprint, given the fixed pinning and
remapping from pin name to this footprint's pin number.
This generates pinning for all BaseIoController IOs.
As a side effect, this assigns self.actual_pin_assigns.

This wraps the above helpers, this should be used in most cases.
"""
remapped_pin_assigns = self._remap_model_pin_assigns(remapping, self.get(self.pin_assigns))
pin_dict = self._generator_pin_dict()
fixed_pinning.update(self._remap_to_footprint_pinning(remapped_pin_assigns, pin_dict))
self.assign(self.actual_pin_assigns, self._remap_assigns_to_value(remapped_pin_assigns))
return fixed_pinning


class BaseIoControllerWrapper(BaseIoController):
"""Base class for a block that contains a BaseIoControllerWrapped as the physical footprint
as well as a non-physical device model.

This provides utilities to remap pin assignments from the device specification to the model.
"""

def _export_tap_ios_inner(self, inner: "BaseIoController") -> None:
"""Export-taps all IO ports from some inner BaseIoController.
This must be a SubboardBlock to support the export_tap connection.
This must be called in contents() or generate(), after IOs have been defined."""
from ..core.Blocks import BlockElaborationState

assert isinstance(self, WrapperSubboardBlock)
assert self._elaboration_state in (
BlockElaborationState.contents,
BlockElaborationState.generate,
), "can only run in contents() or generate()"

inner_ios_by_type = {self._type_of_io(io_port): io_port for io_port in inner._io_ports}
for self_io in self._io_ports:
self_io_type = self._type_of_io(self_io)
assert self_io_type in inner_ios_by_type, f"inner missing IO of type {self_io_type}"
inner_io = inner_ios_by_type[self_io_type]
self.export_tap(self_io, inner_io)

def _generator_pin_type_dict(self) -> Dict[str, Type[Port]]:
"""Returns a dict of pin name to port type for all IO ports, recursing into bundles Ports.
This includes both the bundle container Port and their (recursive) contents.

This does not instantiate Vector subports.
This must be a GeneratorBlock with requested() declared as generator params.

Internal utility.
"""
assert isinstance(self, GeneratorBlock)

pin_type_dict: Dict[str, Type[Port]] = {}

def recurse_port(port: Port, prefix: str) -> None:
assert prefix not in pin_type_dict, f"duplicate pin name {prefix}"
pin_type_dict[prefix] = type(port)

for subport_name, subport in port._ports.items():
recurse_port(subport, f"{prefix}.{subport_name}")

for io_port in self._io_ports:
if isinstance(io_port, Vector):
for subport_name in self.get(io_port.requested()):
recurse_port(io_port._tpe.empty(), subport_name)
elif isinstance(io_port, Port):
if self.get(io_port.is_connected()):
port_name = io_port._name_from(self)
recurse_port(io_port, port_name)
else:
raise NotImplementedError(f"unknown port type {io_port}")

return pin_type_dict

def _make_model_pinning(self, remapping: Dict[str, str], device_assigns: List[str]) -> List[str]:
"""Remaps my own assigns (pinned in device-space) to model-space.
remapping is specified as the forward remapping, from pinname to device pinnum.

Requires _generator_param_all_ios, so all the IOs names are available.
"""
inverse_remapping = {v: k for k, v in remapping.items()}

remapped_assigns: List[str] = []
pin_types = self._generator_pin_type_dict()

for assign in device_assigns:
name, pindef = assign.split("=")
name = name.strip()
pindef = pindef.strip()
assert name in pin_types, f"assign {name} not in IO ports"
pin_type = pin_types[name]
if issubclass(pin_type, HasPassivePort):
if pindef in inverse_remapping:
remapped_assigns.append(f"{name}={inverse_remapping[pindef]}")
elif pindef in remapping:
remapped_assigns.append(assign)
else:
raise ValueError(f"assign {assign} has pindef {pindef} not in remapping")
else:
remapped_assigns.append(assign)

return remapped_assigns
29 changes: 11 additions & 18 deletions edg/abstract_parts/IoController.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,24 +187,17 @@ def _wrap_inner(
self.assign(self.actual_pin_assigns, inner.actual_pin_assigns)
self.assign(self.io_current_draw, inner.io_current_draw)

def _export_tap_ios_inner(self, inner: "BaseIoController") -> None:
"""Export-taps all IO ports from some inner BaseIoController.
This must be a SubboardBlock to support the export_tap connection.
This must be called in contents() or generate(), after IOs have been defined."""
from ..core.Blocks import BlockElaborationState

assert isinstance(self, WrapperSubboardBlock)
assert self._elaboration_state in (
BlockElaborationState.contents,
BlockElaborationState.generate,
), "can only run in contents() or generate()"

inner_ios_by_type = {self._type_of_io(io_port): io_port for io_port in inner._io_ports}
for self_io in self._io_ports:
self_io_type = self._type_of_io(self_io)
assert self_io_type in inner_ios_by_type, f"inner missing IO of type {self_io_type}"
inner_io = inner_ios_by_type[self_io_type]
self.export_tap(self_io, inner_io)
def _generator_param_all_ios(self) -> None:
"""Declares all BaseIoController IOs as generator params.
This must be a GeneratorBlock."""
assert isinstance(self, GeneratorBlock)
for io_port in self._io_ports:
if isinstance(io_port, Vector):
self.generator_param(io_port.requested())
elif isinstance(io_port, Port):
self.generator_param(io_port.is_connected())
else:
raise NotImplementedError(f"unknown port type {io_port}")

@staticmethod
def _instantiate_from(
Expand Down
64 changes: 0 additions & 64 deletions edg/abstract_parts/IoControllerWrapped.py

This file was deleted.

Loading
Loading