diff --git a/docs/docs/main/docs/configuration/appendix.md b/docs/docs/main/docs/configuration/appendix.md
index 0263c31e3..2531e6174 100644
--- a/docs/docs/main/docs/configuration/appendix.md
+++ b/docs/docs/main/docs/configuration/appendix.md
@@ -239,8 +239,10 @@ enabled = true
# Note: When the `dfu_rp` feature is enabled, this value is ignored.
# The storage partition is automatically placed after the DFU download slot.
start_addr = 0xA0000
-# Number of sectors used for storage, >= 2
+# Number of sectors used for storage, >= 2 (defaults to 8 when DFU is enabled)
num_sectors = 16
+# Note: When the `dfu_rp` or `dfu_nrf` feature is enabled, this value is ignored.
+# The storage partition is automatically placed after the DFU download slot (from rmk-boot.x).
# Clear storage at keyboard boot.
# Set it to true will reset the storage(including keymap, BLE bond info, etc.) at each reboot.
# This option is useful when testing the firmware.
diff --git a/docs/docs/main/docs/configuration/bootloader.mdx b/docs/docs/main/docs/configuration/bootloader.mdx
index 94d26f7c3..393620d1a 100644
--- a/docs/docs/main/docs/configuration/bootloader.mdx
+++ b/docs/docs/main/docs/configuration/bootloader.mdx
@@ -4,17 +4,13 @@
RMK supports DFU firmware updates via embassy-boot for **RP2040** and **nRF52840**. An embassy-boot based bootloader splits flash into ACTIVE and DFU slots, providing safe updates with automatic rollback on failure.
This is an optional feature of RMK, the default bootloaders of the devices can still be used as usual without runtime updates via USB DFU.
-A pre-built embassy-boot based bootloader called **rmk-boot** is available for both platforms. The partition formula is identical:
+A pre-built embassy-boot based bootloader called **rmk-boot** is available for both platforms. RMK integrates with rmk-boot through a **`memory.x`** file that ships alongside the bootloader. When using rmk-boot’s prebuilt binaries, download the matching `rmk-*-memory.x` from the [GitHub releases](https://github.com/rmk-rs/rmk-boot/releases).
+When building rmk-boot yourself, it will create it’s matching `rmk-memory.x` in the project directory of rmk-boot.
+In either way, rename the file to `memory.x` and place it next to your projects `Cargo.toml`.
-```text
-bootloader+state = 28K (fixed)
-storage = 128K (fixed — 32 sectors × 4K for persistent keymap storage)
-remaining = flash_size - 28K - 128K
-ACTIVE = (remaining - 4K) / 2
-DFU = ACTIVE + 4K
-```
+At runtime RMK reads partition offsets from **linker symbols** embedded in `memory.x` — you never compute or hardcode partition addresses yourself.
-See the [flashing guide](../user_guide/flash_firmware/use_embassy_boot.mdx) for step-by-step instructions on how to get the bootloader and RMK flashed.
+See the [flashing guide](../user_guide/flash_firmware/use_embassy_boot.mdx) for step-by-step instructions on getting the bootloader and RMK flashed.
## RP2040
@@ -27,12 +23,6 @@ import { Tab, Tabs, Rust, Toml } from '@theme'
```toml title="keyboard.toml"
[dfu]
-# (Optional) Total flash size in bytes. Used to auto-calculate partition addresses.
-# Defaults to 2 MB (2097152) when omitted.
-# ⚠ You can define your own FLASH_SIZE and offset addresses, but then you must build and
-# flash a custom embassy-boot bootloader with a matching memory.x!
-flash_size = 2097152
-
# (Optional) Flash page size in bytes (4096 for RP2040).
page_size = 4096
@@ -40,69 +30,19 @@ page_size = 4096
led = "PIN_25"
# led = "none" to omit DFU LED
-# (Optional) Unlock keys for dfu_lock (physical matrix positions). Only works with dfu_lock feature enabled in Cargo.toml.
+# (Optional) Unlock keys for dfu_lock (physical matrix positions).
+# Only works with dfu_lock feature enabled in Cargo.toml.
unlock_keys = [[0, 0], [1, 1]]
-
-# ── (Optional) Manual overrides (only if auto-calculation is not suitable) ──
-state_offset = 0x6000
-state_size = 0x1000
-dfu_offset = 0x87000
-dfu_size = 528384
```
}>
```rust title="main.rs"
-// Flash layout using the rmk-boot formula:
-// state at 0x6000 (4K), active from 0x7000 (size: (flash_size - 28K (= BOOT2 size + embassy-boot + embassy-boot state) - STORAGE_SIZE (= 128K) - page_size (= 4K)) / 2),
-// dfu follows active (active_size + page_size (= 4K))
-//
-// All offsets (DFU_OFFSET, DFU_SIZE, STORAGE_OFFSET, etc.) are derived
-// automatically from FLASH_SIZE below — change only that constant when using rmk-boot.
-//
-// ⚠ You can define your own FLASH_SIZE and addresses, but then you must build and
-// flash a custom embassy-boot bootloader with a matching memory.x!
-const FLASH_SIZE: u32 = 2 * 1024 * 1024; // 2 MB (default)
-// const FLASH_SIZE: u32 = 4 * 1024 * 1024; // 4 MB
-// const FLASH_SIZE: u32 = 8 * 1024 * 1024; // 8 MB
-// const FLASH_SIZE: u32 = 16 * 1024 * 1024; // 16 MB
-const PAGE_SIZE: u32 = 4 * 1024;
-const STORAGE_SIZE: u32 = 128 * 1024; // 32 sectors × 4K after ACTIVE+DFU
-const STATE_OFFSET: u32 = 0x6000;
-const STATE_SIZE: u32 = 0x1000;
-const ACTIVE_OFFSET: u32 = 0x7000; // after 28K bootloader + state
-let remaining: u32 = FLASH_SIZE
- - 28 * 1024 // size of boot 2 + embassy-boot + embassy-boot state
- - STORAGE_SIZE;
-let active_size: u32 = (remaining - PAGE_SIZE) / 2; // DFU = ACTIVE + 1 page (embassy-boot requirement)
-let dfu_size: u32 = active_size + PAGE_SIZE; // embassy-boot needs that extra page for swap info
-let dfu_offset: u32 = ACTIVE_OFFSET + active_size; // dfu after active
-let storage_offset: u32 = dfu_offset + dfu_size; // storage after active + dfu
-assert!(storage_offset + STORAGE_SIZE == FLASH_SIZE); // sanity check that we fit everything in flash
-
-info!(
- "Flash layout: state @ 0x{:04X} ({}K), active @ 0x{:04X} ({}K), dfu @ 0x{:04X} ({}K), storage @ 0x{:04X} ({}K)",
- STATE_OFFSET,
- STATE_SIZE / 1024,
- ACTIVE_OFFSET,
- active_size / 1024,
- dfu_offset,
- dfu_size / 1024,
- storage_offset,
- STORAGE_SIZE / 1024
+let flash = async_flash_wrapper(
+ rmk::dfu::init_flash_from_linkerscript(p.FLASH)
);
-let flash = async_flash_wrapper(rmk::dfu::init_flash(
- p.FLASH,
- storage_offset,
- STORAGE_SIZE,
- STATE_OFFSET,
- STATE_SIZE,
- dfu_offset,
- dfu_size,
-));
-
// Optional: assign a DFU activity LED
let mut dfu_led_processor =
rmk::processor::builtin::dfu_led::DfuLedProcessor::new(Output::new(p.PIN_25, Level::Low), false);
@@ -114,7 +54,6 @@ rmk::dfu::mark_booted();
let unlock_keys: &[(u8, u8)] = &[(0, 0), (1, 1)];
let mut dfu_lock = ::rmk::dfu::DfuLock::new(unlock_keys, &keymap);
// Then add dfu_lock in run_all!()
-// ...
run_all!(
// other processors ...
dfu_led_processor,
@@ -134,80 +73,26 @@ Add a `[dfu]` section to your `keyboard.toml` or use the Rust API directly.
```toml title="keyboard.toml"
[dfu]
-# (Optional) Total flash size in bytes. Used to auto-calculate partition addresses.
-# 1 MB flash — auto-calculates ACTIVE (432K) and DFU (436K)
-# ⚠ You can define your own FLASH_SIZE and offset addresses, but then you must build and
-# flash a custom embassy-boot bootloader with a matching memory.x!
-flash_size = 1048576
-
-# (Optional) Flash page size in bytes (4096 for RP2040).
+# (Optional) Flash page size in bytes (4096 for nRF52840).
page_size = 4096
# (Optional) DFU activity LED pin, default "P0_15".
led = "P0_15"
# led = "none" to omit DFU LED
-# (Optional) Unlock keys for dfu_lock (physical matrix positions). Only works with dfu_lock feature enabled in Cargo.toml.
+# (Optional) Unlock keys for dfu_lock (physical matrix positions).
+# Only works with dfu_lock feature enabled in Cargo.toml.
unlock_keys = [[0, 0], [1, 1]]
-
-# ── (Optional) Manual overrides (only if auto-calculation is not suitable) ──
-state_offset = 0x6000
-state_size = 0x1000
-dfu_offset = 0x87000
-dfu_size = 528384
```
}>
```rust title="main.rs"
-// Flash layout using the rmk-boot formula:
-// state at 0x6000 (4K), active from 0x7000 (size: (flash_size - 28K (= embassy-boot + embassy-boot state) - STORAGE_SIZE (= 64K) - page_size (= 4K)) / 2),
-// dfu follows active (active_size + page_size (= 4K))
-//
-// All offsets (DFU_OFFSET, DFU_SIZE, STORAGE_OFFSET, etc.) are derived
-// automatically from FLASH_SIZE below — change only that constant when using
-// rmk-boot.
-//
-// ⚠ You can define your own FLASH_SIZE and addresses, but then you must build and
-// flash a custom embassy-boot bootloader with a matching memory.x!
-const FLASH_SIZE: u32 = 1024 * 1024; // 1 MB (nRF52840)
-const PAGE_SIZE: u32 = 4 * 1024;
-const STORAGE_SIZE: u32 = 128 * 1024; // 32 sectors × 4K after ACTIVE+DFU
-const STATE_OFFSET: u32 = 0x6000;
-const STATE_SIZE: u32 = 0x1000;
-const ACTIVE_OFFSET: u32 = 0x7000;
-let remaining: u32 = FLASH_SIZE
- - 28 * 1024 // bootloader (24K) + state (4K)
- - STORAGE_SIZE;
-let active_size: u32 = (remaining - PAGE_SIZE) / 2;
-let dfu_size: u32 = active_size + PAGE_SIZE;
-let dfu_offset: u32 = ACTIVE_OFFSET + active_size;
-let storage_offset: u32 = dfu_offset + dfu_size;
-assert!(storage_offset + STORAGE_SIZE == FLASH_SIZE);
-
-info!(
- "Flash layout: state @ 0x{:04X} ({}K), active @ 0x{:04X} ({}K), dfu @ 0x{:04X} ({}K), storage @ 0x{:04X} ({}K)",
- STATE_OFFSET,
- STATE_SIZE / 1024,
- ACTIVE_OFFSET,
- active_size / 1024,
- dfu_offset,
- dfu_size / 1024,
- storage_offset,
- STORAGE_SIZE / 1024
+let flash = async_flash_wrapper(
+ rmk::dfu::init_flash_from_linkerscript(p.NVMC)
);
-let flash = async_flash_wrapper(rmk::dfu::init_flash(
- p.NVMC,
- storage_offset,
- STORAGE_SIZE,
- STATE_OFFSET,
- STATE_SIZE,
- dfu_offset,
- dfu_size,
-));
-
// Optional: assign a DFU activity LED
let mut dfu_led_processor = rmk::processor::builtin::dfu_led::DfuLedProcessor::new(
Output::new(p.P0_15, Level::Low, OutputDrive::Standard),
@@ -222,10 +107,9 @@ let unlock_keys: &[(u8, u8)] = &[(0, 0), (1, 1)];
let mut dfu_lock = ::rmk::dfu::DfuLock::new(unlock_keys, &keymap);
// Then add dfu_lock in run_all!()
-// ...
run_all!(
// other processors ...
- dfu_led_processor
+ dfu_led_processor,
dfu_lock,
)
```
@@ -235,21 +119,34 @@ run_all!(
## Partition layout
-The bootloader divides flash into regions. The defaults follow the [rmk-boot](https://github.com/rmk-rs/rmk-boot) convention and are automatically calculated from `flash_size`:
+The bootloader divides flash into regions. All offsets and sizes come from linker symbols in the `memory.x` file from rmk-boot. The default layout with rmk-boot (2MB RP2040, 32K storage) is:
| Region | Offset | Size |
|-----------------|----------------|--------------------------------------|
-| Bootloader(s) | `0x0000000` | 28 KB |
+| Bootloader(s) | `0x0000000` | 24 KB |
| Boot state | `0x6000` | 4 KB |
-| Active firmware | `0x7000` | `(flash_size - 28K - 128K - 4K) / 2` |
-| DFU download | follows active | `active_size + 4K` |
-| Storage | follows DFU | 128 KB |
+| Active firmware | `0x7000` | `(flash_size - 28K (Bootloader) - 32K (Storage) - 4K (1 Page)) / 2` |
+| DFU download | follows active | `active_size + 4K (1 Page)` |
+| Storage | follows DFU | 32 KB (8 sectors × 4K) |
The DFU partition size follows embassy-boot guidelines, the additional page is used for status information during flashing.
-All `[dfu]` fields are optional. The partition values are auto-calculated from `flash_size` and `page_size` using the rmk-boot formula. If you supply any of `state_offset`, `state_size`, `dfu_offset`, or `dfu_size` directly, auto-calculation is disabled and your values are used as-is.
+The `[dfu]` section is **optional** and configures only DFU behaviour (LED, unlock keys, page size). Partition offsets are read at link time from `memory.x` — you do **not** set `state_offset`, `dfu_offset`, or `flash_size` in `keyboard.toml`.
+
+## Custom bootloader
-Your `memory.x` must match this partition layout — see the [flashing guide](../user_guide/flash_firmware/use_embassy_boot.mdx) for details.
+If you built your own embassy-boot bootloader, add these six symbols with matching values to your `memory.x` (all values are flash-relative offsets):
+
+```text
+__rmk_boot_state_offset = 0x6000;
+__rmk_boot_state_size = 0x1000;
+__rmk_boot_dfu_offset = 0xF3000;
+__rmk_boot_dfu_size = 0xED000;
+__rmk_boot_storage_offset = 0x1E0000;
+__rmk_boot_storage_size = 0x20000;
+```
+
+Make sure `FLASH : ORIGIN` in your `MEMORY` block starts at your ACTIVE partition. RMK's `init_flash_from_linkerscript()` picks up the symbols at runtime.
## DFU LED (optional)
@@ -271,4 +168,3 @@ See the [DFU lock section](../user_guide/flash_firmware/use_embassy_boot.mdx#unl
::: tip
Choose keys that are easy to press simultaneously but not commonly pressed together accidentally.
:::
-
diff --git a/docs/docs/main/docs/configuration/storage.mdx b/docs/docs/main/docs/configuration/storage.mdx
index 74922317e..f397d7782 100644
--- a/docs/docs/main/docs/configuration/storage.mdx
+++ b/docs/docs/main/docs/configuration/storage.mdx
@@ -47,3 +47,12 @@ let rmk_config = RmkConfig {
+
+## Storage and DFU
+
+When using DFU (`dfu_rp` / `dfu_nrf`), the storage partition is placed **after the DFU download slot**, determined by the `rmk-boot.x` linker script. In this mode:
+
+- `start_addr` is **ignored** — RMK automatically places storage at the address defined in `rmk-boot.x` (`__rmk_boot_storage_offset`).
+- The default `num_sectors` is **8** (32 KB, matching rmk-boot's default storage area). You can override `num_sectors` to use fewer sectors, but cannot exceed the allocated area.
+
+If you change the storage size in rmk-boot's `build.rs`, rebuild the bootloader and regenerate `rmk-boot.x`. All partition addresses recalculate automatically.
diff --git a/docs/docs/main/docs/features/use_rust_api.md b/docs/docs/main/docs/features/use_rust_api.md
index df197fdda..90fc3941e 100644
--- a/docs/docs/main/docs/features/use_rust_api.md
+++ b/docs/docs/main/docs/features/use_rust_api.md
@@ -12,6 +12,10 @@ If you're using **nRF52840**, ensure that you have [Adafruit_nRF52_Bootloader](h
You can check either your microcontroller's datasheet or an existing Rust project for your microcontroller for the correct values.
+::: note
+If you're using **rmk-boot** with DFU firmware updates (`dfu_rp` / `dfu_nrf`), you don't need to write a `memory.x` manually. Download `rmk-boot.x` alongside the bootloader binary, place it next to your `Cargo.toml`, and configure your `build.rs` as described in the [embassy-boot flashing guide](../user_guide/flash_firmware/use_embassy_boot.mdx).
+:::
+
### Update `main.rs`
The generated `main.rs` needs to be updated as well to use Rust code. You can copy the code from RMK's Rust example, such as to `src/main.rs` to get started.
diff --git a/docs/docs/main/docs/getting_started/faq.md b/docs/docs/main/docs/getting_started/faq.md
index 757bffb9b..3221456fc 100644
--- a/docs/docs/main/docs/getting_started/faq.md
+++ b/docs/docs/main/docs/getting_started/faq.md
@@ -183,7 +183,7 @@ ERROR Keymap reading aborted!
└─ rmk::keymap::{impl#0}::new_from_storage::{async_fn#0} @ /Users/haobogu/Projects/keyboard/rmk/rmk/src/keymap.rs:38
```
-If you have more sectors available in your internal flash, you can increase `num_sectors` in `[storage]` section of your `keyboard.toml`, or change `storage_config` in your [`RmkConfig`](https://docs.rs/rmk/latest/rmk/config/struct.RmkConfig.html) if you're using Rust API.
+If you have more sectors available in your internal flash, you can increase `num_sectors` in `[storage]` section of your `keyboard.toml`, or change `storage_config` in your [`RmkConfig`](https://docs.rs/rmk/latest/rmk/config/struct.RmkConfig.html) if you're using Rust API. When using DFU (`dfu_rp` / `dfu_nrf`), the storage partition is placed after the DFU slot (via `rmk-boot.x`) and the default is 8 sectors (32 KB).
### OUTDATED: panicked at embassy-executor: task arena is full.
diff --git a/docs/docs/main/docs/user_guide/flash_firmware/use_embassy_boot.mdx b/docs/docs/main/docs/user_guide/flash_firmware/use_embassy_boot.mdx
index 329db0483..64d11e6b4 100644
--- a/docs/docs/main/docs/user_guide/flash_firmware/use_embassy_boot.mdx
+++ b/docs/docs/main/docs/user_guide/flash_firmware/use_embassy_boot.mdx
@@ -6,53 +6,87 @@ RMK supports DFU for **RP2040** (via `dfu_rp`) and **nRF52840** (via `dfu_nrf`).
[rmk-boot](https://github.com/rmk-rs/rmk-boot) is a pre-built embassy-boot bootloader for both platforms. It sits at the beginning of flash and handles the dual-slot firmware switching and rollback on boot. RMK itself provides the DFU USB interface (via `embassy-usb-dfu`) for subsequent updates, so once rmk-boot and RMK are flashed, you never need to press BOOTSEL again.
-RMK and rmk-boot use an **identical partition formula** so the layout always fits perfectly:
+rmk-boot ships a **`rmk-memory.x`** file alongside its firmware binaries. This file **is** your project's `memory.x` — it contains the correct flash layout for the ACTIVE partition. Download it alongside the bootloader, rename it to `memory.x`, and place it next to your `Cargo.toml` (replacing any existing `memory.x`). Your project's `build.rs` already copies `memory.x` into the linker's search path.
+
+Example for RP2040 with 2MB flash:
+```text title="rmk-memory.x → rename to memory.x"
+/* rmk-memory.x for RP2040 2 MB — generated by rmk-boot
+ *
+ * Provides both the MEMORY layout (absolute XIP addresses) and
+ * flash-relative DFU symbols consumed by init_flash_from_linkerscript().
+ */
+
+MEMORY {
+ FLASH : ORIGIN = 0x10007000, LENGTH = 1015808 /* ACTIVE region */
+ RAM : ORIGIN = 0x20000000, LENGTH = 256K
+}
+
+/* DFU partition symbols — offsets relative to flash start */
+__rmk_boot_state_offset = 0x6000;
+__rmk_boot_state_size = 0x1000;
+__rmk_boot_dfu_offset = 0xFF000;
+__rmk_boot_dfu_size = 0xF9000;
+__rmk_boot_storage_offset = 0x1F8000;
+__rmk_boot_storage_size = 0x8000;
+```
-```text
-bootloader+state = 28K (fixed)
-storage = 128K (fixed — 32 sectors × 4K for persistent keymap storage)
-remaining = flash_size - 28K - 128K
-ACTIVE = (remaining - 4K) / 2
-DFU = ACTIVE + 4K (1 page delta required by embassy-boot swap)
+At runtime, RMK reads the DFU partition offsets from the **linker symbols** embedded in the file.
+
+```rust
+// All offsets come from linker symbols in memory.x
+let flash = async_flash_wrapper(
+ rmk::dfu::init_flash_from_linkerscript(p.FLASH)
+);
```
-The 4K page delta is an **invariant of embassy-boot's swap algorithm**: the DFU slot must always be exactly one erase page larger than the ACTIVE slot. Storage is reserved at the end of flash and **cannot be reconfigured** when using rmk-boot — it is always 128K (32 sectors × 4K).
+The `#[rmk_keyboard]` macro generates this call automatically when a `[dfu]` section is present in `keyboard.toml`.
-| Region | 2MB (RP2040) | 4MB (RP2040) | 8MB (RP2040) | 16MB (RP2040) | 1MB (nRF52840) |
-|-------------|-------------------|-------------------|-------------------|-------------------|------------------|
-| Boot+State | 28K (0x7000) | 28K (0x7000) | 28K (0x7000) | 28K (0x7000) | 28K (0x7000) |
-| ACTIVE | **944K** (0xEC000)| **1968K** (0x1EC000) | **4016K** (0x3EC000) | **8112K** (0x7EC000) | **432K** (0x73000) |
-| DFU | **948K** (0xED000)| **1972K** (0x1ED000) | **4020K** (0x3ED000) | **8116K** (0x7ED000) | **436K** (0x7B000) |
-| Storage | 128K (0x20000) | 128K (0x20000) | 128K (0x20000) | 128K (0x20000) | 128K (0x20000) |
+The storage size defaults to **32 KB** (8 sectors × 4K pages). To change it, edit `STORAGE_SIZE` in rmk-boot's `build.rs`, rebuild, adjust `num_sectors` in your `[storage]` section accordingly and copy the newly generated `rmk-memory.x` into your project.
-The size of the ACTIVE Region is what you need to plug in as the flash size in `memory.x`.
-(see also the examples `memory.x` in `rmk/examples/use_rust/rp2040_embassy_boot/` for RP2040 or `rmk/examples/use_rust/nrf52840_embassy_boot/` for nRF)
+### Using a custom bootloader
-If you override `flash_size` in your `keyboard.toml`'s `[dfu]` section, RMK's auto-calc recomputes the layout on the fly. You never need to manually set the addresses like `dfu_offset` or `dfu_size` when using rmk-boot.
+If you built your own embassy-boot bootloader with a different flash layout, add these six symbols to your existing `memory.x` (all values are flash-relative offsets):
+
+```text
+__rmk_boot_state_offset = 0x6000; /* boot state start */
+__rmk_boot_state_size = 0x1000; /* boot state size */
+__rmk_boot_dfu_offset = 0xF3000; /* DFU download slot start */
+__rmk_boot_dfu_size = 0xED000; /* DFU download slot size */
+__rmk_boot_storage_offset = 0x1E0000; /* storage partition start */
+__rmk_boot_storage_size = 0x20000; /* storage partition size */
+```
+
+Make sure `FLASH : ORIGIN` in your `MEMORY` block starts at your ACTIVE partition. RMK's `init_flash_from_linkerscript()` picks up the symbols at runtime. All symbols must use **flash-relative offsets** (byte offsets from the start of the flash chip, not absolute XIP addresses). For RP2040, subtract the flash base (`0x10000000`). For nRF52840, flash-relative and absolute offsets are the same (flash starts at `0x00000000`).
## Prerequisites
-### rmk-boot
+### rmk-boot + rmk-memory.x
+
+You need the [rmk-boot](https://github.com/rmk-rs/rmk-boot) bootloader in the **correct flash size** for your board, and the matching `rmk-memory.x` file. Both are available from the [GitHub releases page](https://github.com/rmk-rs/rmk-boot/releases). Alternatively, build rmk-boot yourself — the build also produces `rmk-memory.x` in the project root.
-You need the [rmk-boot](https://github.com/rmk-rs/rmk-boot) bootloader in the **correct flash size** for your board. The available versions are named by flash size, e.g.:
+Download the **matching pair** for your board, e.g.:
+- `rmk-boot-rp2040-2mb.uf2` + `rmk-rp2040-2mb-memory.x`
+- `rmk-boot-nrf52840.uf2` + `rmk-nrf52840-memory.x`
+
+Rename the `*-memory.x` file to `memory.x` and place it next to your `Cargo.toml`.
**RP2040:**
-- `rmk-boot-rp2040-2mb.uf2` – for 2 MB flash
-- `rmk-boot-rp204o-4mb.uf2` – for 4 MB flash
-- `rmk-boot-rp204o-8mb.uf2` – for 8 MB flash
-- `rmk-boot-rp204o-16mb.uf2` – for 16 MB flash
+- `rmk-boot-rp2040-2mb` – for 2 MB flash
+- `rmk-boot-rp2040-4mb` – for 4 MB flash
+- `rmk-boot-rp2040-8mb` – for 8 MB flash
+- `rmk-boot-rp2040-16mb` – for 16 MB flash
::: tip
-The **2 MB version works with larger flash chips too** (e.g. 4 MB or 8 MB), using only the first 2 MB. On 2 MB you get 944K of ACTIVE space, which is ample for most RMK firmwares. If you need more room (e.g. with large keymaps, displays, RGB, or many features), pick the matching flash size variant — see the table above for exact slot sizes.
+The **2 MB version works with larger flash chips as well** (e.g. 4 MB or 8 MB), using only the first 2 MB. On 2 MB you get ~992K of ACTIVE space, which is ample for most RMK firmwares. If you need more room (e.g. with large keymaps, displays, RGB, or many features), pick the matching flash size variant.
:::
**nRF52840:**
-- `rmk-boot-nrf52840.uf2` – for 1 MB flash
-- `rmk-boot-nrf52840.elf` – for 1 MB flash, for direct flashing via probe-rs
+- `rmk-boot-nrf52840.uf2` + `rmk-nrf52840-memory.x` – for 1 MB flash
+- `rmk-boot-nrf52840.elf` + `rmk-nrf52840-memory.x` – for direct flashing via probe-rs
::: warning
When using the nRF52840 with the **Adafruit UF2 bootloader**, flashing rmk-boot **overwrites** the Adafruit bootloader (even when flashing via UF2). Subsequent UF2 uploads won't work.
-:::
+:::
### Compile RMK with DFU support
@@ -64,15 +98,25 @@ RMK must be compiled with the `dfu` feature and the platform-specific feature:
If configuring manually, ensure:
- `Cargo.toml` uses the `dfu_rp` (RP2040) or `dfu_nrf` (nRF52840) feature
-- `keyboard.toml` has `[dfu]` section
-- The flash partitioning in `memory.x` matches your bootloader and flash size (see `rmk/examples/use_rust/rp2040_embassy_boot/` for RP2040 or `rmk/examples/use_rust/nrf52840_embassy_boot/` for nRF)
+- `memory.x` from rmk-boot is present next to your `Cargo.toml`
+- `keyboard.toml` has a `[dfu]` section (at minimum an empty `[dfu]`)
+
+The `[dfu]` section needs only behavioural options — partition offsets come from the linker symbols in `memory.x`:
+
+```toml
+[dfu]
+led = "PIN_25" # optional DFU activity LED
+unlock_keys = [[0, 0], [1, 1]] # optional DFU lock key combo
+```
-**Important:** For RP2040 the rmk-boot version (2MB, 4MB, ...) and the RMK settings must use the **same flash size**. Example: rmk-boot 2 MB → RMK with 2 MB flash configuration. So that means, if you have a RP2040 with bigger flash, you can always use a rmk-boot and a memory.x in your firmware for a smaller flash size, but the both must be for the same flash size.
+All partition offsets are read at link time via `init_flash_from_linkerscript()`.
+
+**Important:** The flash size variant of your `memory.x` must match the rmk-boot binary flashed to your board.
::: tip
-You can still flash firmware via UF2 (when using RP2040, on nRF52840 the UF2 bootloader was overwritten by rmk-boot) or probe-rs — as long as it was built with the correct `memory.x` for the embassy-boot partition layout (i.e. with `dfu_rp` or `dfu_nrf` feature), it will land in the active firmware slot and leave rmk-boot untouched.
+You can still flash firmware via UF2 (when using RP2040, on nRF52840 the UF2 bootloader was overwritten by rmk-boot) or probe-rs — as long as the firmware is built with the correct `memory.x` (from rmk-boot), the linker places it at the ACTIVE partition offset and leaves rmk-boot untouched.
-If you flash a RMK firmware built with a **normal** `memory.x` (without embassy-boot), it will start at flash address `0x0` and **overwrite rmk-boot**.
+If you flash RMK firmware built with a **normal** `memory.x` (FLASH ORIGIN = `0x0`), it will **overwrite rmk-boot**.
:::
::: tip
@@ -106,7 +150,6 @@ The first time, you need to flash the bootloader first, then RMK.
**Via debug probe (probe-rs):**
```shell
# in the rmk-boot directory
-cargo build --release
# RP2040
probe-rs run --chip RP2040 target/thumbv6m-none-eabi/release/rmk-boot
# nRF52840
@@ -138,7 +181,7 @@ cargo run --release
### All subsequent updates via DFU
-Once rmk-boot and the RMK firmware have been flashed once, all future updates can be done **easily over USB via DFU**. (but as stated above, as long as the `memory.x` of your firmware fits, you can still use probe-rs (RP2040 and nRF52840) or the UF2 bootloader (RP2040))
+Once rmk-boot and the RMK firmware have been flashed once, all future updates can be done **easily over USB via DFU**. As stated above, as long as you build with `memory.x` present, you can still use probe-rs (RP2040 and nRF52480), the DFU functionality of rmk-boot (nRF52840) or the UF2 bootloader. (RP2040)
#### Generate the .bin file
@@ -156,7 +199,7 @@ rust-objcopy -O binary target/thumbv7em-none-eabihf/release/your-firmware your-f
```shell
cargo make bin --release
```
-(Requires a `Makefile.toml` with a `bin` task — the generated template from `rmkit` and the examples already include one or see embassy-boot examples.)
+(Requires a `Makefile.toml` with a `bin` task — the generated template from `rmkit` and the examples already include one.)
#### Flash via dfu-util
@@ -171,7 +214,7 @@ dfu-util -d 4c4b:4643 -D your-firmware.bin -R
Find your device's VID:PID via `lsusb` on Linux or Device Manager (under "Universal Serial Bus devices") on Windows.
-This will download the firmware binary onto the DFU partition of the flash and set a flag to tell embassy-boot at next boot that the firmware should be updated. Embassy-boot then proceeds to copy the data in the DFU partition into the ACTIVE partition by swapping page-wise. (This way the data in ACTIVE is swapped and can be rolled back). This process takes about 20 seconds for a ~120kB firmware, during which there is not LED activity.
+This will download the firmware binary onto the DFU partition of the flash and set a flag to tell embassy-boot at next boot that the firmware should be updated. Embassy-boot then proceeds to copy the data in the DFU partition into the ACTIVE partition by swapping page-wise. This process takes about 20 seconds for a ~120kB firmware. `rmk-boot` pulses the LED during that.
**Installing `dfu-util`:**
- Linux: `sudo apt install dfu-util` (Debian/Ubuntu) or `sudo pacman -S dfu-util` (Arch)
@@ -339,43 +382,24 @@ The index of the alternate setting (`-a`) is off by one due to technical reasons
### Peripheral firmware requirements
-The peripheral must call `init_flash()` to partition its flash and `mark_booted()` so that the
+The peripheral must call `init_flash_from_linkerscript()` to partition its flash and `mark_booted()` so that the
bootloader does not revert the update. With `#[rmk_peripheral(id = …)]`
(TOML API) this is generated automatically when a `[dfu]` section is
present in `keyboard.toml`.
-If you are using the Rust API on the peripheral, ensure you have this in your main function of the peripheral:
+If you are using the Rust API on the peripheral, place the `memory.x` from rmk-boot next to `Cargo.toml` (same as the central) and call:
```rust
-// for the different addresses for different chips see above
-const FLASH_SIZE: u32 = 2 * 1024 * 1024;
-const PAGE_SIZE: u32 = 4 * 1024;
-const STORAGE_SIZE: u32 = 128 * 1024;
-const STATE_OFFSET: u32 = 0x6000;
-const STATE_SIZE: u32 = 0x1000;
-const ACTIVE_OFFSET: u32 = 0x7000;
-let remaining: u32 = FLASH_SIZE - 28 * 1024 - STORAGE_SIZE;
-let active_size: u32 = (remaining - PAGE_SIZE) / 2;
-let dfu_size: u32 = active_size + PAGE_SIZE;
-let dfu_offset: u32 = ACTIVE_OFFSET + active_size;
-let storage_offset: u32 = dfu_offset + dfu_size;
-
-rmk::dfu::init_flash(
- p.FLASH,
- storage_offset,
- STORAGE_SIZE,
- STATE_OFFSET,
- STATE_SIZE,
- dfu_offset,
- dfu_size,
-);
+// All partition offsets come from linker symbols in memory.x
+rmk::dfu::init_flash_from_linkerscript(p.FLASH);
// mark the firmware as booted otherwise the bootloader thinks it didn't and will revert to the old firmware
rmk::dfu::mark_booted();
-// you can also set a DFU LED for the peripheral, it flashes during updates
+// optional DFU LED for the peripheral
let mut dfu_led_processor = DfuLedProcessor::new(Output::new(p.PIN_25, Level::Low), false);
-// if used, run the processor in run_all:
+
+// if used, run the processor of the DFU LED in run_all:
join(
run_all!(matrix, dfu_led_processor, watchdog_runner),
run_rmk_split_peripheral(uart_instance),
@@ -391,4 +415,3 @@ Without `mark_booted()` embassy-boot will revert the update at next reboot, beca
|---|---|
| Peripheral flashes 3 times after update | Firmware crashed or `mark_booted()` not called in the peripheral. Add `[dfu]` to `keyboard.toml` (using config) or call `rmk::dfu::mark_booted()` (using Rust). |
| `dfu-util -a 1` shows `dfuERROR` or hangs at 0% | Peripheral not connected or its firmware is built without the `dfu_split` feature. |
-
diff --git a/examples/use_config/nrf52840_embassy_boot/keyboard.toml b/examples/use_config/nrf52840_embassy_boot/keyboard.toml
index 2bfad1887..ceb398653 100644
--- a/examples/use_config/nrf52840_embassy_boot/keyboard.toml
+++ b/examples/use_config/nrf52840_embassy_boot/keyboard.toml
@@ -8,8 +8,8 @@ chip = "nrf52840"
usb_enable = true
[matrix]
-row_pins = ["P0_07", "P0_22", "P0_11", "P0_12"]
-col_pins = ["P0_13", "P0_17", "P0_20"]
+row_pins = ["P0_02", "P0_03", "P0_04", "P0_05"]
+col_pins = ["P0_06", "P0_07", "P0_08"]
[layout]
rows = 4
@@ -36,26 +36,6 @@ _ Kp0 _
[ble]
enabled = true
-# DFU partition layout — must match your bootloader's memory.x
-#
-# By default RMK uses the rmk-boot formula with flash_size from the
-# chip default (1 MB for nRF52840).
-# Calculation:
-# 2 * 1024 * 1024; // 2 MB (default)
-# 4 * 1024 * 1024; // 4 MB
-# and so on
-# the addresses for the state, dfu, active and storage partitions are then automatically calculated.
-# Uncomment and adjust to override:
-# [dfu]
-# flash_size = 2097152 # total flash (default: 1 MB)
-#
-# To set addresses manually, uncomment and edit the values below.
-# Doing so disables the auto-calculation:
-# state_offset = 0x6000
-# state_size = 0x1000
-# dfu_offset = 0x87000
-# dfu_size = 528384
-
[dfu]
led = "P0_15" # default P0_15, to not use set to "none"
# Optional DFU lock — physical keys to press simultaneously to unlock DFU firmware download.
diff --git a/examples/use_config/nrf52840_embassy_boot/memory.x b/examples/use_config/nrf52840_embassy_boot/memory.x
index cc390b3a9..b0c96f0b5 100644
--- a/examples/use_config/nrf52840_embassy_boot/memory.x
+++ b/examples/use_config/nrf52840_embassy_boot/memory.x
@@ -1,11 +1,22 @@
-/*
-Flash layout matching rmk-boot nRF layout:
- bootloader: 24K at 0x0, state: 4K at 0x6000
- ACTIVE (this firmware): 432K at 0x7000
- DFU: 436K at 0x73000
- storage: 128K at 0xE0000
-*/
+/* rmk-memory.x for nRF52840 — generated by rmk-boot
+ *
+ * Provides both the MEMORY layout (absolute XIP addresses) and
+ * flash-relative DFU symbols consumed by init_flash_from_linkerscript().
+ *
+ * If your board has a different flash size, replace this file with the
+ * matching file from the rmk-boot releases:
+ * https://github.com/rmk-rs/rmk-boot/releases
+ */
+
MEMORY {
- FLASH : ORIGIN = 0x00007000, LENGTH = 432K
- RAM : ORIGIN = 0x20000000, LENGTH = 256K
+ FLASH : ORIGIN = 0x00007000, LENGTH = 491520 /* ACTIVE region */
+ RAM : ORIGIN = 0x20000000, LENGTH = 256K
}
+
+/* DFU partition symbols — offsets relative to flash start */
+__rmk_boot_state_offset = 0x6000;
+__rmk_boot_state_size = 0x1000;
+__rmk_boot_dfu_offset = 0x7F000;
+__rmk_boot_dfu_size = 0x79000;
+__rmk_boot_storage_offset = 0xF8000;
+__rmk_boot_storage_size = 0x8000;
diff --git a/examples/use_config/rp2040_dfu_split/memory.x b/examples/use_config/rp2040_dfu_split/memory.x
index 88fbc73cb..674a22345 100644
--- a/examples/use_config/rp2040_dfu_split/memory.x
+++ b/examples/use_config/rp2040_dfu_split/memory.x
@@ -1,13 +1,22 @@
-/*
-This memory.x is for the RP2040 with 2MB flash.
-For different flash sizes uncomment below.
-Be aware you need to change FLASH_SIZE in src/central.rs and src/peripheral.rs accordingly
-and flash the matching bootloader.
-*/
+/* rmk-memory.x for for RP2040 2 MB — generated by rmk-boot
+ *
+ * Provides both the MEMORY layout (absolute XIP addresses) and
+ * flash-relative DFU symbols consumed by init_flash_from_linkerscript().
+ *
+ * If your board has a different flash size, replace this file with the
+ * matching file from the rmk-boot releases:
+ * https://github.com/rmk-rs/rmk-boot/releases
+ */
+
MEMORY {
-FLASH : ORIGIN = 0x10007000, LENGTH = 944K
-/* 4MB: FLASH : ORIGIN = 0x10007000, LENGTH = 1968K */
-/* 8MB: FLASH : ORIGIN = 0x10007000, LENGTH = 4016K */
-/* 16MB: FLASH : ORIGIN = 0x10007000, LENGTH = 8112K */
-RAM : ORIGIN = 0x20000000, LENGTH = 256K
+ FLASH : ORIGIN = 0x10007000, LENGTH = 1015808 /* ACTIVE region */
+ RAM : ORIGIN = 0x20000000, LENGTH = 256K /* SRAM */
}
+
+/* DFU partition symbols — offsets relative to flash start */
+__rmk_boot_state_offset = 0x6000;
+__rmk_boot_state_size = 0x1000;
+__rmk_boot_dfu_offset = 0xFF000;
+__rmk_boot_dfu_size = 0xF9000;
+__rmk_boot_storage_offset = 0x1F8000;
+__rmk_boot_storage_size = 0x8000;
diff --git a/examples/use_config/rp2040_embassy_boot/keyboard.toml b/examples/use_config/rp2040_embassy_boot/keyboard.toml
index c3cd80d94..0c0b74d30 100644
--- a/examples/use_config/rp2040_embassy_boot/keyboard.toml
+++ b/examples/use_config/rp2040_embassy_boot/keyboard.toml
@@ -39,26 +39,6 @@ _ _ _
_ _ _
"""
-# DFU partition layout — must match your bootloader's memory.x
-#
-# By default RMK uses the rmk-boot formula with flash_size from the
-# chip default (2 MB for RP2040).
-# Calculation:
-# 2 * 1024 * 1024; // 2 MB (default)
-# 4 * 1024 * 1024; // 4 MB
-# and so on
-# the addresses for the state, dfu, active and storage partitions are then automatically calculated.
-# Uncomment and adjust to override:
-# [dfu]
-# flash_size = 2097152 # total flash (default: 2 MB)
-#
-# To set addresses manually, uncomment and edit the values below.
-# Doing so disables the auto-calculation:
-# state_offset = 0x6000
-# state_size = 0x1000
-# dfu_offset = 0x87000
-# dfu_size = 528384
-
[dfu]
led = "PIN_25"
# Optional DFU lock — physical keys to press simultaneously to unlock DFU firmware download.
diff --git a/examples/use_config/rp2040_embassy_boot/memory.x b/examples/use_config/rp2040_embassy_boot/memory.x
index c6f982df6..674a22345 100644
--- a/examples/use_config/rp2040_embassy_boot/memory.x
+++ b/examples/use_config/rp2040_embassy_boot/memory.x
@@ -1,13 +1,22 @@
-/*
-This memory.x is for the RP2040 with 2MB flash.
-For different flash sizes uncomment below.
-Be aware you need to change [dfu] flash_size in keyboard.toml accordingly
-and flash the matching bootloader.
-*/
+/* rmk-memory.x for for RP2040 2 MB — generated by rmk-boot
+ *
+ * Provides both the MEMORY layout (absolute XIP addresses) and
+ * flash-relative DFU symbols consumed by init_flash_from_linkerscript().
+ *
+ * If your board has a different flash size, replace this file with the
+ * matching file from the rmk-boot releases:
+ * https://github.com/rmk-rs/rmk-boot/releases
+ */
+
MEMORY {
- FLASH : ORIGIN = 0x10007000, LENGTH = 944K
- /* 4MB: FLASH : ORIGIN = 0x10007000, LENGTH = 1968K */
- /* 8MB: FLASH : ORIGIN = 0x10007000, LENGTH = 4016K */
- /* 16MB: FLASH : ORIGIN = 0x10007000, LENGTH = 8112K */
- RAM : ORIGIN = 0x20000000, LENGTH = 256K
+ FLASH : ORIGIN = 0x10007000, LENGTH = 1015808 /* ACTIVE region */
+ RAM : ORIGIN = 0x20000000, LENGTH = 256K /* SRAM */
}
+
+/* DFU partition symbols — offsets relative to flash start */
+__rmk_boot_state_offset = 0x6000;
+__rmk_boot_state_size = 0x1000;
+__rmk_boot_dfu_offset = 0xFF000;
+__rmk_boot_dfu_size = 0xF9000;
+__rmk_boot_storage_offset = 0x1F8000;
+__rmk_boot_storage_size = 0x8000;
diff --git a/examples/use_rust/nrf52840_embassy_boot/memory.x b/examples/use_rust/nrf52840_embassy_boot/memory.x
index 43c50bdd2..b0c96f0b5 100644
--- a/examples/use_rust/nrf52840_embassy_boot/memory.x
+++ b/examples/use_rust/nrf52840_embassy_boot/memory.x
@@ -1,11 +1,22 @@
-/*
-Flash layout matching rmk-boot nRF layout:
- bootloader: 24K at 0x0, state: 4K at 0x6000
- ACTIVE (this firmware): 464K at 0x7000
- DFU: 468K at 0x7B000
- storage: 64K at 0xF0000
-*/
+/* rmk-memory.x for nRF52840 — generated by rmk-boot
+ *
+ * Provides both the MEMORY layout (absolute XIP addresses) and
+ * flash-relative DFU symbols consumed by init_flash_from_linkerscript().
+ *
+ * If your board has a different flash size, replace this file with the
+ * matching file from the rmk-boot releases:
+ * https://github.com/rmk-rs/rmk-boot/releases
+ */
+
MEMORY {
- FLASH : ORIGIN = 0x00007000, LENGTH = 432K
- RAM : ORIGIN = 0x20000000, LENGTH = 256K
+ FLASH : ORIGIN = 0x00007000, LENGTH = 491520 /* ACTIVE region */
+ RAM : ORIGIN = 0x20000000, LENGTH = 256K
}
+
+/* DFU partition symbols — offsets relative to flash start */
+__rmk_boot_state_offset = 0x6000;
+__rmk_boot_state_size = 0x1000;
+__rmk_boot_dfu_offset = 0x7F000;
+__rmk_boot_dfu_size = 0x79000;
+__rmk_boot_storage_offset = 0xF8000;
+__rmk_boot_storage_size = 0x8000;
diff --git a/examples/use_rust/nrf52840_embassy_boot/src/main.rs b/examples/use_rust/nrf52840_embassy_boot/src/main.rs
index d9c0b5ba1..3a0513450 100644
--- a/examples/use_rust/nrf52840_embassy_boot/src/main.rs
+++ b/examples/use_rust/nrf52840_embassy_boot/src/main.rs
@@ -50,52 +50,8 @@ async fn main(_spawner: Spawner) {
let (row_pins, col_pins) =
config_matrix_pins_nrf!(peripherals: p, input: [P0_07, P0_22, P0_11, P0_12], output: [P0_13, P0_17, P0_20]);
- // Flash layout using the rmk-boot formula:
- // state at 0x6000 (4K), active from 0x7000 (size: (flash_size - 28K (= embassy-boot + embassy-boot state) - STORAGE_SIZE (= 64K) - page_size (= 4K)) / 2),
- // dfu follows active (active_size + page_size (= 4K))
- //
- // All offsets (DFU_OFFSET, DFU_SIZE, STORAGE_OFFSET, etc.) are derived
- // automatically from FLASH_SIZE below — change only that constant when using
- // rmk-boot.
- //
- // ⚠ You can define your own FLASH_SIZE and addresses, but then you must build and
- // flash a custom embassy-boot bootloader with a matching memory.x!
- const FLASH_SIZE: u32 = 1024 * 1024; // 1 MB (nRF52840)
- const PAGE_SIZE: u32 = 4 * 1024;
- const STORAGE_SIZE: u32 = 128 * 1024; // 32 sectors × 4K after ACTIVE+DFU
- const STATE_OFFSET: u32 = 0x6000;
- const STATE_SIZE: u32 = 0x1000;
- const ACTIVE_OFFSET: u32 = 0x7000;
- let remaining: u32 = FLASH_SIZE
- - 28 * 1024 // bootloader (24K) + state (4K)
- - STORAGE_SIZE;
- let active_size: u32 = (remaining - PAGE_SIZE) / 2;
- let dfu_size: u32 = active_size + PAGE_SIZE;
- let dfu_offset: u32 = ACTIVE_OFFSET + active_size;
- let storage_offset: u32 = dfu_offset + dfu_size;
- assert!(storage_offset + STORAGE_SIZE == FLASH_SIZE);
-
- info!(
- "Flash layout: state @ 0x{:04X} ({}K), active @ 0x{:04X} ({}K), dfu @ 0x{:04X} ({}K), storage @ 0x{:04X} ({}K)",
- STATE_OFFSET,
- STATE_SIZE / 1024,
- ACTIVE_OFFSET,
- active_size / 1024,
- dfu_offset,
- dfu_size / 1024,
- storage_offset,
- STORAGE_SIZE / 1024
- );
-
- let flash = async_flash_wrapper(rmk::dfu::init_flash(
- p.NVMC,
- storage_offset,
- STORAGE_SIZE,
- STATE_OFFSET,
- STATE_SIZE,
- dfu_offset,
- dfu_size,
- ));
+ // Flash partition layout comes from rmk-boot.x linker script.
+ let flash = async_flash_wrapper(rmk::dfu::init_flash_from_linkerscript(p.NVMC));
let mut dfu_led_processor = rmk::processor::builtin::dfu_led::DfuLedProcessor::new(
Output::new(p.P0_15, Level::Low, OutputDrive::Standard),
@@ -120,7 +76,7 @@ async fn main(_spawner: Spawner) {
let mut keymap_data = KeymapData::new(keymap::get_default_keymap());
let storage_config = StorageConfig {
- num_sectors: 16,
+ num_sectors: 8,
start_addr: 0,
clear_storage: false,
clear_layout: false,
diff --git a/examples/use_rust/rp2040_dfu_split/memory.x b/examples/use_rust/rp2040_dfu_split/memory.x
index 88fbc73cb..674a22345 100644
--- a/examples/use_rust/rp2040_dfu_split/memory.x
+++ b/examples/use_rust/rp2040_dfu_split/memory.x
@@ -1,13 +1,22 @@
-/*
-This memory.x is for the RP2040 with 2MB flash.
-For different flash sizes uncomment below.
-Be aware you need to change FLASH_SIZE in src/central.rs and src/peripheral.rs accordingly
-and flash the matching bootloader.
-*/
+/* rmk-memory.x for for RP2040 2 MB — generated by rmk-boot
+ *
+ * Provides both the MEMORY layout (absolute XIP addresses) and
+ * flash-relative DFU symbols consumed by init_flash_from_linkerscript().
+ *
+ * If your board has a different flash size, replace this file with the
+ * matching file from the rmk-boot releases:
+ * https://github.com/rmk-rs/rmk-boot/releases
+ */
+
MEMORY {
-FLASH : ORIGIN = 0x10007000, LENGTH = 944K
-/* 4MB: FLASH : ORIGIN = 0x10007000, LENGTH = 1968K */
-/* 8MB: FLASH : ORIGIN = 0x10007000, LENGTH = 4016K */
-/* 16MB: FLASH : ORIGIN = 0x10007000, LENGTH = 8112K */
-RAM : ORIGIN = 0x20000000, LENGTH = 256K
+ FLASH : ORIGIN = 0x10007000, LENGTH = 1015808 /* ACTIVE region */
+ RAM : ORIGIN = 0x20000000, LENGTH = 256K /* SRAM */
}
+
+/* DFU partition symbols — offsets relative to flash start */
+__rmk_boot_state_offset = 0x6000;
+__rmk_boot_state_size = 0x1000;
+__rmk_boot_dfu_offset = 0xFF000;
+__rmk_boot_dfu_size = 0xF9000;
+__rmk_boot_storage_offset = 0x1F8000;
+__rmk_boot_storage_size = 0x8000;
diff --git a/examples/use_rust/rp2040_dfu_split/src/central.rs b/examples/use_rust/rp2040_dfu_split/src/central.rs
index 1a09c2c40..baf9798ec 100644
--- a/examples/use_rust/rp2040_dfu_split/src/central.rs
+++ b/examples/use_rust/rp2040_dfu_split/src/central.rs
@@ -53,55 +53,7 @@ async fn main(_spawner: Spawner) {
let (row_pins, col_pins) = config_matrix_pins_rp!(peripherals: p, input: [PIN_6, PIN_7], output: [PIN_19, PIN_20]);
- // Flash layout using the rmk-boot formula:
- // state at 0x6000 (4K), active from 0x7000 (size: (flash_size - 28K (= BOOT2 size + embassy-boot + embassy-boot state) - STORAGE_SIZE (= 128K) - page_size (= 4K)) / 2),
- // dfu follows active (active_size + page_size (= 4K))
- //
- // All offsets (DFU_OFFSET, DFU_SIZE, STORAGE_OFFSET, etc.) are derived
- // automatically from FLASH_SIZE below --- change only that constant when using
- // rmk-boot.
- //
- // ⚠ You can define your own FLASH_SIZE and addresses, but then you must build and
- // flash a custom embassy-boot bootloader with a matching memory.x!
- const FLASH_SIZE: u32 = 2 * 1024 * 1024; // 2 MB (default)
- // const FLASH_SIZE: u32 = 4 * 1024 * 1024; // 4 MB
- // const FLASH_SIZE: u32 = 8 * 1024 * 1024; // 8 MB
- // const FLASH_SIZE: u32 = 16 * 1024 * 1024; // 16 MB
- const PAGE_SIZE: u32 = 4 * 1024;
- const STORAGE_SIZE: u32 = 128 * 1024; // 32 sectors × 4K after ACTIVE+DFU
- const STATE_OFFSET: u32 = 0x6000;
- const STATE_SIZE: u32 = 0x1000;
- const ACTIVE_OFFSET: u32 = 0x7000; // after 28K bootloader + state
- let remaining: u32 = FLASH_SIZE
- - 28 * 1024 // size of boot 2 + embassy-boot + embassy-boot state
- - STORAGE_SIZE;
- let active_size: u32 = (remaining - PAGE_SIZE) / 2; // DFU = ACTIVE + 1 page (embassy-boot requirement)
- let dfu_size: u32 = active_size + PAGE_SIZE; // embassy-boot needs that extra page for swap info
- let dfu_offset: u32 = ACTIVE_OFFSET + active_size; // dfu after active
- let storage_offset: u32 = dfu_offset + dfu_size; // storage after active + dfu
- assert!(storage_offset + STORAGE_SIZE == FLASH_SIZE); // sanity check that we fit everything in flash
-
- info!(
- "Flash layout: state @ 0x{:04X} ({}K), active @ 0x{:04X} ({}K), dfu @ 0x{:04X} ({}K), storage @ 0x{:04X} ({}K)",
- STATE_OFFSET,
- STATE_SIZE / 1024,
- ACTIVE_OFFSET,
- active_size / 1024,
- dfu_offset,
- dfu_size / 1024,
- storage_offset,
- STORAGE_SIZE / 1024
- );
-
- let flash = async_flash_wrapper(rmk::dfu::init_flash(
- p.FLASH,
- storage_offset,
- STORAGE_SIZE,
- STATE_OFFSET,
- STATE_SIZE,
- dfu_offset,
- dfu_size,
- ));
+ let flash = async_flash_wrapper(rmk::dfu::init_flash_from_linkerscript(p.FLASH));
let keyboard_device_config = DeviceConfig {
vid: 0x4c4b,
@@ -121,7 +73,7 @@ async fn main(_spawner: Spawner) {
let mut keymap_data = KeymapData::new(keymap::get_default_keymap());
let storage_config = StorageConfig {
- num_sectors: 32,
+ num_sectors: 8,
start_addr: 0,
clear_storage: false,
clear_layout: false,
diff --git a/examples/use_rust/rp2040_dfu_split/src/peripheral.rs b/examples/use_rust/rp2040_dfu_split/src/peripheral.rs
index 6a6e4834f..41ae451c0 100644
--- a/examples/use_rust/rp2040_dfu_split/src/peripheral.rs
+++ b/examples/use_rust/rp2040_dfu_split/src/peripheral.rs
@@ -35,29 +35,7 @@ async fn main(_spawner: Spawner) {
let (row_pins, col_pins) = config_matrix_pins_rp!(peripherals: p, input: [PIN_8, PIN_9], output: [PIN_10]);
- // Flash layout for embassy-boot on the peripheral,
- // matching the central's layout for consistency.
- const FLASH_SIZE: u32 = 2 * 1024 * 1024;
- const PAGE_SIZE: u32 = 4 * 1024;
- const STORAGE_SIZE: u32 = 128 * 1024;
- const STATE_OFFSET: u32 = 0x6000;
- const STATE_SIZE: u32 = 0x1000;
- const ACTIVE_OFFSET: u32 = 0x7000;
- let remaining: u32 = FLASH_SIZE - 28 * 1024 - STORAGE_SIZE;
- let active_size: u32 = (remaining - PAGE_SIZE) / 2;
- let dfu_size: u32 = active_size + PAGE_SIZE;
- let dfu_offset: u32 = ACTIVE_OFFSET + active_size;
- let storage_offset: u32 = dfu_offset + dfu_size;
-
- rmk::dfu::init_flash(
- p.FLASH,
- storage_offset,
- STORAGE_SIZE,
- STATE_OFFSET,
- STATE_SIZE,
- dfu_offset,
- dfu_size,
- );
+ rmk::dfu::init_flash_from_linkerscript(p.FLASH);
// mark the firmware as booted otherwise the bootloader thinks it didn't and will revert to the old firmware
rmk::dfu::mark_booted();
diff --git a/examples/use_rust/rp2040_embassy_boot/memory.x b/examples/use_rust/rp2040_embassy_boot/memory.x
index ed0413401..674a22345 100644
--- a/examples/use_rust/rp2040_embassy_boot/memory.x
+++ b/examples/use_rust/rp2040_embassy_boot/memory.x
@@ -1,13 +1,22 @@
-/*
-This memory.x is for the RP2040 with 2MB flash.
-For different flash sizes uncomment below.
-Be aware you need to change FLASH_SIZE in main.rs accordingly
-and flash the matching bootloader.
-*/
+/* rmk-memory.x for for RP2040 2 MB — generated by rmk-boot
+ *
+ * Provides both the MEMORY layout (absolute XIP addresses) and
+ * flash-relative DFU symbols consumed by init_flash_from_linkerscript().
+ *
+ * If your board has a different flash size, replace this file with the
+ * matching file from the rmk-boot releases:
+ * https://github.com/rmk-rs/rmk-boot/releases
+ */
+
MEMORY {
-FLASH : ORIGIN = 0x10007000, LENGTH = 944K
-/* 4MB: FLASH : ORIGIN = 0x10007000, LENGTH = 1968K */
-/* 8MB: FLASH : ORIGIN = 0x10007000, LENGTH = 4016K */
-/* 16MB: FLASH : ORIGIN = 0x10007000, LENGTH = 8112K */
-RAM : ORIGIN = 0x20000000, LENGTH = 256K
+ FLASH : ORIGIN = 0x10007000, LENGTH = 1015808 /* ACTIVE region */
+ RAM : ORIGIN = 0x20000000, LENGTH = 256K /* SRAM */
}
+
+/* DFU partition symbols — offsets relative to flash start */
+__rmk_boot_state_offset = 0x6000;
+__rmk_boot_state_size = 0x1000;
+__rmk_boot_dfu_offset = 0xFF000;
+__rmk_boot_dfu_size = 0xF9000;
+__rmk_boot_storage_offset = 0x1F8000;
+__rmk_boot_storage_size = 0x8000;
diff --git a/examples/use_rust/rp2040_embassy_boot/src/main.rs b/examples/use_rust/rp2040_embassy_boot/src/main.rs
index 4780c7602..7fd0c9aeb 100644
--- a/examples/use_rust/rp2040_embassy_boot/src/main.rs
+++ b/examples/use_rust/rp2040_embassy_boot/src/main.rs
@@ -43,55 +43,10 @@ async fn main(_spawner: Spawner) {
let (row_pins, col_pins) =
config_matrix_pins_rp!(peripherals: p, input: [PIN_6, PIN_7, PIN_8, PIN_9], output: [PIN_19, PIN_20, PIN_21]);
- // Flash layout using the rmk-boot formula:
- // state at 0x6000 (4K), active from 0x7000 (size: (flash_size - 28K (= BOOT2 size + embassy-boot + embassy-boot state) - STORAGE_SIZE (= 128K) - page_size (= 4K)) / 2),
- // dfu follows active (active_size + page_size (= 4K))
- //
- // All offsets (DFU_OFFSET, DFU_SIZE, STORAGE_OFFSET, etc.) are derived
- // automatically from FLASH_SIZE below — change only that constant when using
- // rmk-boot.
- //
- // ⚠ You can define your own FLASH_SIZE and addresses, but then you must build and
- // flash a custom embassy-boot bootloader with a matching memory.x!
- const FLASH_SIZE: u32 = 2 * 1024 * 1024; // 2 MB (default)
- // const FLASH_SIZE: u32 = 4 * 1024 * 1024; // 4 MB
- // const FLASH_SIZE: u32 = 8 * 1024 * 1024; // 8 MB
- // const FLASH_SIZE: u32 = 16 * 1024 * 1024; // 16 MB
- const PAGE_SIZE: u32 = 4 * 1024;
- const STORAGE_SIZE: u32 = 128 * 1024; // 32 sectors × 4K after ACTIVE+DFU
- const STATE_OFFSET: u32 = 0x6000;
- const STATE_SIZE: u32 = 0x1000;
- const ACTIVE_OFFSET: u32 = 0x7000; // after 28K bootloader + state
- let remaining: u32 = FLASH_SIZE
- - 28 * 1024 // size of boot 2 + embassy-boot + embassy-boot state
- - STORAGE_SIZE;
- let active_size: u32 = (remaining - PAGE_SIZE) / 2; // DFU = ACTIVE + 1 page (embassy-boot requirement)
- let dfu_size: u32 = active_size + PAGE_SIZE; // embassy-boot needs that extra page for swap info
- let dfu_offset: u32 = ACTIVE_OFFSET + active_size; // dfu after active
- let storage_offset: u32 = dfu_offset + dfu_size; // storage after active + dfu
- assert!(storage_offset + STORAGE_SIZE == FLASH_SIZE); // sanity check that we fit everything in flash
-
- info!(
- "Flash layout: state @ 0x{:04X} ({}K), active @ 0x{:04X} ({}K), dfu @ 0x{:04X} ({}K), storage @ 0x{:04X} ({}K)",
- STATE_OFFSET,
- STATE_SIZE / 1024,
- ACTIVE_OFFSET,
- active_size / 1024,
- dfu_offset,
- dfu_size / 1024,
- storage_offset,
- STORAGE_SIZE / 1024
- );
-
- let flash = async_flash_wrapper(rmk::dfu::init_flash(
- p.FLASH,
- storage_offset,
- STORAGE_SIZE,
- STATE_OFFSET,
- STATE_SIZE,
- dfu_offset,
- dfu_size,
- ));
+ // Flash partition layout comes from rmk-boot.x linker script.
+ // No manual offsets needed — init_flash_from_linkerscript reads them
+ // from the linked symbols.
+ let flash = async_flash_wrapper(rmk::dfu::init_flash_from_linkerscript(p.FLASH));
let mut dfu_led_processor =
rmk::processor::builtin::dfu_led::DfuLedProcessor::new(Output::new(p.PIN_25, Level::Low), false);
@@ -114,7 +69,7 @@ async fn main(_spawner: Spawner) {
let mut keymap_data = KeymapData::new(keymap::get_default_keymap());
let storage_config = StorageConfig {
- num_sectors: 32,
+ num_sectors: 8,
start_addr: 0,
clear_storage: false,
clear_layout: false,
diff --git a/examples/use_rust/rp2040_embassy_boot_split/memory.x b/examples/use_rust/rp2040_embassy_boot_split/memory.x
index 1a1c12a71..674a22345 100644
--- a/examples/use_rust/rp2040_embassy_boot_split/memory.x
+++ b/examples/use_rust/rp2040_embassy_boot_split/memory.x
@@ -1,4 +1,22 @@
+/* rmk-memory.x for for RP2040 2 MB — generated by rmk-boot
+ *
+ * Provides both the MEMORY layout (absolute XIP addresses) and
+ * flash-relative DFU symbols consumed by init_flash_from_linkerscript().
+ *
+ * If your board has a different flash size, replace this file with the
+ * matching file from the rmk-boot releases:
+ * https://github.com/rmk-rs/rmk-boot/releases
+ */
+
MEMORY {
- FLASH : ORIGIN = 0x10007000, LENGTH = 944K
- RAM : ORIGIN = 0x20000000, LENGTH = 256K
+ FLASH : ORIGIN = 0x10007000, LENGTH = 1015808 /* ACTIVE region */
+ RAM : ORIGIN = 0x20000000, LENGTH = 256K /* SRAM */
}
+
+/* DFU partition symbols — offsets relative to flash start */
+__rmk_boot_state_offset = 0x6000;
+__rmk_boot_state_size = 0x1000;
+__rmk_boot_dfu_offset = 0xFF000;
+__rmk_boot_dfu_size = 0xF9000;
+__rmk_boot_storage_offset = 0x1F8000;
+__rmk_boot_storage_size = 0x8000;
diff --git a/examples/use_rust/rp2040_embassy_boot_split/src/central.rs b/examples/use_rust/rp2040_embassy_boot_split/src/central.rs
index 457d9f7de..70a1b3fec 100644
--- a/examples/use_rust/rp2040_embassy_boot_split/src/central.rs
+++ b/examples/use_rust/rp2040_embassy_boot_split/src/central.rs
@@ -38,13 +38,6 @@ bind_interrupts!(struct Irqs {
DMA_IRQ_0 => dma::InterruptHandler;
});
-const FLASH_SIZE: u32 = 2 * 1024 * 1024;
-const PAGE_SIZE: u32 = 4 * 1024;
-const STORAGE_SIZE: u32 = 128 * 1024;
-const STATE_OFFSET: u32 = 0x6000;
-const STATE_SIZE: u32 = 0x1000;
-const ACTIVE_OFFSET: u32 = 0x7000;
-
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
info!("RMK central start!");
@@ -54,32 +47,7 @@ async fn main(_spawner: Spawner) {
let (row_pins, col_pins) = config_matrix_pins_rp!(peripherals: p, input: [PIN_9, PIN_11], output: [PIN_10, PIN_12]);
- let remaining = FLASH_SIZE - 28 * 1024 - STORAGE_SIZE;
- let active_size = (remaining - PAGE_SIZE) / 2;
- let dfu_size = active_size + PAGE_SIZE;
- let dfu_offset = ACTIVE_OFFSET + active_size;
- let storage_offset = dfu_offset + dfu_size;
- assert!(storage_offset + STORAGE_SIZE == FLASH_SIZE);
-
- info!(
- "Flash: state=0x{:04X} active=0x{:04X}({}K) dfu=0x{:04X}({}K) storage=0x{:04X}",
- STATE_OFFSET,
- ACTIVE_OFFSET,
- active_size / 1024,
- dfu_offset,
- dfu_size / 1024,
- storage_offset
- );
-
- let flash = async_flash_wrapper(rmk::dfu::init_flash(
- p.FLASH,
- storage_offset,
- STORAGE_SIZE,
- STATE_OFFSET,
- STATE_SIZE,
- dfu_offset,
- dfu_size,
- ));
+ let flash = async_flash_wrapper(rmk::dfu::init_flash_from_linkerscript(p.FLASH));
rmk::dfu::mark_booted();
@@ -108,7 +76,7 @@ async fn main(_spawner: Spawner) {
let mut keymap_data = KeymapData::new(keymap::get_default_keymap());
let mut behavior_config = BehaviorConfig::default();
let storage_config = StorageConfig {
- num_sectors: 32,
+ num_sectors: 8,
start_addr: 0,
clear_storage: false,
clear_layout: false,
diff --git a/examples/use_rust/rp2040_embassy_boot_split/src/peripheral.rs b/examples/use_rust/rp2040_embassy_boot_split/src/peripheral.rs
index 1a2436878..b4a5395bd 100644
--- a/examples/use_rust/rp2040_embassy_boot_split/src/peripheral.rs
+++ b/examples/use_rust/rp2040_embassy_boot_split/src/peripheral.rs
@@ -31,13 +31,6 @@ bind_interrupts!(struct Irqs {
DMA_IRQ_0 => dma::InterruptHandler;
});
-const FLASH_SIZE: u32 = 2 * 1024 * 1024;
-const PAGE_SIZE: u32 = 4 * 1024;
-const STORAGE_SIZE: u32 = 128 * 1024;
-const STATE_OFFSET: u32 = 0x6000;
-const STATE_SIZE: u32 = 0x1000;
-const ACTIVE_OFFSET: u32 = 0x7000;
-
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
info!("RMK peripheral start!");
@@ -45,21 +38,7 @@ async fn main(_spawner: Spawner) {
let (row_pins, col_pins) = config_matrix_pins_rp!(peripherals: p, input: [PIN_9, PIN_11], output: [PIN_10, PIN_12]);
- let remaining = FLASH_SIZE - 28 * 1024 - STORAGE_SIZE;
- let active_size = (remaining - PAGE_SIZE) / 2;
- let dfu_size = active_size + PAGE_SIZE;
- let dfu_offset = ACTIVE_OFFSET + active_size;
- let storage_offset = dfu_offset + dfu_size;
-
- let flash = async_flash_wrapper(rmk::dfu::init_flash(
- p.FLASH,
- storage_offset,
- STORAGE_SIZE,
- STATE_OFFSET,
- STATE_SIZE,
- dfu_offset,
- dfu_size,
- ));
+ let flash = async_flash_wrapper(rmk::dfu::init_flash_from_linkerscript(p.FLASH));
rmk::dfu::mark_booted();
@@ -86,7 +65,7 @@ async fn main(_spawner: Spawner) {
let mut matrix = Matrix::<_, _, _, 2, 2, true>::new(row_pins, col_pins, debouncer);
let storage_config = rmk::config::StorageConfig {
- num_sectors: 32,
+ num_sectors: 8,
start_addr: 0,
clear_storage: false,
clear_layout: false,
diff --git a/rmk-config/src/default_config/nice_nano.toml b/rmk-config/src/default_config/nice_nano.toml
index a4dc0f3df..c0758450c 100644
--- a/rmk-config/src/default_config/nice_nano.toml
+++ b/rmk-config/src/default_config/nice_nano.toml
@@ -8,7 +8,6 @@ adc_divider_measured = 2000
adc_divider_total = 2806
[dfu]
-flash_size = 1048576
[storage]
enabled = true
diff --git a/rmk-config/src/default_config/nice_nano_v2.toml b/rmk-config/src/default_config/nice_nano_v2.toml
index b2860b209..d77202164 100644
--- a/rmk-config/src/default_config/nice_nano_v2.toml
+++ b/rmk-config/src/default_config/nice_nano_v2.toml
@@ -6,7 +6,6 @@ enabled = true
battery_adc_pin = "vddh"
[dfu]
-flash_size = 1048576
[storage]
enabled = true
diff --git a/rmk-config/src/default_config/nrf52840.toml b/rmk-config/src/default_config/nrf52840.toml
index 1deae99c1..df3cfdb8e 100644
--- a/rmk-config/src/default_config/nrf52840.toml
+++ b/rmk-config/src/default_config/nrf52840.toml
@@ -12,7 +12,6 @@ start_addr = 0xA0000
num_sectors = 32
[dfu]
-flash_size = 1048576
[chip.nrf52840]
# DCDC regulator 0 enabled (default: true)
diff --git a/rmk-config/src/default_config/pi_pico_w.toml b/rmk-config/src/default_config/pi_pico_w.toml
index 300aa825e..de0b3cc22 100644
--- a/rmk-config/src/default_config/pi_pico_w.toml
+++ b/rmk-config/src/default_config/pi_pico_w.toml
@@ -10,4 +10,3 @@ start_addr = 0x100000
num_sectors = 32
[dfu]
-flash_size = 2097152
\ No newline at end of file
diff --git a/rmk-config/src/default_config/rp2040.toml b/rmk-config/src/default_config/rp2040.toml
index e0b731315..2d07ed299 100644
--- a/rmk-config/src/default_config/rp2040.toml
+++ b/rmk-config/src/default_config/rp2040.toml
@@ -4,7 +4,5 @@ usb_enable = true
[storage]
enabled = true
start_addr = 0x100000
-num_sectors = 32
[dfu]
-flash_size = 2097152
\ No newline at end of file
diff --git a/rmk-config/src/lib.rs b/rmk-config/src/lib.rs
index d858da563..462211ebd 100644
--- a/rmk-config/src/lib.rs
+++ b/rmk-config/src/lib.rs
@@ -623,27 +623,15 @@ pub(crate) struct StorageConfig {
pub clear_layout: Option,
}
-/// Config for DFU partition layout (embassy-boot).
+/// Config for DFU (embassy-boot).
///
-/// These values must match the bootloader's `memory.x` / linker script.
+/// Offsets come from `rmk-boot.x` linker symbols. This section only
+/// configures DFU behaviour (LED, unlock keys, page size).
#[derive(Clone, Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct DfuTomlConfig {
- /// Offset of the boot state partition
- pub state_offset: Option,
- /// Size of the boot state partition
- pub state_size: Option,
- /// Offset of the DFU download partition
- pub dfu_offset: Option,
- /// Size of the DFU download partition
- pub dfu_size: Option,
/// Flash page size in bytes (e.g. 4096 for RP2040).
- /// Used with `flash_size` to auto-calculate partition addresses.
pub page_size: Option,
- /// Total flash size in bytes. When set, DFU partition addresses are
- /// calculated automatically using the rmk-boot formula.
- /// Defaults to 2 MB (2097152) when omitted.
- pub flash_size: Option,
/// Optional DFU activity LED pin, e.g. `"PIN_16"`. When set, the LED
/// is lit while a DFU download is in progress.
pub led: Option,
diff --git a/rmk-config/src/resolved/hardware.rs b/rmk-config/src/resolved/hardware.rs
index d544834ef..c9f9a431b 100644
--- a/rmk-config/src/resolved/hardware.rs
+++ b/rmk-config/src/resolved/hardware.rs
@@ -24,10 +24,6 @@ pub struct Storage {
/// Resolved DFU partition config
pub struct DfuConfig {
- pub state_offset: u32,
- pub state_size: u32,
- pub dfu_offset: u32,
- pub dfu_size: u32,
pub page_size: u32,
pub led: Option,
pub unlock_keys: Vec<[u8; 2]>,
@@ -58,89 +54,33 @@ impl crate::KeyboardTomlConfig {
let storage = if storage_toml.enabled {
Some(Storage {
start_addr: storage_toml.start_addr.unwrap_or(0),
- num_sectors: storage_toml.num_sectors.unwrap_or(2),
+ num_sectors: if self.get_dfu_config().is_some() {
+ if self.storage_user_set {
+ storage_toml.num_sectors.unwrap_or(8)
+ } else {
+ 8
+ }
+ } else {
+ storage_toml.num_sectors.unwrap_or(2)
+ },
clear_storage: storage_toml.clear_storage.unwrap_or(false),
clear_layout: storage_toml.clear_layout.unwrap_or(false),
})
} else {
None
};
- let (dfu, dfu_auto_calc) = match self.get_dfu_config() {
+ let dfu = match self.get_dfu_config() {
Some(d) => {
- let vals = [d.state_offset, d.state_size, d.dfu_offset, d.dfu_size];
- let any_set = vals.iter().any(|v| v.is_some());
- let all_set = vals.iter().all(|v| v.is_some());
-
- if any_set && !all_set {
- return Err(
- "If you set one of state_offset/state_size/dfu_offset/dfu_size, you must set all four"
- .to_string(),
- );
- }
-
let unlock_keys = d.unlock_keys.clone().unwrap_or_default();
validate_unlock_keys("[dfu]", &unlock_keys, self.layout.as_ref())?;
-
- if all_set {
- (
- Some(DfuConfig {
- state_offset: d.state_offset.unwrap(),
- state_size: d.state_size.unwrap(),
- dfu_offset: d.dfu_offset.unwrap(),
- dfu_size: d.dfu_size.unwrap(),
- page_size: d.page_size.unwrap_or(4096),
- led: d.led.clone().map(|pin| PinConfig { pin, low_active: false }),
- unlock_keys,
- }),
- false,
- )
- } else {
- // Auto-calculate: use ALL remaining flash for ACTIVE+DFU+storage
- // layout: [28K bootloader+state][ACTIVE][DFU(ACTIVE+1page)][storage]
- let flash_size = d.flash_size.unwrap_or(2 * 1024 * 1024);
- let page_size = d.page_size.unwrap_or(4096);
- let bootloader_state_end = 0x7000u32; // 28K
- // Reserve 128 KB for storage behind DFU, DFU auto-calc always assumes this
- let storage_size = 32u32 * page_size;
- let remaining = flash_size - bootloader_state_end - storage_size;
- let active_size = (remaining - page_size) / 2;
- (
- Some(DfuConfig {
- state_offset: 0x6000,
- state_size: 0x1000,
- dfu_offset: bootloader_state_end + active_size,
- dfu_size: active_size + page_size,
- page_size,
- led: d.led.clone().map(|pin| PinConfig { pin, low_active: false }),
- unlock_keys,
- }),
- true,
- )
- }
+ Some(DfuConfig {
+ page_size: d.page_size.unwrap_or(4096),
+ led: d.led.clone().map(|pin| PinConfig { pin, low_active: false }),
+ unlock_keys,
+ })
}
- None => (None, false),
+ None => None,
};
- if self.storage_user_set
- && dfu_auto_calc
- && let Some(storage_cfg) = &self.storage
- {
- if let Some(num_sectors) = storage_cfg.num_sectors
- && num_sectors != 32
- {
- eprintln!(
- "warning: `[storage].num_sectors = {}` is ignored with DFU auto-calculation. The DFU layout always reserves 128 KB (32 sectors) at the end of flash. Values < 32 waste reserved space, values > 32 risk flash overflow.",
- num_sectors
- );
- }
- if let Some(start_addr) = storage_cfg.start_addr
- && start_addr != 0
- {
- eprintln!(
- "warning: `[storage].start_addr = {:#x}` has no effect with DFU. The storage partition is automatically placed after the DFU partition.",
- start_addr
- );
- }
- }
let light = self.get_light_config();
let display = self.get_display_config();
let output = self.get_output_config()?;
diff --git a/rmk-macro/src/codegen/chip/flash.rs b/rmk-macro/src/codegen/chip/flash.rs
index 031d257a0..139dac55d 100644
--- a/rmk-macro/src/codegen/chip/flash.rs
+++ b/rmk-macro/src/codegen/chip/flash.rs
@@ -51,27 +51,11 @@ pub(crate) fn expand_flash_init(hardware: &Hardware) -> TokenStream2 {
let dfu = hardware.dfu.as_ref().expect(
"[dfu] section is required in keyboard.toml (or chip default) when dfu_nrf is enabled"
);
- let storage_num_sectors = hardware.storage.as_ref().map(|s| s.num_sectors).unwrap_or(32) as u32;
- let erase_size = dfu.page_size;
- let storage_offset = dfu.dfu_offset + dfu.dfu_size;
- let storage_size = storage_num_sectors * erase_size;
- let state_offset = dfu.state_offset;
- let state_size = dfu.state_size;
- let dfu_offset = dfu.dfu_offset;
- let dfu_size = dfu.dfu_size;
let dfu_unlock_keys = expand_dfu_unlock_keys(dfu);
quote! {
#dfu_unlock_keys
let flash = ::rmk::storage::async_flash_wrapper(
- ::rmk::dfu::init_flash(
- p.NVMC,
- #storage_offset,
- #storage_size,
- #state_offset,
- #state_size,
- #dfu_offset,
- #dfu_size,
- )
+ ::rmk::dfu::init_flash_from_linkerscript(p.NVMC)
);
}
};
@@ -96,27 +80,11 @@ pub(crate) fn expand_flash_init(hardware: &Hardware) -> TokenStream2 {
let dfu = hardware.dfu.as_ref().expect(
"[dfu] section is required in keyboard.toml (or chip default) when dfu_rp is enabled"
);
- let storage_num_sectors = hardware.storage.as_ref().map(|s| s.num_sectors).unwrap_or(32) as u32;
- let erase_size = dfu.page_size;
- let storage_offset = dfu.dfu_offset + dfu.dfu_size;
- let storage_size = storage_num_sectors * erase_size;
- let state_offset = dfu.state_offset;
- let state_size = dfu.state_size;
- let dfu_offset = dfu.dfu_offset;
- let dfu_size = dfu.dfu_size;
let dfu_unlock_keys = expand_dfu_unlock_keys(dfu);
quote! {
#dfu_unlock_keys
let flash = ::rmk::storage::async_flash_wrapper(
- ::rmk::dfu::init_flash(
- p.FLASH,
- #storage_offset,
- #storage_size,
- #state_offset,
- #state_size,
- #dfu_offset,
- #dfu_size,
- )
+ ::rmk::dfu::init_flash_from_linkerscript(p.FLASH)
);
}
}
diff --git a/rmk/src/dfu/mod.rs b/rmk/src/dfu/mod.rs
index 07297d14d..d047f0769 100644
--- a/rmk/src/dfu/mod.rs
+++ b/rmk/src/dfu/mod.rs
@@ -28,9 +28,9 @@ mod nrf;
mod rp;
#[cfg(feature = "dfu_nrf")]
-pub use self::nrf::{DFU_WRITE_SIZE, get_manager, init_flash, mark_booted};
+pub use self::nrf::{DFU_WRITE_SIZE, get_manager, init_flash, init_flash_from_linkerscript, mark_booted};
#[cfg(feature = "dfu_rp")]
-pub use self::rp::{DFU_WRITE_SIZE, get_manager, init_flash, mark_booted};
+pub use self::rp::{DFU_WRITE_SIZE, get_manager, init_flash, init_flash_from_linkerscript, mark_booted};
// ---------------------------------------------------------------------------
// Chip-specific type aliases
diff --git a/rmk/src/dfu/nrf.rs b/rmk/src/dfu/nrf.rs
index c47b9e9de..d2e643112 100644
--- a/rmk/src/dfu/nrf.rs
+++ b/rmk/src/dfu/nrf.rs
@@ -64,3 +64,33 @@ pub fn mark_booted() {
pub fn get_manager() -> Option<&'static DfuFlashManager> {
MANAGER.try_get()
}
+
+/// Initialize flash using partition offsets from the linker (rmk-boot.x).
+///
+/// Requires `rmk-boot.x` to be linked into the firmware binary
+/// (e.g. `-Trmk-boot.x` in `.cargo/config.toml`).
+///
+/// # Safety
+///
+/// Reads linker-defined absolute symbols. The symbols must be present in the
+/// linked binary or the firmware will not link.
+pub fn init_flash_from_linkerscript(flash_peri: Peri<'static, NVMC>) -> PartitionType {
+ unsafe extern "C" {
+ static __rmk_boot_state_offset: u8;
+ static __rmk_boot_state_size: u8;
+ static __rmk_boot_dfu_offset: u8;
+ static __rmk_boot_dfu_size: u8;
+ static __rmk_boot_storage_offset: u8;
+ static __rmk_boot_storage_size: u8;
+ }
+ // SAFETY: linker-defined symbols — reading their addresses is safe.
+ init_flash(
+ flash_peri,
+ core::ptr::addr_of!(__rmk_boot_storage_offset) as usize as u32,
+ core::ptr::addr_of!(__rmk_boot_storage_size) as usize as u32,
+ core::ptr::addr_of!(__rmk_boot_state_offset) as usize as u32,
+ core::ptr::addr_of!(__rmk_boot_state_size) as usize as u32,
+ core::ptr::addr_of!(__rmk_boot_dfu_offset) as usize as u32,
+ core::ptr::addr_of!(__rmk_boot_dfu_size) as usize as u32,
+ )
+}
diff --git a/rmk/src/dfu/rp.rs b/rmk/src/dfu/rp.rs
index d33685f7b..6bc552c52 100644
--- a/rmk/src/dfu/rp.rs
+++ b/rmk/src/dfu/rp.rs
@@ -74,3 +74,33 @@ pub fn mark_booted() {
pub fn get_manager() -> Option<&'static DfuFlashManager> {
MANAGER.try_get()
}
+
+/// Initialize flash using partition offsets from the linker (rmk-boot.x).
+///
+/// Requires `rmk-boot.x` to be linked into the firmware binary
+/// (e.g. `-Trmk-boot.x` in `.cargo/config.toml`).
+///
+/// # Safety
+///
+/// Reads linker-defined absolute symbols. The symbols must be present in the
+/// linked binary or the firmware will not link.
+pub fn init_flash_from_linkerscript(flash_peri: Peri<'static, FLASH>) -> PartitionType {
+ unsafe extern "C" {
+ static __rmk_boot_state_offset: u8;
+ static __rmk_boot_state_size: u8;
+ static __rmk_boot_dfu_offset: u8;
+ static __rmk_boot_dfu_size: u8;
+ static __rmk_boot_storage_offset: u8;
+ static __rmk_boot_storage_size: u8;
+ }
+ // SAFETY: linker-defined symbols — reading their addresses is safe.
+ init_flash(
+ flash_peri,
+ core::ptr::addr_of!(__rmk_boot_storage_offset) as usize as u32,
+ core::ptr::addr_of!(__rmk_boot_storage_size) as usize as u32,
+ core::ptr::addr_of!(__rmk_boot_state_offset) as usize as u32,
+ core::ptr::addr_of!(__rmk_boot_state_size) as usize as u32,
+ core::ptr::addr_of!(__rmk_boot_dfu_offset) as usize as u32,
+ core::ptr::addr_of!(__rmk_boot_dfu_size) as usize as u32,
+ )
+}