From bae262eeb7ccfbbec10cd15e18c458cb6b679597 Mon Sep 17 00:00:00 2001 From: iopapamanoglou Date: Tue, 12 Nov 2024 18:46:34 +0100 Subject: [PATCH] multiple path limits; fix rp2040; fix minimal_led_order --- examples/minimal_led_orderable.py | 3 +- src/faebryk/core/cpp/__init__.pyi | 2 +- .../cpp/include/pathfinder/pathfinder.hpp | 14 ++- src/faebryk/core/cpp/src/main.cpp | 2 +- .../core/cpp/src/pathfinder/pathfinder.cpp | 13 +- src/faebryk/core/module.py | 4 + src/faebryk/core/moduleinterface.py | 2 +- src/faebryk/core/pathfinder.py | 12 +- src/faebryk/library/RP2040_ReferenceDesign.py | 8 +- src/faebryk/library/USB2_0_IF.py | 6 +- src/faebryk/libs/app/parameters.py | 6 +- test/core/test_hierarchy_connect.py | 113 +++++++++++++++++- 12 files changed, 164 insertions(+), 21 deletions(-) diff --git a/examples/minimal_led_orderable.py b/examples/minimal_led_orderable.py index da5777a2..b440a909 100644 --- a/examples/minimal_led_orderable.py +++ b/examples/minimal_led_orderable.py @@ -19,7 +19,7 @@ from faebryk.exporters.pcb.layout.typehierarchy import LayoutTypeHierarchy from faebryk.libs.app.checks import run_checks from faebryk.libs.app.manufacturing import export_pcba_artifacts -from faebryk.libs.app.parameters import replace_tbd_with_any +from faebryk.libs.app.parameters import replace_tbd_with_any, resolve_dynamic_parameters from faebryk.libs.brightness import TypicalLuminousIntensity from faebryk.libs.examples.buildutil import BUILD_DIR, PCB_FILE, apply_design_to_pcb from faebryk.libs.examples.pickers import add_example_pickers @@ -130,6 +130,7 @@ def main(): logger.info("Building app") app = App() G = app.get_graph() + resolve_dynamic_parameters(G) # picking ---------------------------------------------------------------- replace_tbd_with_any(app, recursive=True) diff --git a/src/faebryk/core/cpp/__init__.pyi b/src/faebryk/core/cpp/__init__.pyi index 64457813..c7224a32 100644 --- a/src/faebryk/core/cpp/__init__.pyi +++ b/src/faebryk/core/cpp/__init__.pyi @@ -200,4 +200,4 @@ def find_paths(src: Node, dst: Sequence[Node]) -> tuple[list[Path], list[Counter def print_obj(obj: object) -> None: ... def set_indiv_measure(value: bool) -> None: ... def set_leak_warnings(value: bool) -> None: ... -def set_max_paths(value: int) -> None: ... +def set_max_paths(arg0: int, arg1: int, arg2: int, /) -> None: ... diff --git a/src/faebryk/core/cpp/include/pathfinder/pathfinder.hpp b/src/faebryk/core/cpp/include/pathfinder/pathfinder.hpp index 318645f2..df8e3ec6 100644 --- a/src/faebryk/core/cpp/include/pathfinder/pathfinder.hpp +++ b/src/faebryk/core/cpp/include/pathfinder/pathfinder.hpp @@ -15,10 +15,18 @@ #include #include -inline uint32_t MAX_PATHS = 1 << 31; +struct PathLimits { + uint32_t absolute = 1 << 31; + uint32_t no_new_weak = 1 << 31; + uint32_t no_weak = 1 << 31; +}; + +inline PathLimits PATH_LIMITS; -inline void set_max_paths(uint32_t v) { - MAX_PATHS = v; +inline void set_max_paths(uint32_t absolute, uint32_t no_new_weak, uint32_t no_weak) { + PATH_LIMITS.absolute = absolute; + PATH_LIMITS.no_new_weak = no_new_weak; + PATH_LIMITS.no_weak = no_weak; } class PathFinder; diff --git a/src/faebryk/core/cpp/src/main.cpp b/src/faebryk/core/cpp/src/main.cpp index f2f4af45..04959dad 100644 --- a/src/faebryk/core/cpp/src/main.cpp +++ b/src/faebryk/core/cpp/src/main.cpp @@ -69,8 +69,8 @@ PYMOD(m) { // TODO why this rv_pol needed m.def("find_paths", &find_paths, "src"_a, "dst"_a, nb::rv_policy::reference); m.def("set_indiv_measure", &set_indiv_measure, "value"_a); - m.def("set_max_paths", &set_max_paths, "value"_a); + m.def("set_max_paths", &set_max_paths); // Graph using GI = GraphInterface; diff --git a/src/faebryk/core/cpp/src/pathfinder/pathfinder.cpp b/src/faebryk/core/cpp/src/pathfinder/pathfinder.cpp index 88715deb..da79e4f6 100644 --- a/src/faebryk/core/cpp/src/pathfinder/pathfinder.cpp +++ b/src/faebryk/core/cpp/src/pathfinder/pathfinder.cpp @@ -54,7 +54,7 @@ PathFinder::PathFinder() }, Filter{ .filter = &PathFinder::_build_path_stack, - .discovery = false, + .discovery = true, .counter = Counter{ .name = "build stack", @@ -177,7 +177,7 @@ bool PathFinder::_count(BFSPath &p) { if (path_cnt % 50000 == 0) { printf("path_cnt: %lld\n", path_cnt); } - if (path_cnt > MAX_PATHS) { + if (path_cnt > PATH_LIMITS.absolute) { p.stop = true; } return true; @@ -251,11 +251,20 @@ bool PathFinder::_build_path_stack(BFSPath &p) { auto &split_stack = splits.split_stack; size_t split_cnt = split_stack.size(); + if (split_cnt > 0 && path_cnt > PATH_LIMITS.no_weak) { + return false; + } + _extend_fold_stack(elem.value(), unresolved_stack, split_stack); int split_growth = split_stack.size() - split_cnt; p.confidence *= std::pow(0.5, split_growth); + // heuristic, stop making weaker paths after limit + if (split_growth > 0 && path_cnt > PATH_LIMITS.no_new_weak) { + return false; + } + return true; } diff --git a/src/faebryk/core/module.py b/src/faebryk/core/module.py index adb8d27f..bf1287e0 100644 --- a/src/faebryk/core/module.py +++ b/src/faebryk/core/module.py @@ -145,6 +145,10 @@ def connect_all_interfaces_by_name( for k, (src_m, dst_m) in src_.zip_children_by_name_with( dst_, ModuleInterface ).items(): + # TODO: careful this also connects runtime children + # for now skip stuff prefixed with _ + if k.startswith("_"): + continue if src_m is None or dst_m is None: if not allow_partial: raise Exception(f"Node with name {k} not present in both") diff --git a/src/faebryk/core/moduleinterface.py b/src/faebryk/core/moduleinterface.py index 28fb8df5..43c12718 100644 --- a/src/faebryk/core/moduleinterface.py +++ b/src/faebryk/core/moduleinterface.py @@ -31,7 +31,7 @@ logger = logging.getLogger(__name__) -IMPLIED_PATHS = ConfigFlag("IMPLIED_PATHS", default=True, descr="Use implied paths") +IMPLIED_PATHS = ConfigFlag("IMPLIED_PATHS", default=False, descr="Use implied paths") class ModuleInterface(Node): diff --git a/src/faebryk/core/pathfinder.py b/src/faebryk/core/pathfinder.py index 0ba1e931..bda3aa80 100644 --- a/src/faebryk/core/pathfinder.py +++ b/src/faebryk/core/pathfinder.py @@ -17,9 +17,17 @@ INDIV_MEASURE = ConfigFlag( "INDIV_MEASURE", default=True, descr="Measure individual paths" ) -MAX_PATHS = ConfigFlagInt("MAX_PATHS", default=int(1e5), descr="Max paths to search") set_indiv_measure(bool(INDIV_MEASURE)) -set_max_paths(int(MAX_PATHS)) + + +MAX_PATHS = ConfigFlagInt("MAX_PATHS", default=int(1e6), descr="Max paths to search") +MAX_PATHS_NO_NEW_WEAK = ConfigFlagInt( + "MAX_PATHS_NO_NEW_WEAK", default=int(1e3), descr="Max paths with no new weak" +) +MAX_PATHS_NO_WEAK = ConfigFlagInt( + "MAX_PATHS_NO_WEAK", default=int(1e4), descr="Max paths with no weak" +) +set_max_paths(int(MAX_PATHS), int(MAX_PATHS_NO_NEW_WEAK), int(MAX_PATHS_NO_WEAK)) def find_paths(src: Node, dst: Sequence[Node]) -> Sequence[Path]: diff --git a/src/faebryk/library/RP2040_ReferenceDesign.py b/src/faebryk/library/RP2040_ReferenceDesign.py index c2a8056b..1abb106e 100644 --- a/src/faebryk/library/RP2040_ReferenceDesign.py +++ b/src/faebryk/library/RP2040_ReferenceDesign.py @@ -108,8 +108,10 @@ def __preinit__(self): ) # USB - terminated_usb = self.usb.usb_if.d.terminated() - terminated_usb.impedance.merge(F.Range.from_center_rel(27.4 * P.ohm, 0.05)) + terminated_usb_data = self.add( + self.usb.usb_if.d.terminated(), "_terminated_usb_data" + ) + terminated_usb_data.impedance.merge(F.Range.from_center_rel(27.4 * P.ohm, 0.05)) # Flash self.flash.memory_size.merge(16 * P.Mbit) @@ -157,7 +159,7 @@ def __preinit__(self): self.flash.qspi.connect(self.rp2040.qspi) self.flash.qspi.chip_select.connect(self.boot_selector.logic_out) - terminated_usb.connect(self.rp2040.usb) + terminated_usb_data.connect(self.rp2040.usb) self.rp2040.xtal_if.connect(self.clock_source.xtal_if) diff --git a/src/faebryk/library/USB2_0_IF.py b/src/faebryk/library/USB2_0_IF.py index 0a892f70..7da0933b 100644 --- a/src/faebryk/library/USB2_0_IF.py +++ b/src/faebryk/library/USB2_0_IF.py @@ -9,6 +9,7 @@ class USB2_0_IF(ModuleInterface): class Data(F.DifferentialPair): + # FIXME: this should be in diffpair right? @L.rt_field def single_electric_reference(self): return F.has_single_electric_reference_defined( @@ -16,8 +17,9 @@ def single_electric_reference(self): ) def __preinit__(self): - self.p.reference.voltage.merge(F.Range(0 * P.V, 3.6 * P.V)) - self.n.reference.voltage.merge(F.Range(0 * P.V, 3.6 * P.V)) + self.single_electric_reference.get_reference().voltage.merge( + F.Range(0 * P.V, 3.6 * P.V) + ) d: Data buspower: F.ElectricPower diff --git a/src/faebryk/libs/app/parameters.py b/src/faebryk/libs/app/parameters.py index 7d5e9666..2399995a 100644 --- a/src/faebryk/libs/app/parameters.py +++ b/src/faebryk/libs/app/parameters.py @@ -83,15 +83,13 @@ def _resolve_dynamic_parameters_connection( set() ) - debug_stuff = {} - while params_grouped_by_mif: bus_representative_mif, bus_representative_params = ( params_grouped_by_mif.popitem() ) # expensive call - connections = set(bus_representative_mif.get_connected(include_self=True)) - debug_stuff[bus_representative_mif] = connections + paths = bus_representative_mif.get_connected(include_self=True) + connections = set(paths.keys()) busses.append(connections) if len(set(map(type, connections))) > 1: diff --git a/test/core/test_hierarchy_connect.py b/test/core/test_hierarchy_connect.py index c7a8c40f..1c16a11a 100644 --- a/test/core/test_hierarchy_connect.py +++ b/test/core/test_hierarchy_connect.py @@ -8,6 +8,7 @@ import faebryk.library._F as F from faebryk.core.link import ( + LinkDirect, LinkDirectConditional, LinkDirectConditionalFilterResult, LinkDirectDerived, @@ -15,8 +16,9 @@ from faebryk.core.module import Module from faebryk.core.moduleinterface import IMPLIED_PATHS, ModuleInterface from faebryk.libs.app.erc import ERCPowerSourcesShortedError, simple_erc +from faebryk.libs.app.parameters import resolve_dynamic_parameters from faebryk.libs.library import L -from faebryk.libs.util import times +from faebryk.libs.util import cast_assert, times logger = logging.getLogger(__name__) @@ -451,3 +453,112 @@ def test_shallow_implied_paths(): assert powers[3] in powers[0].get_connected() assert not powers[0].hv.is_connected_to(powers[3].hv) + + +def test_direct_shallow_instance(): + class MIFType(ModuleInterface): + pass + + mif1 = MIFType() + mif2 = MIFType() + mif3 = MIFType() + + mif1.connect_shallow(mif2, mif3) + assert isinstance( + mif1.connected.is_connected_to(mif2.connected), MIFType.LinkDirectShallow() + ) + assert isinstance( + mif1.connected.is_connected_to(mif3.connected), MIFType.LinkDirectShallow() + ) + + +def test_regression_rp2040_usb_diffpair_minimal(): + usb = F.USB2_0_IF.Data() + terminated_usb = usb.terminated() + + other_usb = F.USB2_0_IF.Data() + terminated_usb.connect(other_usb) + + n_ref = usb.n.reference + p_ref = usb.p.reference + t_n_ref = terminated_usb.n.reference + t_p_ref = terminated_usb.p.reference + o_n_ref = other_usb.n.reference + o_p_ref = other_usb.p.reference + refs = {n_ref, p_ref, t_n_ref, t_p_ref, o_n_ref, o_p_ref} + + assert isinstance( + usb.connected.is_connected_to(terminated_usb.connected), + F.USB2_0_IF.Data.LinkDirectShallow(), + ) + assert isinstance( + other_usb.connected.is_connected_to(terminated_usb.connected), LinkDirect + ) + assert usb.connected.is_connected_to(other_usb.connected) is None + + connected_per_mif = {ref: ref.get_connected(include_self=True) for ref in refs} + + assert not {n_ref, p_ref} & connected_per_mif[t_n_ref].keys() + assert not {n_ref, p_ref} & connected_per_mif[t_p_ref].keys() + assert not {t_n_ref, t_p_ref} & connected_per_mif[n_ref].keys() + assert not {t_n_ref, t_p_ref} & connected_per_mif[p_ref].keys() + + assert set(connected_per_mif[n_ref].keys()) == {n_ref, p_ref} + assert set(connected_per_mif[p_ref].keys()) == {n_ref, p_ref} + assert set(connected_per_mif[t_n_ref].keys()) == { + t_n_ref, + t_p_ref, + o_n_ref, + o_p_ref, + } + assert set(connected_per_mif[t_p_ref].keys()) == { + t_n_ref, + t_p_ref, + o_n_ref, + o_p_ref, + } + + # close references + p_ref.connect(other_usb.p.reference) + + connected_per_mif_post = {ref: ref.get_connected(include_self=True) for ref in refs} + for _, connected in connected_per_mif_post.items(): + assert set(connected.keys()).issuperset(refs) + + +def test_regression_rp2040_usb_diffpair(): + app = F.RP2040_ReferenceDesign() + + terminated_usb = cast_assert(F.USB2_0_IF.Data, app.runtime["_terminated_usb_data"]) + rp_usb = app.rp2040.usb + + t_p_ref = terminated_usb.p.reference + t_n_ref = terminated_usb.n.reference + r_p_ref = rp_usb.p.reference + r_n_ref = rp_usb.n.reference + refs = [ + r_p_ref, + r_n_ref, + t_p_ref, + t_n_ref, + ] + + connected_per_mif = {ref: ref.get_connected(include_self=True) for ref in refs} + for connected in connected_per_mif.values(): + assert set(connected.keys()) == set(refs) + + +def test_regression_rp2040_usb_diffpair_full(): + app = F.RP2040_ReferenceDesign() + rp2040_2 = F.RP2040() + rp2040_3 = F.RP2040() + + # make graph bigger + app.rp2040.i2c[0].connect(rp2040_2.i2c[0]) + app.rp2040.i2c[0].connect(rp2040_3.i2c[0]) + + resolve_dynamic_parameters(app.get_graph()) + + +if __name__ == "__main__": + test_regression_rp2040_usb_diffpair()