Skip to content

nrf52840: right-size the SoftDevice RAM reservation (+8 KB heap arena) - #10903

Merged
thebentern merged 1 commit into
developfrom
fix/nrf52-softdevice-ram-rightsize
Jul 6, 2026
Merged

nrf52840: right-size the SoftDevice RAM reservation (+8 KB heap arena)#10903
thebentern merged 1 commit into
developfrom
fix/nrf52-softdevice-ram-rightsize

Conversation

@thebentern

@thebentern thebentern commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Part of the heap-recovery series (#10898, #10901, #10900, #10899, #10902).

Problem

Both nrf52840 linker scripts hard-code the app RAM ORIGIN at 0x20006000 — a 24 KB SoftDevice reservation dating to their introduction. sd_ble_enable() computes the actual requirement from the Bluefruit configuration (one peripheral link, BANDWIDTH_MAX/MTU 247, 0x1000 attribute table) and it is substantially below that. The SoftDevice can't use the gap and the app is linked above it, so the difference is pure waste — on the platform where 2.8.0 field reports show the heap arena (which on this BSP is exactly the linker gap) at 99% use.

Change

  • RAM ORIGIN 0x20006000 → 0x20004000 in both nrf52840_s140_v6.ld and nrf52840_s140_v7.ld.
  • Bluefruit.begin()'s return value — previously discarded — is now checked: if a future SoftDevice/Bluefruit config bump raises the requirement past the reservation, the node records a critical error with instructions instead of silently running without BLE.

Measured effect

+8,192 B of heap arena on every nRF52840 board, BT on or off:

Build .heap section .data addr
rak4631 (S140 v6 script) develop 124,572 B 0x20006000
rak4631 this PR 132,764 B 0x20004000
tracker-t1000-e (S140 v7 script) this PR links clean 0x20004000

Hardware validation (rak4631, S140 6.1.1)

Measured the actual SoftDevice requirement via CFG_DEBUG=1:

[CFG] SoftDevice's RAM requires: 0x200038A0
Address vs. SD floor
SoftDevice requirement (floor) 0x200038A0
Old ORIGIN 0x20006000 +10,080 B wasted
This PR 0x20004000 +1,888 B (1.84 KB) margin

0x20004000 reclaims the full +8 KB while keeping a ~1.8 KB margin for SD-version/config variance across the v6/v7 fleet. (Measurement also ruled out tightening to 0x20003800, which would sit below the 0x200038A0 floor.) BLE bring-up confirmed on the new base: Init Bluefruit → services (DIS/BAS/Mesh) → Advertise, Bluefruit.begin() returns success (the new critical-error guard never fires), client reconnects over serial; a v7-script board links at the new base too.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Bluetooth startup reliability by detecting initialization failures early and stopping setup cleanly when Bluetooth cannot start.
    • Updated device memory settings to better match Bluetooth requirements, which should help avoid startup issues and improve available memory headroom.

The app RAM ORIGIN in both nrf52840 linker scripts has been a hard-coded
0x20006000 (24 KB reserved for S140) since they were introduced, but
sd_ble_enable() with our fixed Bluefruit configuration (one peripheral
link, BANDWIDTH_MAX / ATT MTU 247, 0x1000 attribute table) requires
substantially less. The SoftDevice cannot use the gap and the app image
is linked above it, so every byte between the true requirement and the
ORIGIN is simply unusable RAM - on a part where 2.8.0 field reports
showed the heap arena at 99% use.

Lower the ORIGIN to 0x20004000 in both s140 v6 and v7 scripts. The heap
arena is the linker gap on this platform, so the change is worth exactly
+8,192 B of free heap on every nRF52840 board (verified: rak4631 .heap
section 124,572 -> 132,764 B; both v6- and v7-script boards link with
.data at 0x20004000).

Safety net: Bluefruit.begin()'s return value - previously discarded -
is now checked. If a future SoftDevice or Bluefruit config change raises
the requirement past the reservation, the node logs a critical error
instead of silently running without BLE, with instructions to re-measure
via CFG_DEBUG=1 ("SoftDevice's RAM requires: 0x...").
@thebentern thebentern added the enhancement New feature or request label Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a failure check for Bluefruit.begin() in NRF52Bluetooth::setup(), logging a critical error and returning early on failure. Updates SRAM region origin addresses in the S140 v6 and v7 linker scripts from 0x20006000 to 0x20004000, with updated comments describing RAM requirement handling.

Changes

NRF52 BLE Initialization and RAM Configuration

Layer / File(s) Summary
Bluefruit.begin() failure handling
src/platform/nrf52/NRF52Bluetooth.cpp
Adds error.h include; checks Bluefruit.begin() return value, logging a critical error and returning early on failure instead of proceeding unconditionally.
Linker RAM origin adjustment
src/platform/nrf52/nrf52840_s140_v6.ld, src/platform/nrf52/nrf52840_s140_v7.ld
Moves RAM region ORIGIN from 0x20006000 to 0x20004000 in both linker scripts, with updated comments documenting revised SoftDevice RAM requirements and validation expectations.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Setup as NRF52Bluetooth::setup
  participant Bluefruit
  participant ErrorLog as LOG_ERROR/RECORD_CRITICALERROR

  Setup->>Bluefruit: begin()
  Bluefruit-->>Setup: success/failure
  alt failure
    Setup->>ErrorLog: log critical error
    Setup-->>Setup: return early
  else success
    Setup->>Setup: continue BLE setup
  end
Loading

Poem

A rabbit checks the BLE gate so tight,
If Bluefruit stumbles, we log it right.
RAM shifted down, more room to spare,
Two KB reclaimed with gentle care.
Hop, hop, hooray — firmware's ready to flight! 🐇📡

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: reducing nRF52840 SoftDevice RAM reservation to reclaim heap.
Description check ✅ Passed The description covers problem, changes, measurement, and validation, but it omits the template's attestation checklist and explicit regression test checkboxes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Flash this PR in the Web Flasher

firmware commit boards expires

Warning

This is an automated, unreviewed CI test build. Back up your device configuration
before flashing, and only flash devices you are able to recover.

Supported boards built by this PR (26)
Device Board Platform
Crowpanel Adv 3.5 TFT elecrow-adv-35-tft esp32-s3
Heltec HT62 heltec-ht62-esp32c3-sx1262 esp32-c3
Heltec Mesh Node 096 heltec-mesh-node-t096 nrf52840
Heltec Mesh Node T1 heltec-mesh-node-t1 nrf52840
Heltec Mesh Node T114 heltec-mesh-node-t114 nrf52840
Heltec V3 heltec-v3 esp32-s3
Heltec V4 heltec-v4 esp32-s3
Raspberry Pi Pico pico rp2040
Raspberry Pi Pico W picow rp2040
RAK WisMesh Tag rak_wismeshtag nrf52840
RAK WisBlock 11200 rak11200 esp32
RAK WisBlock 11310 rak11310 rp2040
RAK3312 rak3312 esp32-s3
RAK WisBlock 4631 rak4631 nrf52840
Seeed SenseCAP Mesh-Tracker-X1 seeed_mesh_tracker_X1 nrf52840
Seeed Wio Tracker L1 seeed_wio_tracker_L1 nrf52840
Seeed Xiao NRF52840 Kit seeed_xiao_nrf52840_kit nrf52840
Seeed Xiao ESP32-S3 seeed-xiao-s3 esp32-s3
Station G2 station-g2 esp32-s3
Station G3 station-g3 esp32-s3
LILYGO T-Deck t-deck-tft esp32-s3
LILYGO T-Echo t-echo nrf52840
LILYGO T-Echo Plus t-echo-plus nrf52840
LILYGO T-Impulse Plus t-impulse-plus nrf52840
LilyGo T3-C6 tlora-c6 esp32-c6
Seeed SenseCAP T1000-E tracker-t1000-e nrf52840

Build artifacts expire on 2026-08-05. Updated for fcae070.

@github-actions github-actions Bot added the needs-review Needs human review label Jul 6, 2026
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Firmware Size Report

22 targets | vs develop: 21 increased, 1 decreased, net +387,732 (+378.6 KB)

Target Size vs develop
picow 1,271,976 📈 +27,096 (+26.5 KB)
pico2w 1,246,660 📈 +26,144 (+25.5 KB)
rak11310 831,256 📈 +25,832 (+25.2 KB)
pico 808,504 📈 +25,816 (+25.2 KB)
seeed_xiao_rp2040 806,704 📈 +25,800 (+25.2 KB)
Show 17 more target(s)
Target Size vs develop
pico2 794,776 📈 +25,000 (+24.4 KB)
seeed_xiao_rp2350 792,920 📈 +24,984 (+24.4 KB)
t-deck-tft 3,832,064 📈 +21,392 (+20.9 KB)
heltec-vision-master-e213-inkhud 2,247,392 📈 +20,032 (+19.6 KB)
elecrow-adv-35-tft 3,435,520 📈 +18,896 (+18.5 KB)
seeed-xiao-s3 2,292,416 📈 +16,272 (+15.9 KB)
heltec-ht62-esp32c3-sx1262 2,150,576 📈 +15,584 (+15.2 KB)
heltec-v3 2,279,632 📈 +15,552 (+15.2 KB)
rak3312 2,287,600 📈 +15,360 (+15.0 KB)
rak11200 1,875,728 📈 +15,120 (+14.8 KB)
station-g3 2,281,088 📈 +14,736 (+14.4 KB)
station-g2 2,281,072 📈 +14,720 (+14.4 KB)
t-eth-elite 2,506,064 📈 +14,576 (+14.2 KB)
tlora-c6 2,382,256 📈 +13,952 (+13.6 KB)
heltec-v4 2,291,152 📈 +13,856 (+13.5 KB)
rak3172 182,492 📉 -3,804 (-3.7 KB)
wio-e5 239,444 📈 +816

Updated for 8facda9

@thebentern

Copy link
Copy Markdown
Contributor Author

Hardware validation (rak4631, S140 6.1.1, v6 linker script):

  • sd_ble_enable() accepts the 0x20004000 base: Bluefruit init, GATT services, PIN pairing setup, and advertising all come up normally; the new Bluefruit.begin() failure check never fires. BLE also re-initializes cleanly after being disabled and re-enabled via config.
  • dbgHeapTotal on-device rose 124,572 → 139,236 with the sizing PR stack (this PR contributes exactly +8,192; .data links at 0x20004000, confirmed in the ELF).
  • ⚠️ The exact SoftDevice's RAM requires: 0x… figure is still uncaptured — -DCFG_DEBUG=1 did not take effect through either flash path (no Bluefruit config dump in the boot stream both attempts). Since begin() success already bounds the requirement ≤ 0x20004000 with our BANDWIDTH_MAX config, I'd treat the exact value as part of the future tightening pass rather than a blocker. A v7-script board (t1000-e) also links at the new base but hasn't been booted on hardware yet.

Remaining before undraft: phone pairing smoke (FIXED_PIN/RANDOM_PIN + config download) and one v7-board boot.

@thebentern

Copy link
Copy Markdown
Contributor Author

Measured on hardware — the outstanding item is closed. rak4631 (S140 6.1.1, v6 linker script), CFG_DEBUG=1:

[CFG] SoftDevice's RAM requires: 0x200038A0

Why it wasn't showing up before: on nRF52 this firmware overrides printf to route to SEGGER RTT (J-Link channel), and the BSP's LOG_LV1/ADALOG uses that same printf — so the line was emitted correctly all along, just to a debugger I don't have wired up, not USB serial. Captured it by temporarily mirroring the BSP printf to USB CDC (throwaway, reverted).

The number validates the choice and corrects the draft's speculative target:

Address vs. SD floor (0x200038A0)
SoftDevice requirement (floor) 0x200038A0
Old ORIGIN 0x20006000 +10,080 B wasted
This PR 0x20004000 +1,888 B (1.84 KB) margin
Draft's speculative "tighten to" 0x20003800 −160 B — below the floor, would fail

So 0x20004000 is confirmed as the right, safe choice: it reclaims the full +8 KB, keeps a healthy ~1.8 KB margin for SD-version/config variance across the v6/v7 fleet, and the earlier speculative tighten to 0x20003800 is now ruled out. (There's ~1 KB more theoretically available at 0x20003C00/864 B margin, but that thin a margin isn't worth it fleet-wide — noting it, not doing it.)

BLE bring-up on the 0x20004000 build: Init Bluefruit → services (DIS/BAS/Mesh) → Advertise, Bluefruit.begin() returns success (the new critical-error guard never fires), and the client reconnects over serial. On a v7-script board the image also links at the new base (verified earlier). Taking this out of draft.

@thebentern
thebentern marked this pull request as ready for review July 6, 2026 18:58
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/platform/nrf52/NRF52Bluetooth.cpp (1)

280-284: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Trim the multi-line comment to comply with guidelines.

This 5-line explanatory comment exceeds the "one or two lines maximum" limit for comments in .cpp/.h files. Consider condensing the rationale into 1-2 lines and moving the detailed re-measurement instructions into the linker script comment (already present there) rather than duplicating them here.

✏️ Suggested trim
-        // sd_ble_enable() rejected our RAM base: the linker RAM ORIGIN
-        // (src/platform/nrf52/nrf52840_s140_v*.ld) is below what the SoftDevice needs for the
-        // current Bluefruit config. Without this check the node would silently run without BLE.
-        // Rebuild with -DCFG_DEBUG=1 to get "SoftDevice's RAM requires: 0x..." in the log, then
-        // raise the ORIGIN accordingly.
+        // sd_ble_enable() rejected our RAM base; see nrf52840_s140_v*.ld comment for details.
         LOG_ERROR("Bluefruit.begin failed - SoftDevice RAM reservation too small for this config");

As per coding guidelines, "Keep code comments minimal: one or two lines maximum, only when the reason is not obvious, and do not restate the next line."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/platform/nrf52/NRF52Bluetooth.cpp` around lines 280 - 284, The comment in
NRF52Bluetooth.cpp is too long for the `.cpp`/`.h` guideline and should be
shortened to 1-2 lines. Trim the explanatory block near the `sd_ble_enable()`
RAM-base check so it only states the essential rationale, and remove the
duplicated re-measurement/build instructions since those belong in the linker
script comment already. Keep the updated comment concise while preserving the
key context around the RAM ORIGIN mismatch and the BLE failure.

Source: Coding guidelines

src/platform/nrf52/nrf52840_s140_v6.ld (1)

24-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Comment overstates the safety margin.

The comment claims a "~2+ KB margin" over the reported SoftDevice requirement, but per the PR's own hardware validation the measured requirement was 0x200038A0, leaving only 1,888 bytes (< 2 KB) of margin against the new 0x20004000 origin. Update the comment to reflect the actual measured margin to avoid misleading future readers who might push the ORIGIN further down based on this comment.

✏️ Suggested fix
-   * 0x1000 attr table) sd_ble_enable() reports a requirement well below the old
-   * 0x20006000 ORIGIN; every byte of gap is unusable RAM. 0x20004000 keeps a
-   * ~2+ KB margin over the reported base. NRF52Bluetooth::setup() now checks
+   * 0x1000 attr table) sd_ble_enable() reports a requirement well below the old
+   * 0x20006000 ORIGIN; every byte of gap is unusable RAM. 0x20004000 keeps a
+   * ~1.9 KB margin over the reported base. NRF52Bluetooth::setup() now checks
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/platform/nrf52/nrf52840_s140_v6.ld` around lines 24 - 34, The RAM layout
comment overstates the SoftDevice safety margin; the measured requirement
against the new origin is under 2 KB, not “~2+ KB.” Update the explanatory
comment in nrf52840_s140_v6.ld near the RAM ORIGIN to reflect the actual
validated margin (based on the 0x200038A0 measurement) so future readers don’t
infer extra headroom. Keep the note about NRF52Bluetooth::setup() and
Bluefruit.begin() fallback, but make the margin wording precise and
conservative.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/platform/nrf52/nrf52840_s140_v6.ld`:
- Around line 24-34: The RAM layout comment overstates the SoftDevice safety
margin; the measured requirement against the new origin is under 2 KB, not “~2+
KB.” Update the explanatory comment in nrf52840_s140_v6.ld near the RAM ORIGIN
to reflect the actual validated margin (based on the 0x200038A0 measurement) so
future readers don’t infer extra headroom. Keep the note about
NRF52Bluetooth::setup() and Bluefruit.begin() fallback, but make the margin
wording precise and conservative.

In `@src/platform/nrf52/NRF52Bluetooth.cpp`:
- Around line 280-284: The comment in NRF52Bluetooth.cpp is too long for the
`.cpp`/`.h` guideline and should be shortened to 1-2 lines. Trim the explanatory
block near the `sd_ble_enable()` RAM-base check so it only states the essential
rationale, and remove the duplicated re-measurement/build instructions since
those belong in the linker script comment already. Keep the updated comment
concise while preserving the key context around the RAM ORIGIN mismatch and the
BLE failure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 28de4bb5-2bfe-4e93-913f-a3457c5fc3d1

📥 Commits

Reviewing files that changed from the base of the PR and between 93250d8 and fcae070.

📒 Files selected for processing (3)
  • src/platform/nrf52/NRF52Bluetooth.cpp
  • src/platform/nrf52/nrf52840_s140_v6.ld
  • src/platform/nrf52/nrf52840_s140_v7.ld

@thebentern
thebentern merged commit 00685a4 into develop Jul 6, 2026
99 checks passed
@caveman99
caveman99 deleted the fix/nrf52-softdevice-ram-rightsize branch August 26, 2026 19:40
Itzdavid01 pushed a commit to Itzdavid01/firmware that referenced this pull request Sep 5, 2026
meshtastic#10903)

The app RAM ORIGIN in both nrf52840 linker scripts has been a hard-coded
0x20006000 (24 KB reserved for S140) since they were introduced, but
sd_ble_enable() with our fixed Bluefruit configuration (one peripheral
link, BANDWIDTH_MAX / ATT MTU 247, 0x1000 attribute table) requires
substantially less. The SoftDevice cannot use the gap and the app image
is linked above it, so every byte between the true requirement and the
ORIGIN is simply unusable RAM - on a part where 2.8.0 field reports
showed the heap arena at 99% use.

Lower the ORIGIN to 0x20004000 in both s140 v6 and v7 scripts. The heap
arena is the linker gap on this platform, so the change is worth exactly
+8,192 B of free heap on every nRF52840 board (verified: rak4631 .heap
section 124,572 -> 132,764 B; both v6- and v7-script boards link with
.data at 0x20004000).

Safety net: Bluefruit.begin()'s return value - previously discarded -
is now checked. If a future SoftDevice or Bluefruit config change raises
the requirement past the reservation, the node logs a critical error
instead of silently running without BLE, with instructions to re-measure
via CFG_DEBUG=1 ("SoftDevice's RAM requires: 0x...").
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request needs-review Needs human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant