diff --git a/edg/abstract_parts/BaseIoControllerWrapped.py b/edg/abstract_parts/BaseIoControllerWrapped.py new file mode 100644 index 000000000..2c8e8493c --- /dev/null +++ b/edg/abstract_parts/BaseIoControllerWrapped.py @@ -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]) + 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 diff --git a/edg/abstract_parts/IoController.py b/edg/abstract_parts/IoController.py index b1946f1b7..299cdfa88 100644 --- a/edg/abstract_parts/IoController.py +++ b/edg/abstract_parts/IoController.py @@ -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( diff --git a/edg/abstract_parts/IoControllerWrapped.py b/edg/abstract_parts/IoControllerWrapped.py deleted file mode 100644 index 7202b3347..000000000 --- a/edg/abstract_parts/IoControllerWrapped.py +++ /dev/null @@ -1,64 +0,0 @@ -from typing import * - -from ..electronics_interfaces import * -from .IoController import BaseIoController - - -class IoControllerWrapped(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. - """ - - def _remap_pinning_assigns( - self, model_pin_assigns: List[str], remapping: Dict[str, str] - ) -> Tuple[Dict[str, HasPassivePort], Dict[str, str]]: - """Given the actual pin assignments and a remapping dict, returns the pinning dict for the footprint - and the updated actual pin assignments. - Generates concrete ports elements for IO Vectors""" - pinning: Dict[str, HasPassivePort] = {} - actual_pin_assigns: Dict[str, str] = {} - seen_names: Set[str] = set() - - model_pin_assigns_dict: Dict[str, str] = {} - for assign in model_pin_assigns: - name, pindef = assign.split("=") - pins = pindef.split(",") - model_pin_assigns_dict[name.strip()] = pins[0].strip() # use the GPIO name - - def remap_port_recursive(port: Port, prefix: str = "") -> None: - """Remaps a port, recursively for bundles""" - if isinstance(port, HasPassivePort): - if prefix not in model_pin_assigns_dict: - raise ValueError(f"pin {prefix} not assigned") - pin = model_pin_assigns_dict[prefix] - if pin not in remapping: - raise ValueError(f"pin {pin} not in remapping") - remapped_pin = remapping[pin] - pinning[remapped_pin] = port - actual_pin_assigns[prefix] = f"{pin}, {remapped_pin}" - - for subport_name, subport in port._ports.items(): - remap_port_recursive(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()): - assert subport_name not in seen_names, f"duplicate pin name {subport_name}" - subport = io_port.append_elt(io_port._tpe.empty(), subport_name) - remap_port_recursive(subport, subport_name) - seen_names.add(subport_name) - elif isinstance(io_port, Port): - if self.get(io_port.is_connected()): - raise NotImplementedError("TODO implement me") - else: - raise NotImplementedError(f"unknown port type {io_port}") - - return pinning, actual_pin_assigns - - def _remap_assigns_to_value(self, assigns: Dict[str, 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""" - return [f"{name}={assign}" for name, assign in assigns.items()] diff --git a/edg/abstract_parts/PinMappable.py b/edg/abstract_parts/PinMappable.py index fc40eb065..a192f26e3 100644 --- a/edg/abstract_parts/PinMappable.py +++ b/edg/abstract_parts/PinMappable.py @@ -53,15 +53,24 @@ class BaseDelegatingPinMapResource(BasePinMapResource): class PinResource(BaseLeafPinMapResource): - """A resource for a single chip pin, which can be one of several port types (eg, an ADC and DIO sharing a pin).""" + """A resource for a single chip pin, which can be one of several port types (eg, an ADC and DIO sharing a pin). - def __init__(self, pin: str, name_models: Mapping[str, Union[Passive, HasPassivePort]]): + Generally, this is initially created with pin = pin name (like GPIO0), then remapped so the pin = pin number, + with the pinname inheriting the prior pin name.""" + + def __init__( + self, pin: str, name_models: Mapping[str, Union[Passive, HasPassivePort]], pinname: Optional[str] = None + ): self.pin = pin self.name_models = name_models + if pinname is not None: + self.pinname = pinname + else: + self.pinname = pin @override def __repr__(self) -> str: - return f"PinResource({self.pin}, {self.name_models})" + return f"PinResource({self.pinname}, {self.pin}, {self.name_models})" @override def __eq__(self, other: Any) -> bool: @@ -79,14 +88,24 @@ class PeripheralFixedPin(BaseLeafPinMapResource): """A resource for a peripheral as a bundle port, where the internal ports are fixed. No allocation happens. The internal port model must be fully defined here.""" - def __init__(self, name: str, port_model: Port, inner_allowed_pins: Dict[str, str]): + def __init__( + self, + name: str, + port_model: Port, + inner_allowed_pins: Dict[str, str], + inner_pinnames: Optional[Dict[str, str]] = None, + ): self.name = name self.port_model = port_model self.inner_allowed_pins = inner_allowed_pins + if inner_pinnames is not None: + self.inner_pinnames = inner_pinnames + else: + self.inner_pinnames = inner_allowed_pins @override def __repr__(self) -> str: - return f"PeripheralFixedPin({self.name}, {self.port_model.__class__.__name__} {self.inner_allowed_pins})" + return f"PeripheralFixedPin({self.name}, {self.port_model.__class__.__name__} {self.inner_allowed_pins} {self.inner_pinnames})" @override def __eq__(self, other: Any) -> bool: @@ -96,6 +115,7 @@ def __eq__(self, other: Any) -> bool: and self.name == other.name and self.port_model is other.port_model and self.inner_allowed_pins == other.inner_allowed_pins + and self.inner_pinnames == other.inner_pinnames ) @@ -274,7 +294,7 @@ def remap_pins(self, pinmap: Dict[str, str]) -> "PinMapUtil": def remap_resource(resource: BasePinMapResource) -> Optional[BasePinMapResource]: if isinstance(resource, PinResource): if resource.pin in pinmap: - return PinResource(pinmap[resource.pin], resource.name_models) + return PinResource(pinmap[resource.pin], resource.name_models, resource.pinname) else: return None elif isinstance(resource, PeripheralFixedPin): @@ -283,7 +303,7 @@ def remap_resource(resource: BasePinMapResource) -> Optional[BasePinMapResource] for elt_name, elt_pin in resource.inner_allowed_pins.items() if elt_pin in pinmap } - return PeripheralFixedPin(resource.name, resource.port_model, remapped_pins) + return PeripheralFixedPin(resource.name, resource.port_model, remapped_pins, resource.inner_pinnames) elif isinstance(resource, BaseDelegatingPinMapResource): return resource else: @@ -363,7 +383,7 @@ def try_allocate_resource( if isinstance(resource, PinResource): # single pin: just assign it sub_assignments.check_empty() resource_name, resource_model = resource.get_name_model_for_type(port_type) - allocated_resource = AllocatedResource(resource_model, port_name, resource_name, resource.pin) + allocated_resource = AllocatedResource(resource_model, port_name, resource.pinname, resource.pin) return allocated_resource elif isinstance(resource, PeripheralFixedPin): # fixed pin: check user-assignment, or assign first inner_pin_map: Dict[str, Tuple[str, Optional[str]]] = {} @@ -372,7 +392,7 @@ def try_allocate_resource( if inner_assignment is not None and inner_assignment != inner_pin: raise BadUserAssignError(f"invalid assignment to {port_name}.{inner_name}: {inner_assignment}") - inner_pin_map[inner_name] = (inner_pin, None) + inner_pin_map[inner_name] = (inner_pin, resource.inner_pinnames[inner_name]) inner_sub_assignments.check_empty() sub_assignments.check_empty() diff --git a/edg/abstract_parts/__init__.py b/edg/abstract_parts/__init__.py index d2b35fdc7..6a43150fc 100644 --- a/edg/abstract_parts/__init__.py +++ b/edg/abstract_parts/__init__.py @@ -129,7 +129,7 @@ from .IoController import BaseIoController, IoController, IoControllerPowerRequired, BaseIoControllerPinmapGenerator from .IoControllerExportable import BaseIoControllerExportable -from .IoControllerWrapped import IoControllerWrapped +from .BaseIoControllerWrapped import BaseIoControllerWrapped, BaseIoControllerWrapper from .IoControllerInterfaceMixins import ( IoControllerSpiPeripheral, IoControllerI2cTarget, diff --git a/edg/abstract_parts/test_pinmappable.py b/edg/abstract_parts/test_pinmappable.py index 210753e5d..3b1985851 100644 --- a/edg/abstract_parts/test_pinmappable.py +++ b/edg/abstract_parts/test_pinmappable.py @@ -61,10 +61,42 @@ def test_assign_assigned(self) -> None: # fully user-specified ).allocate( [(DigitalBidir, ["DIO3", "DIO2"]), (AnalogSink, ["AIO4", "AIO5"])], ["DIO3=3", "DIO2=2", "AIO4=4", "AIO5=5"] ) - self.assertIn(AllocatedResource(dio_model, "DIO3", "PIO3", "3"), allocated) - self.assertIn(AllocatedResource(dio_model, "DIO2", "PIO2", "2"), allocated) - self.assertIn(AllocatedResource(ain_model, "AIO4", "AIn4", "4"), allocated) - self.assertIn(AllocatedResource(ain_model, "AIO5", "AIn5", "5"), allocated) + self.assertIn(AllocatedResource(dio_model, "DIO3", "3", "3"), allocated) + self.assertIn(AllocatedResource(dio_model, "DIO2", "2", "2"), allocated) + self.assertIn(AllocatedResource(ain_model, "AIO4", "4", "4"), allocated) + self.assertIn(AllocatedResource(ain_model, "AIO5", "5", "5"), allocated) + + def test_assign_remapped(self) -> None: # fully user-specified + dio_model = DigitalBidir() + ain_model = AnalogSink() + allocated = ( + PinMapUtil( + [ + PinResource("P1", {"PIO1": dio_model}), + PinResource("P2", {"PIO2": dio_model}), + PinResource("P3", {"PIO3": dio_model, "AIn3": ain_model}), + PinResource("P4", {"PIO4": dio_model, "AIn4": ain_model}), + PinResource("P5", {"AIn5": ain_model}), + ] + ) + .remap_pins( + { + "P1": "1", + "P2": "2", + "P3": "3", + "P4": "4", + "P5": "5", + } + ) + .allocate( + [(DigitalBidir, ["DIO3", "DIO2"]), (AnalogSink, ["AIO4", "AIO5"])], + ["DIO3=3", "DIO2=2", "AIO4=4", "AIO5=5"], + ) + ) + self.assertIn(AllocatedResource(dio_model, "DIO3", "P3", "3"), allocated) + self.assertIn(AllocatedResource(dio_model, "DIO2", "P2", "2"), allocated) + self.assertIn(AllocatedResource(ain_model, "AIO4", "P4", "4"), allocated) + self.assertIn(AllocatedResource(ain_model, "AIO5", "P5", "5"), allocated) def test_assign_mixed(self) -> None: # mix of user-specified and automatic assignments, assuming greedy algo dio_model = DigitalBidir() @@ -78,10 +110,10 @@ def test_assign_mixed(self) -> None: # mix of user-specified and automatic assi PinResource("5", {"AIn5": ain_model}), ] ).allocate([(DigitalBidir, ["DIO3", "DIO1"]), (AnalogSink, ["AIO5", "AIO4"])], ["DIO3=3", "AIO4=4"]) - self.assertIn(AllocatedResource(dio_model, "DIO3", "PIO3", "3"), allocated) - self.assertIn(AllocatedResource(dio_model, "DIO1", "PIO1", "1"), allocated) - self.assertIn(AllocatedResource(ain_model, "AIO4", "AIn4", "4"), allocated) - self.assertIn(AllocatedResource(ain_model, "AIO5", "AIn5", "5"), allocated) + self.assertIn(AllocatedResource(dio_model, "DIO3", "3", "3"), allocated) + self.assertIn(AllocatedResource(dio_model, "DIO1", "1", "1"), allocated) + self.assertIn(AllocatedResource(ain_model, "AIO4", "4", "4"), allocated) + self.assertIn(AllocatedResource(ain_model, "AIO5", "5", "5"), allocated) def test_assign_bad(self) -> None: # bad user-specified assignments dio_model = DigitalBidir() @@ -123,7 +155,7 @@ def test_assign_bundle_fixed(self) -> None: PeripheralFixedPin("USB0", usb_model, {"dm": "2", "dp": "3"}), ] ).allocate([(UsbDevicePort, ["usb"])], ["usb.dm=2", "usb.dp=3"]) - self.assertIn(AllocatedResource(usb_model, "usb", "USB0", {"dm": ("2", None), "dp": ("3", None)}), allocated) + self.assertIn(AllocatedResource(usb_model, "usb", "USB0", {"dm": ("2", "2"), "dp": ("3", "3")}), allocated) def test_assign_bundle_fixed_auto(self) -> None: usb_model = UsbDevicePort() @@ -132,7 +164,7 @@ def test_assign_bundle_fixed_auto(self) -> None: PeripheralFixedPin("USB0", usb_model, {"dm": "2", "dp": "3"}), ] ).allocate([(UsbDevicePort, ["usb"])]) - self.assertIn(AllocatedResource(usb_model, "usb", "USB0", {"dm": ("2", None), "dp": ("3", None)}), allocated) + self.assertIn(AllocatedResource(usb_model, "usb", "USB0", {"dm": ("2", "2"), "dp": ("3", "3")}), allocated) def test_assign_bundle_fixed_badspec(self) -> None: usb_model = UsbDevicePort() @@ -163,7 +195,7 @@ def test_assign_bundle_delegating(self) -> None: ).allocate([(UartPort, ["uart"])], ["uart.tx=1", "uart.rx=3"]) self.assertEqual(allocated[0].name, "uart") self.assertEqual(allocated[0].resource_name, "UART0") - self.assertEqual(allocated[0].pin, {"tx": ("1", "PIO1"), "rx": ("3", "PIO3")}) + self.assertEqual(allocated[0].pin, {"tx": ("1", "1"), "rx": ("3", "3")}) def test_assign_bundle_delegating_auto(self) -> None: dio_model = DigitalBidir() @@ -179,7 +211,7 @@ def test_assign_bundle_delegating_auto(self) -> None: ).allocate([(UartPort, ["uart"])]) self.assertEqual(allocated[0].name, "uart") self.assertEqual(allocated[0].resource_name, "UART0") - self.assertEqual(allocated[0].pin, {"tx": ("1", "PIO1"), "rx": ("2", "PIO2")}) + self.assertEqual(allocated[0].pin, {"tx": ("1", "1"), "rx": ("2", "2")}) def test_assign_bundle_delegating_badspec(self) -> None: dio_model = DigitalBidir() @@ -223,7 +255,7 @@ def test_assign_bundle_delegating_fixed(self) -> None: ).allocate([(UartPort, ["uart"])]) self.assertEqual(allocated[0].name, "uart") self.assertEqual(allocated[0].resource_name, "UART0") - self.assertEqual(allocated[0].pin, {"tx": ("3", "PIO3"), "rx": ("1", "PIO1")}) + self.assertEqual(allocated[0].pin, {"tx": ("3", "3"), "rx": ("1", "1")}) assert isinstance(allocated[0].port_model, UartPort) self.assertTrue(allocated[0].port_model.tx.voltage_out.initializer is not None) @@ -241,4 +273,4 @@ def test_assign_bundle_delegating_notconnected(self) -> None: ).allocate([(UartPort, ["uart"])], ["uart.tx=NC"]) self.assertEqual(allocated[0].name, "uart") self.assertEqual(allocated[0].resource_name, "UART0") - self.assertEqual(allocated[0].pin, {"rx": ("1", "PIO1")}) + self.assertEqual(allocated[0].pin, {"rx": ("1", "1")}) diff --git a/edg/electronics_model/BoardScopedTransform.py b/edg/electronics_model/BoardScopedTransform.py index ae7aca4b5..8b7c6fa74 100644 --- a/edg/electronics_model/BoardScopedTransform.py +++ b/edg/electronics_model/BoardScopedTransform.py @@ -41,6 +41,8 @@ def visit_block(self, context: TransformContext, block: edgir.HierarchyBlock) -> if "fp_subboard" in block.meta.members.node or "fp_subboard_connector_pair" in block.meta.members.node: fp_external_blocks = self._design.get_value(context.path.to_tuple() + ("fp_external_blocks",)) + if fp_external_blocks is None: + fp_external_blocks = [] assert isinstance(fp_external_blocks, list) external_blocks: Optional[List[str]] = cast(List[str], fp_external_blocks) if "fp_subblocks_ignored" in block.meta.members.node: diff --git a/edg/parts/microcontroller/Lpc1549.py b/edg/parts/microcontroller/Lpc1549.py index ec908d498..e819eac71 100644 --- a/edg/parts/microcontroller/Lpc1549.py +++ b/edg/parts/microcontroller/Lpc1549.py @@ -370,7 +370,6 @@ class Lpc1549Base( IoControllerWithSwdTargetConnector, WithCrystalGenerator, IoControllerPowerRequired, - BaseIoControllerExportable, GeneratorBlock, ): DEVICE: Type[Lpc1549Base_Device] = Lpc1549Base_Device @@ -378,8 +377,13 @@ class Lpc1549Base( def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) - self.ic: Lpc1549Base_Device - self.generator_param(self.reset.is_connected()) + self.generator_param( + self.reset.is_connected(), + self.pin_assigns, + self.gpio.requested(), + self.usb.requested(), + self.can.requested(), + ) @override def contents(self) -> None: @@ -412,6 +416,9 @@ def contents(self) -> None: def generate(self) -> None: super().generate() + # add a passthrough for gpio (DigitalBidir) to allow the SWD pins to be attached, if using + self._wrap_inner(self.ic, {DigitalBidir: lambda port, assign: port}) + if self.get(self.reset.is_connected()): self.connect(self.reset, self.ic.reset) diff --git a/edg/parts/microcontroller/Rp2040.py b/edg/parts/microcontroller/Rp2040.py index c4ce7bce4..6982de4ea 100644 --- a/edg/parts/microcontroller/Rp2040.py +++ b/edg/parts/microcontroller/Rp2040.py @@ -51,16 +51,17 @@ class Rp2040_Device( "SWCLK": "24", } - def __init__(self, **kwargs: Any) -> None: + def __init__(self, *, _model: BoolLike = False, **kwargs: Any) -> None: super().__init__(**kwargs) + self._model = self.ArgParameter(_model) + self.gnd = self.Port(Ground(), [Common]) self.iovdd = self.Port( VoltageSink( voltage_limits=(1.62, 3.63) * Volt, # Table 628 current_draw=(1.2, 4.3) * mAmp + self.io_current_draw.upper(), # Table 629 ), - [Power], ) self.dvdd = self.Port( @@ -75,6 +76,7 @@ def __init__(self, **kwargs: Any) -> None: current_limits=(0, 100) * mAmp, # Table 1, max current ) ) + self.vreg_vin = self.Port( VoltageSink( voltage_limits=(1.62, 3.63) * Volt, # Table 628 @@ -107,10 +109,19 @@ def __init__(self, **kwargs: Any) -> None: pulldown_capable=True, ) - self.qspi = self.Port(SpiController(self._dio_std_model)) # TODO actually QSPI - self.qspi_cs = self.Port(self._dio_std_model) - self.qspi_sd2 = self.Port(self._dio_std_model) - self.qspi_sd3 = self.Port(self._dio_std_model) + self.qspi = self.Port(SpiController(self._dio_std_model), optional=True) # TODO actually QSPI + self.qspi_cs = self.Port(self._dio_std_model, optional=True) + self.qspi_sd2 = self.Port(self._dio_std_model, optional=True) + self.qspi_sd3 = self.Port(self._dio_std_model, optional=True) + self.require( + (~self._model).implies( + self.qspi.is_connected() + & self.qspi_cs.is_connected() + & self.qspi_sd2.is_connected() + & self.qspi_sd3.is_connected() + ), + "SPI memory required", + ) self.xosc = self.Port( CrystalDriver( @@ -119,7 +130,7 @@ def __init__(self, **kwargs: Any) -> None: optional=True, ) - self.swd = self.Port(SwdTargetPort.empty()) + self.swd = self.Port(SwdTargetPort.empty(), optional=True) self.run = self.Port(DigitalSink.from_bidir(self._dio_ft_model), optional=True) # internally pulled up self._io_ports.insert(0, self.swd) @@ -338,7 +349,7 @@ def contents(self) -> None: self.connect(self.ic.qspi_sd2, mem_qspi.io2) self.connect(self.ic.qspi_sd3, mem_qspi.io3) - self.connect(self.pwr, self.ic.vreg_vin, self.ic.adc_avdd, self.ic.usb_vdd) + self.connect(self.pwr, self.ic.iovdd, self.ic.vreg_vin, self.ic.adc_avdd, self.ic.usb_vdd) self.connect(self.ic.vreg_vout, self.ic.dvdd) self.dvdd_cap = ElementDict[DecouplingCapacitor]() @@ -367,7 +378,9 @@ def _crystal_required(self) -> bool: # crystal needed for USB b/c tighter freq return len(self.get(self.usb.requested())) > 0 or super()._crystal_required() -class Xiao_Rp2040_Device(Rp2040_Interfaces, IoControllerWrapped, InternalSubcircuit, GeneratorBlock, FootprintBlock): +class Xiao_Rp2040_Device( + Rp2040_Interfaces, BaseIoControllerWrapped, InternalSubcircuit, GeneratorBlock, FootprintBlock +): """Footprint-only device model for the Xiao RP2040 microcontroller dev board""" _PIN_REMAPPING = { @@ -384,44 +397,29 @@ class Xiao_Rp2040_Device(Rp2040_Interfaces, IoControllerWrapped, InternalSubcirc "GPIO3": "11", } - def __init__(self, model_pin_assigns: ArrayStringLike): - super().__init__() - self.model_pin_assigns = self.ArgParameter(model_pin_assigns) + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) self.gnd = self.Port(Ground.empty(), optional=True) - self.v3v3 = self.Port(VoltageSink.empty(), optional=True) - self.v3v3_out = self.Port(VoltageSource.empty(), optional=True) - self.vcc = self.Port(VoltageSink.empty(), optional=True) # VUsb - self.vcc_out = self.Port(VoltageSource.empty(), optional=True) - self.generator_param(self.v3v3.is_connected(), self.vcc.is_connected(), self.model_pin_assigns) - - # TODO MOVE TO INFRASTRUCTURE - 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}") + self.v3v3 = self.Port(Passive.empty(), optional=True) + self.vcc = self.Port(Passive.empty(), optional=True) # VUsb + self.generator_param(self.pin_assigns) + self._generator_param_all_ios() @override def generate(self) -> None: super().generate() - pinning: Dict[str, HasPassivePort] = { - "12": self.v3v3 if self.get(self.v3v3.is_connected()) else self.v3v3_out, - "13": self.gnd, - "14": self.vcc if self.get(self.vcc.is_connected()) else self.vcc_out, # VUsb - } - remap_pinnings, remap_pin_assigns = self._remap_pinning_assigns( - self.get(self.model_pin_assigns), self._PIN_REMAPPING - ) - pinning.update(remap_pinnings) - self.assign(self.actual_pin_assigns, self._remap_assigns_to_value(remap_pin_assigns)) - self.footprint( "U", "Seeed Studio XIAO Series Library:XIAO-RP2040-SMD", - pinning, + self._make_pinning( + { + "12": self.v3v3, + "13": self.gnd, + "14": self.vcc, # VUsb + }, + self._PIN_REMAPPING, + ), mfr="", part="XIAO RP2040", datasheet="https://www.seeedstudio.com/XIAO-RP2040-v1-0-p-5026.html", @@ -432,6 +430,8 @@ class Xiao_Rp2040( IoControllerUsbOut, IoControllerPowerOut, IoControllerVin, + Rp2040_Interfaces, + BaseIoControllerWrapper, IoController, GeneratorBlock, WrapperSubboardBlock, @@ -439,8 +439,6 @@ class Xiao_Rp2040( """RP2040 development board, a tiny development (21x17.5mm) daughterboard. Has an onboard USB connector, so this can also source power. - Limited pins (only 11 for IOs, of which 6 are usable as the other 5 have boot requirements). - Requires Seeed Studio's KiCad library for the footprint: https://github.com/Seeed-Studio/OPL_Kicad_Library The 'Seeed Studio XIAO Series Library' must have been added as a footprint library of the same name. @@ -451,16 +449,18 @@ class Xiao_Rp2040( def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) self.generator_param( + self.pin_assigns, self.gnd.is_connected(), self.pwr.is_connected(), self.pwr_out.is_connected(), self.pwr_vin.is_connected(), self.vusb_out.is_connected(), ) + self._generator_param_all_ios() @override - def contents(self) -> None: - super().contents() + def generate(self) -> None: + super().generate() self.pwr_vin.init_from( VoltageSink( # based on RS3236-3.3 @@ -492,21 +492,23 @@ def contents(self) -> None: "ground required if power used", ) - self.model = self.Block(Rp2040(pin_assigns=ArrayStringExpr())) - model_pin_assigns = self._export_ios_inner(self.model) - self.assign(self.model.pin_assigns, model_pin_assigns) + self.model = self.Block( + Rp2040_Device( + pin_assigns=self._make_model_pinning(Xiao_Rp2040_Device._PIN_REMAPPING, self.get(self.pin_assigns)), + _model=True, + ) + ) + self._export_ios_inner(self.model) - self.device = self.Block(Xiao_Rp2040_Device(model_pin_assigns=self.model.actual_pin_assigns), external=True) + self.device = self.Block(Xiao_Rp2040_Device(pin_assigns=self.model.actual_pin_assigns), external=True) self._export_tap_ios_inner(self.device) self.assign(self.actual_pin_assigns, self.device.actual_pin_assigns) - @override - def generate(self) -> None: - super().generate() - + self.connect(self.model.vreg_vout, self.model.dvdd) + model_pwr = self.connect(self.model.iovdd, self.model.vreg_vin, self.model.adc_avdd, self.model.usb_vdd) if self.get(self.pwr.is_connected()): # power supplied externally - self.connect(self.pwr, self.model.pwr) - self.export_tap(self.pwr, self.device.v3v3) + self.connect(self.pwr, model_pwr) + self.export_tap(self.pwr.net, self.device.v3v3) else: # board sources power from USB self.pwr_out_model = self.Block( DummyVoltageSource( @@ -514,15 +516,15 @@ def generate(self) -> None: current_limits=UsbConnector.USB2_CURRENT_LIMITS, ) ) - self.connect(self.pwr_out_model.pwr, self.model.pwr) + self.connect(self.pwr_out_model.pwr, model_pwr) if self.get(self.pwr_out.is_connected()): self.connect(self.pwr_out, self.pwr_out_model.pwr) - self.export_tap(self.pwr_out, self.device.v3v3_out) + self.export_tap(self.pwr_out.net, self.device.v3v3) if self.get(self.pwr_vin.is_connected()): - self.export_tap(self.pwr_vin, self.device.vcc) + self.export_tap(self.pwr_vin.net, self.device.vcc) if self.get(self.vusb_out.is_connected()): - self.export_tap(self.vusb_out, self.device.vcc_out) + self.export_tap(self.vusb_out.net, self.device.vcc) self.export_tap(self.gnd, self.device.gnd) if self.get(self.gnd.is_connected()): diff --git a/edg/parts/microcontroller/Stm32f103.py b/edg/parts/microcontroller/Stm32f103.py index c9eb5b7e3..dca5196df 100644 --- a/edg/parts/microcontroller/Stm32f103.py +++ b/edg/parts/microcontroller/Stm32f103.py @@ -294,7 +294,6 @@ class Stm32f103Base( IoControllerWithSwdTargetConnector, WithCrystalGenerator, IoControllerPowerRequired, - BaseIoControllerExportable, GeneratorBlock, ): DEVICE: Type[Stm32f103Base_Device] = Stm32f103Base_Device @@ -302,8 +301,13 @@ class Stm32f103Base( def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) - self.ic: Stm32f103Base_Device - self.generator_param(self.reset.is_connected()) + self.generator_param( + self.reset.is_connected(), + self.pin_assigns, + self.gpio.requested(), + self.can.requested(), + self.usb.requested(), + ) @override def contents(self) -> None: @@ -329,24 +333,19 @@ def contents(self) -> None: def generate(self) -> None: super().generate() - if self.get(self.reset.is_connected()): - self.connect(self.reset, self.ic.nrst) - - ExportType = TypeVar("ExportType", bound=Port) - - @override - def _make_export_vector( - self, self_io: ExportType, inner_vector: Vector[ExportType], name: str, assign: Optional[str] - ) -> Optional[str]: - if isinstance(self_io, UsbDevicePort): # assumed at most one USB port generates - inner_io = inner_vector.request(name) + def usb_export_transform(self_io: BasePort, assign: Optional[str]) -> Optional[BasePort]: self.usb_pull = self.Block( UsbDpPullUp(resistance=1.5 * kOhm(tol=0.01)) ) # required by datasheet Table 44 # TODO proper tolerancing? self.connect(self.usb_pull.pwr, self.pwr) - self.connect(inner_io, self_io, self.usb_pull.usb) - return assign - return super()._make_export_vector(self_io, inner_vector, name, assign) + self.connect(self_io, self.usb_pull.usb) + return self_io + + # add a passthrough for gpio (DigitalBidir) to allow the SWD pins to be attached, if using + self._wrap_inner(self.ic, {UsbDevicePort: usb_export_transform, DigitalBidir: lambda port, assign: port}) + + if self.get(self.reset.is_connected()): + self.connect(self.reset, self.ic.nrst) @override def _crystal_required(self) -> bool: # crystal needed for CAN or USB b/c tighter freq tolerance diff --git a/edg/parts/microcontroller/Stm32g031.py b/edg/parts/microcontroller/Stm32g031.py index 441f25f69..d3d4c948e 100644 --- a/edg/parts/microcontroller/Stm32g031.py +++ b/edg/parts/microcontroller/Stm32g031.py @@ -240,7 +240,6 @@ class Stm32g031Base( Microcontroller, IoControllerWithSwdTargetConnector, IoControllerPowerRequired, - BaseIoControllerExportable, GeneratorBlock, ): DEVICE: Type[Stm32g031Base_Device] = Stm32g031Base_Device @@ -248,7 +247,7 @@ class Stm32g031Base( def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) self.ic: Stm32g031Base_Device - self.generator_param(self.reset.is_connected()) + self.generator_param(self.reset.is_connected(), self.pin_assigns, self.gpio.requested()) @override def contents(self) -> None: @@ -267,6 +266,9 @@ def contents(self) -> None: def generate(self) -> None: super().generate() + # add a passthrough for gpio (DigitalBidir) to allow the SWD pins to be attached, if using + self._wrap_inner(self.ic, {DigitalBidir: lambda port, assign: port}) + if self.get(self.reset.is_connected()): self.connect(self.reset, self.ic.nrst) # otherwise NRST has internal pull-up diff --git a/edg/parts/microcontroller/Stm32g431.py b/edg/parts/microcontroller/Stm32g431.py index 00b99edb3..3f9c48da5 100644 --- a/edg/parts/microcontroller/Stm32g431.py +++ b/edg/parts/microcontroller/Stm32g431.py @@ -267,15 +267,13 @@ class Stm32g431Base( Microcontroller, IoControllerWithSwdTargetConnector, IoControllerPowerRequired, - BaseIoControllerExportable, GeneratorBlock, ): DEVICE: Type[Stm32g431Base_Device] = Stm32g431Base_Device def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) - self.ic: Stm32g431Base_Device - self.generator_param(self.reset.is_connected()) + self.generator_param(self.reset.is_connected(), self.pin_assigns, self.gpio.requested()) @override def contents(self) -> None: @@ -296,6 +294,10 @@ def contents(self) -> None: @override def generate(self) -> None: super().generate() + + # add a passthrough for gpio (DigitalBidir) to allow the SWD pins to be attached, if using + self._wrap_inner(self.ic, {DigitalBidir: lambda port, assign: port}) + if self.get(self.reset.is_connected()): self.connect(self.reset, self.ic.nrst) # otherwise NRST has internal pull-up diff --git a/edg/parts/microcontroller/Stm32l432.py b/edg/parts/microcontroller/Stm32l432.py index 569b06e5e..90a7686d5 100644 --- a/edg/parts/microcontroller/Stm32l432.py +++ b/edg/parts/microcontroller/Stm32l432.py @@ -244,15 +244,13 @@ class Stm32l432Base( IoControllerWithSwdTargetConnector, WithCrystalGenerator, IoControllerPowerRequired, - BaseIoControllerExportable, GeneratorBlock, ): DEVICE: Type[Stm32l432Base_Device] = Stm32l432Base_Device def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) - self.ic: Stm32l432Base_Device - self.generator_param(self.reset.is_connected()) + self.generator_param(self.reset.is_connected(), self.pin_assigns, self.gpio.requested(), self.can.requested()) @override def contents(self) -> None: @@ -275,6 +273,9 @@ def contents(self) -> None: def generate(self) -> None: super().generate() + # add a passthrough for gpio (DigitalBidir) to allow the SWD pins to be attached, if using + self._wrap_inner(self.ic, {DigitalBidir: lambda port, assign: port}) + if self.get(self.reset.is_connected()): self.connect(self.reset, self.ic.nrst) # otherwise NRST has internal pull-up diff --git a/edg/parts/microcontroller/nRF52840.py b/edg/parts/microcontroller/nRF52840.py index 9adc4609e..06c3c21f6 100644 --- a/edg/parts/microcontroller/nRF52840.py +++ b/edg/parts/microcontroller/nRF52840.py @@ -14,45 +14,139 @@ class Nrf52840_Interfaces( """Defines base interfaces for nRF52840 microcontrollers""" -@non_library -class Nrf52840_Ios(Nrf52840_Interfaces, BaseIoControllerPinmapGenerator, GeneratorBlock, FootprintBlock): - """nRF52840 IO mappings - https://infocenter.nordicsemi.com/pdf/nRF52840_PS_v1.7.pdf""" +class Mdbt50q_1mv2_Device( + Nrf52840_Interfaces, BaseIoControllerPinmapGenerator, InternalSubcircuit, JlcPart, GeneratorBlock, FootprintBlock +): + # in the absence of a chip-level subcircuit, this is used as the authoritative base device model + # that other modules should wrap + + _PIN_MAPPING = { # boundary pins only, inner pins ignored + "P1.10": "3", + "P1.11": "4", + "P1.12": "5", + "P1.13": "6", + "P1.14": "7", + "P1.15": "8", + "P0.03": "9", + "P0.29": "10", + "P0.02": "11", + "P0.31": "12", + "P0.28": "13", + "P0.30": "14", + "P0.27": "16", + "P0.00": "17", + "P0.01": "18", + "P0.26": "19", + "P0.04": "20", + "P0.05": "21", + "P0.06": "22", + "P0.07": "23", + "P0.08": "24", + "P1.08": "25", + "P1.09": "26", + "P0.11": "27", + "P0.12": "29", + "D-": "34", + "D+": "35", + "P0.14": "36", + "P0.13": "37", + "P0.16": "38", + "P0.15": "39", + "P0.17": "41", + "P0.19": "42", + "P0.21": "43", + "P0.20": "44", + "P0.23": "45", + "P0.22": "46", + "P1.00": "47", + "P0.24": "48", + "P0.25": "49", + "P1.02": "50", + "SWDIO": "51", + "P0.09": "52", + "SWCLK": "53", + "P0.10": "54", + "P1.04": "56", + "P1.06": "57", + "P1.07": "58", + "P1.05": "59", + "P1.03": "60", + "P1.01": "61", + } - RESOURCE_PIN_REMAP: Dict[str, str] # resource name in base -> pin name + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) - @abstractmethod - def _vddio(self) -> Port[VoltageLink]: ... + self.gnd = self.Port(Ground(), [Common]) + self.pwr = self.Port( + VoltageSink( + voltage_limits=(1.75, 3.6) * Volt, # 1.75 minimum for power-on reset + current_draw=(0, 212 / 64 + 4.8) * mAmp + + self.io_current_draw.upper(), # CPU @ max 212 Coremarks + 4.8mA in RF transmit + ), + [Power], + ) - def _vdd_model(self) -> VoltageSink: - return VoltageSink( - voltage_limits=(1.75, 3.6) * Volt, # 1.75 minimum for power-on reset - current_draw=(0, 212 / 64 + 4.8) * mAmp - + self.io_current_draw.upper(), # CPU @ max 212 Coremarks + 4.8mA in RF transmit + self.pwr_usb = self.Port( + VoltageSink( + voltage_limits=(4.35, 5.5) * Volt, + current_draw=(0.262, 7.73) * mAmp, # CPU/USB sleeping to everything active + ), + optional=True, ) + self.require((self.usb.length() > 0).implies(self.pwr_usb.is_connected()), "USB require Vbus connected") - def _dio_model(self, pwr: Port[VoltageLink]) -> DigitalBidir: - return DigitalBidir.from_supply( + self._dio_model = DigitalBidir.from_supply( self.gnd, - pwr, + self.pwr, voltage_limit_tolerance=(-0.3, 0.3) * Volt, current_limits=(-6, 6) * mAmp, # minimum current, high drive, Vdd>2.7 input_threshold_factor=(0.3, 0.7), pullup_capable=True, pulldown_capable=True, ) + self._dio_lf_model = self._dio_model # "standard drive, low frequency IO only" (differences not modeled) + self.swd = self.Port(SwdTargetPort.empty(), optional=True) + self.nreset = self.Port(DigitalSink.from_bidir(self._dio_model), optional=True) + self._io_ports.insert(0, self.swd) + + @override + def generate(self) -> None: + super().generate() + + self.assign(self.lcsc_part, "C5118826") + self.assign(self.actual_basic_part, False) + self.footprint( + "U", + "RF_Module:Raytac_MDBT50Q", + self._make_pinning(), + mfr="Raytac", + part="MDBT50Q-1MV2", + datasheet="https://www.raytac.com/download/index.php?index_id=43", + ) + + @override + def _system_pinmap(self) -> Dict[str, Union[Passive, HasPassivePort]]: + return { + "28": self.pwr, # Vdd + "30": self.pwr, # VddH + # "31": DccH is disconnected - from section 8.3 for input voltage <3.6v + "1": self.gnd, + "2": self.gnd, + "15": self.gnd, + "33": self.gnd, + "55": self.gnd, + "32": self.pwr_usb, + "40": self.nreset, + } @override def _io_pinmap(self) -> PinMapUtil: """Returns the mappable for given the input power and ground references. This separates the system pins definition from the IO pins definition.""" - pwr = self._vddio() - dio_model = self._dio_model(pwr) - dio_lf_model = dio_model # "standard drive, low frequency IO only" (differences not modeled) - adc_model = AnalogSink.from_supply( self.gnd, - pwr, + self.pwr, voltage_limit_tolerance=(0, 0), # datasheet 6.23.2, analog inputs cannot exceed Vdd or be lower than Vss signal_limit_tolerance=(0, 0), impedance=Range.from_lower(1) * MOhm, @@ -96,59 +190,59 @@ def _io_pinmap(self) -> PinMapUtil: return PinMapUtil( [ # Section 7.1.2 with QIAA aQFN73 & QFAA QFN48 pins only - PinResource("P0.31", {"P0.31": dio_lf_model, "AIN7": adc_model}), - PinResource("P0.29", {"P0.29": dio_lf_model, "AIN5": adc_model}), - PinResource("P0.02", {"P0.02": dio_lf_model, "AIN0": adc_model}), - PinResource("P1.15", {"P1.15": dio_lf_model}), - PinResource("P1.13", {"P1.13": dio_lf_model}), - PinResource("P1.10", {"P1.10": dio_lf_model}), - PinResource("P0.30", {"P0.30": dio_lf_model, "AIN6": adc_model}), - PinResource("P0.28", {"P0.28": dio_lf_model, "AIN4": adc_model}), - PinResource("P0.03", {"P0.03": dio_lf_model, "AIN1": adc_model}), - PinResource("P1.14", {"P1.14": dio_lf_model}), - PinResource("P1.12", {"P1.12": dio_lf_model}), - PinResource("P1.11", {"P1.11": dio_lf_model}), - PinResource("P0.00", {"P0.00": dio_model}), # TODO also 32.768 kHz crystal in - PinResource("P0.01", {"P0.01": dio_model}), # TODO also 32.768 kHz crystal in - PinResource("P0.26", {"P0.26": dio_model}), - PinResource("P0.27", {"P0.27": dio_model}), - PinResource("P0.04", {"P0.04": dio_model, "AIN2": adc_model}), - PinResource("P0.10", {"P0.10": dio_lf_model}), # TODO also NFC2 - PinResource("P0.05", {"P0.05": dio_model, "AIN3": adc_model}), - PinResource("P0.06", {"P0.06": dio_model}), - PinResource("P0.09", {"P0.09": dio_lf_model}), # TODO also NFC1 - PinResource("P0.07", {"P0.07": dio_model}), - PinResource("P0.08", {"P0.08": dio_model}), - PinResource("P1.08", {"P1.08": dio_model}), - PinResource("P1.07", {"P1.07": dio_lf_model}), - PinResource("P1.09", {"P1.09": dio_model}), - PinResource("P1.06", {"P1.06": dio_lf_model}), - PinResource("P0.11", {"P0.11": dio_model}), - PinResource("P1.05", {"P1.05": dio_lf_model}), - PinResource("P0.12", {"P0.12": dio_model}), - PinResource("P1.04", {"P1.04": dio_lf_model}), - PinResource("P1.03", {"P1.03": dio_lf_model}), - PinResource("P1.02", {"P1.02": dio_lf_model}), - PinResource("P1.01", {"P1.01": dio_lf_model}), - PinResource("P0.14", {"P0.14": dio_model}), - PinResource("P0.16", {"P0.16": dio_model}), + PinResource("P0.31", {"P0.31": self._dio_lf_model, "AIN7": adc_model}), + PinResource("P0.29", {"P0.29": self._dio_lf_model, "AIN5": adc_model}), + PinResource("P0.02", {"P0.02": self._dio_lf_model, "AIN0": adc_model}), + PinResource("P1.15", {"P1.15": self._dio_lf_model}), + PinResource("P1.13", {"P1.13": self._dio_lf_model}), + PinResource("P1.10", {"P1.10": self._dio_lf_model}), + PinResource("P0.30", {"P0.30": self._dio_lf_model, "AIN6": adc_model}), + PinResource("P0.28", {"P0.28": self._dio_lf_model, "AIN4": adc_model}), + PinResource("P0.03", {"P0.03": self._dio_lf_model, "AIN1": adc_model}), + PinResource("P1.14", {"P1.14": self._dio_lf_model}), + PinResource("P1.12", {"P1.12": self._dio_lf_model}), + PinResource("P1.11", {"P1.11": self._dio_lf_model}), + PinResource("P0.00", {"P0.00": self._dio_model}), # TODO also 32.768 kHz crystal in + PinResource("P0.01", {"P0.01": self._dio_model}), # TODO also 32.768 kHz crystal in + PinResource("P0.26", {"P0.26": self._dio_model}), + PinResource("P0.27", {"P0.27": self._dio_model}), + PinResource("P0.04", {"P0.04": self._dio_model, "AIN2": adc_model}), + PinResource("P0.10", {"P0.10": self._dio_lf_model}), # TODO also NFC2 + PinResource("P0.05", {"P0.05": self._dio_model, "AIN3": adc_model}), + PinResource("P0.06", {"P0.06": self._dio_model}), + PinResource("P0.09", {"P0.09": self._dio_lf_model}), # TODO also NFC1 + PinResource("P0.07", {"P0.07": self._dio_model}), + PinResource("P0.08", {"P0.08": self._dio_model}), + PinResource("P1.08", {"P1.08": self._dio_model}), + PinResource("P1.07", {"P1.07": self._dio_lf_model}), + PinResource("P1.09", {"P1.09": self._dio_model}), + PinResource("P1.06", {"P1.06": self._dio_lf_model}), + PinResource("P0.11", {"P0.11": self._dio_model}), + PinResource("P1.05", {"P1.05": self._dio_lf_model}), + PinResource("P0.12", {"P0.12": self._dio_model}), + PinResource("P1.04", {"P1.04": self._dio_lf_model}), + PinResource("P1.03", {"P1.03": self._dio_lf_model}), + PinResource("P1.02", {"P1.02": self._dio_lf_model}), + PinResource("P1.01", {"P1.01": self._dio_lf_model}), + PinResource("P0.14", {"P0.14": self._dio_model}), + PinResource("P0.16", {"P0.16": self._dio_model}), # PinResource('P0.18', {'P0.18': dio_model}), # configurable as RESET, mappable - PinResource("P0.19", {"P0.19": dio_model}), - PinResource("P0.21", {"P0.21": dio_model}), - PinResource("P0.23", {"P0.23": dio_model}), - PinResource("P0.25", {"P0.25": dio_model}), - PinResource("P0.13", {"P0.13": dio_model}), - PinResource("P0.15", {"P0.15": dio_model}), - PinResource("P0.17", {"P0.17": dio_model}), - PinResource("P0.20", {"P0.20": dio_model}), - PinResource("P0.22", {"P0.22": dio_model}), - PinResource("P0.24", {"P0.24": dio_model}), + PinResource("P0.19", {"P0.19": self._dio_model}), + PinResource("P0.21", {"P0.21": self._dio_model}), + PinResource("P0.23", {"P0.23": self._dio_model}), + PinResource("P0.25", {"P0.25": self._dio_model}), + PinResource("P0.13", {"P0.13": self._dio_model}), + PinResource("P0.15", {"P0.15": self._dio_model}), + PinResource("P0.17", {"P0.17": self._dio_model}), + PinResource("P0.20", {"P0.20": self._dio_model}), + PinResource("P0.22", {"P0.22": self._dio_model}), + PinResource("P0.24", {"P0.24": self._dio_model}), PinResource( - "P1.00", {"P1.00": dio_model} + "P1.00", {"P1.00": self._dio_model} ), # TRACEDATA[0] and SWO, if used as IO must clear TRACECONFIG reg PeripheralFixedPin( "SWD", - SwdTargetPort(dio_model), + SwdTargetPort(self._dio_model), { "swclk": "SWCLK", "swdio": "SWDIO", @@ -276,67 +370,59 @@ def _io_pinmap(self) -> PinMapUtil: }, ), ] - ).remap_pins(self.RESOURCE_PIN_REMAP) + ).remap_pins(self._PIN_MAPPING) -@abstract_block -class Nrf52840_Base(Nrf52840_Ios, GeneratorBlock): - SYSTEM_PIN_REMAP: Dict[str, Union[str, List[str]]] # pin name in base -> pin name(s) +class Mdbt50q_1mv2( + Microcontroller, + Radiofrequency, + Resettable, + Nrf52840_Interfaces, + IoControllerWithSwdTargetConnector, + IoControllerPowerRequired, + GeneratorBlock, +): + """Wrapper around the Mdbt50q_1mv2 that includes the reference schematic.""" - @override - def _vddio(self) -> Port[VoltageLink]: - return self.pwr + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.ic = self.Block(Mdbt50q_1mv2_Device(pin_assigns=ArrayStringExpr())) + self.pwr_usb = self.Export(self.ic.pwr_usb, optional=True) + self.generator_param(self.reset.is_connected(), self.pin_assigns, self.gpio.requested(), self.usb.requested()) @override - def _system_pinmap(self) -> Dict[str, Union[Passive, HasPassivePort]]: - return VariantPinRemapper( - { - "Vdd": self.pwr, - "Vss": self.gnd, - "Vbus": self.pwr_usb, - "nRESET": self.nreset, - } - ).remap(self.SYSTEM_PIN_REMAP) + def contents(self) -> None: + super().contents() + self.connect(self.pwr, self.ic.pwr) + self.connect(self.gnd, self.ic.gnd) - def __init__(self, **kwargs: Any) -> None: - super().__init__(**kwargs) + self.connect(self.swd_node, self.ic.swd) + self.connect(self.reset_node, self.ic.nreset) - self.gnd = self.Port(Ground(), [Common]) - self.pwr = self.Port(self._vdd_model(), [Power]) + with self.implicit_connect(ImplicitConnect(self.pwr, [Power]), ImplicitConnect(self.gnd, [Common])) as imp: + self.vcc_cap = imp.Block(DecouplingCapacitor(10 * uFarad(tol=0.2))) - self.pwr_usb = self.Port( - VoltageSink( - voltage_limits=(4.35, 5.5) * Volt, - current_draw=(0.262, 7.73) * mAmp, # CPU/USB sleeping to everything active - ), - optional=True, - ) - self.require((self.usb.length() > 0).implies(self.pwr_usb.is_connected()), "USB require Vbus connected") + @override + def generate(self) -> None: + super().generate() - # Additional ports (on top of IoController) - # Crystals from table 15, 32, 33 - # TODO Table 32, model crystal load capacitance and series resistance ratings - self.xtal = self.Port( - CrystalDriver(frequency_limits=(1, 25) * MHertz, voltage_out=self.pwr.link().voltage), optional=True - ) - # Assumed from "32kHz crystal" in 14.5 - self.xtal_rtc = self.Port( - CrystalDriver(frequency_limits=(32, 33) * kHertz, voltage_out=self.pwr.link().voltage), optional=True - ) + def usb_export_transform(self_io: BasePort, assign: Optional[str]) -> Optional[BasePort]: + self.vbus_cap = self.Block(DecouplingCapacitor(10 * uFarad(tol=0.2))).connected(self.gnd, self.pwr_usb) + self.usb_res = self.Block(UsbSeriesResistor(27 * Ohm(tol=0.05))) + self.connect(self_io, self.usb_res.exterior) + return self.usb_res.interior - self.swd = self.Port(SwdTargetPort.empty()) - self.nreset = self.Port(DigitalSink.from_bidir(self._dio_model(self.pwr)), optional=True) - self._io_ports.insert(0, self.swd) + # add a passthrough for gpio (DigitalBidir) to allow the SWD pins to be attached, if using + self._wrap_inner(self.ic, {UsbDevicePort: usb_export_transform, DigitalBidir: lambda port, assign: port}) + if self.get(self.reset.is_connected()): + self.connect(self.reset, self.ic.nreset) -class Holyiot_18010_Device(Nrf52840_Base, InternalSubcircuit): - SYSTEM_PIN_REMAP: Dict[str, Union[str, List[str]]] = { - "Vdd": "14", - "Vss": ["1", "25", "37"], - "Vbus": "22", - "nRESET": "21", - } - RESOURCE_PIN_REMAP = { # boundary pins only, inner pins ignored + +class Holyiot_18010_Footprint( + Nrf52840_Interfaces, BaseIoControllerWrapped, InternalSubcircuit, GeneratorBlock, FootprintBlock +): + _PIN_REMAPPING = { # boundary pins only, inner pins ignored "P1.11": "2", "P1.10": "3", "P1.13": "4", @@ -370,6 +456,17 @@ class Holyiot_18010_Device(Nrf52840_Base, InternalSubcircuit): "P0.10": "36", } + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.gnd = self.Port(Ground.empty()) + self.vdd_nrf = self.Port(VoltageSink.empty(), optional=True) + self.vbus = self.Port(VoltageSink.empty(), optional=True) + self.p0_18 = self.Port(DigitalSink.empty(), optional=True) # nRESET + self.swd = self.Port(SwdTargetPort.empty()) + self._io_ports.insert(0, self.swd) + self.generator_param(self.pin_assigns) + self._generator_param_all_ios() + @override def generate(self) -> None: super().generate() @@ -377,154 +474,94 @@ def generate(self) -> None: self.footprint( "U", "edg:Holyiot-18010-NRF52840", - self._make_pinning(), + self._make_pinning( + { + "14": self.vdd_nrf, + "1": self.gnd, + "25": self.gnd, + "37": self.gnd, + "22": self.vbus, + "21": self.p0_18, + }, + self._PIN_REMAPPING, + ), mfr="Holyiot", part="18010", datasheet="http://www.holyiot.com/tp/2019042516322180424.pdf", ) -class Holyiot_18010( - Microcontroller, - Radiofrequency, - Resettable, +class Holyiot_18010_Device( Nrf52840_Interfaces, - IoControllerWithSwdTargetConnector, - IoControllerPowerRequired, - BaseIoControllerExportable, + BaseIoControllerWrapper, + InternalSubcircuit, GeneratorBlock, + WrapperSubboardBlock, ): - """Wrapper around the Holyiot 18010 that includes supporting components (programming port)""" def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) - self.ic: Holyiot_18010_Device - self.ic = self.Block(Holyiot_18010_Device(pin_assigns=ArrayStringExpr())) - self.pwr_usb = self.Export(self.ic.pwr_usb, optional=True) - self.generator_param(self.reset.is_connected()) - @override - def contents(self) -> None: - super().contents() - self.connect(self.pwr, self.ic.pwr) - self.connect(self.gnd, self.ic.gnd) - - self.connect(self.swd_node, self.ic.swd) - self.connect(self.reset_node, self.ic.nreset) + self.model = self.Block(Mdbt50q_1mv2_Device(pin_assigns=ArrayStringExpr())) + self.gnd = self.Export(self.model.gnd) + self.pwr = self.Export(self.model.pwr) + self.pwr_usb = self.Export(self.model.pwr_usb, optional=True) + self.reset = self.Export(self.model.nreset, optional=True) + self.swd = self.Export(self.model.swd, optional=True) + self.generator_param(self.pin_assigns) + self._generator_param_all_ios() @override def generate(self) -> None: super().generate() - if self.get(self.reset.is_connected()): - self.connect(self.reset, self.ic.nreset) - -class Mdbt50q_1mv2_Device(Nrf52840_Base, InternalSubcircuit, JlcPart): - SYSTEM_PIN_REMAP: Dict[str, Union[str, List[str]]] = { - "Vdd": ["28", "30"], # 28=Vdd, 30=VddH; 31=DccH is disconnected - from section 8.3 for input voltage <3.6v - "Vss": ["1", "2", "15", "33", "55"], - "Vbus": "32", - "nRESET": "40", - } - RESOURCE_PIN_REMAP = { # boundary pins only, inner pins ignored - "P1.10": "3", - "P1.11": "4", - "P1.12": "5", - "P1.13": "6", - "P1.14": "7", - "P1.15": "8", - "P0.03": "9", - "P0.29": "10", - "P0.02": "11", - "P0.31": "12", - "P0.28": "13", - "P0.30": "14", - "P0.27": "16", - "P0.00": "17", - "P0.01": "18", - "P0.26": "19", - "P0.04": "20", - "P0.05": "21", - "P0.06": "22", - "P0.07": "23", - "P0.08": "24", - "P1.08": "25", - "P1.09": "26", - "P0.11": "27", - "P0.12": "29", - "D-": "34", - "D+": "35", - "P0.14": "36", - "P0.13": "37", - "P0.16": "38", - "P0.15": "39", - "P0.17": "41", - "P0.19": "42", - "P0.21": "43", - "P0.20": "44", - "P0.23": "45", - "P0.22": "46", - "P1.00": "47", - "P0.24": "48", - "P0.25": "49", - "P1.02": "50", - "SWDIO": "51", - "P0.09": "52", - "SWCLK": "53", - "P0.10": "54", - "P1.04": "56", - "P1.06": "57", - "P1.07": "58", - "P1.05": "59", - "P1.03": "60", - "P1.01": "61", - } - - @override - def generate(self) -> None: - super().generate() - - self.assign(self.lcsc_part, "C5118826") - self.assign(self.actual_basic_part, False) - self.footprint( - "U", - "RF_Module:Raytac_MDBT50Q", - self._make_pinning(), - mfr="Raytac", - part="MDBT50Q-1MV2", - datasheet="https://www.raytac.com/download/index.php?index_id=43", + self._export_ios_inner(self.model) + self.assign( + self.model.pin_assigns, + self._make_model_pinning(Holyiot_18010_Footprint._PIN_REMAPPING, self.get(self.pin_assigns)), ) + self.device = self.Block(Holyiot_18010_Footprint(pin_assigns=self.model.actual_pin_assigns)) + self.assign(self.actual_pin_assigns, self.device.actual_pin_assigns) + self._export_tap_ios_inner(self.device) + self.export_tap(self.gnd, self.device.gnd) + self.export_tap(self.pwr, self.device.vdd_nrf) + self.export_tap(self.pwr_usb, self.device.vbus) + self.export_tap(self.reset, self.device.p0_18) + self.export_tap(self.swd, self.device.swd) -class Mdbt50q_1mv2( + +class Holyiot_18010( Microcontroller, Radiofrequency, Resettable, Nrf52840_Interfaces, - IoControllerWithSwdTargetConnector, IoControllerPowerRequired, - BaseIoControllerExportable, + IoControllerWithSwdTargetConnector, GeneratorBlock, ): - """Wrapper around the Mdbt50q_1mv2 that includes the reference schematic""" + """Wrapper around the Holyiot 18010 that includes supporting components (programming port)""" - def __init__(self, **kwargs: Any) -> None: + def __init__( + self, + **kwargs: Any, + ) -> None: super().__init__(**kwargs) - self.ic: Mdbt50q_1mv2_Device - self.ic = self.Block( - Mdbt50q_1mv2_Device(pin_assigns=ArrayStringExpr()) - ) # defined in generator to mix in SWO/TDI + + self.ic = self.Block(Holyiot_18010_Device(pin_assigns=ArrayStringExpr())) self.pwr_usb = self.Export(self.ic.pwr_usb, optional=True) - self.generator_param(self.reset.is_connected()) + + self.generator_param(self.reset.is_connected(), self.pin_assigns, self.gpio.requested(), self.usb.requested()) @override def contents(self) -> None: super().contents() - self.connect(self.pwr, self.ic.pwr) + self.connect(self.gnd, self.ic.gnd) + self.connect(self.pwr, self.ic.pwr) self.connect(self.swd_node, self.ic.swd) - self.connect(self.reset_node, self.ic.nreset) + self.connect(self.reset_node, self.ic.reset) with self.implicit_connect(ImplicitConnect(self.pwr, [Power]), ImplicitConnect(self.gnd, [Common])) as imp: self.vcc_cap = imp.Block(DecouplingCapacitor(10 * uFarad(tol=0.2))) @@ -533,39 +570,22 @@ def contents(self) -> None: def generate(self) -> None: super().generate() - if self.get(self.reset.is_connected()): - self.connect(self.reset, self.ic.nreset) + def usb_export_transform(self_io: BasePort, assign: Optional[str]) -> Optional[BasePort]: + self.vbus_cap = self.Block(DecouplingCapacitor(10 * uFarad(tol=0.2))).connected(self.gnd, self.pwr_usb) + self.usb_res = self.Block(UsbSeriesResistor(27 * Ohm(tol=0.05))) + self.connect(self_io, self.usb_res.exterior) + return self.usb_res.interior - ExportType = TypeVar("ExportType", bound=Port) + # add a passthrough for gpio (DigitalBidir) to allow the SWD pins to be attached, if using + self._wrap_inner(self.ic, {UsbDevicePort: usb_export_transform, DigitalBidir: lambda port, assign: port}) - @override - def _make_export_vector( - self, self_io: ExportType, inner_vector: Vector[ExportType], name: str, assign: Optional[str] - ) -> Optional[str]: - if isinstance(self_io, UsbDevicePort): # assumed at most one USB port generates - inner_io = inner_vector.request(name) - (self.usb_res,), self.usb_chain = self.chain( - inner_io, self.Block(UsbSeriesResistor(27 * Ohm(tol=0.05))), self_io - ) - self.vbus_cap = self.Block(DecouplingCapacitor(10 * uFarad(tol=0.2))).connected(self.gnd, self.pwr_usb) - return assign - return super()._make_export_vector(self_io, inner_vector, name, assign) + if self.get(self.reset.is_connected()): + self.connect(self.reset, self.ic.reset) -class Feather_Nrf52840( - IoControllerUsbOut, IoControllerPowerOut, Nrf52840_Ios, IoController, GeneratorBlock, FootprintBlock -): - """Feather nRF52840 socketed dev board as either power source or sink""" +class Feather_Nrf52840_Device(Nrf52840_Interfaces, BaseIoControllerWrapped, GeneratorBlock, FootprintBlock): - SYSTEM_PIN_REMAP: Dict[str, Union[str, List[str]]] = { - "Vdd": "2", # 3v3 - "Vss": "4", - # 'reset': '1', - "Vbus": "26", - # 'EN': '27', # controls the onboard 3.3 LDO, internally pulled up - # 'Vbat': '28', - } - RESOURCE_PIN_REMAP = { # boundary pins only, inner pins ignored + _PIN_REMAPPING = { # boundary pins only, inner pins ignored "P0.31": "3", # AREF "P0.04": "5", # A0 "P0.05": "6", # A1 @@ -594,53 +614,15 @@ class Feather_Nrf52840( # note onboard VBAT sense divider at P0.29 } - @override - def _vddio(self) -> Port[VoltageLink]: - if self.get(self.pwr.is_connected()): # board sinks power - return self.pwr - else: - return self.pwr_out - - @override - def _system_pinmap(self) -> Dict[str, Union[Passive, HasPassivePort]]: - if self.get(self.pwr.is_connected()): # board sinks power - self.require(~self.vusb_out.is_connected(), "can't source USB power if power input connected") - self.require(~self.pwr_out.is_connected(), "can't source 3v3 power if power input connected") - return VariantPinRemapper( - { - "Vdd": self.pwr, - "Vss": self.gnd, - } - ).remap(self.SYSTEM_PIN_REMAP) - else: # board sources power (default) - return VariantPinRemapper( - { - "Vdd": self.pwr_out, - "Vss": self.gnd, - "Vbus": self.vusb_out, - } - ).remap(self.SYSTEM_PIN_REMAP) - - @override - def contents(self) -> None: - super().contents() - - self.gnd.init_from(Ground()) - self.pwr.init_from(self._vdd_model()) - - mbr120_drop = (0, 0.340) * Volt - ap2112_3v3_out = 3.3 * Volt(tol=0.015) # note dropout voltage up to 400mV, current up to 600mA - self.vusb_out.init_from( - VoltageSource( - voltage_out=UsbConnector.USB2_VOLTAGE_RANGE - mbr120_drop, - current_limits=UsbConnector.USB2_CURRENT_LIMITS, - ) - ) - self.pwr_out.init_from( - VoltageSource(voltage_out=ap2112_3v3_out, current_limits=UsbConnector.USB2_CURRENT_LIMITS) - ) + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.gnd = self.Port(Ground.empty(), optional=True) + # power ports are passive so directionality and concrete types can be resolved at the higher modeling level + self.pwr = self.Port(Passive.empty(), optional=True) + self.vusb = self.Port(Passive.empty(), optional=True) - self.generator_param(self.pwr.is_connected()) + self.generator_param(self.pin_assigns) + self._generator_param_all_ios() @override def generate(self) -> None: @@ -649,8 +631,97 @@ def generate(self) -> None: self.footprint( "U", "bldc:FEATHERWING_NODIM", - self._make_pinning(), + self._make_pinning( + { + "2": self.pwr, + "4": self.gnd, + # "1": reset, + "26": self.vusb, + # 'EN': '27', # controls the onboard 3.3 LDO, internally pulled up + # 'Vbat': '28', + }, + self._PIN_REMAPPING, + ), mfr="Adafruit", part="Feather nRF52840 Express", datasheet="https://learn.adafruit.com/assets/68545", ) + + +class Feather_Nrf52840( + IoControllerUsbOut, + IoControllerPowerOut, + Nrf52840_Interfaces, + BaseIoControllerWrapper, + IoController, + WrapperSubboardBlock, + GeneratorBlock, + FootprintBlock, +): + """Feather nRF52840 socketed dev board as either power source or sink""" + + _MBR120_DROP = (0, 0.340) * Volt + _AP2112_3V3_OUT = 3.3 * Volt(tol=0.015) # note dropout voltage up to 400mV, current up to 600mA + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.generator_param( + self.pin_assigns, + self.gnd.is_connected(), + self.pwr.is_connected(), + self.pwr_out.is_connected(), + self.vusb_out.is_connected(), + ) + self._generator_param_all_ios() + + @override + def generate(self) -> None: + super().generate() + + self.require( + self.pwr.is_connected().implies(~self.vusb_out.is_connected()), + "can't source USB power if power input connected", + ) + self.require( + self.pwr.is_connected().implies(~self.pwr_out.is_connected()), + "can't source 3v3 power if power input connected", + ) + + self.model = self.Block( + Mdbt50q_1mv2_Device( + pin_assigns=self._make_model_pinning(Feather_Nrf52840_Device._PIN_REMAPPING, self.get(self.pin_assigns)) + ) + ) + self._export_ios_inner(self.model) + + self.device = self.Block(Feather_Nrf52840_Device(pin_assigns=self.model.actual_pin_assigns), external=True) + self._export_tap_ios_inner(self.device) + self.assign(self.actual_pin_assigns, self.device.actual_pin_assigns) + + if self.get(self.pwr.is_connected()): # power supplied externally + self.connect(self.pwr, self.model.pwr) + self.export_tap(self.pwr.net, self.device.pwr) + else: # board sources power from USB + self.pwr_out_model = self.Block( + DummyVoltageSource(voltage_out=self._AP2112_3V3_OUT, current_limits=UsbConnector.USB2_CURRENT_LIMITS) + ) + self.connect(self.pwr_out_model.pwr, self.model.pwr) + if self.get(self.pwr_out.is_connected()): + self.connect(self.pwr_out, self.pwr_out_model.pwr) + self.export_tap(self.pwr_out.net, self.device.pwr) + + if self.get(self.vusb_out.is_connected()): + self.vusb_out.init_from( + VoltageSource( + voltage_out=UsbConnector.USB2_VOLTAGE_RANGE - self._MBR120_DROP, + current_limits=UsbConnector.USB2_CURRENT_LIMITS, + ) + ) + self.export_tap(self.vusb_out.net, self.device.vusb) + + if self.get(self.gnd.is_connected()): + self.connect(self.gnd, self.model.gnd) + else: + self.gnd_model = self.Block(DummyGround()) + self.connect(self.gnd_model.gnd, self.model.gnd) + self.export_tap(self.gnd, self.device.gnd) diff --git a/edg/parts/microcontroller/test_mcu_wrapper.py b/edg/parts/microcontroller/test_mcu_wrapper.py new file mode 100644 index 000000000..23c40838c --- /dev/null +++ b/edg/parts/microcontroller/test_mcu_wrapper.py @@ -0,0 +1,122 @@ +import unittest +from typing_extensions import override + +from .Rp2040 import Xiao_Rp2040 +from ...vendor_parts.generic import GenericChipResistor +from ...circuits import * + + +class OverallocateTest(DesignTop): + def __init__(self) -> None: + super().__init__() + self.pwr = self.Block(DummyVoltageSource(voltage_out=3.3 * Volt(tol=0))) + self.gnd = self.Block(DummyGround()) + with self.implicit_connect( + ImplicitConnect(self.pwr.pwr, [Power]), + ImplicitConnect(self.gnd.gnd, [Common]), + ) as imp: + self.dut = imp.Block(Xiao_Rp2040()) + + self.ios = ElementDict[DummyDigitalSource]() + for i in range(12): # device only has 11 IOs + self.ios[i] = self.Block(DummyDigitalSource()) + self.connect(self.ios[i].io, self.dut.gpio.request(str(i))) + + +class BaseMcuTest(DesignTop): + def __init__(self) -> None: + super().__init__() + self.pwr = self.Block(DummyVoltageSource(voltage_out=3.3 * Volt(tol=0))) + self.gnd = self.Block(DummyGround()) + with self.implicit_connect( + ImplicitConnect(self.pwr.pwr, [Power]), + ImplicitConnect(self.gnd.gnd, [Common]), + ) as imp: + self.dut = imp.Block(Xiao_Rp2040()) + + self.ios = ElementDict[DummyDigitalSource]() + for i in range(2): + self.ios[i] = self.Block(DummyDigitalSource()) + self.connect(self.ios[i].io, self.dut.gpio.request(str(i))) + + +class AutoPinsTest(BaseMcuTest): + pass + + +class AssignedPinsTest(BaseMcuTest): + @override + def refinements(self) -> Refinements: + return Refinements(instance_values=[(["dut", "pin_assigns"], ["0=1", "1=3"])]) + + +class AssignedInvalidNameTest(BaseMcuTest): + @override + def refinements(self) -> Refinements: + return Refinements(instance_values=[(["dut", "pin_assigns"], ["0=GPIO10"])]) + + +class AssignedInvalidPinNumberTest(BaseMcuTest): + @override + def refinements(self) -> Refinements: + return Refinements(instance_values=[(["dut", "pin_assigns"], ["0=38"])]) # in the IC but not module + + +class AssignedI2cTest(DesignTop): + def __init__(self) -> None: + super().__init__() + self.pwr = self.Block(DummyVoltageSource(voltage_out=3.3 * Volt(tol=0))) + self.gnd = self.Block(DummyGround()) + with self.implicit_connect( + ImplicitConnect(self.pwr.pwr, [Power]), + ImplicitConnect(self.gnd.gnd, [Common]), + ) as imp: + self.dut = imp.Block(Xiao_Rp2040()) + + self.i2c = self.Block(I2cControllerBitBang()) + self.i2c_pull = imp.Block(I2cPullup()) + self.connect(self.i2c.scl, self.dut.gpio.request("0")) + self.connect(self.i2c.sda, self.dut.gpio.request("1")) + self.connect(self.i2c.i2c, self.i2c_pull.i2c, self.dut.i2c_target.request("i2c")) + + @override + def refinements(self) -> Refinements: + return Refinements( + class_refinements=[(Resistor, GenericChipResistor)], + instance_values=[(["dut", "pin_assigns"], ["i2c=I2C1_T", "i2c.sda=5", "i2c.scl=6"])], + ) + + +class McuWrapperTestCase(unittest.TestCase): + def test_overallocate(self) -> None: + with self.assertRaises(CompilerCheckError): + ScalaCompiler.compile(OverallocateTest) + + def test_auto_pins(self) -> None: + compiled = ScalaCompiler.compile(AutoPinsTest) + self.assertEqual(compiled.get_value(["dut", "actual_pin_assigns"]), ["0=GPIO0, 7", "1=GPIO1, 8"]) + self.assertEqual(compiled.get_value(["dut", "model", "actual_pin_assigns"]), ["0=GPIO0, 2", "1=GPIO1, 3"]) + + def test_assigned_pins(self) -> None: + compiled = ScalaCompiler.compile(AssignedPinsTest) + self.assertEqual(compiled.get_value(["dut", "actual_pin_assigns"]), ["0=GPIO26, 1", "1=GPIO28, 3"]) + self.assertEqual(compiled.get_value(["dut", "model", "actual_pin_assigns"]), ["0=GPIO26, 38", "1=GPIO28, 40"]) + + def test_assigned_invalid_name(self) -> None: + with self.assertRaises(CompilerCheckError): + ScalaCompiler.compile(AssignedInvalidNameTest) + + def test_assigned_invalid_pinnumber(self) -> None: + with self.assertRaises(CompilerCheckError): + ScalaCompiler.compile(AssignedInvalidPinNumberTest) + + def test_assigned_i2c(self) -> None: + compiled = ScalaCompiler.compile(AssignedI2cTest) + self.assertEqual( + compiled.get_value(["dut", "actual_pin_assigns"]), + ["i2c=I2C1_T", "i2c.scl=GPIO7, 6", "i2c.sda=GPIO6, 5", "0=GPIO0, 7", "1=GPIO1, 8"], + ) + self.assertEqual( + compiled.get_value(["dut", "model", "actual_pin_assigns"]), + ["i2c=I2C1_T", "i2c.scl=GPIO7, 9", "i2c.sda=GPIO6, 8", "0=GPIO0, 2", "1=GPIO1, 3"], + ) diff --git a/examples/BasicKeyboard/BasicKeyboard.net.ref b/examples/BasicKeyboard/BasicKeyboard.net.ref index d9309348f..3b2479df2 100644 --- a/examples/BasicKeyboard/BasicKeyboard.net.ref +++ b/examples/BasicKeyboard/BasicKeyboard.net.ref @@ -183,7 +183,7 @@ (node (ref U1) (pin 13))) (net (code 7) (name "mcu.pwr_out") (node (ref U1) (pin 12))) -(net (code 8) (name "mcu.device.vcc_out") +(net (code 8) (name "mcu.device.vcc") (node (ref U1) (pin 14))) (net (code 9) (name "sw.sw[0,0].sw") (node (ref SW1) (pin 1)) diff --git a/examples/BasicKeyboard/BasicKeyboard.svgpcb.js b/examples/BasicKeyboard/BasicKeyboard.svgpcb.js index 12949d788..2fe774a62 100644 --- a/examples/BasicKeyboard/BasicKeyboard.svgpcb.js +++ b/examples/BasicKeyboard/BasicKeyboard.svgpcb.js @@ -15,7 +15,7 @@ board.setNetlist([ {name: "mcu.gpio.1_2", pads: [["U1", "10"], ["D3", "2"], ["D6", "2"]]}, {name: "mcu.gnd", pads: [["U1", "13"]]}, {name: "mcu.pwr_out", pads: [["U1", "12"]]}, - {name: "mcu.device.vcc_out", pads: [["U1", "14"]]}, + {name: "mcu.device.vcc", pads: [["U1", "14"]]}, {name: "sw.sw[0,0].sw", pads: [["SW1", "1"], ["D1", "1"]]}, {name: "sw.sw[0,1].sw", pads: [["SW2", "1"], ["D2", "1"]]}, {name: "sw.sw[0,2].sw", pads: [["SW3", "1"], ["D3", "1"]]}, diff --git a/examples/BldcController/BldcController.net.ref b/examples/BldcController/BldcController.net.ref index f1e554dcd..cb7cf5a6e 100644 --- a/examples/BldcController/BldcController.net.ref +++ b/examples/BldcController/BldcController.net.ref @@ -41,7 +41,7 @@ (footprint "bldc:FEATHERWING_NODIM") (property (name "Sheetname") (value "")) (property (name "Sheetfile") (value "")) - (property (name "edg_path") (value "mcu")) + (property (name "edg_path") (value "mcu.device")) (property (name "edg_short_path") (value "mcu")) (property (name "edg_refdes") (value "U1")) (property (name "edg_part") (value "Feather nRF52840 Express (Adafruit)")) diff --git a/examples/BldcController/BldcController.svgpcb.js b/examples/BldcController/BldcController.svgpcb.js index 1385b8e9a..cbd17e214 100644 --- a/examples/BldcController/BldcController.svgpcb.js +++ b/examples/BldcController/BldcController.svgpcb.js @@ -15,7 +15,7 @@ const H3 = board.add(JlcToolingHole_1_152mm, { translate: pt(0.485, 1.803), rotate: 0, id: 'H3' }) -// mcu +// mcu.device const U1 = board.add(FEATHERWING_NODIM, { translate: pt(0.643, 1.763), rotate: 0, id: 'U1' diff --git a/examples/Keyboard/Keyboard.net.ref b/examples/Keyboard/Keyboard.net.ref index f4dfc08a2..e3f510ffb 100644 --- a/examples/Keyboard/Keyboard.net.ref +++ b/examples/Keyboard/Keyboard.net.ref @@ -156,18 +156,6 @@ (property (name "edg_value") (value "50V 1uF X5R ±10% 0603 Multilayer Ceramic Capacitors MLCC - SMD/SMT ROHS")) (sheetpath (names "/mcu/") (tstamps "/02850146/")) (tstamps "15dd03c3")) -(comp (ref "R3") - (value "mcu.usb_pull") - (footprint "Resistor_SMD:R_0603_1608Metric") - (property (name "Sheetname") (value "mcu")) - (property (name "Sheetfile") (value "edg.parts.microcontroller.Stm32f103.Stm32f103_48")) - (property (name "edg_path") (value "mcu.usb_pull.dp")) - (property (name "edg_short_path") (value "mcu.usb_pull")) - (property (name "edg_refdes") (value "R3")) - (property (name "edg_part") (value "0603WAF1501T5E (UNI-ROYAL(Uniroyal Elec))")) - (property (name "edg_value") (value "±1% 1/10W Thick Film Resistors 75V ±100ppm/℃ -55℃~+155℃ 1.5kΩ 0603 Chip Resistor - Surface Mount ROHS")) - (sheetpath (names "/mcu/") (tstamps "/02850146/")) - (tstamps "0f5f0367")) (comp (ref "X1") (value "mcu.crystal.package") (footprint "Crystal:Crystal_SMD_3225-4Pin_3.2x2.5mm") @@ -216,6 +204,18 @@ (property (name "edg_value") (value "PinHeader1.27 Shrouded 2x5")) (sheetpath (names "/mcu/") (tstamps "/02850146/")) (tstamps "02ae014f")) +(comp (ref "R3") + (value "mcu.usb_pull") + (footprint "Resistor_SMD:R_0603_1608Metric") + (property (name "Sheetname") (value "mcu")) + (property (name "Sheetfile") (value "edg.parts.microcontroller.Stm32f103.Stm32f103_48")) + (property (name "edg_path") (value "mcu.usb_pull.dp")) + (property (name "edg_short_path") (value "mcu.usb_pull")) + (property (name "edg_refdes") (value "R3")) + (property (name "edg_part") (value "0603WAF1501T5E (UNI-ROYAL(Uniroyal Elec))")) + (property (name "edg_value") (value "±1% 1/10W Thick Film Resistors 75V ±100ppm/℃ -55℃~+155℃ 1.5kΩ 0603 Chip Resistor - Surface Mount ROHS")) + (sheetpath (names "/mcu/") (tstamps "/02850146/")) + (tstamps "0f5f0367")) (comp (ref "SW1") (value "sw.sw[0,0]") (footprint "Switch_Keyboard_Hotswap_Kailh:SW_Hotswap_Kailh_MX") @@ -426,8 +426,8 @@ (node (ref C6) (pin 1)) (node (ref C7) (pin 1)) (node (ref C8) (pin 1)) - (node (ref R3) (pin 1)) - (node (ref J2) (pin 1))) + (node (ref J2) (pin 1)) + (node (ref R3) (pin 1))) (net (code 8) (name "mcu.gpio.0_0") (node (ref U2) (pin 10)) (node (ref SW1) (pin 2)) diff --git a/examples/Keyboard/Keyboard.svgpcb.js b/examples/Keyboard/Keyboard.svgpcb.js index 45172caa7..cb3b12aca 100644 --- a/examples/Keyboard/Keyboard.svgpcb.js +++ b/examples/Keyboard/Keyboard.svgpcb.js @@ -66,11 +66,6 @@ const C8 = board.add(C_0603_1608Metric, { translate: pt(1.332, 0.647), rotate: 0, id: 'C8' }) -// mcu.usb_pull.dp -const R3 = board.add(R_0603_1608Metric, { - translate: pt(1.488, 0.647), rotate: 0, - id: 'R3' -}) // mcu.crystal.package const X1 = board.add(Crystal_SMD_3225_4Pin_3_2x2_5mm, { translate: pt(1.201, 0.512), rotate: 0, @@ -78,12 +73,12 @@ const X1 = board.add(Crystal_SMD_3225_4Pin_3_2x2_5mm, { }) // mcu.crystal.cap_a const C9 = board.add(C_0603_1608Metric, { - translate: pt(1.644, 0.647), rotate: 0, + translate: pt(1.488, 0.647), rotate: 0, id: 'C9' }) // mcu.crystal.cap_b const C10 = board.add(C_0603_1608Metric, { - translate: pt(1.800, 0.647), rotate: 0, + translate: pt(1.644, 0.647), rotate: 0, id: 'C10' }) // mcu.swd.conn @@ -91,6 +86,11 @@ const J2 = board.add(PinHeader_2x05_P1_27mm_Vertical_SMD, { translate: pt(1.732, 0.146), rotate: 0, id: 'J2' }) +// mcu.usb_pull.dp +const R3 = board.add(R_0603_1608Metric, { + translate: pt(1.800, 0.647), rotate: 0, + id: 'R3' +}) board.setNetlist([ {name: "usb.pwr", pads: [["J1", "A4"], ["J1", "A9"], ["J1", "B4"], ["J1", "B9"], ["U1", "3"], ["C1", "1"]]}, @@ -99,7 +99,7 @@ board.setNetlist([ {name: "usb.usb.dm", pads: [["J1", "A7"], ["J1", "B7"], ["U2", "32"]]}, {name: "usb.conn.cc.cc1", pads: [["J1", "A5"], ["R1", "2"]]}, {name: "usb.conn.cc.cc2", pads: [["J1", "B5"], ["R2", "2"]]}, - {name: "reg.pwr_out", pads: [["U1", "2"], ["C2", "1"], ["U2", "1"], ["U2", "24"], ["U2", "36"], ["U2", "48"], ["U2", "9"], ["C3", "1"], ["C4", "1"], ["C5", "1"], ["C6", "1"], ["C7", "1"], ["C8", "1"], ["R3", "1"], ["J2", "1"]]}, + {name: "reg.pwr_out", pads: [["U1", "2"], ["C2", "1"], ["U2", "1"], ["U2", "24"], ["U2", "36"], ["U2", "48"], ["U2", "9"], ["C3", "1"], ["C4", "1"], ["C5", "1"], ["C6", "1"], ["C7", "1"], ["C8", "1"], ["J2", "1"], ["R3", "1"]]}, {name: "mcu.gpio.0_0", pads: [["U2", "10"], ["SW1", "2"], ["SW2", "2"], ["SW3", "2"]]}, {name: "mcu.gpio.0_1", pads: [["U2", "11"], ["SW4", "2"], ["SW5", "2"], ["SW6", "2"]]}, {name: "mcu.gpio.1_0", pads: [["U2", "12"], ["D1", "2"], ["D4", "2"]]}, diff --git a/examples/Multimeter/Multimeter.net.ref b/examples/Multimeter/Multimeter.net.ref index 33b26525c..3c2ed3555 100644 --- a/examples/Multimeter/Multimeter.net.ref +++ b/examples/Multimeter/Multimeter.net.ref @@ -396,6 +396,30 @@ (property (name "edg_value") (value "X5R 25V ±10% 10uF 0805 Multilayer Ceramic Capacitors MLCC - SMD/SMT ROHS")) (sheetpath (names "/mcu/") (tstamps "/02850146/")) (tstamps "0b5902d0")) +(comp (ref "J2") + (value "mcu.swd") + (footprint "Connector:Tag-Connect_TC2050-IDC-NL_2x05_P1.27mm_Vertical") + (property (name "Sheetname") (value "mcu")) + (property (name "Sheetfile") (value "edg.parts.microcontroller.nRF52840.Mdbt50q_1mv2")) + (property (name "edg_path") (value "mcu.swd.conn")) + (property (name "edg_short_path") (value "mcu.swd")) + (property (name "edg_refdes") (value "J2")) + (property (name "edg_part") (value "")) + (property (name "edg_value") (value "")) + (sheetpath (names "/mcu/") (tstamps "/02850146/")) + (tstamps "02ae014f")) +(comp (ref "C8") + (value "mcu.vbus_cap") + (footprint "Capacitor_SMD:C_0805_2012Metric") + (property (name "Sheetname") (value "mcu")) + (property (name "Sheetfile") (value "edg.parts.microcontroller.nRF52840.Mdbt50q_1mv2")) + (property (name "edg_path") (value "mcu.vbus_cap.cap")) + (property (name "edg_short_path") (value "mcu.vbus_cap")) + (property (name "edg_refdes") (value "C8")) + (property (name "edg_part") (value "CL21A106KAYNNNE (Samsung Electro-Mechanics)")) + (property (name "edg_value") (value "X5R 25V ±10% 10uF 0805 Multilayer Ceramic Capacitors MLCC - SMD/SMT ROHS")) + (sheetpath (names "/mcu/") (tstamps "/02850146/")) + (tstamps "0f3a0354")) (comp (ref "R6") (value "mcu.usb_res.dp") (footprint "Resistor_SMD:R_0603_1608Metric") @@ -420,30 +444,6 @@ (property (name "edg_value") (value "±1% 1/10W Thick Film Resistors 75V ±200ppm/℃ -55℃~+155℃ 27Ω 0603 Chip Resistor - Surface Mount ROHS")) (sheetpath (names "/mcu/usb_res/") (tstamps "/02850146/0be502f4/")) (tstamps "013700d2")) -(comp (ref "C8") - (value "mcu.vbus_cap") - (footprint "Capacitor_SMD:C_0805_2012Metric") - (property (name "Sheetname") (value "mcu")) - (property (name "Sheetfile") (value "edg.parts.microcontroller.nRF52840.Mdbt50q_1mv2")) - (property (name "edg_path") (value "mcu.vbus_cap.cap")) - (property (name "edg_short_path") (value "mcu.vbus_cap")) - (property (name "edg_refdes") (value "C8")) - (property (name "edg_part") (value "CL21A106KAYNNNE (Samsung Electro-Mechanics)")) - (property (name "edg_value") (value "X5R 25V ±10% 10uF 0805 Multilayer Ceramic Capacitors MLCC - SMD/SMT ROHS")) - (sheetpath (names "/mcu/") (tstamps "/02850146/")) - (tstamps "0f3a0354")) -(comp (ref "J2") - (value "mcu.swd") - (footprint "Connector:Tag-Connect_TC2050-IDC-NL_2x05_P1.27mm_Vertical") - (property (name "Sheetname") (value "mcu")) - (property (name "Sheetfile") (value "edg.parts.microcontroller.nRF52840.Mdbt50q_1mv2")) - (property (name "edg_path") (value "mcu.swd.conn")) - (property (name "edg_short_path") (value "mcu.swd")) - (property (name "edg_refdes") (value "J2")) - (property (name "edg_part") (value "")) - (property (name "edg_value") (value "")) - (sheetpath (names "/mcu/") (tstamps "/02850146/")) - (tstamps "02ae014f")) (comp (ref "R8") (value "vbatsense.top_res") (footprint "Resistor_SMD:R_0603_1608Metric") @@ -1315,10 +1315,10 @@ (node (ref U5) (pin 33)) (node (ref U5) (pin 55)) (node (ref C7) (pin 2)) - (node (ref C8) (pin 2)) (node (ref J2) (pin 2)) (node (ref J2) (pin 3)) (node (ref J2) (pin 5)) + (node (ref C8) (pin 2)) (node (ref R9) (pin 2)) (node (ref U6) (pin 3)) (node (ref SW2) (pin 2)) @@ -1549,17 +1549,17 @@ (net (code 35) (name "mcu.reset_node") (node (ref U5) (pin 40)) (node (ref J2) (pin 6))) -(net (code 36) (name "mcu.usb_chain_0.d_P") +(net (code 36) (name "mcu.swd.tdi") + (node (ref J2) (pin 7))) +(net (code 37) (name "mcu.swd.swo") + (node (ref U5) (pin 47)) + (node (ref J2) (pin 8))) +(net (code 38) (name "mcu.usb_res.interior.dp") (node (ref U5) (pin 35)) (node (ref R6) (pin 2))) -(net (code 37) (name "mcu.usb_chain_0.d_N") +(net (code 39) (name "mcu.usb_res.interior.dm") (node (ref U5) (pin 34)) (node (ref R7) (pin 2))) -(net (code 38) (name "mcu.swd.tdi") - (node (ref J2) (pin 7))) -(net (code 39) (name "mcu.swd.swo") - (node (ref U5) (pin 47)) - (node (ref J2) (pin 8))) (net (code 40) (name "vbatsense.output") (node (ref U5) (pin 9)) (node (ref R8) (pin 2)) diff --git a/examples/Multimeter/Multimeter.svgpcb.js b/examples/Multimeter/Multimeter.svgpcb.js index 3cafe9ed5..b0b7ea2fc 100644 --- a/examples/Multimeter/Multimeter.svgpcb.js +++ b/examples/Multimeter/Multimeter.svgpcb.js @@ -165,6 +165,16 @@ const C7 = board.add(C_0805_2012Metric, { translate: pt(0.596, 0.728), rotate: 0, id: 'C7' }) +// mcu.swd.conn +const J2 = board.add(Tag_Connect_TC2050_IDC_NL_2x05_P1_27mm_Vertical, { + translate: pt(0.245, 0.822), rotate: 0, + id: 'J2' +}) +// mcu.vbus_cap.cap +const C8 = board.add(C_0805_2012Metric, { + translate: pt(0.770, 0.728), rotate: 0, + id: 'C8' +}) // mcu.usb_res.dp.res const R6 = board.add(R_0603_1608Metric, { translate: pt(0.588, 0.834), rotate: 0, @@ -175,16 +185,6 @@ const R7 = board.add(R_0603_1608Metric, { translate: pt(0.744, 0.834), rotate: 0, id: 'R7' }) -// mcu.vbus_cap.cap -const C8 = board.add(C_0805_2012Metric, { - translate: pt(0.770, 0.728), rotate: 0, - id: 'C8' -}) -// mcu.swd.conn -const J2 = board.add(Tag_Connect_TC2050_IDC_NL_2x05_P1_27mm_Vertical, { - translate: pt(0.245, 0.822), rotate: 0, - id: 'J2' -}) // vbatsense.div.top_res const R8 = board.add(R_0603_1608Metric, { translate: pt(3.628, 2.237), rotate: 0, @@ -537,7 +537,7 @@ const C31 = board.add(C_1206_3216Metric, { }) board.setNetlist([ - {name: "gnd", pads: [["U1", "2"], ["J1", "A1"], ["J1", "A12"], ["J1", "B1"], ["J1", "B12"], ["J1", "S1"], ["R1", "1"], ["R2", "1"], ["R4", "1"], ["Q2", "2"], ["SW1", "2"], ["U2", "2"], ["C1", "2"], ["C2", "2"], ["D3", "2"], ["U3", "2"], ["C3", "2"], ["C4", "2"], ["D4", "2"], ["U4", "2"], ["C5", "2"], ["C6", "2"], ["D5", "2"], ["U5", "1"], ["U5", "15"], ["U5", "2"], ["U5", "33"], ["U5", "55"], ["C7", "2"], ["C8", "2"], ["J2", "2"], ["J2", "3"], ["J2", "5"], ["R9", "2"], ["U6", "3"], ["SW2", "2"], ["SW3", "2"], ["J3", "2"], ["C9", "2"], ["C10", "2"], ["U7", "7"], ["U7", "9"], ["C11", "2"], ["C12", "2"], ["C14", "2"], ["R18", "2"], ["U8", "2"], ["C15", "2"], ["U9", "2"], ["U9", "3"], ["C16", "2"], ["U10", "2"], ["C17", "2"], ["U11", "2"], ["C18", "2"], ["U12", "2"], ["C19", "2"], ["U13", "2"], ["C20", "2"], ["U14", "19"], ["U14", "2"], ["U14", "3"], ["C21", "2"], ["C22", "2"], ["C23", "2"], ["C24", "2"], ["C25", "2"], ["U15", "2"], ["C26", "2"], ["U16", "2"], ["C27", "2"], ["U17", "2"], ["C28", "2"], ["U18", "2"], ["C29", "2"], ["U19", "2"], ["C30", "2"], ["C31", "2"]]}, + {name: "gnd", pads: [["U1", "2"], ["J1", "A1"], ["J1", "A12"], ["J1", "B1"], ["J1", "B12"], ["J1", "S1"], ["R1", "1"], ["R2", "1"], ["R4", "1"], ["Q2", "2"], ["SW1", "2"], ["U2", "2"], ["C1", "2"], ["C2", "2"], ["D3", "2"], ["U3", "2"], ["C3", "2"], ["C4", "2"], ["D4", "2"], ["U4", "2"], ["C5", "2"], ["C6", "2"], ["D5", "2"], ["U5", "1"], ["U5", "15"], ["U5", "2"], ["U5", "33"], ["U5", "55"], ["C7", "2"], ["J2", "2"], ["J2", "3"], ["J2", "5"], ["C8", "2"], ["R9", "2"], ["U6", "3"], ["SW2", "2"], ["SW3", "2"], ["J3", "2"], ["C9", "2"], ["C10", "2"], ["U7", "7"], ["U7", "9"], ["C11", "2"], ["C12", "2"], ["C14", "2"], ["R18", "2"], ["U8", "2"], ["C15", "2"], ["U9", "2"], ["U9", "3"], ["C16", "2"], ["U10", "2"], ["C17", "2"], ["U11", "2"], ["C18", "2"], ["U12", "2"], ["C19", "2"], ["U13", "2"], ["C20", "2"], ["U14", "19"], ["U14", "2"], ["U14", "3"], ["C21", "2"], ["C22", "2"], ["C23", "2"], ["C24", "2"], ["C25", "2"], ["U15", "2"], ["C26", "2"], ["U16", "2"], ["C27", "2"], ["U17", "2"], ["C28", "2"], ["U18", "2"], ["C29", "2"], ["U19", "2"], ["C30", "2"], ["C31", "2"]]}, {name: "vbat", pads: [["U1", "1"], ["R3", "1"], ["Q1", "2"]]}, {name: "v5v", pads: [["U2", "4"], ["C2", "1"], ["TP1", "1"], ["D3", "1"], ["U3", "1"], ["U3", "3"], ["C3", "1"], ["U4", "1"], ["U4", "3"], ["C5", "1"], ["U7", "1"], ["U7", "6"], ["C11", "1"], ["C12", "1"]]}, {name: "v3v3", pads: [["U3", "5"], ["C4", "1"], ["TP2", "1"], ["D4", "1"], ["U5", "28"], ["U5", "30"], ["C7", "1"], ["J2", "1"], ["D6", "2"], ["J3", "7"], ["R13", "1"], ["C9", "1"], ["R24", "1"]]}, @@ -572,10 +572,10 @@ board.setNetlist([ {name: "mcu.swd_node.swdio", pads: [["U5", "51"], ["J2", "10"]]}, {name: "mcu.swd_node.swclk", pads: [["U5", "53"], ["J2", "9"]]}, {name: "mcu.reset_node", pads: [["U5", "40"], ["J2", "6"]]}, - {name: "mcu.usb_chain_0.d_P", pads: [["U5", "35"], ["R6", "2"]]}, - {name: "mcu.usb_chain_0.d_N", pads: [["U5", "34"], ["R7", "2"]]}, {name: "mcu.swd.tdi", pads: [["J2", "7"]]}, {name: "mcu.swd.swo", pads: [["U5", "47"], ["J2", "8"]]}, + {name: "mcu.usb_res.interior.dp", pads: [["U5", "35"], ["R6", "2"]]}, + {name: "mcu.usb_res.interior.dm", pads: [["U5", "34"], ["R7", "2"]]}, {name: "vbatsense.output", pads: [["U5", "9"], ["R8", "2"], ["R9", "1"]]}, {name: "rgb.package.k_red", pads: [["D6", "3"], ["R10", "1"]]}, {name: "rgb.package.k_green", pads: [["D6", "4"], ["R11", "1"]]}, diff --git a/examples/SwdDebugger/SwdDebugger.net.ref b/examples/SwdDebugger/SwdDebugger.net.ref index 8b0eb1db1..76987738a 100644 --- a/examples/SwdDebugger/SwdDebugger.net.ref +++ b/examples/SwdDebugger/SwdDebugger.net.ref @@ -240,18 +240,6 @@ (property (name "edg_value") (value "25V 1uF X5R ±10% 0402 Multilayer Ceramic Capacitors MLCC - SMD/SMT ROHS")) (sheetpath (names "/mcu/") (tstamps "/02850146/")) (tstamps "15dd03c3")) -(comp (ref "SR3") - (value "mcu.usb_pull") - (footprint "Resistor_SMD:R_0402_1005Metric") - (property (name "Sheetname") (value "mcu")) - (property (name "Sheetfile") (value "edg.parts.microcontroller.Stm32f103.Stm32f103_48")) - (property (name "edg_path") (value "mcu.usb_pull.dp")) - (property (name "edg_short_path") (value "mcu.usb_pull")) - (property (name "edg_refdes") (value "SR3")) - (property (name "edg_part") (value "0402WGF1501TCE (UNI-ROYAL(Uniroyal Elec))")) - (property (name "edg_value") (value "±1% 1/16W Thick Film Resistors 50V ±100ppm/℃ -55℃~+155℃ 1.5kΩ 0402 Chip Resistor - Surface Mount ROHS")) - (sheetpath (names "/mcu/") (tstamps "/02850146/")) - (tstamps "0f5f0367")) (comp (ref "SU4") (value "mcu.crystal") (footprint "Crystal:Resonator_SMD_Murata_CSTxExxV-3Pin_3.0x1.1mm") @@ -276,6 +264,18 @@ (property (name "edg_value") (value "PinHeader1.27 Shrouded 2x5")) (sheetpath (names "/mcu/") (tstamps "/02850146/")) (tstamps "02ae014f")) +(comp (ref "SR3") + (value "mcu.usb_pull") + (footprint "Resistor_SMD:R_0402_1005Metric") + (property (name "Sheetname") (value "mcu")) + (property (name "Sheetfile") (value "edg.parts.microcontroller.Stm32f103.Stm32f103_48")) + (property (name "edg_path") (value "mcu.usb_pull.dp")) + (property (name "edg_short_path") (value "mcu.usb_pull")) + (property (name "edg_refdes") (value "SR3")) + (property (name "edg_part") (value "0402WGF1501TCE (UNI-ROYAL(Uniroyal Elec))")) + (property (name "edg_value") (value "±1% 1/16W Thick Film Resistors 50V ±100ppm/℃ -55℃~+155℃ 1.5kΩ 0402 Chip Resistor - Surface Mount ROHS")) + (sheetpath (names "/mcu/") (tstamps "/02850146/")) + (tstamps "0f5f0367")) (comp (ref "SU5") (value "usb_esd") (footprint "Package_TO_SOT_SMD:SOT-23") @@ -557,8 +557,8 @@ (node (ref SC8) (pin 1)) (node (ref SC9) (pin 1)) (node (ref SC10) (pin 1)) - (node (ref SR3) (pin 1)) (node (ref SJ2) (pin 1)) + (node (ref SR3) (pin 1)) (node (ref SR6) (pin 1)) (node (ref SR12) (pin 1))) (net (code 4) (name "Svtarget") diff --git a/examples/SwdDebugger/SwdDebugger.svgpcb.js b/examples/SwdDebugger/SwdDebugger.svgpcb.js index bf0f0bdb2..4c7a0155a 100644 --- a/examples/SwdDebugger/SwdDebugger.svgpcb.js +++ b/examples/SwdDebugger/SwdDebugger.svgpcb.js @@ -100,11 +100,6 @@ const SC10 = board.add(C_0402_1005Metric, { translate: pt(0.147, 0.613), rotate: 0, id: 'SC10' }) -// mcu.usb_pull.dp -const SR3 = board.add(R_0402_1005Metric, { - translate: pt(0.399, 0.463), rotate: 0, - id: 'SR3' -}) // mcu.crystal const SU4 = board.add(Resonator_SMD_Murata_CSTxExxV_3Pin_3_0x1_1mm, { translate: pt(0.079, 0.508), rotate: 0, @@ -115,6 +110,11 @@ const SJ2 = board.add(PinHeader_2x05_P1_27mm_Vertical_SMD, { translate: pt(0.614, 0.146), rotate: 0, id: 'SJ2' }) +// mcu.usb_pull.dp +const SR3 = board.add(R_0402_1005Metric, { + translate: pt(0.399, 0.463), rotate: 0, + id: 'SR3' +}) // usb_esd const SU5 = board.add(SOT_23, { translate: pt(1.520, 1.225), rotate: 0, @@ -209,7 +209,7 @@ const SR15 = board.add(R_0402_1005Metric, { board.setNetlist([ {name: "Svusb", pads: [["SJ1", "A4"], ["SJ1", "A9"], ["SJ1", "B4"], ["SJ1", "B9"], ["SD1", "1"], ["SU1", "1"], ["SU1", "3"], ["SC1", "1"], ["SU2", "1"], ["SC3", "1"]]}, {name: "Sgnd", pads: [["SJ1", "A1"], ["SJ1", "A12"], ["SJ1", "B1"], ["SJ1", "B12"], ["SJ1", "S1"], ["SR1", "1"], ["SR2", "1"], ["SD1", "2"], ["SU1", "2"], ["SC1", "2"], ["SC2", "2"], ["SU2", "2"], ["SC3", "2"], ["SC4", "2"], ["SU3", "23"], ["SU3", "35"], ["SU3", "44"], ["SU3", "47"], ["SU3", "8"], ["SC5", "2"], ["SC6", "2"], ["SC7", "2"], ["SC8", "2"], ["SC9", "2"], ["SC10", "2"], ["SU4", "2"], ["SJ2", "3"], ["SJ2", "5"], ["SJ2", "9"], ["SU5", "3"], ["SR4", "2"], ["SR5", "2"], ["SSW1", "2"], ["SJ3", "3"], ["SJ3", "5"], ["SJ3", "9"], ["SR13", "2"], ["SR15", "2"]]}, - {name: "Sv3v3", pads: [["SU1", "5"], ["SC2", "1"], ["SU3", "1"], ["SU3", "24"], ["SU3", "36"], ["SU3", "48"], ["SU3", "9"], ["SC5", "1"], ["SC6", "1"], ["SC7", "1"], ["SC8", "1"], ["SC9", "1"], ["SC10", "1"], ["SR3", "1"], ["SJ2", "1"], ["SR6", "1"], ["SR12", "1"]]}, + {name: "Sv3v3", pads: [["SU1", "5"], ["SC2", "1"], ["SU3", "1"], ["SU3", "24"], ["SU3", "36"], ["SU3", "48"], ["SU3", "9"], ["SC5", "1"], ["SC6", "1"], ["SC7", "1"], ["SC8", "1"], ["SC9", "1"], ["SC10", "1"], ["SJ2", "1"], ["SR3", "1"], ["SR6", "1"], ["SR12", "1"]]}, {name: "Svtarget", pads: [["SU2", "5"], ["SC4", "1"], ["SJ3", "1"], ["SD4", "2"], ["SR14", "1"]]}, {name: "Susb_chain_0.d_P", pads: [["SJ1", "A6"], ["SJ1", "B6"], ["SU3", "33"], ["SR3", "2"], ["SU5", "2"]]}, {name: "Susb_chain_0.d_N", pads: [["SJ1", "A7"], ["SJ1", "B7"], ["SU3", "32"], ["SU5", "1"]]}, diff --git a/examples/TestBlinkyBasic/TestBlinkyBasic.net.ref b/examples/TestBlinkyBasic/TestBlinkyBasic.net.ref index 4f6524606..27af5ce41 100644 --- a/examples/TestBlinkyBasic/TestBlinkyBasic.net.ref +++ b/examples/TestBlinkyBasic/TestBlinkyBasic.net.ref @@ -42,7 +42,7 @@ (node (ref R1) (pin 2))) (net (code 2) (name "mcu.pwr_out") (node (ref U1) (pin 12))) -(net (code 3) (name "mcu.device.vcc_out") +(net (code 3) (name "mcu.device.vcc") (node (ref U1) (pin 14))) (net (code 4) (name "led.signal") (node (ref U1) (pin 7)) diff --git a/examples/TestBlinkyBasic/TestBlinkyBasic.svgpcb.js b/examples/TestBlinkyBasic/TestBlinkyBasic.svgpcb.js index 64dacd4ad..ca7adcd27 100644 --- a/examples/TestBlinkyBasic/TestBlinkyBasic.svgpcb.js +++ b/examples/TestBlinkyBasic/TestBlinkyBasic.svgpcb.js @@ -19,7 +19,7 @@ const R1 = board.add(R_0603_1608Metric, { board.setNetlist([ {name: "mcu.gnd", pads: [["U1", "13"], ["R1", "2"]]}, {name: "mcu.pwr_out", pads: [["U1", "12"]]}, - {name: "mcu.device.vcc_out", pads: [["U1", "14"]]}, + {name: "mcu.device.vcc", pads: [["U1", "14"]]}, {name: "led.signal", pads: [["U1", "7"], ["D1", "2"]]}, {name: "led.package.k", pads: [["D1", "1"], ["R1", "1"]]} ]) diff --git a/examples/TofArray/TofArray.net.ref b/examples/TofArray/TofArray.net.ref index 33105948f..cb7adfffa 100644 --- a/examples/TofArray/TofArray.net.ref +++ b/examples/TofArray/TofArray.net.ref @@ -252,18 +252,6 @@ (property (name "edg_value") (value "50V 1uF X5R ±10% 0603 Multilayer Ceramic Capacitors MLCC - SMD/SMT ROHS")) (sheetpath (names "/mcu/") (tstamps "/02850146/")) (tstamps "15dd03c3")) -(comp (ref "R3") - (value "mcu.usb_pull") - (footprint "Resistor_SMD:R_0603_1608Metric") - (property (name "Sheetname") (value "mcu")) - (property (name "Sheetfile") (value "edg.parts.microcontroller.Stm32f103.Stm32f103_48")) - (property (name "edg_path") (value "mcu.usb_pull.dp")) - (property (name "edg_short_path") (value "mcu.usb_pull")) - (property (name "edg_refdes") (value "R3")) - (property (name "edg_part") (value "0603WAF1501T5E (UNI-ROYAL(Uniroyal Elec))")) - (property (name "edg_value") (value "±1% 1/10W Thick Film Resistors 75V ±100ppm/℃ -55℃~+155℃ 1.5kΩ 0603 Chip Resistor - Surface Mount ROHS")) - (sheetpath (names "/mcu/") (tstamps "/02850146/")) - (tstamps "0f5f0367")) (comp (ref "X1") (value "mcu.crystal.package") (footprint "Crystal:Crystal_SMD_3225-4Pin_3.2x2.5mm") @@ -312,6 +300,18 @@ (property (name "edg_value") (value "")) (sheetpath (names "/mcu/") (tstamps "/02850146/")) (tstamps "02ae014f")) +(comp (ref "R3") + (value "mcu.usb_pull") + (footprint "Resistor_SMD:R_0603_1608Metric") + (property (name "Sheetname") (value "mcu")) + (property (name "Sheetfile") (value "edg.parts.microcontroller.Stm32f103.Stm32f103_48")) + (property (name "edg_path") (value "mcu.usb_pull.dp")) + (property (name "edg_short_path") (value "mcu.usb_pull")) + (property (name "edg_refdes") (value "R3")) + (property (name "edg_part") (value "0603WAF1501T5E (UNI-ROYAL(Uniroyal Elec))")) + (property (name "edg_value") (value "±1% 1/10W Thick Film Resistors 75V ±100ppm/℃ -55℃~+155℃ 1.5kΩ 0603 Chip Resistor - Surface Mount ROHS")) + (sheetpath (names "/mcu/") (tstamps "/02850146/")) + (tstamps "0f5f0367")) (comp (ref "SW1") (value "sw1") (footprint "Button_Switch_SMD:SW_SPST_SKQG_WithoutStem") @@ -972,8 +972,8 @@ (node (ref C6) (pin 1)) (node (ref C7) (pin 1)) (node (ref C8) (pin 1)) - (node (ref R3) (pin 1)) (node (ref J3) (pin 1)) + (node (ref R3) (pin 1)) (node (ref D2) (pin 2)) (node (ref D3) (pin 2)) (node (ref D4) (pin 2)) diff --git a/examples/TofArray/TofArray.svgpcb.js b/examples/TofArray/TofArray.svgpcb.js index 6f96cbcfb..7c8565b4c 100644 --- a/examples/TofArray/TofArray.svgpcb.js +++ b/examples/TofArray/TofArray.svgpcb.js @@ -105,11 +105,6 @@ const C8 = board.add(C_0603_1608Metric, { translate: pt(0.214, 0.647), rotate: 0, id: 'C8' }) -// mcu.usb_pull.dp -const R3 = board.add(R_0603_1608Metric, { - translate: pt(0.370, 0.647), rotate: 0, - id: 'R3' -}) // mcu.crystal.package const X1 = board.add(Crystal_SMD_3225_4Pin_3_2x2_5mm, { translate: pt(0.083, 0.512), rotate: 0, @@ -117,12 +112,12 @@ const X1 = board.add(Crystal_SMD_3225_4Pin_3_2x2_5mm, { }) // mcu.crystal.cap_a const C9 = board.add(C_0603_1608Metric, { - translate: pt(0.526, 0.647), rotate: 0, + translate: pt(0.370, 0.647), rotate: 0, id: 'C9' }) // mcu.crystal.cap_b const C10 = board.add(C_0603_1608Metric, { - translate: pt(0.682, 0.647), rotate: 0, + translate: pt(0.526, 0.647), rotate: 0, id: 'C10' }) // mcu.swd.conn @@ -130,6 +125,11 @@ const J3 = board.add(Tag_Connect_TC2050_IDC_FP_2x05_P1_27mm_Vertical, { translate: pt(0.661, 0.167), rotate: 0, id: 'J3' }) +// mcu.usb_pull.dp +const R3 = board.add(R_0603_1608Metric, { + translate: pt(0.682, 0.647), rotate: 0, + id: 'R3' +}) // sw1.package const SW1 = board.add(SW_SPST_SKQG_WithoutStem, { translate: pt(0.742, 1.424), rotate: 0, @@ -364,7 +364,7 @@ const D7 = board.add(LED_LiteOn_LTST_C19HE1WT, { board.setNetlist([ {name: "vusb", pads: [["J1", "A4"], ["J1", "A9"], ["J1", "B4"], ["J1", "B9"], ["TP1", "1"], ["U1", "3"], ["C1", "1"], ["U11", "1"], ["U11", "6"], ["C23", "1"], ["C24", "1"]]}, {name: "gnd", pads: [["J1", "A1"], ["J1", "A12"], ["J1", "B1"], ["J1", "B12"], ["J1", "S1"], ["R1", "1"], ["R2", "1"], ["J2", "3"], ["TP2", "1"], ["U1", "1"], ["C1", "2"], ["C2", "2"], ["D1", "2"], ["U2", "23"], ["U2", "35"], ["U2", "44"], ["U2", "47"], ["U2", "8"], ["C3", "2"], ["C4", "2"], ["C5", "2"], ["C6", "2"], ["C7", "2"], ["C8", "2"], ["X1", "2"], ["X1", "4"], ["C9", "2"], ["C10", "2"], ["J3", "2"], ["J3", "3"], ["J3", "5"], ["SW1", "2"], ["U3", "12"], ["U3", "2"], ["U3", "3"], ["U3", "4"], ["U3", "6"], ["C11", "2"], ["C12", "2"], ["U4", "12"], ["U4", "2"], ["U4", "3"], ["U4", "4"], ["U4", "6"], ["C13", "2"], ["C14", "2"], ["U5", "12"], ["U5", "2"], ["U5", "3"], ["U5", "4"], ["U5", "6"], ["C15", "2"], ["C16", "2"], ["U6", "12"], ["U6", "2"], ["U6", "3"], ["U6", "4"], ["U6", "6"], ["C17", "2"], ["C18", "2"], ["U7", "12"], ["U7", "2"], ["U7", "3"], ["U7", "4"], ["U7", "6"], ["C19", "2"], ["C20", "2"], ["U8", "3"], ["U9", "2"], ["U9", "8"], ["C21", "2"], ["U10", "3"], ["C22", "2"], ["U11", "7"], ["U11", "9"], ["C23", "2"], ["C24", "2"], ["C26", "2"]]}, - {name: "v3v3", pads: [["U1", "2"], ["C2", "1"], ["TP3", "1"], ["D1", "1"], ["U2", "1"], ["U2", "24"], ["U2", "36"], ["U2", "48"], ["U2", "9"], ["C3", "1"], ["C4", "1"], ["C5", "1"], ["C6", "1"], ["C7", "1"], ["C8", "1"], ["R3", "1"], ["J3", "1"], ["D2", "2"], ["D3", "2"], ["D4", "2"], ["D5", "2"], ["D6", "2"], ["U3", "1"], ["U3", "11"], ["C11", "1"], ["C12", "1"], ["U4", "1"], ["U4", "11"], ["C13", "1"], ["C14", "1"], ["U5", "1"], ["U5", "11"], ["C15", "1"], ["C16", "1"], ["U6", "1"], ["U6", "11"], ["C17", "1"], ["C18", "1"], ["U7", "1"], ["U7", "11"], ["C19", "1"], ["C20", "1"], ["R4", "1"], ["R5", "1"], ["U9", "3"], ["C21", "1"], ["D7", "2"]]}, + {name: "v3v3", pads: [["U1", "2"], ["C2", "1"], ["TP3", "1"], ["D1", "1"], ["U2", "1"], ["U2", "24"], ["U2", "36"], ["U2", "48"], ["U2", "9"], ["C3", "1"], ["C4", "1"], ["C5", "1"], ["C6", "1"], ["C7", "1"], ["C8", "1"], ["J3", "1"], ["R3", "1"], ["D2", "2"], ["D3", "2"], ["D4", "2"], ["D5", "2"], ["D6", "2"], ["U3", "1"], ["U3", "11"], ["C11", "1"], ["C12", "1"], ["U4", "1"], ["U4", "11"], ["C13", "1"], ["C14", "1"], ["U5", "1"], ["U5", "11"], ["C15", "1"], ["C16", "1"], ["U6", "1"], ["U6", "11"], ["C17", "1"], ["C18", "1"], ["U7", "1"], ["U7", "11"], ["C19", "1"], ["C20", "1"], ["R4", "1"], ["R5", "1"], ["U9", "3"], ["C21", "1"], ["D7", "2"]]}, {name: "sw1_chain_0", pads: [["U2", "19"], ["SW1", "1"]]}, {name: "leds_chain_0.0", pads: [["U2", "20"], ["RN1", "8"]]}, {name: "leds_chain_0.1", pads: [["U2", "25"], ["RN1", "7"]]},