Skip to content

Add the nRF54LM20-DK variant and fix the SPIM prescaler on 16 MHz instances - #1

Open
cvaldess wants to merge 3 commits into
andyshinn:ashinn/nrf54lm20afrom
cvaldess:pr/nrf54lm20dk-variant
Open

cvaldess wants to merge 3 commits into
andyshinn:ashinn/nrf54lm20afrom
cvaldess:pr/nrf54lm20dk-variant

Conversation

@cvaldess

@cvaldess cvaldess commented Oct 10, 2026 •

Copy link
Copy Markdown

Adds a variant for the Nordic nRF54LM20-DK, plus an SPI fix it needed.

Variant (variants/nrf54lm20dk)

  • Arduino pin = physical GPIO, P0..P3 (109 entries), as on the other nRF54L variants.
  • Pin map from the DK user guide v1.0.2 (cited in variant.h): LEDs P1.22/P1.25/P1.27/P1.28 (active high), buttons P1.26/P1.09/P1.08/P0.05, Serial1 on VCOM0 (UARTE20, P1.16/P1.17), SPI on SPIM00 with the on-board MX25R64 (P2), Wire on TWIM30 (P0.03/P0.04). The pins the board keeps for itself are listed, and the AIN pins cite the datasheet.
  • The DK carries an nRF54LM20B; it builds as the A (the board definition adds DEVELOP_IN_NRF54LM20B so the MDK applies the B's errata).

SPI: derive the SPIM prescaler from the instance's own core clock

_setFrequency() assumed a 128 MHz core clock for every SPIM, but only SPIM00 runs at 128 MHz; SPIM2x and SPIM30 run at 16 MHz (SPIMnn_CORE_FREQUENCY in the MDK), so on those the bus came out eight times slower than requested. The table also wrote divisors 128–1024 into the 7-bit PRESCALER.DIVISOR field (max 126), so 128 landed as 0.

It now computes ceil(base / freq), rounds up to even and clamps to the instance's range (4..126 on SPIM00, 2..126 on the others). SPIM00 at 4 MHz or more gets the same divisor as before. This affects every nRF54L part, not only the LM20.

Tested

On an nRF54LM20-DK with Meshtastic: an EBYTE E22 (SX1262) on SPIM22 (P3), INA228 and BME680 on Wire, LEDs, buttons and Serial1, through the bring-up (no long soak yet). The SPIM00 path (divisor unchanged at 4 MHz and above) was not re-measured on other boards.

Native USB for the LM20 is a separate PR (TinyUSB on USBHS), independent of this one.

Arduino pin = physical GPIO, P0..P3 (109 entries). Pins from Zephyr's
boards/nordic/nrf54lm20dk: LEDs P1.22/25/27/28 active high, buttons
P1.26/P1.09/P1.08/P0.05, Serial1 on VCOM0 (UARTE20, P1.16/P1.17), SPI on
SPIM00 with the MX25R64 (P2). Wire on TWIM30 P0.03/P0.04 is provisional
until it is checked against the DK user guide.
_setFrequency() assumed a 128 MHz core clock for every SPIM, but only
SPIM00 runs at 128 MHz; SPIM2x and SPIM30 run at 16 MHz
(SPIMnn_CORE_FREQUENCY in the MDK), so on those the bus came out eight
times slower than requested. The table also wrote divisors 128-1024 into
the 7-bit PRESCALER.DIVISOR field (max 126), so 128 landed as 0.

Compute ceil(base / freq), round up to even and clamp to the instance's
range (4..126 on SPIM00, 2..126 on the others). SPIM00 at 4 MHz or more
gets the same divisor as before.

nrf54lm20dk variant: cite the DK user guide v1.0.2 for the pin map and
list the pins the board keeps for itself.
@cvaldess

Copy link
Copy Markdown
Author

@andyshinn a question about the nRF54LM20 work, now that you have an L15 too.

I have a Meshtastic variant for the nRF54LM20-DK as a draft, meshtastic/firmware#12130. It can't leave draft while it builds against forks: your ashinn/nrf54lm20a branches of platform-nordicnrf54, nRF54_Arduino and nRF54_Bootloader, plus this PR and #2 here and nRF54_Bootloader#2 on top. The firmware needs the LM20 support in meshtastic's releases of the three.

Where they stand against meshtastic today:

Do you plan to open PRs for the LM20 support to the meshtastic repos? If so, I'll rebase this PR, #2 and Bootloader#2 onto whatever you put up, or you can fold them in.

If you'd rather not, or don't have the time, would you be OK with me opening those PRs, keeping your commits with your authorship, the way meshtastic#7 carries your delay commits?

For context, this runs on an nRF54LM20-DK (LM20B, built as the A) as a Meshtastic node: E22 radio, BLE, native USB (CDC console and API, serial DFU, UF2 drive) and BLE OTA. The longest run so far is 24 h with no resets.

@andyshinn

Copy link
Copy Markdown
Owner

My plan was to open PRs. But there are sweeping changes so it probably should be done in pieces. The first bigs ones were bumping the nrfx to latest version and making softdevice 10 the softdevice for all variants (right now it is 9 on L15 but the LM20A needed 10). If you wanted to split those out as separate PRs I could look at them.

@cvaldess

Copy link
Copy Markdown
Author

Sounds good, I'll split them. First up is your MDK bump, 2aa44f6 unchanged: meshtastic#8. In your branch the bump is the MDK inside the nrfx tree (8.73.2 → 8.75.1); nrfx itself is 4.2.0 on both sides, so I left it there. If you meant a newer nrfx on top of that, let me know and I'll look at it too.

The move to s145 10.0.1 (958690f and 552b4ea) comes next, as its own PR. Before opening it I want to check on my L15-DK how a board that has 9.0.0 today gets to 10.0.1 with the current bootloader, since every L15 out there has 9.0.0 and the SoftDevice base moves from 0x158C00 to 0x15A800.

@cvaldess

Copy link
Copy Markdown
Author

I checked the move to 10.0.1 on my L15-DK, starting from what every L15 has today: bootloader v0.3.0 and s145 9.0.0. Short version: DFU can't get a board there, SWD can, and there are boards in the field.

DFU. The bootloader can't update the SoftDevice (no MBR), and its interrupt forwarding is built for the 9.0.0 base. On top of that, a package built for 10.0.1 (--sd-req 0x310D) doesn't get a clean refusal: the single-bank code erases bank 0 when the start packet arrives (dfu_prepare_func_app_erase) and only checks the SoftDevice later, with the init packet (dfu_init_prevalidate). Over BLE OTA that left the board in the bootloader with no application. A 9.0.0 package, or SWD, brings it back.

SWD. Writing the 10.0.1 SoftDevice, a bootloader built for it and the application, without a chip erase, works. InternalFS and the settings page don't move, so the node kept its config and its BLE bonds: an existing bond reconnected (LESC with MITM), a fresh pairing worked, and going back to 9.0.0 the same way kept the bond made on 10.0.1.

Two things I ran into that affect the PRs:

  • The platform's merged hex takes the bootloader from the pinned bootloader package. With the boards on 10.0.1 and the pin still at v0.3.0, it puts a 9.0.0 bootloader next to the 10.0.1 SoftDevice, which can't work. The bootloader pin has to move in the same step.
  • Dropping NRF_EVT_RADIO_CANCELED renumbers the NRF_SOC_EVTS entries after it: NRF_EVT_RAND_SEED_REQUEST goes from 9 to 8, and it shows in the compiled app. That's fine when everything is rebuilt, but it's one more reason an application and a SoftDevice of different versions can't be mixed.

Users. In meshtastic/firmware the XIAO nRF54L15 LoRa Plus (xiao_nrf54l15_lr2021) is marked actively supported, so there are boards on 9.0.0 out there. Each one would need a one-time reflash of bootloader + SoftDevice over the onboard CMSIS-DAP, and shouldn't be offered an OTA built for 10.0.1 before that. I see two ways:

  1. Move everything to 10.0.1 as in your branch, with migration notes. Moving the bootloader's SoftDevice check ahead of the erase would stop the lockout for later updates, though not for boards already on v0.3.0.
  2. Keep L05/L10/L15 on 9.0.0 for now and use 10.0.1 only for the LM20A, per board through sd_version, as 958690f allowed on its own.

Which do you prefer?

I have the branches for option 1 ready, not opened yet, all with your authorship: the core (958690f + 552b4ea squashed onto master, without the LM20A files since master has no LM20A yet), the bootloader (c4778bd + 1d14806) and the platform (514f920 + 648a966). One more question for that route: the core's CI builds tests/blink against the meshtastic platform, so it fails until the platform has the 10.0.1 boards. I can point the tests at the platform PR branch for the time being, or keep the 9.0.0 files and v9 scripts in the core for one release so the three can merge in any order. Any preference?

@caveman99

Copy link
Copy Markdown

Let's go with option 1 and move every board to s145 10.0.1. There are no production devices on 9.0.0 that we need to carry, so a one-time SWD reflash is fine. Please cover it in the migration notes: flashing without a chip erase keeps the config and bonds, as you showed, and with an erase both are lost.

For CI, please point the core's tests at the platform PR branch for now instead of keeping the 9.0.0 files for a release. Merge order: the bootloader first, released so the hex files are on the tag, then the core, then the platform PR moving both pins and the boards together. After that the core tests go back to platform master.

Separately, I'll take the bootloader fix: moving the SoftDevice check ahead of the bank 0 erase (dfu_init_prevalidate before dfu_prepare_func_app_erase), so a package built for the wrong SoftDevice is refused instead of leaving the board without an application. It won't help boards already on v0.3.0, but it covers every later SoftDevice bump.

@cvaldess

Copy link
Copy Markdown
Author

Thanks, going with that. Opened in that order:

  1. Move nRF54L05/L10/L15 to the s145 10.0.1 SoftDevice meshtastic/nRF54_Bootloader#4: the bootloader (c4778bd + 1d14806), to be released first. It notes that Check the init packet before invalidating the application meshtastic/nRF54_Bootloader#3 is meant to go in before the release.
  2. Move the nRF54L05/L10/L15 to the s145 10.0.1 SoftDevice meshtastic/nRF54_Arduino#9: the core (958690f + 552b4ea squashed onto master, without the LM20A files), with tests/blink pointed at the platform PR branch for now.
  3. Move the boards to s145 10.0.1 and pick SoftDevice files by SoC family meshtastic/platform-nordicnrf54#3: the platform (514f920 + 648a966), as a draft until the two releases exist; both pins move there.

All three keep @andyshinn's authorship, and the migration notes cover the SWD reflash with and without a chip erase.

@andyshinn

Copy link
Copy Markdown
Owner

Were these meant to be opened against my fork or to go upstream? Just want to confirm if the intention here.

@cvaldess

Copy link
Copy Markdown
Author

Upstream, on purpose. @caveman99 set the order for them in this thread, and none of them needs the LM20A work, so they can land in master before it. They're your commits with your authorship, and your review on them would help.

Once they're in, ashinn/nrf54lm20a can drop them on a rebase and keep only the LM20A parts I left out; each PR lists them.

If you'd rather carry them through your fork, or open them under your own name, tell me and I'll close mine.

@andyshinn

Copy link
Copy Markdown
Owner

I think they are opened against the wrong parent in that case. They should be against meshtastic, not andyshinn.

@cvaldess

Copy link
Copy Markdown
Author

The split PRs already are against meshtastic master: meshtastic#8 (MDK) and meshtastic#9 (core), meshtastic/nRF54_Bootloader#4 and meshtastic/platform-nordicnrf54#3. Only this conversation is on your fork.

The ones against your fork are the LM20-DK work: this PR, #2 here and andyshinn/nRF54_Bootloader#2. They're based on ashinn/nrf54lm20a because they need your LM20A support, which isn't in meshtastic yet, so they can't go to master as they are. Once your LM20A pieces land upstream I'll rebase them onto master and open them against meshtastic. Should I close these three here now, or leave them open until then?

@andyshinn

Copy link
Copy Markdown
Owner

Leave them as-is. I will take a look in another week or two.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants