Save RAK repeater flash while retaining fast field arithmetic
Use compact scalar crypto, reviewed GNSS version parsing and shared CLI reply copies for RAK3401/RAK4631 repeaters. Preserve keys, protocols, commands and configured capacities. Both Reduced images save 11,580 bytes. SWD measurements on the RAK3401 found scalar-only signing still near 50.9 ms and key exchange near 38 ms. Keep compact field arithmetic off: its extra 4.3 KiB saving adds about a quarter second and delays radio servicing. Validate Full and Reduced production OTA packages on both boards, focused crypto/GPS/CLI tests, and linked ARM crypto outputs. Include the RAM benchmark harness and scoped results. No live RF packet-loss qualification is claimed.
This commit is contained in:
1 parent
308fec1162
commit
08b1c8826c
9 files changed
+1256
-10
No files matched your search
@@ -5,6 +5,9 @@ and flags: build.sh can synthesize profiles over another PlatformIO recipe.
|
||||
Unknown roles fail closed. No shared framework or downloaded library is edited.
|
||||
CDC, USB host, sensor/GPS support, BLE P-256, and stored Ed25519 keys remain.
|
||||
Infrastructure uses RAM-built compact Ed25519 and display-font trims.
|
||||
RAK3401/RAK4631 repeaters also share CLI reply copies and use compact scalar
|
||||
arithmetic and the reviewed GNSS version parser without changing protocols.
|
||||
Fast field arithmetic is retained to avoid long radio-service stalls.
|
||||
Companion uses a 960-byte Ed25519 flash table to keep signing stack bounded,
|
||||
and retains all UI fonts and the Reader. The nRF52 base keeps an 8 KiB loop
|
||||
stack for crypto and the existing OTA path.
|
||||
@@ -70,6 +73,11 @@ COMPANION_TRIM_DEFINES["ED25519_SMALL_BASE"] = 1
|
||||
COMPANION_TRIM_DEFINES["ED25519_COMPACT_SHA512"] = 1
|
||||
COMPANION_TRIM_DEFINES["ED25519_COMPACT_SCALAR"] = 1
|
||||
|
||||
RAK_REPEATER_TRIM_DEFINES = {
|
||||
"ED25519_COMPACT_SCALAR": 1,
|
||||
"MESH_CLI_SHARE_REPLIES": 1,
|
||||
}
|
||||
|
||||
|
||||
def flag_defines(flags=(), cppdefines=()):
|
||||
"""Read effective defines without importing PlatformIO/SCons for tests."""
|
||||
@@ -115,6 +123,15 @@ def source_selected(src_filter, source):
|
||||
return selected
|
||||
|
||||
|
||||
def is_rak_repeater(mcu, src_filter, values):
|
||||
"""Qualify hardware and the effective role, including release overlays."""
|
||||
return (str(mcu).lower() == "nrf52840"
|
||||
and any(_enabled(values.get(board)) for board in ("RAK_3401", "RAK_4631"))
|
||||
and source_selected(src_filter, ROLE_SOURCES[0])
|
||||
and not any(source_selected(src_filter, source)
|
||||
for source in ROLE_SOURCES[1:] + COMPANION_SOURCES))
|
||||
|
||||
|
||||
def trim_policy(platform, mcu, env_name, src_filter, flags=(), cppdefines=(),
|
||||
ota_name_mode="front-coded"):
|
||||
"""Return defaults for a qualified non-Companion role, otherwise nothing.
|
||||
@@ -141,6 +158,8 @@ def trim_policy(platform, mcu, env_name, src_filter, flags=(), cppdefines=(),
|
||||
if ota_name_mode != "front-coded":
|
||||
raise ValueError("unsupported nRF52 OTA name trim policy: " + ota_name_mode)
|
||||
definitions = dict(TRIM_DEFINES, OTA_TARGET_NAME_FRONT_CODED=1)
|
||||
if is_rak_repeater(mcu, src_filter, values):
|
||||
definitions.update(RAK_REPEATER_TRIM_DEFINES)
|
||||
if _enabled(values.get("MESH_OTA_LOCAL_TARGET_NAME_ONLY")):
|
||||
# Release overlays can compile a different PIOENV from their deployed
|
||||
# OTA identity. Keep the logical name/ID in that case; direct builds
|
||||
@@ -240,6 +259,20 @@ def compile_packed_help(build_env, node):
|
||||
return build_env.Object(node, **flags)
|
||||
|
||||
|
||||
def compile_shared_cli_replies(build_env, node):
|
||||
"""Keep constant reply copies out of line in the qualified CLI object only."""
|
||||
source = Path(node.srcnode().get_abspath()).resolve()
|
||||
expected = Path(build_env.subst("$PROJECT_DIR/src/helpers/CommonCLI.cpp")).resolve()
|
||||
if source != expected:
|
||||
return node
|
||||
# GCC otherwise expands many constant strcpy replies into repeated stores.
|
||||
# Use the same libc function and strings, with no parser or output changes.
|
||||
flags = list(build_env.get("CCFLAGS", ()))
|
||||
if "-fno-builtin-strcpy" not in flags:
|
||||
flags.append("-fno-builtin-strcpy")
|
||||
return build_env.Object(node, CCFLAGS=flags)
|
||||
|
||||
|
||||
def install(build_env):
|
||||
platform = build_env.PioPlatform()
|
||||
mcu = build_env.BoardConfig().get("build.mcu", "")
|
||||
@@ -265,6 +298,12 @@ def install(build_env):
|
||||
hook = importlib.util.module_from_spec(hook_spec)
|
||||
hook_spec.loader.exec_module(hook)
|
||||
hook.install(build_env)
|
||||
values = flag_defines(flags, cppdefines)
|
||||
rak_repeater = infrastructure and is_rak_repeater(
|
||||
mcu, build_env.get("SRC_FILTER", ()), values)
|
||||
if rak_repeater and _enabled(values.get("MESH_CLI_SHARE_REPLIES",
|
||||
definitions.get("MESH_CLI_SHARE_REPLIES"))):
|
||||
build_env.AddBuildMiddleware(compile_shared_cli_replies, "*CommonCLI.cpp")
|
||||
if infrastructure:
|
||||
names = ("local OTA target name only" if definitions.get("OTA_TARGET_NAME_TABLE") == 0
|
||||
or flag_defines(flags, cppdefines).get("OTA_TARGET_NAME_TABLE") == "0"
|
||||
@@ -272,13 +311,14 @@ def install(build_env):
|
||||
print("nRF52 infrastructure flash trim: compact crypto, unused USB device classes/GPS cache/vendor splash, " + names)
|
||||
else:
|
||||
print("nRF52 Companion flash trim: small crypto table, unused USB device classes/GPS cache/vendor splash; fonts and Reader retained")
|
||||
if (not infrastructure or rak_repeater) and _enabled(
|
||||
values.get("MESH_NRF52_GNSS_VERSION_TRIM", "1")):
|
||||
hook_path = build_env.subst("$PROJECT_DIR/scripts/nrf52_gnss_version_trim.py")
|
||||
hook_spec = importlib.util.spec_from_file_location("mesh_nrf52_gnss_trim", hook_path)
|
||||
hook = importlib.util.module_from_spec(hook_spec)
|
||||
hook_spec.loader.exec_module(hook)
|
||||
hook.install(build_env)
|
||||
framework = platform.get_package_dir("framework-arduinoadafruitnrf52")
|
||||
values = flag_defines(flags, cppdefines)
|
||||
if "MESH_NRF52_NO_PROGRAM_EXIT" not in values and supported_program_lifetime(framework):
|
||||
build_env.AppendUnique(CPPDEFINES=[("MESH_NRF52_NO_PROGRAM_EXIT", 1)])
|
||||
print("nRF52 reviewed firmware lifetime: omit unreachable process-exit registrations")
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"""Keep the reviewed GNSS version parser without linking the scanf engine.
|
||||
|
||||
Only the qualified nRF52 Companion trim installs this middleware. Compile a
|
||||
private copy of the reviewed SparkFun GNSS sources; never change downloaded dependencies.
|
||||
Qualified nRF52 Companion and RAK3401/RAK4631 Repeater trims install this
|
||||
middleware. Compile a private copy of the reviewed SparkFun GNSS sources;
|
||||
never change downloaded dependencies.
|
||||
Unknown sources or headers retain their ordinary implementation.
|
||||
"""
|
||||
|
||||
@@ -11,6 +12,9 @@ from pathlib import Path
|
||||
|
||||
SOURCE_NAME = "SparkFun_u-blox_GNSS_Arduino_Library.cpp"
|
||||
SOURCE_SHA256 = "8ed1d313177df864b6821b3cb3e4fbcc59d850d09e2460269d571f70cd2b9f1c"
|
||||
# 2.2.28 has the same version parser and headers as 2.2.29. Its only source
|
||||
# differences are two file-buffer boundary checks, which this trim preserves.
|
||||
SOURCE_2_2_28_SHA256 = "4c6c40fe01b15b486791d9ceb045093a394bf157489c2b22d96d7afb92789c49"
|
||||
HEADERS = {
|
||||
"SparkFun_u-blox_GNSS_Arduino_Library.h": "5da7a95ddd4b24e26ca474d017a6a5347137a31aee58fb7b450a4ce85bbcc07a",
|
||||
"u-blox_config_keys.h": "86cae35ec200871f748be21d6d59583c48db82621f4c5504e257e8358a241499",
|
||||
@@ -38,7 +42,8 @@ def digest(source):
|
||||
|
||||
def patched_source(source):
|
||||
source = source.replace("\r\n", "\n")
|
||||
if (digest(source) != SOURCE_SHA256 or source.count(ORIGINAL_CONVERSION) != 1
|
||||
if (digest(source) not in (SOURCE_SHA256, SOURCE_2_2_28_SHA256)
|
||||
or source.count(ORIGINAL_CONVERSION) != 1
|
||||
or source.count(INCLUDE) != 1):
|
||||
raise RuntimeError("changed reviewed SparkFun GNSS source")
|
||||
return source.replace(INCLUDE, "#include <stdlib.h>\n" + INCLUDE, 1).replace(
|
||||
@@ -63,7 +68,7 @@ def replace_gnss_source(build_env, node):
|
||||
raise RuntimeError("changed reviewed SparkFun GNSS header: " + name)
|
||||
headers[name] = header
|
||||
except (OSError, UnicodeError, RuntimeError) as error:
|
||||
print("nRF52 Companion GNSS trim: ordinary source retained (" + str(error) + ")")
|
||||
print("nRF52 GNSS trim: ordinary source retained (" + str(error) + ")")
|
||||
return node
|
||||
destination = Path(build_env.subst("$BUILD_DIR")) / "trimmed-nrf52-gnss" / source.name
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
@@ -71,7 +76,7 @@ def replace_gnss_source(build_env, node):
|
||||
for name, header in headers.items():
|
||||
write_if_changed(destination.parent / name, header)
|
||||
write_if_changed(destination, patched)
|
||||
print("nRF52 Companion GNSS trim: reviewed version conversion uses strtol")
|
||||
print("nRF52 GNSS trim: reviewed version conversion uses strtol")
|
||||
return build_env.File(str(destination))
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user