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
@@ -584,6 +584,11 @@ reduction does not disable generic I2C or unrelated board-integrated peripherals
|
||||
These reduced RAK profiles keep their own readable OTA target name and omit
|
||||
the all-board name catalog. Updates for other targets display numeric IDs;
|
||||
OTA compatibility checks and transfers are unchanged.
|
||||
RAK3401/RAK4631 Repeater builds also use compact scalar arithmetic, shared CLI
|
||||
reply copies, and a smaller GPS version parser. Keys, packet formats, commands,
|
||||
responses and GPS support are preserved. Fast field arithmetic remains enabled
|
||||
to avoid the long crypto pause; see the
|
||||
[measured size and timing comparison](test-results/rak_compact_crypto_2026-10-27.md).
|
||||
Reduced RAK3401 and RAK4631 targets retain INA219/INA226/INA260/INA3221 as
|
||||
voltage/current entries in the optional sensor table. They are not the only I2C
|
||||
users: the SSD1306 OLED, supported autodiscovered RTCs, and RAK12500 GPS remain
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
# RAK repeater flash and crypto timing, 27 October 2026
|
||||
|
||||
Selected: **compact scalar arithmetic with the existing fast field arithmetic**.
|
||||
The GPS parser and CLI reply-copy changes are also enabled. No commands, GPS
|
||||
providers, keys, packet formats or configured capacities are removed.
|
||||
|
||||
Both Reduced RAK repeater builds save **11,580 bytes (11.31 KiB)**. The much
|
||||
slower compact-field experiment saves only another 4,384 bytes (4.28 KiB) and
|
||||
is **not enabled by this change**.
|
||||
|
||||
## Physical RAK3401 measurements
|
||||
|
||||
The Pi's GPIO SWD debugger ran each implementation on the same RAK3401. Values
|
||||
below are medians from 18 measurements per operation, using two public test
|
||||
seeds from RFC 8032 and one discarded warmup per seed/operation.
|
||||
|
||||
| Operation | Before, ms | Selected scalar-only, ms | Rejected scalar + field, ms |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Generate keypair | 47.513 | 47.145 | 297.863 |
|
||||
| Sign empty message | 48.738 | 48.753 | 299.472 |
|
||||
| Sign 96-byte message | 50.887 | 50.902 | 301.621 |
|
||||
| Sign 256-byte message | 52.991 | 53.006 | 303.725 |
|
||||
| Calculate shared secret | 38.147 | 37.951 | 274.195 |
|
||||
|
||||
The selected implementation adds **0.015 ms** to signing in this measurement.
|
||||
The unchanged field routines also show small timing differences between
|
||||
layouts; these measurements do not promise a 15 us difference in every build.
|
||||
The rejected field implementation adds about 251 ms to signing and 236 ms to
|
||||
key exchange.
|
||||
|
||||
Code and constants ran from **SRAM**, at 64 MHz, with interrupts masked. The
|
||||
Cortex-M4 DWT cycle counter measured each operation. The 20-cycle measurement
|
||||
overhead is included in every result. These are isolated algorithm timings,
|
||||
not flash-resident firmware, Bluetooth, LoRa airtime or end-to-end CLI timings.
|
||||
|
||||
Every measured public key, signature and shared secret matched independent
|
||||
host cryptography. Signature checks covered empty, 96-byte and 256-byte
|
||||
messages for both seeds. Shared secrets were checked through an independent
|
||||
Edwards-to-Montgomery conversion and X25519 implementation.
|
||||
|
||||
The existing watchdog survives software reset. The harness feeds its enabled
|
||||
channels between operations, outside the timed region. Initial attempts that
|
||||
failed reset/watchdog setup gates produced no accepted measurements. The final
|
||||
three runs completed and reset into the existing application. Application
|
||||
flash, bootloader and stored data were not updated; the Pi's gateway and host
|
||||
CLI services were restored and reported active.
|
||||
|
||||
## When this work runs
|
||||
|
||||
- Signing happens when creating the repeater's own adverts and signed fleet or
|
||||
host-service messages. Advert frequency follows the saved advert intervals.
|
||||
- Fresh shared-secret calculations happen for login attempts and anonymous
|
||||
region, owner and clock requests addressed to the node, including outgoing
|
||||
region queries. Boot also reconstructs secrets for stored ACL entries.
|
||||
- Established ACL sessions copy cached secrets. Ordinary relaying does not
|
||||
sign every forwarded packet. Receive-side signature verification retains
|
||||
its existing CC310 implementation and existing software fallback.
|
||||
|
||||
Already queued software packets can wait during crypto, but the radio ISR only
|
||||
records an event. The main loop reads the radio FIFO and services radio2
|
||||
switching later. Extending a synchronous operation by a quarter second can
|
||||
therefore lose new RF packets; software queues do not make that lossless.
|
||||
The selected policy avoids that added pause. **Live RF packet-loss testing was
|
||||
not performed for this optimization.**
|
||||
|
||||
## Firmware size and build checks
|
||||
|
||||
Sizes are ELF `.text + .data`, not the larger UF2 container size. Baselines
|
||||
already include the earlier reduced-build OTA target-name catalog saving.
|
||||
|
||||
| Build | Before, bytes | Selected, bytes | Saving, bytes |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| RAK3401 Reduced repeater | 575,644 | 564,064 | 11,580 |
|
||||
| RAK4631 Reduced repeater | 626,884 | 615,304 | 11,580 |
|
||||
|
||||
Full and Reduced production packaging passed for both RAKs with the Cascade
|
||||
profile and required LoRa OTA. Checks included capability markers, conservative
|
||||
RAM budgets, compiled Bluetooth DFU handoff and application-only UF2 address
|
||||
ranges. This is build validation, not physical OTA qualification. Full RAK4631
|
||||
retains a tight conservative RAM budget: 83,180 bytes available against an
|
||||
83,136-byte estimate. The existing 8 KiB loop stack is retained.
|
||||
|
||||
Focused scalar/field arithmetic, RFC/random crypto parity, sanitizer, GNSS
|
||||
parser, CLI argument and fleet schedule tests passed. Checks of both selected
|
||||
linked ARM firmware images also reproduced the expected public keys,
|
||||
signatures and shared secrets. Their emulated signing frame grows from 3,312
|
||||
to 3,512 bytes, excluding caller and interrupt frames; keypair and key-exchange
|
||||
frames are unchanged. This fits within the retained loop stack but is not a
|
||||
measurement of whole-application stack usage.
|
||||
|
||||
## Reproduction and evidence
|
||||
|
||||
The [RAM harness](https://github.com/mikecarper/MeshCore/blob/keymindCascade/tools/hil/nrf52_crypto_benchmark.c) and
|
||||
[linker script](https://github.com/mikecarper/MeshCore/blob/keymindCascade/tools/hil/nrf52_crypto_benchmark.ld) are for debugger use
|
||||
on an nRF52840 only. They are not production application images. Build with
|
||||
GCC 14.2.1, `-Os -flto -mcpu=cortex-m4 -mthumb -mfloat-abi=hard
|
||||
-mfpu=fpv4-sp-d16 -ffunction-sections -fdata-sections -fno-strict-aliasing`,
|
||||
`ED25519_COMPACT_BASE=1`, `ED25519_COMPACT_SHA512=1` and `ED25519_NO_SEED=1`.
|
||||
Link `fe.c`, `ge.c`, `keypair.c`, `sc.c`, `sha512.c`, `sign.c` and
|
||||
`key_exchange.c` from `lib/ed25519`, with `-nostartfiles`, the supplied linker
|
||||
script, `--gc-sections`, libc and libgcc.
|
||||
|
||||
Use scalar/field flags `0/0` for baseline, `1/0` for selected, and `1/1` for the
|
||||
rejected experiment. `BENCH_COMPACT` is only a report label (0, 2 and 1,
|
||||
respectively). The recorded scalar-only run used scalar flag 2, which selects
|
||||
the same nonzero preprocessor branch as production value 1.
|
||||
|
||||
After verifying the physical chip identity, reset/halt, load the ELF into RAM,
|
||||
resume at `bench_start`, and read `report` only after its completion marker is
|
||||
`0xC0DEF17E`. The watchdog must pause while halted. Reset back into the normal
|
||||
application afterward. No flash programming is needed for this benchmark.
|
||||
|
||||
[Machine-readable results](rak_compact_crypto_2026-10-27.safe.json) include raw
|
||||
cycle counts, image and harness hashes, build checks, and the rejected candidate
|
||||
for comparison.
|
||||
@@ -0,0 +1,923 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"date": "2026-10-27",
|
||||
"base_commit": "308fec1162e77134d15206b819dd59e898ed910b",
|
||||
"selected_policy": "Compact scalar arithmetic, ordinary fast field arithmetic, reviewed GPS parser, shared CLI reply-copy compiler policy. RAK3401/RAK4631 repeater roles only.",
|
||||
"scope": "RAK3401 physical RAM-resident algorithm timing and production Full/Reduced build checks for both RAKs. This is not flash-resident application timing or live RF/OTA qualification.",
|
||||
"selected_flash_images": {
|
||||
"3401": {
|
||||
"full": {
|
||||
"flash_text_bytes": 622456,
|
||||
"flash_data_bytes": 880,
|
||||
"flash_bytes": 623336,
|
||||
"bss_bytes": 234568,
|
||||
"elf_sha256": "1e7764ba9d5b081fe1dde7cff5d6a67a067d38ab03549d2889eadace09706dd7",
|
||||
"available_internal_bytes": 80212,
|
||||
"conservative_required_heap_bytes": 75920,
|
||||
"capabilities_manifest_sha256": "5d1dcea9e49241c94caf77632ae5e277794571ce8b079817027a24c8f228a5bf"
|
||||
},
|
||||
"reduced": {
|
||||
"flash_text_bytes": 563184,
|
||||
"flash_data_bytes": 880,
|
||||
"flash_bytes": 564064,
|
||||
"bss_bytes": 234568,
|
||||
"elf_sha256": "4f7ea9df20e9fbbcab97fc1f98d67cbaaea9fd59d79111b4c01dcf83b4338330",
|
||||
"available_internal_bytes": 82720,
|
||||
"conservative_required_heap_bytes": 73872,
|
||||
"capabilities_manifest_sha256": "9e9d02a55b1ace5be35a092ce51cb90509523183cfccfce6597c8e7d4a1eb4da"
|
||||
},
|
||||
"reduced_before": {
|
||||
"flash_text_bytes": 574764,
|
||||
"flash_data_bytes": 880,
|
||||
"flash_bytes": 575644,
|
||||
"bss_bytes": 234568,
|
||||
"elf_sha256": "cc735f36b3976527b7a295fed78a43627c41e565b81ce54418ff1ecd22e712f2"
|
||||
},
|
||||
"reduced_saved_bytes": 11580
|
||||
},
|
||||
"4631": {
|
||||
"full": {
|
||||
"flash_text_bytes": 682064,
|
||||
"flash_data_bytes": 884,
|
||||
"flash_bytes": 682948,
|
||||
"bss_bytes": 234564,
|
||||
"elf_sha256": "a4b4fda84ca6cadb733de703e692f608691a5dcd3cf31e474ad0c3898105eef4",
|
||||
"available_internal_bytes": 83180,
|
||||
"conservative_required_heap_bytes": 83136,
|
||||
"capabilities_manifest_sha256": "c1b05b741f6a822334d33083d51737435b2f7e72eea3633b24eda07fc43a6971"
|
||||
},
|
||||
"reduced": {
|
||||
"flash_text_bytes": 614420,
|
||||
"flash_data_bytes": 884,
|
||||
"flash_bytes": 615304,
|
||||
"bss_bytes": 234564,
|
||||
"elf_sha256": "23fae794233d6df9d2c44cf490dba5ee1b4454f49d614a8291fd92e10e04eab1",
|
||||
"available_internal_bytes": 85696,
|
||||
"conservative_required_heap_bytes": 81088,
|
||||
"capabilities_manifest_sha256": "4aa11fff367ddd1f7c70b103994dd50cad984158658eb361a5b411c2e8fcefde"
|
||||
},
|
||||
"reduced_before": {
|
||||
"flash_text_bytes": 626000,
|
||||
"flash_data_bytes": 884,
|
||||
"flash_bytes": 626884,
|
||||
"bss_bytes": 234564,
|
||||
"elf_sha256": "560e9863598a122fe39bc85fa150d0ec4c96482cd00e74779d99c74fe1407bb9"
|
||||
},
|
||||
"reduced_saved_bytes": 11580
|
||||
}
|
||||
},
|
||||
"hardware_timing": {
|
||||
"baseline": {
|
||||
"independent_outputs_checked": 10,
|
||||
"elf_sha256": "05e2292b9cc1ea3273dc21c366d0244a8c8176a7d817f65308fc70db51b14d90",
|
||||
"report_sha256": "e14a1135ddb7cc8f3800a6d75320b3aced6390ed09900b9a85179b832757f8e4",
|
||||
"cpu_hz": 64000000,
|
||||
"cpuid": "0x410fc241",
|
||||
"clock_stat": "0x10001",
|
||||
"watchdog": {
|
||||
"running": 1,
|
||||
"config": 1,
|
||||
"timeout_s": 60.0
|
||||
},
|
||||
"measurement_overhead_cycles": [
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20
|
||||
],
|
||||
"measurements": {
|
||||
"keypair": {
|
||||
"cycles": [
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814,
|
||||
3040814
|
||||
],
|
||||
"min_ms": 47.51271875,
|
||||
"median_ms": 47.51271875,
|
||||
"max_ms": 47.51271875
|
||||
},
|
||||
"sign_empty": {
|
||||
"cycles": [
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219,
|
||||
3119219
|
||||
],
|
||||
"min_ms": 48.737796875,
|
||||
"median_ms": 48.737796875,
|
||||
"max_ms": 48.737796875
|
||||
},
|
||||
"sign96": {
|
||||
"cycles": [
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753,
|
||||
3256753
|
||||
],
|
||||
"min_ms": 50.886765625,
|
||||
"median_ms": 50.886765625,
|
||||
"max_ms": 50.886765625
|
||||
},
|
||||
"sign256": {
|
||||
"cycles": [
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411,
|
||||
3391411
|
||||
],
|
||||
"min_ms": 52.990796875,
|
||||
"median_ms": 52.990796875,
|
||||
"max_ms": 52.990796875
|
||||
},
|
||||
"shared_secret": {
|
||||
"cycles": [
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426,
|
||||
2441426
|
||||
],
|
||||
"min_ms": 38.14728125,
|
||||
"median_ms": 38.14728125,
|
||||
"max_ms": 38.14728125
|
||||
}
|
||||
}
|
||||
},
|
||||
"compact": {
|
||||
"independent_outputs_checked": 10,
|
||||
"elf_sha256": "f637e1d5fda112ca1a8b19aa1cee6589d70db30de894ec28529c7a3dce8d16d3",
|
||||
"report_sha256": "fa1ba95afd7a7d7d1b1047e82894ed0644608ddafc3ce9403949b62842721bd6",
|
||||
"cpu_hz": 64000000,
|
||||
"cpuid": "0x410fc241",
|
||||
"clock_stat": "0x10001",
|
||||
"watchdog": {
|
||||
"running": 1,
|
||||
"config": 1,
|
||||
"timeout_s": 60.0
|
||||
},
|
||||
"measurement_overhead_cycles": [
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20
|
||||
],
|
||||
"measurements": {
|
||||
"keypair": {
|
||||
"cycles": [
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260,
|
||||
19063260
|
||||
],
|
||||
"min_ms": 297.8634375,
|
||||
"median_ms": 297.8634375,
|
||||
"max_ms": 297.8634375
|
||||
},
|
||||
"sign_empty": {
|
||||
"cycles": [
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191,
|
||||
19166191
|
||||
],
|
||||
"min_ms": 299.471734375,
|
||||
"median_ms": 299.471734375,
|
||||
"max_ms": 299.471734375
|
||||
},
|
||||
"sign96": {
|
||||
"cycles": [
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725,
|
||||
19303725
|
||||
],
|
||||
"min_ms": 301.620703125,
|
||||
"median_ms": 301.620703125,
|
||||
"max_ms": 301.620703125
|
||||
},
|
||||
"sign256": {
|
||||
"cycles": [
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383,
|
||||
19438383
|
||||
],
|
||||
"min_ms": 303.724734375,
|
||||
"median_ms": 303.724734375,
|
||||
"max_ms": 303.724734375
|
||||
},
|
||||
"shared_secret": {
|
||||
"cycles": [
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456,
|
||||
17548456
|
||||
],
|
||||
"min_ms": 274.194625,
|
||||
"median_ms": 274.194625,
|
||||
"max_ms": 274.194625
|
||||
}
|
||||
}
|
||||
},
|
||||
"scalar_only": {
|
||||
"independent_outputs_checked": 10,
|
||||
"elf_sha256": "b4da043bc9e91fb4ddacce31e766d363e01dcfb861bff796c76d35391a1450d8",
|
||||
"report_sha256": "ce660aa04cacfa7ef671c943611ac2ce7b56e53fe5e91a2012e5d03a33dbdde4",
|
||||
"cpu_hz": 64000000,
|
||||
"cpuid": "0x410fc241",
|
||||
"clock_stat": "0x10001",
|
||||
"watchdog": {
|
||||
"running": 1,
|
||||
"config": 1,
|
||||
"timeout_s": 60.0
|
||||
},
|
||||
"measurement_overhead_cycles": [
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20,
|
||||
20
|
||||
],
|
||||
"measurements": {
|
||||
"keypair": {
|
||||
"cycles": [
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258,
|
||||
3017258
|
||||
],
|
||||
"min_ms": 47.14465625,
|
||||
"median_ms": 47.14465625,
|
||||
"max_ms": 47.14465625
|
||||
},
|
||||
"sign_empty": {
|
||||
"cycles": [
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189,
|
||||
3120189
|
||||
],
|
||||
"min_ms": 48.752953125,
|
||||
"median_ms": 48.752953125,
|
||||
"max_ms": 48.752953125
|
||||
},
|
||||
"sign96": {
|
||||
"cycles": [
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723,
|
||||
3257723
|
||||
],
|
||||
"min_ms": 50.901921875,
|
||||
"median_ms": 50.901921875,
|
||||
"max_ms": 50.901921875
|
||||
},
|
||||
"sign256": {
|
||||
"cycles": [
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381,
|
||||
3392381
|
||||
],
|
||||
"min_ms": 53.005953125,
|
||||
"median_ms": 53.005953125,
|
||||
"max_ms": 53.005953125
|
||||
},
|
||||
"shared_secret": {
|
||||
"cycles": [
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881,
|
||||
2428881
|
||||
],
|
||||
"min_ms": 37.951265625,
|
||||
"median_ms": 37.951265625,
|
||||
"max_ms": 37.951265625
|
||||
}
|
||||
}
|
||||
},
|
||||
"comparison": {
|
||||
"keypair": {
|
||||
"added_ms": 250.35071874999997,
|
||||
"ratio": 6.269130568328086
|
||||
},
|
||||
"sign_empty": {
|
||||
"added_ms": 250.73393749999997,
|
||||
"ratio": 6.144548042314438
|
||||
},
|
||||
"sign96": {
|
||||
"added_ms": 250.73393749999997,
|
||||
"ratio": 5.927291692062615
|
||||
},
|
||||
"sign256": {
|
||||
"added_ms": 250.73393750000002,
|
||||
"ratio": 5.731650631551293
|
||||
},
|
||||
"shared_secret": {
|
||||
"added_ms": 236.04734374999998,
|
||||
"ratio": 7.187789431258617
|
||||
}
|
||||
},
|
||||
"scalar_only_comparison": {
|
||||
"keypair": {
|
||||
"added_ms": -0.3680625000000006
|
||||
},
|
||||
"sign_empty": {
|
||||
"added_ms": 0.015156249999996874
|
||||
},
|
||||
"sign96": {
|
||||
"added_ms": 0.015156249999996874
|
||||
},
|
||||
"sign256": {
|
||||
"added_ms": 0.015156249999996874
|
||||
},
|
||||
"shared_secret": {
|
||||
"added_ms": -0.19601562500000114
|
||||
}
|
||||
}
|
||||
},
|
||||
"hardware_method": {
|
||||
"clock_hz": 64000000,
|
||||
"code_and_constants": "SRAM",
|
||||
"interrupts": "masked",
|
||||
"per_operation_samples": 18,
|
||||
"vectors": 2,
|
||||
"warmups_per_vector_and_operation": 1,
|
||||
"watchdog": "fed before each operation, outside the timed region; 60 second configured timeout",
|
||||
"counter": "Cortex-M4 DWT CYCCNT",
|
||||
"overhead": "20 cycles, included in the displayed timings",
|
||||
"normal_application_flash_changed": false,
|
||||
"bootloader_changed": false,
|
||||
"existing_application_and_gateway_services_restored": true
|
||||
},
|
||||
"benchmark_sources": {
|
||||
"tools/hil/nrf52_crypto_benchmark.c": "64f4d2f4d189e9d4b01a64b33f1f2d461a86bc7c276f4a514459325c7a022a23",
|
||||
"tools/hil/nrf52_crypto_benchmark.ld": "c12f3aaabad0a81c788c5eb869d09e5ecd5de5b2982af5c034daf2140b688f17"
|
||||
},
|
||||
"compiler": "arm-none-eabi-gcc 14.2.1",
|
||||
"benchmark_flags": [
|
||||
"-std=c99",
|
||||
"-Os",
|
||||
"-flto",
|
||||
"-mcpu=cortex-m4",
|
||||
"-mthumb",
|
||||
"-mfloat-abi=hard",
|
||||
"-mfpu=fpv4-sp-d16",
|
||||
"-ffunction-sections",
|
||||
"-fdata-sections",
|
||||
"-fno-strict-aliasing",
|
||||
"-DED25519_NO_SEED=1",
|
||||
"-DED25519_COMPACT_BASE=1",
|
||||
"-DED25519_COMPACT_SHA512=1"
|
||||
],
|
||||
"benchmark_modes": {
|
||||
"baseline": {
|
||||
"BENCH_COMPACT": 0,
|
||||
"ED25519_COMPACT_SCALAR": 0,
|
||||
"ED25519_COMPACT_FIELD": 0
|
||||
},
|
||||
"compact": {
|
||||
"BENCH_COMPACT": 1,
|
||||
"ED25519_COMPACT_SCALAR": 1,
|
||||
"ED25519_COMPACT_FIELD": 1
|
||||
},
|
||||
"scalar_only": {
|
||||
"BENCH_COMPACT": 2,
|
||||
"ED25519_COMPACT_SCALAR": 2,
|
||||
"ED25519_COMPACT_FIELD": 0,
|
||||
"note": "Scalar flag 2 is true in the same preprocessor gate as the selected production value 1."
|
||||
}
|
||||
},
|
||||
"rejected_field_candidate": {
|
||||
"reason": "Adds about 251 ms to signing and 236 ms to shared-secret calculation; synchronous radio servicing can be delayed.",
|
||||
"production_flash_images": {
|
||||
"3401": {
|
||||
"reduced_before": {
|
||||
"flash_text_bytes": 574764,
|
||||
"flash_data_bytes": 880,
|
||||
"flash_bytes": 575644,
|
||||
"bss_bytes": 234568,
|
||||
"elf_sha256": "cc735f36b3976527b7a295fed78a43627c41e565b81ce54418ff1ecd22e712f2"
|
||||
},
|
||||
"reduced_after": {
|
||||
"flash_text_bytes": 558800,
|
||||
"flash_data_bytes": 880,
|
||||
"flash_bytes": 559680,
|
||||
"bss_bytes": 234568,
|
||||
"elf_sha256": "4306af7914d4f2a21296d21537a6959e9d69bddb6c6f5ac24b007ce1bfaea34a"
|
||||
},
|
||||
"full_after": {
|
||||
"flash_text_bytes": 618056,
|
||||
"flash_data_bytes": 880,
|
||||
"flash_bytes": 618936,
|
||||
"bss_bytes": 234568,
|
||||
"elf_sha256": "320cf1ef5a436c77781f69618fee4824cf52518d6756f216fdebc8a2e3b46b71"
|
||||
},
|
||||
"reduced_saved_bytes": 15964
|
||||
},
|
||||
"4631": {
|
||||
"reduced_before": {
|
||||
"flash_text_bytes": 626000,
|
||||
"flash_data_bytes": 884,
|
||||
"flash_bytes": 626884,
|
||||
"bss_bytes": 234564,
|
||||
"elf_sha256": "560e9863598a122fe39bc85fa150d0ec4c96482cd00e74779d99c74fe1407bb9"
|
||||
},
|
||||
"reduced_after": {
|
||||
"flash_text_bytes": 610036,
|
||||
"flash_data_bytes": 884,
|
||||
"flash_bytes": 610920,
|
||||
"bss_bytes": 234564,
|
||||
"elf_sha256": "09355d3e839821ad3147be87199dd3cd608b3a1978cbd19234661f304d0a7e84"
|
||||
},
|
||||
"full_after": {
|
||||
"flash_text_bytes": 677680,
|
||||
"flash_data_bytes": 884,
|
||||
"flash_bytes": 678564,
|
||||
"bss_bytes": 234564,
|
||||
"elf_sha256": "5f75a4b1b699279d0746aa8c041015665948c6bc3ba02a1d1c1e146c23b7aa10"
|
||||
},
|
||||
"reduced_saved_bytes": 15964
|
||||
}
|
||||
},
|
||||
"additional_reduced_savings_bytes": 4384,
|
||||
"prior_4631_incremental_savings_bytes": {
|
||||
"compact_scalar_and_field": 14008,
|
||||
"gps_version_parser": 1940,
|
||||
"cli_reply_copy_compiler_policy": 16,
|
||||
"total": 15964
|
||||
}
|
||||
},
|
||||
"production_validation": {
|
||||
"3401_full": {
|
||||
"capability_markers": 14,
|
||||
"passed": true
|
||||
},
|
||||
"3401_reduced": {
|
||||
"capability_markers": 12,
|
||||
"passed": true
|
||||
},
|
||||
"4631_full": {
|
||||
"capability_markers": 16,
|
||||
"passed": true
|
||||
},
|
||||
"4631_reduced": {
|
||||
"capability_markers": 14,
|
||||
"passed": true
|
||||
},
|
||||
"checks": [
|
||||
"LoRa OTA required by production packaging",
|
||||
"Conservative RAM budget",
|
||||
"Compiled Bluetooth DFU handoff",
|
||||
"Application-only UF2 address range"
|
||||
]
|
||||
},
|
||||
"linked_arm_checks": {
|
||||
"3401": {
|
||||
"passed": true,
|
||||
"elf_sha256": "4f7ea9df20e9fbbcab97fc1f98d67cbaaea9fd59d79111b4c01dcf83b4338330",
|
||||
"instruction_meter": "native",
|
||||
"measurements": [
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 1,
|
||||
"message_bytes": 0,
|
||||
"stack_bytes": 3512,
|
||||
"instructions": 1678474
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 1,
|
||||
"message_bytes": 96,
|
||||
"stack_bytes": 3512,
|
||||
"instructions": 1706221
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 1,
|
||||
"message_bytes": 256,
|
||||
"stack_bytes": 3512,
|
||||
"instructions": 1733968
|
||||
},
|
||||
{
|
||||
"operation": "keypair",
|
||||
"vector": 1,
|
||||
"stack_bytes": 2808,
|
||||
"instructions": 1645602
|
||||
},
|
||||
{
|
||||
"operation": "shared_secret",
|
||||
"vector": 1,
|
||||
"stack_bytes": 768,
|
||||
"instructions": 1290321
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 2,
|
||||
"message_bytes": 1,
|
||||
"stack_bytes": 3512,
|
||||
"instructions": 1678524
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 2,
|
||||
"message_bytes": 96,
|
||||
"stack_bytes": 3512,
|
||||
"instructions": 1706221
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 2,
|
||||
"message_bytes": 256,
|
||||
"stack_bytes": 3512,
|
||||
"instructions": 1733968
|
||||
},
|
||||
{
|
||||
"operation": "keypair",
|
||||
"vector": 2,
|
||||
"stack_bytes": 2808,
|
||||
"instructions": 1645602
|
||||
},
|
||||
{
|
||||
"operation": "shared_secret",
|
||||
"vector": 2,
|
||||
"stack_bytes": 768,
|
||||
"instructions": 1289673
|
||||
}
|
||||
],
|
||||
"scope": "Actual linked ARM crypto frames/instruction counts only; excludes caller/ISR stacks and physical timing."
|
||||
},
|
||||
"4631": {
|
||||
"passed": true,
|
||||
"elf_sha256": "23fae794233d6df9d2c44cf490dba5ee1b4454f49d614a8291fd92e10e04eab1",
|
||||
"instruction_meter": "native",
|
||||
"measurements": [
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 1,
|
||||
"message_bytes": 0,
|
||||
"stack_bytes": 3512,
|
||||
"instructions": 1678474
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 1,
|
||||
"message_bytes": 96,
|
||||
"stack_bytes": 3512,
|
||||
"instructions": 1706221
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 1,
|
||||
"message_bytes": 256,
|
||||
"stack_bytes": 3512,
|
||||
"instructions": 1733968
|
||||
},
|
||||
{
|
||||
"operation": "keypair",
|
||||
"vector": 1,
|
||||
"stack_bytes": 2808,
|
||||
"instructions": 1645602
|
||||
},
|
||||
{
|
||||
"operation": "shared_secret",
|
||||
"vector": 1,
|
||||
"stack_bytes": 768,
|
||||
"instructions": 1290321
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 2,
|
||||
"message_bytes": 1,
|
||||
"stack_bytes": 3512,
|
||||
"instructions": 1678524
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 2,
|
||||
"message_bytes": 96,
|
||||
"stack_bytes": 3512,
|
||||
"instructions": 1706221
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 2,
|
||||
"message_bytes": 256,
|
||||
"stack_bytes": 3512,
|
||||
"instructions": 1733968
|
||||
},
|
||||
{
|
||||
"operation": "keypair",
|
||||
"vector": 2,
|
||||
"stack_bytes": 2808,
|
||||
"instructions": 1645602
|
||||
},
|
||||
{
|
||||
"operation": "shared_secret",
|
||||
"vector": 2,
|
||||
"stack_bytes": 768,
|
||||
"instructions": 1289673
|
||||
}
|
||||
],
|
||||
"scope": "Actual linked ARM crypto frames/instruction counts only; excludes caller/ISR stacks and physical timing."
|
||||
}
|
||||
},
|
||||
"linked_arm_baseline_4631": {
|
||||
"passed": true,
|
||||
"elf_sha256": "2beff2f916231421287e0a774b6289df365336bfaa0d325a1521985b9001a18b",
|
||||
"instruction_meter": "native",
|
||||
"measurements": [
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 1,
|
||||
"message_bytes": 0,
|
||||
"stack_bytes": 3312,
|
||||
"instructions": 1666244
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 1,
|
||||
"message_bytes": 96,
|
||||
"stack_bytes": 3312,
|
||||
"instructions": 1693991
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 1,
|
||||
"message_bytes": 256,
|
||||
"stack_bytes": 3312,
|
||||
"instructions": 1721738
|
||||
},
|
||||
{
|
||||
"operation": "keypair",
|
||||
"vector": 1,
|
||||
"stack_bytes": 2808,
|
||||
"instructions": 1645602
|
||||
},
|
||||
{
|
||||
"operation": "shared_secret",
|
||||
"vector": 1,
|
||||
"stack_bytes": 768,
|
||||
"instructions": 1290329
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 2,
|
||||
"message_bytes": 1,
|
||||
"stack_bytes": 3312,
|
||||
"instructions": 1666294
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 2,
|
||||
"message_bytes": 96,
|
||||
"stack_bytes": 3312,
|
||||
"instructions": 1693991
|
||||
},
|
||||
{
|
||||
"operation": "sign",
|
||||
"vector": 2,
|
||||
"message_bytes": 256,
|
||||
"stack_bytes": 3312,
|
||||
"instructions": 1721738
|
||||
},
|
||||
{
|
||||
"operation": "keypair",
|
||||
"vector": 2,
|
||||
"stack_bytes": 2808,
|
||||
"instructions": 1645602
|
||||
},
|
||||
{
|
||||
"operation": "shared_secret",
|
||||
"vector": 2,
|
||||
"stack_bytes": 768,
|
||||
"instructions": 1289681
|
||||
}
|
||||
],
|
||||
"scope": "Actual linked ARM crypto frames/instruction counts only; excludes caller/ISR stacks and physical timing."
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
|
||||
|
||||
@@ -67,6 +67,29 @@ class Nrf52FlashTrimTest(unittest.TestCase):
|
||||
self.assertNotIn("SSD1306_NO_SPLASH", defines)
|
||||
self.assertEqual(TRIM.flag_defines("-DMESH_NRF52_FLASH_TRIM=1 -UMESH_NRF52_FLASH_TRIM")["MESH_NRF52_FLASH_TRIM"], None)
|
||||
|
||||
def test_rak_repeater_trims_follow_hardware_and_effective_role(self):
|
||||
for board in ("RAK_3401", "RAK_4631"):
|
||||
for suffix in ("repeater", "repeater_lora_ota_no_external_sensors",
|
||||
"repeater_unified_lora_ota", "repeater_bridge_rs232_serial1"):
|
||||
with self.subTest(board=board, suffix=suffix):
|
||||
defines = self.policy(name=board + "_" + suffix, flags="-D" + board)
|
||||
for name in ("ED25519_COMPACT_SCALAR", "MESH_CLI_SHARE_REPLIES"):
|
||||
self.assertEqual(defines[name], 1)
|
||||
self.assertNotIn("ED25519_COMPACT_FIELD", defines)
|
||||
for role in ("simple_room_server", "simple_sensor", "kiss_modem", "companion_radio"):
|
||||
defines = self.policy(role=role, flags="-D" + board)
|
||||
for name in TRIM.RAK_REPEATER_TRIM_DEFINES:
|
||||
self.assertNotIn(name, defines)
|
||||
for options in (dict(flags="-DRAK_3401=0"), dict(mcu="nrf52832", flags="-DRAK_3401"),
|
||||
dict(src_filter="+<../examples/simple_repeater> +<../examples/simple_sensor>",
|
||||
flags="-DRAK_4631")):
|
||||
for name in TRIM.RAK_REPEATER_TRIM_DEFINES:
|
||||
self.assertNotIn(name, self.policy(**options))
|
||||
defines = self.policy(flags="-DRAK_3401 -DED25519_COMPACT_SCALAR=0 "
|
||||
"-UED25519_COMPACT_FIELD -DMESH_CLI_SHARE_REPLIES=0")
|
||||
for name in TRIM.RAK_REPEATER_TRIM_DEFINES:
|
||||
self.assertNotIn(name, defines)
|
||||
|
||||
def test_local_names_honor_overlay_identity_and_explicit_defines(self):
|
||||
flags = ('-DMESH_OTA_LOCAL_TARGET_NAME_ONLY=1 '
|
||||
'-DMOTA_TARGET_ID=0x29a0da19 '
|
||||
@@ -82,6 +105,25 @@ class Nrf52FlashTrimTest(unittest.TestCase):
|
||||
for flags in ("", "-DMESH_OTA_LOCAL_TARGET_NAME_ONLY=0"):
|
||||
self.assertNotIn("OTA_TARGET_NAME_TABLE", self.policy(flags=flags))
|
||||
|
||||
def test_cli_reply_copy_flag_is_attached_only_to_exact_cli_object(self):
|
||||
class Node:
|
||||
def __init__(self, path): self.path = path
|
||||
def srcnode(self): return self
|
||||
def get_abspath(self): return str(self.path)
|
||||
|
||||
class Env(dict):
|
||||
def subst(self, value): return value.replace("$PROJECT_DIR", str(ROOT))
|
||||
def Object(self, node, **flags): return node, flags
|
||||
|
||||
env = Env(CCFLAGS=["-Os", "-flto"])
|
||||
cli = Node(ROOT / "src/helpers/CommonCLI.cpp")
|
||||
result, flags = TRIM.compile_shared_cli_replies(env, cli)
|
||||
self.assertIs(result, cli)
|
||||
self.assertEqual(flags["CCFLAGS"], ["-Os", "-flto", "-fno-builtin-strcpy"])
|
||||
self.assertEqual(env["CCFLAGS"], ["-Os", "-flto"])
|
||||
other = Node(ROOT / "test/fixtures/CommonCLI.cpp")
|
||||
self.assertIs(TRIM.compile_shared_cli_replies(env, other), other)
|
||||
|
||||
def test_local_names_do_not_change_companion_or_unqualified_roles(self):
|
||||
for options in (dict(name="RAK_4631_companion_radio_full"),
|
||||
dict(role="custom"), dict(platform="espressif32", mcu="esp32")):
|
||||
|
||||
@@ -50,6 +50,13 @@ class BuildEnv:
|
||||
|
||||
|
||||
class GnssVersionTrimTests(unittest.TestCase):
|
||||
def test_both_reviewed_revisions_use_the_same_bounded_transform(self):
|
||||
for hash_name in ("SOURCE_SHA256", "SOURCE_2_2_28_SHA256"):
|
||||
with self.subTest(revision=hash_name), patch.object(TRIM, hash_name, TRIM.digest(SOURCE)):
|
||||
patched = TRIM.patched_source(SOURCE)
|
||||
self.assertNotIn(TRIM.ORIGINAL_CONVERSION, patched)
|
||||
self.assertIn(TRIM.VERSION_CONVERSION, patched)
|
||||
|
||||
def test_private_copy_checks_source_and_all_headers_and_preserves_downloads(self):
|
||||
# Small synthetic dependencies test the transform mechanics; production
|
||||
# fingerprints remain fixed to the complete reviewed library files.
|
||||
@@ -90,7 +97,7 @@ class GnssVersionTrimTests(unittest.TestCase):
|
||||
self.assertFalse(other_env.path.exists())
|
||||
target.write_bytes(before)
|
||||
|
||||
def test_only_qualified_companion_installs_middleware(self):
|
||||
def test_only_qualified_roles_install_middleware_and_opt_out_is_preserved(self):
|
||||
spec = importlib.util.spec_from_file_location("flash_trim", ROOT / "scripts/nrf52_flash_trim.py")
|
||||
policy = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(policy)
|
||||
@@ -107,14 +114,22 @@ class GnssVersionTrimTests(unittest.TestCase):
|
||||
def subst(self, value): return value.replace("$PROJECT_DIR", str(ROOT))
|
||||
def AddBuildMiddleware(self, callback, pattern): self.setdefault("middleware", []).append(pattern)
|
||||
|
||||
for role, flags, expected in (("companion_radio", "", True),
|
||||
("companion_radio", "-DMESH_NRF52_COMPANION_TRIM=0", False),
|
||||
("simple_repeater", "", False), ("custom", "", False)):
|
||||
for role, flags, expected, shared_replies in (("companion_radio", "", True, False),
|
||||
("companion_radio", "-DMESH_NRF52_COMPANION_TRIM=0", False, False),
|
||||
("simple_repeater", "", False, False), ("custom", "", False, False),
|
||||
("simple_repeater", "-DRAK_3401", True, True),
|
||||
("simple_repeater", "-DRAK_4631", True, True),
|
||||
("simple_room_server", "-DRAK_4631", False, False),
|
||||
("simple_sensor", "-DRAK_3401", False, False),
|
||||
("simple_repeater", "-DRAK_4631 -DMESH_NRF52_GNSS_VERSION_TRIM=0", False, True),
|
||||
("simple_repeater", "-DRAK_4631 -DMESH_CLI_SHARE_REPLIES=0", True, False),
|
||||
("simple_repeater", "-DRAK_3401 -DMESH_NRF52_FLASH_TRIM=0", False, False)):
|
||||
with self.subTest(role=role, flags=flags):
|
||||
env = Env(BUILD_FLAGS=flags, CPPDEFINES=[], PIOENV="custom",
|
||||
SRC_FILTER="+<../examples/" + role + ">")
|
||||
policy.install(env)
|
||||
self.assertEqual("*" + TRIM.SOURCE_NAME in env.get("middleware", []), expected)
|
||||
self.assertEqual("*CommonCLI.cpp" in env.get("middleware", []), shared_replies)
|
||||
|
||||
def test_native_gnss_conversion_parity_and_bounded_cli_tokens(self):
|
||||
compiler = shutil.which("g++") or shutil.which("clang++")
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "ed_25519.h"
|
||||
|
||||
#ifndef BENCH_COMPACT
|
||||
#define BENCH_COMPACT 0
|
||||
#endif
|
||||
#define SAMPLES 9
|
||||
#define REG32(a) (*(volatile uint32_t *)(a))
|
||||
struct Case {
|
||||
uint8_t public_key[32], shared_secret[32], signatures[3][64];
|
||||
uint32_t cycles[5][SAMPLES];
|
||||
};
|
||||
struct Report {
|
||||
uint32_t magic, schema, compact, cpu_hz, samples, complete, cpuid, clock_stat;
|
||||
uint32_t overhead[SAMPLES];
|
||||
uint32_t wdt_run, wdt_config, wdt_crv;
|
||||
struct Case cases[2];
|
||||
};
|
||||
__attribute__((used)) volatile struct Report report;
|
||||
static const uint8_t seeds[2][32] = {
|
||||
{0x9d,0x61,0xb1,0x9d,0xef,0xfd,0x5a,0x60,0xba,0x84,0x4a,0xf4,0x92,0xec,0x2c,0xc4,0x44,0x49,0xc5,0x69,0x7b,0x32,0x69,0x19,0x70,0x3b,0xac,0x03,0x1c,0xae,0x7f,0x60},
|
||||
{0x4c,0xcd,0x08,0x9b,0x28,0xff,0x96,0xda,0x9d,0xb6,0xc3,0x46,0xec,0x11,0x4e,0x0f,0x5b,0x8a,0x31,0x9f,0x35,0xab,0xa6,0x24,0xda,0x8c,0xf6,0xed,0x4f,0xb8,0xa6,0xfb}
|
||||
};
|
||||
static uint8_t public_keys[2][32], private_keys[2][64], message[256];
|
||||
static void feed_watchdog(void) {
|
||||
if (REG32(0x40010400)) {
|
||||
uint32_t enabled = REG32(0x40010508);
|
||||
for (unsigned n = 0; n < 8; ++n)
|
||||
if (enabled & (1U << n)) REG32(0x40010600 + n*4) = 0x6E524635;
|
||||
}
|
||||
}
|
||||
static inline void barrier(void) { __asm volatile("dsb\n isb" ::: "memory"); }
|
||||
static inline uint32_t ticks(void) { barrier(); return REG32(0xE0001004); }
|
||||
__attribute__((noinline,used,externally_visible)) void benchmark(void) {
|
||||
/* Reset has disabled the SoftDevice. Mask all interrupts, including USB. */
|
||||
__asm volatile("cpsid i" ::: "memory");
|
||||
REG32(0xE000E010) = 0;
|
||||
REG32(0xE000ED88) |= 0xF << 20;
|
||||
feed_watchdog();
|
||||
REG32(0x40000000) = 1; /* HFCLKSTART */
|
||||
while ((REG32(0x4000040C) & 0x10001) != 0x10001) { feed_watchdog(); }
|
||||
REG32(0xE000EDFC) |= 1 << 24; /* TRCENA */
|
||||
REG32(0xE0001004) = 0;
|
||||
REG32(0xE0001000) |= 1;
|
||||
barrier();
|
||||
memset((void *)&report, 0, sizeof(report));
|
||||
report.magic = 0x52414B43;
|
||||
report.schema = 1;
|
||||
report.compact = BENCH_COMPACT;
|
||||
report.cpu_hz = 64000000;
|
||||
report.samples = SAMPLES;
|
||||
report.cpuid = REG32(0xE000ED00);
|
||||
report.clock_stat = REG32(0x4000040C);
|
||||
report.wdt_run = REG32(0x40010400);
|
||||
report.wdt_config = REG32(0x4001050C);
|
||||
report.wdt_crv = REG32(0x40010504);
|
||||
for (unsigned i = 0; i < sizeof(message); ++i) message[i] = (i * 73 + 19) & 255;
|
||||
for (unsigned i = 0; i < SAMPLES; ++i) {
|
||||
uint32_t start = ticks();
|
||||
uint32_t end = ticks();
|
||||
report.overhead[i] = end - start;
|
||||
}
|
||||
for (unsigned v = 0; v < 2; ++v) ed25519_create_keypair(public_keys[v], private_keys[v], seeds[v]);
|
||||
for (unsigned v = 0; v < 2; ++v) {
|
||||
for (unsigned operation = 0; operation < 5; ++operation) {
|
||||
for (unsigned n = 0; n < SAMPLES + 1; ++n) {
|
||||
feed_watchdog();
|
||||
uint32_t start = ticks();
|
||||
if (operation == 0) {
|
||||
ed25519_create_keypair(public_keys[v], private_keys[v], seeds[v]);
|
||||
} else if (operation == 4) {
|
||||
ed25519_key_exchange((uint8_t *)report.cases[v].shared_secret, public_keys[1-v], private_keys[v]);
|
||||
} else {
|
||||
unsigned length = operation == 1 ? 0 : (operation == 2 ? 96 : 256);
|
||||
ed25519_sign((uint8_t *)report.cases[v].signatures[operation-1], message, length, public_keys[v], private_keys[v]);
|
||||
}
|
||||
uint32_t end = ticks();
|
||||
if (n) report.cases[v].cycles[operation][n-1] = end-start;
|
||||
}
|
||||
}
|
||||
memcpy((void *)report.cases[v].public_key, public_keys[v], 32);
|
||||
}
|
||||
report.complete = 0xC0DEF17E;
|
||||
barrier();
|
||||
__asm volatile("bkpt #0");
|
||||
for (;;) {}
|
||||
}
|
||||
__attribute__((naked,used,section(".entry"))) void bench_start(void) {
|
||||
__asm volatile("cpsid i\n ldr sp, =0x20040000\n movs r0, #0\n msr control, r0\n isb\n bl benchmark\n bkpt #0\n b .");
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
ENTRY(bench_start)
|
||||
MEMORY { RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K }
|
||||
SECTIONS {
|
||||
.text : { KEEP(*(.entry)) *(.text*) *(.rodata*) } > RAM
|
||||
.ARM.exidx : { *(.ARM.exidx*) } > RAM
|
||||
.data : { *(.data*) } > RAM
|
||||
.bss (NOLOAD) : { *(.bss*) *(COMMON) } > RAM
|
||||
/DISCARD/ : { *(.comment*) *(.note*) }
|
||||
}
|
||||
Reference in new issue
Block a user