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

Commit

Permalink
Finish base layout mcu example
Browse files Browse the repository at this point in the history
  • Loading branch information
iopapamanoglou committed Sep 13, 2024
1 parent b07cd57 commit df97b0a
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 42 deletions.
32 changes: 20 additions & 12 deletions examples/mcu.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

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
Expand All @@ -24,7 +25,7 @@
class App(Module):
led = L.f_field(F.LEDIndicator)(use_mosfet=False)
mcu: F.RP2040_ReferenceDesign
usb_power: F.USB_C_5V_PSU
usb_power: F.USB_C_PSU_Vertical

def __preinit__(self) -> None:
# Parametrize
Expand All @@ -40,22 +41,29 @@ def __preinit__(self) -> None:
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(
F.has_pcb_position.Point(
(50, 50, 0, F.has_pcb_position.layer_type.TOP_LAYER)
)
)
)
self.add(F.has_pcb_position_defined(Point((50, 50, 0, LT.TOP_LAYER))))

# distribute horizontally
layout = LayoutTypeHierarchy(
layouts=[
LayoutTypeHierarchy.Level(
mod_type=Module,
layout=LayoutExtrude((20, 0)),
)
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))
Expand Down
1 change: 1 addition & 0 deletions src/faebryk/exporters/pcb/layout/heuristic_pulls.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@ def add_to_all_suitable_modules(cls, node: Node, params: Params | None = None):
layout = cls(params)
candidates = cls.find_module_candidates(node)
for c in candidates:
logger.debug(f"Adding {cls.__name__} to {c}")
c.add(F.has_pcb_layout_defined(layout))
return candidates
23 changes: 18 additions & 5 deletions src/faebryk/exporters/pcb/layout/typehierarchy.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ class Level:
mod_type: type[Module] | tuple[type[Module], ...]
layout: Layout
children_layout: Layout | None = None
direct_children_only: bool = True

layouts: list[Level]

def apply(self, *node: Node):
"""
Tip: Make sure at least one parent of node has an absolute position defined
"""
from faebryk.library.has_footprint import has_footprint

# Find the layout for each node (isinstance mod_type) and group by matched level
levels = groupby(
Expand All @@ -46,29 +48,40 @@ def apply(self, *node: Node):
for level, nodes_tuple in levels.items():
nodes = [n for n, _ in nodes_tuple]

direct_children = {
children = {
c
for n in nodes
for c in n.get_children(
direct_only=True, types=(Module, ModuleInterface)
)
}
logger.debug(
f"Level: {level.mod_type if level else None},"
f" Children: {direct_children}"
f"Level: {level.mod_type if level else None}," f" Children: {children}"
)

# No type match, search for children instead
if level is None:
self.apply(*direct_children)
self.apply(*children)
continue

level.layout.apply(*nodes)

if not level.children_layout:
continue

level.children_layout.apply(*direct_children)
if not level.direct_children_only:
children = {
c
for n in nodes
for c in Module.get_children_modules(
n,
direct_only=False,
types=Module,
f_filter=lambda c: c.has_trait(has_footprint),
)
}

level.children_layout.apply(*children)

def __hash__(self):
return sum(map(hash, self.layouts))
34 changes: 34 additions & 0 deletions src/faebryk/library/Crystal_Oscillator.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,40 @@ def __preinit__(self):
)
self.crystal.unnamed[1].connect(self.xtal_if.xin)

@L.rt_field
def pcb_layout(self):
from faebryk.exporters.pcb.layout.absolute import LayoutAbsolute
from faebryk.exporters.pcb.layout.extrude import LayoutExtrude
from faebryk.exporters.pcb.layout.typehierarchy import LayoutTypeHierarchy

Point = F.has_pcb_position.Point
L = F.has_pcb_position.layer_type

return F.has_pcb_layout_defined(
LayoutTypeHierarchy(
layouts=[
LayoutTypeHierarchy.Level(
mod_type=F.Crystal,
layout=LayoutAbsolute(
Point((0, 0, 0, L.NONE)),
),
),
LayoutTypeHierarchy.Level(
mod_type=F.Capacitor,
layout=LayoutExtrude(
base=Point((-3, 0, 0, L.NONE)),
vector=(0, 6, 180),
dynamic_rotation=True,
),
),
LayoutTypeHierarchy.Level(
mod_type=F.Resistor,
layout=LayoutAbsolute(Point((-3, -3, 0, L.NONE))),
),
]
),
)

@L.rt_field
def can_bridge(self):
return F.can_bridge_defined(self.xtal_if.xin, self.xtal_if.xout)
28 changes: 8 additions & 20 deletions src/faebryk/library/RP2040_ReferenceDesign.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ def __preinit__(self):
)
)

@L.rt_field
def single_reference(self):
return F.has_single_electric_reference_defined(self.logic_out.reference)

