Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Commit

Permalink
Library: Improve RP2040 (#57)
Browse files Browse the repository at this point in the history
* Graph: Extend bfs
* Core: Fix node get_children
* Library: Move ElectricLogic stuff to SignalElectric
* Improve auto placing of decoupling caps & pull resistors
* transformer pad bbox
* typehierarchy recursive
* Examples: MCU example
* Library: Fix a bunch of refs & add a bunch of modules
* Library: RecursionGuard power connections
  • Loading branch information
iopapamanoglou authored Sep 13, 2024
1 parent 9586bf9 commit 61b25c2
Show file tree
Hide file tree
Showing 55 changed files with 1,908 additions and 563 deletions.
87 changes: 87 additions & 0 deletions examples/mcu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# This file is part of the faebryk project
# SPDX-License-Identifier: MIT

"""
This file contains a faebryk sample.
"""

import logging

import typer

import faebryk.library._F as F
from faebryk.core.module import Module
from faebryk.exporters.pcb.layout.absolute import LayoutAbsolute
from faebryk.exporters.pcb.layout.extrude import LayoutExtrude
from faebryk.exporters.pcb.layout.typehierarchy import LayoutTypeHierarchy
from faebryk.libs.brightness import TypicalLuminousIntensity
from faebryk.libs.examples.buildutil import apply_design_to_pcb
from faebryk.libs.library import L
from faebryk.libs.logging import setup_basic_logging

logger = logging.getLogger(__name__)


class App(Module):
led = L.f_field(F.LEDIndicator)(use_mosfet=False)
mcu: F.RP2040_ReferenceDesign
usb_power: F.USB_C_PSU_Vertical

def __preinit__(self) -> None:
# Parametrize
self.led.led.led.color.merge(F.LED.Color.YELLOW)
self.led.led.led.brightness.merge(
TypicalLuminousIntensity.APPLICATION_LED_INDICATOR_INSIDE.value.value
)

self.usb_power.power_out.connect(self.mcu.usb.usb_if.buspower)
self.mcu.rp2040.gpio[25].connect(self.led.logic_in)
self.mcu.rp2040.pinmux.enable(self.mcu.rp2040.gpio[25])

self._set_layout()

def _set_layout(self):
LT = F.has_pcb_position.layer_type
Point = F.has_pcb_position.Point
# set center
self.add(F.has_pcb_position_defined(Point((50, 50, 0, LT.TOP_LAYER))))

layout = LayoutTypeHierarchy(
layouts=[
LayoutTypeHierarchy.Level(
mod_type=type(self.led),
layout=LayoutAbsolute(Point((0, 0, 0, LT.NONE))),
children_layout=LayoutExtrude((0, -5)),
direct_children_only=False,
),
LayoutTypeHierarchy.Level(
mod_type=type(self.usb_power),
layout=LayoutAbsolute(Point((-20, 0, 0, LT.NONE))),
children_layout=LayoutExtrude((0, -5)),
direct_children_only=False,
),
LayoutTypeHierarchy.Level(
mod_type=type(self.mcu),
layout=LayoutAbsolute(Point((30, 0, 0, LT.NONE))),
),
]
)
self.add(F.has_pcb_layout_defined(layout))


# Boilerplate -----------------------------------------------------------------


def main():
logger.info("Building app")
app = App()

logger.info("Export")
apply_design_to_pcb(app)


if __name__ == "__main__":
setup_basic_logging()
logger.info("Running example")

typer.run(main)
12 changes: 10 additions & 2 deletions src/faebryk/core/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,19 @@ def get_edges(self, obj: T) -> Mapping[T, "Link"]: ...
def _union(rep: GT, old: GT) -> GT: ...

def bfs_visit(
self, filter: Callable[[T], bool], start: Iterable[T], G: GT | None = None
self,
filter: Callable[[list[T], "Link"], bool],
start: Iterable[T],
G: GT | None = None,
):
G = G or self()

return bfs_visit(lambda n: [o for o in self.get_edges(n) if filter(o)], start)
return bfs_visit(
lambda n: [
o for o, link in self.get_edges(n[-1]).items() if filter(n + [o], link)
],
start,
)

def __str__(self) -> str:
return f"{type(self).__name__}(V={self.node_cnt}, E={self.edge_cnt})"
Expand Down
3 changes: 0 additions & 3 deletions src/faebryk/core/graph_backends/graphpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,6 @@ def is_connected(self, from_obj: T, to_obj: T) -> "Link | None":
def get_edges(self, obj: T) -> Mapping[T, L]:
return self().edges(obj)

def bfs_visit(self, filter: Callable[[T], bool], start: Iterable[T], G=None):
return super().bfs_visit(filter, start, G)

@staticmethod
def _union(rep: GI, old: GI):
# merge big into small
Expand Down
34 changes: 25 additions & 9 deletions src/faebryk/core/module.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# This file is part of the faebryk project
# SPDX-License-Identifier: MIT
import logging
from typing import TYPE_CHECKING, Iterable
from typing import TYPE_CHECKING, Callable, Iterable

from faebryk.core.graphinterface import GraphInterface
from faebryk.core.node import Node
from faebryk.core.moduleinterface import GraphInterfaceModuleSibling
from faebryk.core.node import Node, f_field
from faebryk.core.trait import Trait
from faebryk.libs.util import unique_ref

Expand All @@ -17,8 +17,8 @@
class Module(Node):
class TraitT(Trait): ...

specializes: GraphInterface
specialized: GraphInterface
specializes = f_field(GraphInterfaceModuleSibling)(is_parent=False)
specialized = f_field(GraphInterfaceModuleSibling)(is_parent=True)

def get_most_special(self) -> "Module":
specialers = {
Expand All @@ -39,12 +39,28 @@ def get_most_special(self) -> "Module":
), f"Ambiguous specialest {specialest_next} for {self}"
return next(iter(specialest_next))

def get_children_modules(
self: Node, most_special: bool = True, direct_only: bool = False
):
out = self.get_children(direct_only=direct_only, types=Module)
def get_children_modules[T: Module](
self: Node,
types: type[T] | tuple[type[T], ...],
most_special: bool = True,
direct_only: bool = False,
include_root: bool = False,
f_filter: Callable[[T], bool] | None = None,
sort: bool = True,
special_filter: bool = True,
) -> set[T]:
out = self.get_children(
direct_only=direct_only,
types=types,
include_root=include_root,
f_filter=f_filter,
sort=sort,
)
if most_special:
out = {n.get_most_special() for n in out}
if special_filter and f_filter:
out = {n for n in out if f_filter(n)}

return out

def specialize[T: Module](
Expand Down
9 changes: 2 additions & 7 deletions src/faebryk/core/moduleinterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,6 @@ class _LinkDirectShallowMif(
LinkDirectShallow(lambda link, gif: test(gif.node))
): ...

print("Make shallow for", cls)

return _LinkDirectShallowMif

def __preinit__(self) -> None: ...
Expand Down Expand Up @@ -333,9 +331,7 @@ def connect(self: Self, *other: Self, linkcls=None) -> Self:
self._connect_siblings_and_connections(o, linkcls=linkcls)
return other[-1] if other else self

def connect_via(
self, bridge: Node | Sequence[Node], other: Self | None = None, linkcls=None
):
def connect_via(self, bridge: Node | Sequence[Node], *other: Self, linkcls=None):
from faebryk.library.can_bridge import can_bridge

bridges = [bridge] if isinstance(bridge, Node) else bridge
Expand All @@ -345,8 +341,7 @@ def connect_via(
intf.connect(t.get_in(), linkcls=linkcls)
intf = t.get_out()

if other:
intf.connect(other, linkcls=linkcls)
intf.connect(*other, linkcls=linkcls)

def connect_shallow(self, other: Self) -> Self:
return self.connect(other, linkcls=type(self).LinkDirectShallow())
Expand Down
91 changes: 72 additions & 19 deletions src/faebryk/core/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
GraphInterfaceHierarchical,
GraphInterfaceSelf,
)
from faebryk.core.link import LinkNamedParent, LinkSibling
from faebryk.core.link import Link, LinkNamedParent, LinkSibling
from faebryk.libs.exceptions import FaebrykException
from faebryk.libs.util import (
KeyErrorNotFound,
Expand All @@ -33,6 +33,7 @@
find,
times,
try_avoid_endless_recursion,
try_or,
zip_dicts_by_key,
)

Expand Down Expand Up @@ -106,6 +107,18 @@ def __() -> T:
return _


def list_f_field[T, **P](n: int, con: Callable[P, T]) -> Callable[P, list[T]]:
assert isinstance(con, type)

def _(*args: P.args, **kwargs: P.kwargs) -> Callable[[], list[T]]:
def __() -> list[T]:
return [con(*args, **kwargs) for _ in range(n)]

return _d_field(__)

return _


class NodeException(FaebrykException):
def __init__(self, node: "Node", *args: object) -> None:
super().__init__(*args)
Expand Down Expand Up @@ -141,11 +154,11 @@ class Node(FaebrykLibObject, metaclass=PostInitCaller):
specialized: list["Node"]

self_gif: GraphInterfaceSelf
children: GraphInterfaceHierarchical = d_field(
lambda: GraphInterfaceHierarchical(is_parent=True)
children: GraphInterfaceHierarchical = f_field(GraphInterfaceHierarchical)(
is_parent=True
)
parent: GraphInterfaceHierarchical = d_field(
lambda: GraphInterfaceHierarchical(is_parent=False)
parent: GraphInterfaceHierarchical = f_field(GraphInterfaceHierarchical)(
is_parent=False
)

_init: bool = False
Expand Down Expand Up @@ -527,10 +540,28 @@ def _get_children_direct(self):
def _get_children_all(self, include_root: bool):
# TODO looks like get_node_tree is 2x faster

out = self.bfs_node(
lambda x: isinstance(x, (GraphInterfaceSelf, GraphInterfaceHierarchical))
and x is not self.parent,
)
def _filter(path, link):
next_node = path[-1]
prev_node = path[-2] if len(path) >= 2 else None

# Only look at hierarchy
if not isinstance(
next_node, (GraphInterfaceSelf, GraphInterfaceHierarchical)
):
return False

# Only children
if (
isinstance(prev_node, GraphInterfaceHierarchical)
and isinstance(next_node, GraphInterfaceHierarchical)
and not prev_node.is_parent
and next_node.is_parent
):
return False

return True

out = self.bfs_node(_filter)

if not include_root:
out.remove(self)
Expand Down Expand Up @@ -560,7 +591,12 @@ def get_children[T: Node](
out = cast(set[T], out)

if sort:
out = set(sorted(out, key=lambda n: n.get_name()))
out = set(
sorted(
out,
key=lambda n: try_or(n.get_name, default="", catch=NodeNoParent),
)
)

return out

Expand Down Expand Up @@ -597,7 +633,7 @@ def get_tree[T: Node](

return tree

def bfs_node(self, filter: Callable[[GraphInterface], bool]):
def bfs_node(self, filter: Callable[[list[GraphInterface], Link], bool]):
return Node.get_nodes_from_gifs(
self.get_graph().bfs_visit(filter, [self.self_gif])
)
Expand All @@ -611,15 +647,32 @@ def get_nodes_from_gifs(gifs: Iterable[GraphInterface]):

# Hierarchy queries ----------------------------------------------------------------

def get_parent_f(self, filter_expr: Callable[["Node"], bool]):
candidates = [p for p, _ in self.get_hierarchy() if filter_expr(p)]
if not candidates:
return None
return candidates[-1]

def get_parent_of_type[T: Node](self, parent_type: type[T]) -> T | None:
def get_parent_f(
self,
filter_expr: Callable[["Node"], bool],
direct_only: bool = False,
include_root: bool = True,
):
parents = [p for p, _ in self.get_hierarchy()]
if not include_root:
parents = parents[:-1]
if direct_only:
parents = parents[-1:]
for p in reversed(parents):
if filter_expr(p):
return p
return None

def get_parent_of_type[T: Node](
self, parent_type: type[T], direct_only: bool = False, include_root: bool = True
) -> T | None:
return cast(
parent_type, self.get_parent_f(lambda p: isinstance(p, parent_type))
parent_type | None,
self.get_parent_f(
lambda p: isinstance(p, parent_type),
direct_only=direct_only,
include_root=include_root,
),
)

def get_parent_with_trait[TR: Trait](
Expand Down
2 changes: 1 addition & 1 deletion src/faebryk/exporters/parameters/parameters_to_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def export_parameters_to_file(module: Module, path: Path):
# {module_name: [{param_name: param_value}, {param_name: param_value},...]}
parameters = dict[str, list[dict[str, Parameter]]]()

for m in module.get_children_modules():
for m in module.get_children_modules(types=Module):
parameters[m.get_full_name(types=True).split(".", maxsplit=1)[-1]] = [
{param.get_full_name().split(".")[-1]: param}
for param in m.get_children(direct_only=True, types=Parameter)
Expand Down
Loading

0 comments on commit 61b25c2

Please sign in to comment.