From 94a0ea2d7abb6162a550bf5ef39920cc755d5186 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Fri, 14 Aug 2026 19:57:13 -0700 Subject: [PATCH] Support subdivided nRF52 MOTA bridges --- .../build_rak3401_accelerated_bridges.py | 18 ++++++++- tools/lora_ota/rak3401_selective_os.py | 38 ++++++++++++++++++- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/tools/lora_ota/build_rak3401_accelerated_bridges.py b/tools/lora_ota/build_rak3401_accelerated_bridges.py index 79bba2ee..11281d63 100755 --- a/tools/lora_ota/build_rak3401_accelerated_bridges.py +++ b/tools/lora_ota/build_rak3401_accelerated_bridges.py @@ -44,6 +44,10 @@ class Target: source_commit: str bridge_stage: int = 0 os_stage: int = 0 + os_stage_part: int = 0 + os_stage_parts: int = 0 + os_stage_subpart: int = 0 + os_stage_subparts: int = 0 split_stage5: str = "" mymesh_opt: str = "" flood_rule_engine: bool = True @@ -255,7 +259,9 @@ def main() -> int: firmware_version = f"v{target.version}-{VERSION_SUFFIX}" build_env = dict(os.environ) for name in ( - "MOTA_BRIDGE_OS_STAGE", "MOTA_BRIDGE_SPLIT_STAGE5", + "MOTA_BRIDGE_OS_STAGE", "MOTA_BRIDGE_OS_STAGE_PART", + "MOTA_BRIDGE_OS_STAGE_PARTS", "MOTA_BRIDGE_OS_STAGE_SUBPART", + "MOTA_BRIDGE_OS_STAGE_SUBPARTS", "MOTA_BRIDGE_SPLIT_STAGE5", "MOTA_BRIDGE_MY_MESH_OPT", "MOTA_BRIDGE_OS_VERBOSE", "PLATFORMIO_BUILD_FLAGS", "PLATFORMIO_EXTRA_SCRIPTS", ): @@ -272,6 +278,12 @@ def main() -> int: if target.os_stage: build_env["MOTA_BRIDGE_OS_STAGE"] = str(target.os_stage) build_env["PLATFORMIO_EXTRA_SCRIPTS"] = f"pre:{SELECTIVE_OS_HOOK}" + if target.os_stage_parts: + build_env["MOTA_BRIDGE_OS_STAGE_PART"] = str(target.os_stage_part) + build_env["MOTA_BRIDGE_OS_STAGE_PARTS"] = str(target.os_stage_parts) + if target.os_stage_subparts: + build_env["MOTA_BRIDGE_OS_STAGE_SUBPART"] = str(target.os_stage_subpart) + build_env["MOTA_BRIDGE_OS_STAGE_SUBPARTS"] = str(target.os_stage_subparts) if target.split_stage5: build_env["MOTA_BRIDGE_SPLIT_STAGE5"] = target.split_stage5 if target.mymesh_opt: @@ -315,6 +327,10 @@ def main() -> int: record["build_flags"] = flags record["bridge_stage"] = target.bridge_stage record["os_stage"] = target.os_stage + record["os_stage_part"] = target.os_stage_part + record["os_stage_parts"] = target.os_stage_parts + record["os_stage_subpart"] = target.os_stage_subpart + record["os_stage_subparts"] = target.os_stage_subparts record["split_stage5"] = target.split_stage5 record["mymesh_opt"] = target.mymesh_opt record["flood_rule_engine"] = target.flood_rule_engine diff --git a/tools/lora_ota/rak3401_selective_os.py b/tools/lora_ota/rak3401_selective_os.py index 2f484f71..9f1bbe01 100755 --- a/tools/lora_ota/rak3401_selective_os.py +++ b/tools/lora_ota/rak3401_selective_os.py @@ -3,8 +3,8 @@ Stage 1 size-optimizes crypto/Curve25519 units first. The remaining C/C++ units are assigned deterministically to stages 2 through 13. A stage includes all earlier stages, so every intermediate is a normal bootable build and stage -13 is equivalent to globally selecting ``-Os``. The last two primary buckets -are subdivided because their measured nRF52 deltas exceeded internal staging. +13 is equivalent to globally selecting ``-Os``. A selected stage can also be +enabled cumulatively in deterministic parts when a board needs smaller deltas. """ import hashlib @@ -22,6 +22,21 @@ except ValueError as exc: if STAGE < 1 or STAGE > 13: raise RuntimeError("MOTA_BRIDGE_OS_STAGE must be in 1..13") +try: + STAGE_PART = int(os.environ.get("MOTA_BRIDGE_OS_STAGE_PART", "1")) + STAGE_PARTS = int(os.environ.get("MOTA_BRIDGE_OS_STAGE_PARTS", "1")) + STAGE_SUBPART = int(os.environ.get("MOTA_BRIDGE_OS_STAGE_SUBPART", "1")) + STAGE_SUBPARTS = int(os.environ.get("MOTA_BRIDGE_OS_STAGE_SUBPARTS", "1")) +except ValueError as exc: + raise RuntimeError("MOTA bridge stage part values must be integers") from exc + +if STAGE_PARTS < 1 or STAGE_PART < 1 or STAGE_PART > STAGE_PARTS: + raise RuntimeError("MOTA bridge stage part must be in 1..stage parts") +if STAGE_SUBPARTS < 1 or STAGE_SUBPART < 1 or STAGE_SUBPART > STAGE_SUBPARTS: + raise RuntimeError("MOTA bridge stage subpart must be in 1..stage subparts") +if STAGE_PARTS == 1 and STAGE_SUBPARTS != 1: + raise RuntimeError("MOTA bridge stage subparts require multiple stage parts") + CRYPTO_MARKERS = ( "ed25519", "curve25519", @@ -152,6 +167,18 @@ def _size_optimize_selected(env, node): unit_stage = _unit_stage(path, env) if unit_stage > STAGE: return node + if unit_stage == STAGE and STAGE_PARTS > 1: + normalized = _normalized_unit_path(path, env) + digest = hashlib.sha256(normalized.encode("utf-8")).digest() + bucket = digest[4] % STAGE_PARTS + if bucket >= STAGE_PART: + return node + if ( + bucket == STAGE_PART - 1 + and STAGE_SUBPARTS > 1 + and digest[5] % STAGE_SUBPARTS >= STAGE_SUBPART + ): + return node if ( STAGE == 5 and unit_stage == 5 @@ -175,6 +202,13 @@ def _size_optimize_selected(env, node): print(f"MOTA bridge selective optimizer stage {STAGE}/13") +if STAGE_PARTS > 1: + print(f"MOTA bridge stage {STAGE} cumulative part {STAGE_PART}/{STAGE_PARTS}") +if STAGE_SUBPARTS > 1: + print( + f"MOTA bridge stage {STAGE} current bucket subpart " + f"{STAGE_SUBPART}/{STAGE_SUBPARTS}" + ) if STAGE == 5 and os.environ.get("MOTA_BRIDGE_SPLIT_STAGE5") == "first": print("MOTA bridge stage 5 split: first half") env.AddBuildMiddleware(_size_optimize_selected)