# ----------------------------------------
# modules, interfaces, parameters
# ----------------------------------------
Expand Down Expand Up @@ -192,8 +196,10 @@ def pcb_layout(self):
),
LayoutTypeHierarchy.Level(
mod_type=type(self.boot_selector),
layout=LayoutExtrude(
base=Point((-1.75, -11.5, 0, L.NONE)),
layout=LayoutAbsolute(
Point((-1.75, -11.5, 0, L.NONE)),
),
children_layout=LayoutExtrude(
vector=(3.5, 0, 90),
),
),
Expand All @@ -208,24 +214,6 @@ def pcb_layout(self):
layout=LayoutAbsolute(
Point((-10, 15, 0, L.NONE)),
),
children_layout=LayoutTypeHierarchy(
layouts=[
LayoutTypeHierarchy.Level(
mod_type=F.Crystal,
layout=LayoutAbsolute(
Point((0, 0, 0, L.NONE)),
),
),
LayoutTypeHierarchy.Level(
mod_type=F.Capacitor,
layout=LayoutExtrude(
base=Point((-3, 0, 90, L.NONE)),
vector=(0, 6, 180),
dynamic_rotation=True,
),
),
]
),
),
LayoutTypeHierarchy.Level(
mod_type=F.Resistor,
Expand Down
6 changes: 6 additions & 0 deletions src/faebryk/library/SPIFlash.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ class SPIFlash(Module):

memory_size: F.TBD[Quantity]
designator_prefix = L.f_field(F.has_designator_prefix_defined)("U")

@L.rt_field
def single_reference(self):
return F.has_single_electric_reference_defined(
F.ElectricLogic.connect_all_module_references(self)
)
4 changes: 4 additions & 0 deletions src/faebryk/library/USB2_0_ESD_Protection.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
import faebryk.library._F as F
from faebryk.core.module import Module
from faebryk.libs.library import L
from faebryk.libs.picker.picker import has_part_picked_remove
from faebryk.libs.units import P

logger = logging.getLogger(__name__)


# TODO this seems like it should be doing more
class USB2_0_ESD_Protection(Module):
usb = L.list_field(2, F.USB2_0)

Expand All @@ -23,6 +25,8 @@ def __preinit__(self):
self.usb[0].usb_if.buspower.connect(self.usb[1].usb_if.buspower)
self.usb[0].usb_if.buspower.decoupled.decouple()

no_pick: has_part_picked_remove

@L.rt_field
def can_bridge(self):
return F.can_bridge_defined(self.usb[0].usb_if.d, self.usb[1].usb_if.d)
Expand Down
9 changes: 5 additions & 4 deletions src/faebryk/library/USB_C_PSU_Vertical.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ class USB_C_PSU_Vertical(Module):
fuse: F.Fuse

def __preinit__(self):
self.gnd_capacitor.capacitance.merge(100 * P.nF)
self.gnd_capacitor.rated_voltage.merge(16 * P.V)
self.gnd_resistor.resistance.merge(1 * P.Mohm)
self.gnd_capacitor.capacitance.merge(F.Range.from_center_rel(100 * P.nF, 0.05))
self.gnd_capacitor.rated_voltage.merge(F.Range.from_center_rel(16 * P.V, 0.05))
self.gnd_resistor.resistance.merge(F.Range.from_center_rel(1 * P.Mohm, 0.05))
for res in self.configuration_resistors:
res.resistance.merge(5.1 * P.kohm)
res.resistance.merge(F.Range.from_center_rel(5.1 * P.kohm, 0.05))
self.fuse.fuse_type.merge(F.Fuse.FuseType.RESETTABLE)
self.fuse.trip_current.merge(F.Range.from_center_rel(1 * P.A, 0.05))

Expand All @@ -35,6 +35,7 @@ def __preinit__(self):
vusb = self.usb.usb_if.buspower
v5 = self.power_out
gnd = v5.lv
v5.voltage.merge(F.Range.from_center_rel(5 * P.V, 0.05))

vcon.hv.connect_via(self.fuse, v5.hv)
vcon.lv.connect(gnd)
Expand Down
8 changes: 8 additions & 0 deletions src/faebryk/library/USB_Type_C_Receptacle_14_pin_Vertical.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import faebryk.library._F as F
from faebryk.core.module import Module
from faebryk.libs.library import L
from faebryk.libs.picker.picker import DescriptiveProperties


class USB_Type_C_Receptacle_14_pin_Vertical(Module):
Expand All @@ -23,6 +24,13 @@ class USB_Type_C_Receptacle_14_pin_Vertical(Module):
# diffpairs: p, n
usb: F.USB2_0

descriptive_properties = L.f_field(F.has_descriptive_properties_defined)(
{
DescriptiveProperties.manufacturer.value: "Jing Extension of the Electronic Co.", # noqa: E501
DescriptiveProperties.partno: "918-418K2022Y40000",
}
)

@L.rt_field
def attach_to_footprint(self):
return F.can_attach_to_footprint_via_pinmap(
Expand Down
11 changes: 10 additions & 1 deletion src/faebryk/libs/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,20 @@ def find[T](haystack: Iterable[T], needle: Callable[[T], bool]) -> T:
return results[0]


def find_or[T](haystack: Iterable[T], needle: Callable[[T], bool], default: T) -> T:
def find_or[T](
haystack: Iterable[T],
needle: Callable[[T], bool],
default: T,
default_multi: Callable[[list[T]], T] | None = None,
) -> T:
try:
return find(haystack, needle)
except KeyErrorNotFound:
return default
except KeyErrorAmbiguous as e:
if default_multi is not None:
return default_multi(e.duplicates)
raise


def groupby[T, U](it: Iterable[T], key: Callable[[T], U]) -> dict[U, list[T]]:
Expand Down

0 comments on commit df97b0a

Please sign in to comment.