From b010d1d19a680eaec967dc16b2cfc914576a30c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20J=C3=A4ger?= Date: Mon, 3 Aug 2026 00:43:55 +0200 Subject: [PATCH] feat: create rmk-boot.x linkerscript RMK can use to automagically partition flash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pascal Jäger --- .cargo/config.toml | 5 ++ .github/workflows/release.yml | 1 + Cargo.lock | 36 ++++----- Makefile.toml | 124 +++++++++++++++++++++-------- Readme.org | 122 ++++++++++++++++++----------- build.rs | 143 +++++++++++++++++++++++----------- 6 files changed, 293 insertions(+), 138 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..d217f9d --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ +[target.thumbv6m-none-eabi] +runner = "probe-rs run --chip RP2040" + +[target.thumbv7em-none-eabihf] +runner = "probe-rs run --chip nRF52840_xxAA" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 27f7e1e..25b8f7f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,6 +29,7 @@ jobs: files: | *.uf2 *.hex + *-memory.x rmk-boot-nrf52840.elf env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 16abc28..34b110c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,24 +83,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "rmk-boot" -version = "0.1.0" -dependencies = [ - "cortex-m", - "cortex-m-rt", - "embassy-boot", - "embassy-boot-rp", - "embassy-embedded-hal", - "embassy-nrf", - "embassy-rp", - "embassy-sync", - "embassy-time", - "embassy-usb", - "embassy-usb-dfu", - "embedded-storage", -] - [[package]] name = "bytemuck" version = "1.25.0" @@ -1022,6 +1004,24 @@ version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +[[package]] +name = "rmk-boot" +version = "0.1.0" +dependencies = [ + "cortex-m", + "cortex-m-rt", + "embassy-boot", + "embassy-boot-rp", + "embassy-embedded-hal", + "embassy-nrf", + "embassy-rp", + "embassy-sync", + "embassy-time", + "embassy-usb", + "embassy-usb-dfu", + "embedded-storage", +] + [[package]] name = "rp-pac" version = "7.0.0" diff --git a/Makefile.toml b/Makefile.toml index e58e273..f5114bc 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -4,75 +4,135 @@ install_crate = { rustup_component_name = "llvm-tools" } [tasks.flip-link] install_crate = { crate_name = "flip-link", binary = "flip-link", test_arg = ["-h"] } -[tasks.build-2mb] +[tasks.build-rp2040-2mb] command = "cargo" args = ["build", "--release", "--target", "thumbv6m-none-eabi", "--features", "rp2040-2mb"] -[tasks.build-4mb] +[tasks.copy-x-rp2040-2mb] +dependencies = ["build-rp2040-2mb"] +script = [ +''' +if [ ! -f rmk-memory.x ]; then + touch build.rs + cargo build --release --target thumbv6m-none-eabi --features rp2040-2mb +fi +cp rmk-memory.x rmk-rp2040-2mb-memory.x +''' +] + +[tasks.build-rp2040-4mb] command = "cargo" args = ["build", "--release", "--target", "thumbv6m-none-eabi", "--features", "rp2040-4mb"] -[tasks.build-8mb] +[tasks.copy-x-rp2040-4mb] +dependencies = ["build-rp2040-4mb"] +script = [ +''' +if [ ! -f rmk-memory.x ]; then + touch build.rs + cargo build --release --target thumbv6m-none-eabi --features rp2040-4mb +fi +cp rmk-memory.x rmk-rp2040-4mb-memory.x +''' +] + +[tasks.build-rp2040-8mb] command = "cargo" args = ["build", "--release", "--target", "thumbv6m-none-eabi", "--features", "rp2040-8mb"] -[tasks.build-16mb] +[tasks.copy-x-rp2040-8mb] +dependencies = ["build-rp2040-8mb"] +script = [ +''' +if [ ! -f rmk-memory.x ]; then + touch build.rs + cargo build --release --target thumbv6m-none-eabi --features rp2040-8mb +fi +cp rmk-memory.x rmk-rp2040-8mb-memory.x +''' +] + +[tasks.build-rp2040-16mb] command = "cargo" args = ["build", "--release", "--target", "thumbv6m-none-eabi", "--features", "rp2040-16mb"] -[tasks.objcopy-2mb] -dependencies = ["install-llvm-tools", "build-2mb"] +[tasks.copy-x-rp2040-16mb] +dependencies = ["build-rp2040-16mb"] +script = [ +''' +if [ ! -f rmk-memory.x ]; then + touch build.rs + cargo build --release --target thumbv6m-none-eabi --features rp2040-16mb +fi +cp rmk-memory.x rmk-rp2040-16mb-memory.x +''' +] + +[tasks.build-nrf52840] +command = "cargo" +args = ["build", "--release", "--target", "thumbv7em-none-eabihf", "--features", "nrf52840"] + +[tasks.copy-x-nrf52840] +dependencies = ["build-nrf52840"] +script = [ +''' +if [ ! -f rmk-memory.x ]; then + touch build.rs + cargo build --release --target thumbv7em-none-eabihf --features nrf52840 +fi +cp rmk-memory.x rmk-nrf52840-memory.x +''' +] + +[tasks.objcopy-rp2040-2mb] +dependencies = ["install-llvm-tools", "build-rp2040-2mb"] command = "cargo" args = ["objcopy", "--release", "--target", "thumbv6m-none-eabi", "--features", "rp2040-2mb", "--", "-O", "ihex", "rmk-boot-rp2040-2mb.hex"] -[tasks.objcopy-4mb] -dependencies = ["install-llvm-tools", "build-4mb"] +[tasks.objcopy-rp2040-4mb] +dependencies = ["install-llvm-tools", "build-rp2040-4mb"] command = "cargo" args = ["objcopy", "--release", "--target", "thumbv6m-none-eabi", "--features", "rp2040-4mb", "--", "-O", "ihex", "rmk-boot-rp2040-4mb.hex"] -[tasks.objcopy-8mb] -dependencies = ["install-llvm-tools", "build-8mb"] +[tasks.objcopy-rp2040-8mb] +dependencies = ["install-llvm-tools", "build-rp2040-8mb"] command = "cargo" args = ["objcopy", "--release", "--target", "thumbv6m-none-eabi", "--features", "rp2040-8mb", "--", "-O", "ihex", "rmk-boot-rp2040-8mb.hex"] -[tasks.objcopy-16mb] -dependencies = ["install-llvm-tools", "build-16mb"] +[tasks.objcopy-rp2040-16mb] +dependencies = ["install-llvm-tools", "build-rp2040-16mb"] command = "cargo" args = ["objcopy", "--release", "--target", "thumbv6m-none-eabi", "--features", "rp2040-16mb", "--", "-O", "ihex", "rmk-boot-rp2040-16mb.hex"] -[tasks.uf2-2mb] -dependencies = ["objcopy-2mb"] +[tasks.objcopy-nrf52840] +dependencies = ["install-llvm-tools", "build-nrf52840"] +command = "cargo" +args = ["objcopy", "--release", "--target", "thumbv7em-none-eabihf", "--features", "nrf52840", "--", "-O", "ihex", "rmk-boot-nrf52840.hex"] + +[tasks.uf2-rp2040-2mb] +dependencies = ["copy-x-rp2040-2mb", "objcopy-rp2040-2mb"] command = "cargo" args = ["hex-to-uf2", "--input-path", "rmk-boot-rp2040-2mb.hex", "--output-path", "rmk-boot-rp2040-2mb.uf2", "--family", "rp2040"] -[tasks.uf2-4mb] -dependencies = ["objcopy-4mb"] +[tasks.uf2-rp2040-4mb] +dependencies = ["copy-x-rp2040-4mb", "objcopy-rp2040-4mb"] command = "cargo" args = ["hex-to-uf2", "--input-path", "rmk-boot-rp2040-4mb.hex", "--output-path", "rmk-boot-rp2040-4mb.uf2", "--family", "rp2040"] -[tasks.uf2-8mb] -dependencies = ["objcopy-8mb"] +[tasks.uf2-rp2040-8mb] +dependencies = ["copy-x-rp2040-8mb", "objcopy-rp2040-8mb"] command = "cargo" args = ["hex-to-uf2", "--input-path", "rmk-boot-rp2040-8mb.hex", "--output-path", "rmk-boot-rp2040-8mb.uf2", "--family", "rp2040"] -[tasks.uf2-16mb] -dependencies = ["objcopy-16mb"] +[tasks.uf2-rp2040-16mb] +dependencies = ["copy-x-rp2040-16mb", "objcopy-rp2040-16mb"] command = "cargo" args = ["hex-to-uf2", "--input-path", "rmk-boot-rp2040-16mb.hex", "--output-path", "rmk-boot-rp2040-16mb.uf2", "--family", "rp2040"] -[tasks.build-nrf] -command = "cargo" -args = ["build", "--release", "--target", "thumbv7em-none-eabihf", "--features", "nrf52840"] - -[tasks.objcopy-nrf] -dependencies = ["install-llvm-tools", "build-nrf"] -command = "cargo" -args = ["objcopy", "--release", "--target", "thumbv7em-none-eabihf", "--features", "nrf52840", "--", "-O", "ihex", "rmk-boot-nrf52840.hex"] - -[tasks.uf2-nrf] -dependencies = ["objcopy-nrf"] +[tasks.uf2-nrf52840] +dependencies = ["copy-x-nrf52840", "objcopy-nrf52840"] command = "cargo" args = ["hex-to-uf2", "--input-path", "rmk-boot-nrf52840.hex", "--output-path", "rmk-boot-nrf52840.uf2", "--family", "nrf52840"] [tasks.uf2-all] -dependencies = ["uf2-2mb", "uf2-4mb", "uf2-8mb", "uf2-16mb", "uf2-nrf"] +dependencies = ["uf2-rp2040-2mb", "uf2-rp2040-4mb", "uf2-rp2040-8mb", "uf2-rp2040-16mb", "uf2-nrf52840"] diff --git a/Readme.org b/Readme.org index c4951d5..b770ae0 100644 --- a/Readme.org +++ b/Readme.org @@ -10,6 +10,12 @@ Combine it with RMK's =dfu_rp= or =dfu_nrf= feature — after the initial flash you never need to press BOOTSEL again; all subsequent firmware updates happen over USB via =dfu-util=. +rmk-boot ships a linker script called **=rmk-memory.x=** alongside its firmware +binaries. This file is the single source of truth for the flash partition +layout — RMK reads partition offsets from embedded linker symbols at runtime +via =init_flash_from_linkerscript()=. No manual address calculation, no +matching =memory.x= needed. + * Features - **Dual-slot firmware** — flash is split into ACTIVE and DFU partitions; @@ -23,6 +29,8 @@ over USB via =dfu-util=. - **Flash-size variants** — pre-configured for 2 MB, 4 MB, 8 MB and 16 MB (RP2040) and 1 MB (nRF52840) flash chips. - **UF2 builds** — =cargo make uf2-* = generates ready-to-flash =*.uf2= files. +- **rmk-memory.x** — each build also produces a matching linker script that RMK + consumes at link time for zero-config flash layout. - **Panic handler** — on bootloader panic, blinks SOS in Morse code on the LED so you know something went wrong inside the bootloader itself (as distinct from a firmware panic, which is handled by the app's own handler). @@ -33,7 +41,7 @@ over USB via =dfu-util=. Needs a target toolchain, install with =rustup target add thumbv6m-none-eabi= (RP2040) / =rustup target add thumbv7em-none-eabihf= (NRF52840) #+BEGIN_SRC sh -cargo make build-2mb # or build-4mb / build-8mb / build-16mb / build-nrf +cargo make build-rp2040-2mb # or build-rp2040-4mb / build-rp2040-8mb / build-rp2040-16mb / build-nrf52840 #+END_SRC ** Flash via probe-rs @@ -45,14 +53,16 @@ cargo run --release --target thumbv6m-none-eabi --features rp2040-2mb cargo run --release --target thumbv7em-none-eabihf --features nrf52840 #+END_SRC -** Generate a UF2 (for drag-and-drop flashing) +** Generate a UF2 (for drag-and-drop flashing) and rmk-memory.x + +Each =cargo make uf2-*= target also produces a matching =rmk-memory.x= linker script: #+BEGIN_SRC sh -cargo make uf2-2mb # → rmk-boot-rp2040-2mb.uf2 -cargo make uf2-4mb # → rmk-boot-rp2040-4mb.uf2 -cargo make uf2-8mb # → rmk-boot-rp2040-8mb.uf2 -cargo make uf2-16mb # → rmk-boot-rp2040-16mb.uf2 -cargo make uf2-nrf # → rmk-boot-nrf52840.uf2 +cargo make uf2-rp2040-2mb # → rmk-boot-rp2040-2mb.uf2 + rmk-rp2040-2mb-memory.x +cargo make uf2-rp2040-4mb # → rmk-boot-rp2040-4mb.uf2 + rmk-rp2040-4mb-memory.x +cargo make uf2-rp2040-8mb # → rmk-boot-rp2040-8mb.uf2 + rmk-rp2040-8mb-memory.x +cargo make uf2-rp2040-16mb # → rmk-boot-rp2040-16mb.uf2 + rmk-rp2040-16mb-memory.x +cargo make uf2-nrf52840 # → rmk-boot-nrf52840.uf2 + rmk-nrf52840-memory.x #+END_SRC Copy the =*.uf2= to the =RPI-RP2= mass-storage device that appears when you @@ -73,15 +83,16 @@ to LED (P0_15 by default) starts breathing slowly to show that DFU mode is activ * Flash-size variants -Exactly one feature must be enabled at build time: +Exactly one feature must be enabled at build time. Storage defaults to 32 KB +(8 sectors × 4 K). To change it, edit =STORAGE_SIZE= in =build.rs= and rebuild. -| Feature | Flash size | ACTIVE | DFU | ACTIVE start | DFU start | -|---------------+---------------+--------+--------+--------------+------------| -| =rp2040-2mb= | 2 MB | 944 K | 948 K | 0x10007000 | 0x100F3000 | -| =rp2040-4mb= | 4 MB | 1968 K | 1972 K | 0x10007000 | 0x101F3000 | -| =rp2040-8mb= | 8 MB | 4016 K | 4020 K | 0x10007000 | 0x103F3000 | -| =rp2040-16mb= | 16 MB | 8112 K | 8116 K | 0x10007000 | 0x104F3000 | -| =nrf52840= | 1 MB | 432 K | 436 K | 0x00007000 | 0x00073000 | +| Feature | Flash size | ACTIVE | DFU | ACTIVE start | DFU start | +|---------------+---------------+---------+---------+--------------+------------| +| =rp2040-2mb= | 2 MB | 992 K | 996 K | 0x10007000 | 0x10100000 | +| =rp2040-4mb= | 4 MB | 2016 K | 2020 K | 0x10007000 | 0x101FF000 | +| =rp2040-8mb= | 8 MB | 4064 K | 4068 K | 0x10007000 | 0x103FF000 | +| =rp2040-16mb= | 16 MB | 8160 K | 8164 K | 0x10007000 | 0x107FF000 | +| =nrf52840= | 1 MB | 480 K | 484 K | 0x00007000 | 0x0007B000 | The fixed regions for RP2040 (identical for all variants): @@ -102,14 +113,38 @@ Fixed regions for nRF52840: The 2 MB variant works on boards with 4 MB, 8 MB or 16 MB flash too — it simply leaves the extra space unused. You must switch to a larger variant -only if your RMK firmware exceeds 944 KB. +only if your RMK firmware exceeds 992 KB. * Integration with RMK -rmk-boot's partition layout is the default that RMK's =[dfu]= -configuration expects. When you compile your RMK firmware with the =dfu_rp= or -=dfu_nrf= feature, the DFU USB interface is registered automatically and the -flash partitioning matches rmk-boot's =memory.x=. +RMK integrates with rmk-boot through the **=rmk-memory.x=** linker script. The +file is the single source of truth — it contains both the flash memory layout +(=MEMORY=) for the linker and DFU partition symbols (=__rmk_boot_*=) that RMK +reads at runtime via =init_flash_from_linkerscript()=. + +To use rmk-boot with RMK: + +1. Download the matching =rmk-boot-*.uf2= and =rmk-boot-*.x= from the + [[https://github.com/rmk-rs/rmk-boot/releases][GitHub releases]]. +2. Rename the =.x= file to =rmk-memory.x= and place it next to your =Cargo.toml=. +3. Enable =dfu_rp= or =dfu_nrf= in RMK and add a =[dfu]= section to + =keyboard.toml= (only =led= / =unlock_keys= / =page_size= are needed — + partition offsets come from rmk-memory.x). + +RMK's =init_flash_from_linkerscript()= reads these linker symbols from +=rmk-memory.x= at runtime (all values are flash-relative offsets): + +| Symbol | Description | +|-----------------------------+--------------------------| +| =__rmk_boot_state_offset= | Boot state start | +| =__rmk_boot_state_size= | Boot state size | +| =__rmk_boot_dfu_offset= | DFU download slot start | +| =__rmk_boot_dfu_size= | DFU download slot size | +| =__rmk_boot_storage_offset= | Storage partition start | +| =__rmk_boot_storage_size= | Storage partition size | + +If you build a custom embassy-boot bootloader, define these same symbols in +your own linker script — RMK will pick them up automatically. After the initial flash (bootloader + RMK firmware), all subsequent updates can be done over USB: @@ -243,28 +278,28 @@ PWM peripheral and releases the GPIO pin so the firmware can reclaim it. * Partition layout detail -The build script (=build.rs=) generates =memory.x= at compile time based on the -selected feature. The formula for each variant: +The build script (=build.rs=) generates both =memory.x= (for the bootloader's +own linking) and =rmk-memory.x= (for the firmware's linking) at compile time +based on the selected feature. Both share the same computed values. -- ACTIVE offset: =0x10007000= (fixed, after embassy-boot) -- RP2040 ACTIVE size: =(flash_size - 28K (BOOT2 + bootloader + state) - - STORAGE_SIZE - 4K (1 page)) / 2= -- nRF52840 ACTIVE size: =(flash_size - 28K (bootloader + state) - STORAGE_SIZE - - 4K (1 page)) / 2= +The formula for each variant (=STORAGE_SIZE= defaults to 32 K): + +- ACTIVE offset: =0x10007000= (RP2040) / =0x00007000= (nRF52840) +- ACTIVE size: =(flash_size - 28K - STORAGE_SIZE - 4K) / 2 - DFU offset: =ACTIVE offset + ACTIVE size= -- DFU size: =active_size + 4 K= (one extra page for embassy-boot's swap - algorithm) +- DFU size: =ACTIVE size + 4 K= (one extra page for embassy-boot's swap algorithm) -The linker symbols that embassy-boot reads: +The =rmk-memory.x= file exposes these values as linker symbols that RMK's +=init_flash_from_linkerscript()= reads at runtime (all flash-relative offsets): -| Symbol | Value | -|----------------------------+-------| -| =__bootloader_state_start= | state offset relative to BOOT2 base | -| =__bootloader_state_end= | end of state partition | -| =__bootloader_active_start= | ACTIVE offset relative to BOOT2 base | -| =__bootloader_active_end= | end of ACTIVE partition | -| =__bootloader_dfu_start= | DFU offset relative to BOOT2 base | -| =__bootloader_dfu_end= | end of DFU partition | +| Symbol | Description | +|----------------------------+--------------------------| +| =__rmk_boot_state_offset= | Boot state start | +| =__rmk_boot_state_size= | Boot state size | +| =__rmk_boot_dfu_offset= | DFU download slot start | +| =__rmk_boot_dfu_size= | DFU download slot size | +| =__rmk_boot_storage_offset= | Storage partition start | +| =__rmk_boot_storage_size= | Storage partition size | * Testing the DFU→ACTIVE copy manually (for developers) @@ -275,7 +310,7 @@ cargo build arm-none-eabi-objcopy -O binary target/thumbv6m-none-eabi/debug/firmware firmware.bin # 2. Write it to the DFU partition -probe-rs download --chip RP2040 --binary-format bin --base-address 0x10087000 firmware.bin +probe-rs download --chip RP2040 --binary-format bin --base-address 0x100FF000 firmware.bin # 3. Set the SWAP magic in the state partition python3 -c "open('state_swap.bin', 'wb').write(b'\xF0' + b'\xFF'*4095)" @@ -284,10 +319,10 @@ python3 -c "open('state_swap.bin', 'wb').write(b'\xF0' + b'\xFF'*4095)" probe-rs download --chip RP2040 --binary-format bin --base-address 0x10006000 state_swap.bin # 5. Power-cycle the board -# → LED goes solid for 1 s (forward swap in progress) -# → LED fades (copy in progress) -# → 5 quick blinks (copy done) -# → new firmware boots +# -> LED goes solid for 1 s (forward swap in progress) +# -> LED fades (copy in progress) +# -> 5 quick blinks (copy done) +# -> new firmware boots #+END_SRC * Notes @@ -299,4 +334,3 @@ probe-rs download --chip RP2040 --binary-format bin --base-address 0x10006000 st - If you use the =Pico W= board, the on-board CYW43 wireless LED is not connected to a normal GPIO — pick a different pin such as =GPIO 16= (the default) for an external LED. - diff --git a/build.rs b/build.rs index d576b76..f81c42a 100644 --- a/build.rs +++ b/build.rs @@ -1,45 +1,43 @@ use std::env; use std::fs; -use std::path::PathBuf; +use std::path::Path; fn main() { - let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let out_dir = env::var("OUT_DIR").unwrap(); + let out = Path::new(&out_dir); let is_rp2040 = env::var("CARGO_FEATURE_RP2040").is_ok(); let is_nrf52840 = env::var("CARGO_FEATURE_NRF52840").is_ok(); const PAGE_SIZE: usize = 4 * 1024; - - fn fmt_size(bytes: usize) -> String { - if bytes % (1024 * 1024) == 0 { - format!("{}M", bytes / (1024 * 1024)) - } else if bytes % 1024 == 0 { - format!("{}K", bytes / 1024) - } else { - bytes.to_string() - } - } + const STORAGE_SIZE: usize = 32 * 1024; if is_rp2040 { - let flash_size = if env::var("CARGO_FEATURE_RP2040_2MB").is_ok() { - 2 * 1024 * 1024 + let (variant_label, flash_size) = if env::var("CARGO_FEATURE_RP2040_2MB").is_ok() { + ("RP2040 2 MB", 2 * 1024 * 1024) } else if env::var("CARGO_FEATURE_RP2040_4MB").is_ok() { - 4 * 1024 * 1024 + ("RP2040 4 MB", 4 * 1024 * 1024) } else if env::var("CARGO_FEATURE_RP2040_8MB").is_ok() { - 8 * 1024 * 1024 + ("RP2040 8 MB", 8 * 1024 * 1024) } else if env::var("CARGO_FEATURE_RP2040_16MB").is_ok() { - 16 * 1024 * 1024 + ("RP2040 16 MB", 16 * 1024 * 1024) } else { panic!("No RP2040 flash size feature enabled"); }; - let flash_base = 0x1000_0000u32; - const STORAGE_SIZE: usize = 128 * 1024; let remaining = flash_size - 28 * 1024 - STORAGE_SIZE; let active_size = (remaining - PAGE_SIZE) / 2; let dfu_size = active_size + PAGE_SIZE; - let active_offset = flash_base + 0x7000; - let dfu_offset = active_offset + active_size as u32; + // Absolute XIP addresses + let abs_active_offset = 0x1000_7000u32; + let abs_dfu_offset = abs_active_offset + active_size as u32; + let abs_state_offset = 0x1000_6000u32; + // Flash-relative offsets (for DFU symbols) + let flash_base = 0x1000_0000u32; + let rel_state_offset = abs_state_offset - flash_base; + let rel_dfu_offset = abs_dfu_offset - flash_base; + let rel_dfu_size = dfu_size as u32; + let rel_storage_offset = abs_dfu_offset - flash_base + dfu_size as u32; let memory_x = format!( "\ @@ -48,8 +46,8 @@ MEMORY BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 FLASH : ORIGIN = 0x10000100, LENGTH = 24K - 0x100 BOOTLOADER_STATE : ORIGIN = 0x10006000, LENGTH = 4K - ACTIVE : ORIGIN = 0x{:08X}, LENGTH = {} - DFU : ORIGIN = 0x{:08X}, LENGTH = {} + ACTIVE : ORIGIN = 0x{abs_active_offset:08X}, LENGTH = {active_size} + DFU : ORIGIN = 0x{abs_dfu_offset:08X}, LENGTH = {dfu_size} RAM : ORIGIN = 0x20000000, LENGTH = 256K }} @@ -62,12 +60,10 @@ __bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(BOOT2); __bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOT2); __bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOT2); -", - active_offset, fmt_size(active_size), - dfu_offset, fmt_size(dfu_size), +" ); - fs::write(out.join("memory.x"), memory_x).unwrap(); + fs::write(out.join("memory.x"), &memory_x).unwrap(); println!("cargo:rustc-link-search={}", out.display()); println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rustc-link-arg-bins=-Tlink-rp.x"); @@ -76,27 +72,40 @@ __bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOT2); println!("cargo:rerun-if-env-changed=CARGO_FEATURE_RP2040_4MB"); println!("cargo:rerun-if-env-changed=CARGO_FEATURE_RP2040_8MB"); println!("cargo:rerun-if-env-changed=CARGO_FEATURE_RP2040_16MB"); + + let rmk_boot_x = build_rmk_boot_x( + variant_label, + abs_active_offset, + active_size as u32, + rel_state_offset, + 0x1000, + rel_dfu_offset, + rel_dfu_size, + rel_storage_offset, + STORAGE_SIZE as u32, + ); + let project_root = Path::new(env!("CARGO_MANIFEST_DIR")); + fs::write(project_root.join("rmk-memory.x"), &rmk_boot_x).unwrap(); } else if is_nrf52840 { let flash_size = 1024 * 1024; let bootloader_size = 24 * 1024; let state_size = 4 * 1024; - let storage_size = 128 * 1024; - let remaining = flash_size - bootloader_size - state_size - storage_size; + let remaining = flash_size - bootloader_size - state_size - STORAGE_SIZE; let active_size = (remaining - PAGE_SIZE) / 2; let dfu_size = active_size + PAGE_SIZE; - let state_offset = bootloader_size as u32; - let active_offset = (bootloader_size + state_size) as u32; - let dfu_offset = active_offset + active_size as u32; + let abs_state_offset = bootloader_size as u32; + let abs_active_offset = (bootloader_size + state_size) as u32; + let abs_dfu_offset = abs_active_offset + active_size as u32; let memory_x = format!( "\ MEMORY {{ FLASH : ORIGIN = 0x00000000, LENGTH = {bootloader_size} - BOOTLOADER_STATE : ORIGIN = 0x{state_offset:08X}, LENGTH = {state_size} - ACTIVE : ORIGIN = 0x{active_offset:08X}, LENGTH = {active_size} - DFU : ORIGIN = 0x{dfu_offset:08X}, LENGTH = {dfu_size} + BOOTLOADER_STATE : ORIGIN = 0x{abs_state_offset:08X}, LENGTH = {state_size} + ACTIVE : ORIGIN = 0x{abs_active_offset:08X}, LENGTH = {active_size} + DFU : ORIGIN = 0x{abs_dfu_offset:08X}, LENGTH = {dfu_size} RAM : ORIGIN = 0x20000000, LENGTH = 256K }} @@ -109,22 +118,68 @@ __bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE); __bootloader_dfu_start = ORIGIN(DFU); __bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU); -", - bootloader_size = bootloader_size, - state_offset = state_offset, - state_size = state_size, - active_offset = active_offset, - active_size = fmt_size(active_size), - dfu_offset = dfu_offset, - dfu_size = fmt_size(dfu_size), +" ); - fs::write(out.join("memory.x"), memory_x).unwrap(); + fs::write(out.join("memory.x"), &memory_x).unwrap(); println!("cargo:rustc-link-search={}", out.display()); println!("cargo:rustc-link-arg-bins=-Tlink.x"); + + let rmk_boot_x = build_rmk_boot_x( + "nRF52840", + abs_active_offset, + active_size as u32, + abs_state_offset, + state_size as u32, + abs_dfu_offset, + dfu_size as u32, + abs_dfu_offset + dfu_size as u32, + STORAGE_SIZE as u32, // nRF flash_base is 0, so absolute = relative + ); + let project_root = Path::new(env!("CARGO_MANIFEST_DIR")); + fs::write(project_root.join("rmk-memory.x"), &rmk_boot_x).unwrap(); } else { panic!("No platform feature enabled (rp2040 or nrf52840)"); } println!("cargo:rerun-if-changed=build.rs"); } + +fn build_rmk_boot_x( + variant: &str, + active_offset: u32, + active_size: u32, + state_offset: u32, + state_size: u32, + dfu_offset: u32, + dfu_size: u32, + storage_offset: u32, + storage_size: u32, +) -> String { + format!( + "\ +/* rmk-memory.x for {variant} — generated by rmk-boot/build.rs + * + * 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 variant from the rmk-boot releases: + * https://github.com/rmk-rs/rmk-boot/releases + */ + +MEMORY {{ + FLASH : ORIGIN = 0x{active_offset:08X}, LENGTH = {active_size} /* ACTIVE region */ + RAM : ORIGIN = 0x20000000, LENGTH = 256K /* SRAM */ +}} + +/* DFU partition symbols — offsets relative to flash start */ +__rmk_boot_state_offset = 0x{state_offset:X}; +__rmk_boot_state_size = 0x{state_size:X}; +__rmk_boot_dfu_offset = 0x{dfu_offset:X}; +__rmk_boot_dfu_size = 0x{dfu_size:X}; +__rmk_boot_storage_offset = 0x{storage_offset:X}; +__rmk_boot_storage_size = 0x{storage_size:X}; +" + ) +}