Rust port Stage 3 milestone 1: pm-kit boot-proof blink (RP2350)
First per-board binary. rust/pm-kit/ is a minimal rp235x-hal firmware that blinks GP25 on the Pico 2 — proves the toolchain, RP2350 boot block (ImageDef), memory layout, and flash before we add any drivers. - src/main.rs + memory.x + build.rs + .cargo/config.toml: rp235x-hal blink, builds for thumbv8m.main-none-eabihf. - build.sh + uf2.py: one command builds the ELF in the container, objcopies to a raw image, and packs pm-kit.uf2 (rp2350-arm-s family). Drag onto the Pico 2 in BOOTSEL. Verified: builds clean; produces a valid 6-block UF2. Runtime (does it blink?) is the on-device check. Next: drivers (display first) + link pm-core. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
400d896518
commit
0e224393f7
9 changed files with 199 additions and 1 deletions
|
|
@ -81,7 +81,13 @@ this timeline against the wall clock.
|
|||
**Also done:** the crate is now `#![no_std]` + `alloc` and **builds for the RP2350 target**
|
||||
(`cargo build --lib --target thumbv8m.main-none-eabihf`) — the codec + scheduler are firmware-ready.
|
||||
|
||||
### Stage 3 — drivers (hardware)
|
||||
### Stage 3 — drivers (hardware) 🔧 IN PROGRESS (`rust/pm-kit/`)
|
||||
**Milestone 1 (boot-proof) built:** `rust/pm-kit/` is a minimal RP2350 binary (rp235x-hal) that
|
||||
blinks GP25 — compiles for the target and packs to `pm-kit.uf2` via `./rust/pm-kit/build.sh`.
|
||||
Confirms toolchain + RP2350 boot block + flash before any drivers. Once it blinks on the Pico 2
|
||||
we add drivers (display first) and link `pm-core`. HAL choice (rp235x-hal vs embassy) finalizes
|
||||
with the first real driver.
|
||||
|
||||
On `embassy` / `rp-hal`:
|
||||
- ST7789 240×320 display → `mipidsi` + `embedded-graphics` (mature; the parts are well-supported).
|
||||
- I²S to the PCM5102A → RP2350 PIO.
|
||||
|
|
|
|||
8
rust/pm-kit/.cargo/config.toml
Normal file
8
rust/pm-kit/.cargo/config.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[build]
|
||||
target = "thumbv8m.main-none-eabihf"
|
||||
|
||||
[target.thumbv8m.main-none-eabihf]
|
||||
rustflags = [
|
||||
"-C", "link-arg=--nmagic",
|
||||
"-C", "link-arg=-Tlink.x",
|
||||
]
|
||||
4
rust/pm-kit/.gitignore
vendored
Normal file
4
rust/pm-kit/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
target/
|
||||
Cargo.lock
|
||||
*.uf2
|
||||
*.bin
|
||||
16
rust/pm-kit/Cargo.toml
Normal file
16
rust/pm-kit/Cargo.toml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "pm-kit"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "PM_K-1 firmware (RP2350 / Pico 2). Stage 3 bring-up: boot-proof blink, then drivers + pm-core."
|
||||
|
||||
[dependencies]
|
||||
rp235x-hal = { version = "0.3", features = ["binary-info", "critical-section-impl", "rt"] }
|
||||
cortex-m-rt = "0.7"
|
||||
panic-halt = "1"
|
||||
embedded-hal = "1"
|
||||
|
||||
[profile.release]
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
debug = 2
|
||||
16
rust/pm-kit/build.rs
Normal file
16
rust/pm-kit/build.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//! Put `memory.x` on the linker search path (cortex-m-rt's link.x INCLUDEs it).
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
let out = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
File::create(out.join("memory.x"))
|
||||
.unwrap()
|
||||
.write_all(include_bytes!("memory.x"))
|
||||
.unwrap();
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
println!("cargo:rerun-if-changed=memory.x");
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
}
|
||||
23
rust/pm-kit/build.sh
Executable file
23
rust/pm-kit/build.sh
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env bash
|
||||
# Build pm-kit for the RP2350 and produce pm-kit.uf2.
|
||||
# Flash: hold BOOTSEL on the Pico 2, plug in USB, drag pm-kit.uf2 onto the RP2350 drive.
|
||||
#
|
||||
# ./build.sh
|
||||
#
|
||||
# Override the runtime with RUNTIME=docker ./build.sh
|
||||
set -euo pipefail
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # rust/pm-kit
|
||||
REPO="$(cd "$DIR/../.." && pwd)" # repo root
|
||||
RUNTIME="${RUNTIME:-podman}"
|
||||
IMG="pm-rust:1"
|
||||
|
||||
"$RUNTIME" run --rm -v "$REPO":/work:Z -w /work/rust/pm-kit "$IMG" bash -c '
|
||||
set -e
|
||||
rustup component add llvm-tools-preview >/dev/null 2>&1 || true
|
||||
cargo build --release
|
||||
OBJCOPY="$(rustc --print sysroot)/lib/rustlib/$(rustc -vV | sed -n "s/host: //p")/bin/llvm-objcopy"
|
||||
"$OBJCOPY" -O binary target/thumbv8m.main-none-eabihf/release/pm-kit pm-kit.bin
|
||||
'
|
||||
python3 "$DIR/uf2.py" "$DIR/pm-kit.bin" "$DIR/pm-kit.uf2"
|
||||
echo "→ $DIR/pm-kit.uf2 (hold BOOTSEL on the Pico 2, drag this onto the RP2350 drive)"
|
||||
41
rust/pm-kit/memory.x
Normal file
41
rust/pm-kit/memory.x
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/* RP2350 (Pico 2) memory layout for rp235x-hal + cortex-m-rt.
|
||||
The bootrom requires the IMAGE_DEF (.start_block) right after the vector table. */
|
||||
MEMORY {
|
||||
FLASH : ORIGIN = 0x10000000, LENGTH = 4096K
|
||||
RAM : ORIGIN = 0x20000000, LENGTH = 512K
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
/* ### RP2350 image definition block — the bootrom scans for this to boot the image. */
|
||||
.start_block : ALIGN(4)
|
||||
{
|
||||
__start_block_addr = .;
|
||||
KEEP(*(.start_block));
|
||||
KEEP(*(.boot_info));
|
||||
} > FLASH
|
||||
} INSERT AFTER .vector_table;
|
||||
|
||||
/* move .text after the start block */
|
||||
_stext = ADDR(.start_block) + SIZEOF(.start_block);
|
||||
|
||||
SECTIONS {
|
||||
/* picotool 'Binary Info' entries */
|
||||
.bi_entries : ALIGN(4)
|
||||
{
|
||||
__bi_entries_start = .;
|
||||
KEEP(*(.bi_entries));
|
||||
. = ALIGN(4);
|
||||
__bi_entries_end = .;
|
||||
} > FLASH
|
||||
} INSERT AFTER .text;
|
||||
|
||||
SECTIONS {
|
||||
.end_block : ALIGN(4)
|
||||
{
|
||||
__end_block_addr = .;
|
||||
KEEP(*(.end_block));
|
||||
} > FLASH
|
||||
} INSERT AFTER .bi_entries;
|
||||
|
||||
PROVIDE(start_to_end = __end_block_addr - __start_block_addr);
|
||||
PROVIDE(end_to_start = __start_block_addr - __end_block_addr);
|
||||
57
rust/pm-kit/src/main.rs
Normal file
57
rust/pm-kit/src/main.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
//! PM_K-1 firmware — Stage 3 bring-up milestone 1: prove our Rust boots on the Pico 2 (RP2350).
|
||||
//!
|
||||
//! Blinks the onboard LED (GP25). If it blinks, the whole path works: toolchain, RP2350 boot
|
||||
//! block, memory layout, flash, and the cortex-m-rt entry. Drivers (display / audio / inputs /
|
||||
//! USB-MIDI) and the `pm-core` engine come next, once boot is confirmed on hardware.
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use embedded_hal::delay::DelayNs;
|
||||
use embedded_hal::digital::OutputPin;
|
||||
use panic_halt as _;
|
||||
use rp235x_hal as hal;
|
||||
|
||||
/// Image definition block — the RP2350 bootrom looks for this to boot the image.
|
||||
#[link_section = ".start_block"]
|
||||
#[used]
|
||||
pub static IMAGE_DEF: hal::block::ImageDef = hal::block::ImageDef::secure_exe();
|
||||
|
||||
const XTAL_FREQ_HZ: u32 = 12_000_000; // Pico 2 crystal
|
||||
|
||||
#[hal::entry]
|
||||
fn main() -> ! {
|
||||
let mut pac = hal::pac::Peripherals::take().unwrap();
|
||||
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
|
||||
|
||||
let clocks = hal::clocks::init_clocks_and_plls(
|
||||
XTAL_FREQ_HZ,
|
||||
pac.XOSC,
|
||||
pac.CLOCKS,
|
||||
pac.PLL_SYS,
|
||||
pac.PLL_USB,
|
||||
&mut pac.RESETS,
|
||||
&mut watchdog,
|
||||
)
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
let mut timer = hal::Timer::new_timer0(pac.TIMER0, &mut pac.RESETS, &clocks);
|
||||
|
||||
let sio = hal::Sio::new(pac.SIO);
|
||||
let pins = hal::gpio::Pins::new(pac.IO_BANK0, pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS);
|
||||
let mut led = pins.gpio25.into_push_pull_output();
|
||||
|
||||
loop {
|
||||
led.set_high().unwrap();
|
||||
timer.delay_ms(250);
|
||||
led.set_low().unwrap();
|
||||
timer.delay_ms(250);
|
||||
}
|
||||
}
|
||||
|
||||
/// picotool metadata (visible via `picotool info`).
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 1] =
|
||||
[hal::binary_info::rp_program_name!(c"pm-kit blink")];
|
||||
27
rust/pm-kit/uf2.py
Normal file
27
rust/pm-kit/uf2.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Pack a raw RP2350 flash image (objcopy -O binary output) into a UF2.
|
||||
|
||||
Family rp2350-arm-s (0xe48bff59), flash base 0x10000000. Usage:
|
||||
python3 uf2.py pm-kit.bin pm-kit.uf2
|
||||
"""
|
||||
import struct
|
||||
import sys
|
||||
|
||||
BASE = 0x10000000
|
||||
FAMILY = 0xE48BFF59 # rp2350-arm-s
|
||||
|
||||
src = sys.argv[1] if len(sys.argv) > 1 else "pm-kit.bin"
|
||||
out = sys.argv[2] if len(sys.argv) > 2 else "pm-kit.uf2"
|
||||
|
||||
data = open(src, "rb").read()
|
||||
chunks = [data[i:i + 256] for i in range(0, len(data), 256)] or [b""]
|
||||
n = len(chunks)
|
||||
with open(out, "wb") as f:
|
||||
for i, c in enumerate(chunks):
|
||||
c = c.ljust(256, b"\x00")
|
||||
blk = struct.pack("<IIIIIIII", 0x0A324655, 0x9E5D5157, 0x00002000,
|
||||
BASE + i * 256, 256, i, n, FAMILY)
|
||||
blk += c + b"\x00" * (476 - 256) + struct.pack("<I", 0x0AB16F30)
|
||||
assert len(blk) == 512
|
||||
f.write(blk)
|
||||
print(f"{out}: {n} blocks, {len(data)} bytes payload")
|
||||
Loading…
Reference in a new issue