Skip to content

Commit 7af3d74

Browse files
committed
Add an example using the Daisy bootloader
Signed-off-by: Petr Horacek <petr@zlosynth.com>
1 parent 511e341 commit 7af3d74

File tree

10 files changed

+237
-1
lines changed

10 files changed

+237
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ backwards compatibility.
66

77
## Unreleased
88

9+
* Add a feature flag for longer audio block length.
10+
* Document how to use Daisy bootloader.
11+
912
## 0.10.0
1013

1114
* Bump STM32H7 HAL to version 0.16.
1215
* Introduce `probe-rs` to the project.
1316
* Illustrate use of `defmt` in examples.
1417
* Introduce support for Daisy Seed 1.2.
15-
* Add a feature flag for longer audio block length.
1618

1719
## 0.9.0
1820

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ additional logs and panic messages:
3333
make flash WHAT=blinky BOARD=seed_1_1
3434
```
3535

36+
# Using Daisy bootloader
37+
38+
It is possible to use the [Daisy Bootloader](https://electro-smith.github.io/libDaisy/md_doc_2md_2__a7___getting-_started-_daisy-_bootloader.html)
39+
to extend the maximum firmware capacity. You can find a guide with an example under
40+
[examples/bootloader](examples/bootloader/).
41+
3642
# API stability
3743

3844
I am still trying to figure out a good API for the project. Expect it to change.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[target.thumbv7em-none-eabihf]
2+
runner = 'probe-rs run --chip STM32H750VBTx'
3+
rustflags = [
4+
"-C",
5+
"link-arg=-Tdefmt.x",
6+
]
7+
8+
[build]
9+
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)

examples/bootloader/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Generated by Cargo
2+
0;95;0c# will have compiled files and executables
3+
/target/
4+
5+
# These are backup files generated by rustfmt
6+
**/*.rs.bk

examples/bootloader/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "example_bootloader"
3+
version = "0.10.0" # hack/release.sh
4+
edition = "2024"
5+
6+
[dependencies]
7+
cortex-m = "0.7"
8+
cortex-m-rt = { version = "0.7", features = [ "device", "set-vtor" ] }
9+
daisy = { path = "../../", features = ["patch_sm", "defmt"]}
10+
defmt = { version = "0.3.8" }
11+
defmt-rtt = { version = "0.4.1" }
12+
panic-probe = { version = "0.3.2", features = ["print-defmt"] }

examples/bootloader/Embed.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[default.general]
2+
chip = "STM32H750VBTx"
3+
4+
[default.rtt]
5+
enabled = true
6+
7+
[default.flashing]
8+
enabled = false

examples/bootloader/README.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Uploading firmware through Daisy Bootloader
2+
3+
The chip on board has quite limited flash capacity of 128 kB. To fit larger
4+
firmware on Daisy, [the Daisy Bootloader exists](https://electro-smith.github.io/libDaisy/md_doc_2md_2__a7___getting-_started-_daisy-_bootloader.html).
5+
Check the documentation to learn about the different bootloader modes and the
6+
methods for uploading firmware through it.
7+
8+
In this directory, you'll find an example project that uses the bootloader.
9+
The program is copied via DFU to the onboard 8 MB flash storage. When the board
10+
starts, the bootloader will then copy the program to SDRAM for faster execution.
11+
12+
This example is made for Daisy Patch SM and assumes you're already familiar with
13+
this crate. If you want to flash a different board, update the `Cargo.toml`
14+
accordingly.
15+
16+
## Flashing the example
17+
18+
First, install the bootloader on the board. You can use <https://flash.daisy.audio/>,
19+
go to the "Bootloader" tab, select version "v6.2", and flash it.
20+
Alternatively, you can use the libDaisy project and its Makefile.
21+
22+
First, install the bootloader on the board. You can use <https://flash.daisy.audio/>,
23+
go to the "Bootloader" tab, select version "v6.2", and flash it. Alternatively
24+
you can use the [libDaisy](https://github.com/electro-smith/libDaisy/tree/master)
25+
project and its `Makefile`.
26+
27+
Once the bootloader is installed, restart the module and press the BOOT button
28+
within the first 2 seconds after startup. The onboard LED should start pulsing,
29+
indicating the bootloader is active and waiting.
30+
31+
Now build the example firmware:
32+
33+
```sh
34+
cargo objcopy --release -- -O binary target/program.bin
35+
```
36+
37+
After building, use `dfu-util` to upload the program. Note the `-s` parameter,
38+
which now points to the beginning of the writable onboard flash, not the
39+
internal flash:
40+
41+
After that, it is just a matter of using `dfu-util` to upload the program.
42+
Note the `-s` parameter which now points at the beginning of writeable
43+
on-board flash, instead of the internal flash:
44+
45+
```sh
46+
dfu-util -a 0 -s 0x90040000:leave -D target/program.bin -d ,0483:df11
47+
```
48+
49+
The program should now be uploaded, and the onboard LED should be blinking.
50+
51+
## Attaching to logs
52+
53+
The bootloader doesn't allow flashing the program using
54+
[`probe-rs`](https://probe.rs/), but you can still attach to logs using
55+
[cargo-embed](https://probe.rs/docs/tools/cargo-embed/).
56+
57+
Build the program including INFO-level logs, flash it, and attach to
58+
it using an ST-Link programmer:
59+
60+
```sh
61+
DEFMT_LOG=info cargo objcopy --release -- -O binary target/program.bin
62+
dfu-util -a 0 -s 0x90040000:leave -D target/program.bin -d ,0483:df11
63+
DEFMT_LOG=info cargo-embed --release
64+
```
65+
66+
You should now see the log output.
67+
68+
## What makes this work
69+
70+
### `Cargo.toml`
71+
72+
The `Cargo.toml` config is standard, except for the `set-vtor` feature flag
73+
that must be enabled on `cortex-m-rt`.
74+
75+
### `memory.x`
76+
77+
This file is different from the standard `memory.x`. The main difference is the
78+
region alias replacing `FLASH` of the default link file with `SRAM`.
79+
The rest of the layout is also slightly adjusted to meet the bootloader’s
80+
requirements.
81+
82+
### `Embed.toml`
83+
84+
This file is optional. It makes `cargo-embed` only read logs without trying to
85+
flash the firmware.
86+
87+
## Caveats
88+
89+
### SRAM
90+
91+
Since the program is uploaded to SRAM and runs from there, your program can't
92+
use that memory. Other memory regions are still available, but keep in mind
93+
they are either smaller or slower.
94+
95+
### Reserved flash
96+
97+
Part of the onboard flash is used to store the firmware. It’s still possible
98+
for the firmware to use this flash, but care must be taken when writing to it.
99+
The first four 64 kB blocks are reserved, followed by the firmware itself.
100+
When using the bootloader in SRAM mode (as in this example), the firmware can
101+
take up to 480 kB, so the first 736 kB, or 184 sectors, are occupied and should
102+
not be written to.
103+
104+
## Credits
105+
106+
Kudos to `eulerdisk` for [explaining](https://github.com/rust-embedded/cortex-m/issues/599#issuecomment-2956003568)
107+
how to adjust the linker to work with the bootloader. Thanks to `Corvus Prudens`
108+
and `mito3705` from [Daisy Discord](https://discord.com/channels/1037767234803740694/1039305128886403072)
109+
who shared valuable input on using the Daisy Bootloader with Rust.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//! This build script copies the `memory.x` file from the crate root into
2+
//! a directory where the linker can always find it at build time.
3+
//! For many projects this is optional, as the linker always searches the
4+
//! project root directory -- wherever `Cargo.toml` is. However, if you
5+
//! are using a workspace or have a more complicated build setup, this
6+
//! build script becomes required. Additionally, by requesting that
7+
//! Cargo re-run the build script whenever `memory.x` is changed,
8+
//! updating `memory.x` ensures a rebuild of the application with the
9+
//! new memory settings.
10+
11+
use std::env;
12+
use std::fs::File;
13+
use std::io::Write;
14+
use std::path::PathBuf;
15+
16+
fn main() {
17+
// Put `memory.x` and `link_ram.x` in our output directory and ensure it's
18+
// on the linker search path.
19+
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20+
File::create(out.join("memory.x"))
21+
.unwrap()
22+
.write_all(include_bytes!("memory.x"))
23+
.unwrap();
24+
File::create(out.join("link_ram.x"))
25+
.unwrap()
26+
.write_all(include_bytes!("link_ram.x"))
27+
.unwrap();
28+
println!("cargo:rustc-link-search={}", out.display());
29+
30+
// By default, Cargo will re-run a build script whenever
31+
// any file in the project changes. By specifying `memory.x`
32+
// here, we ensure the build script is only re-run when
33+
// `memory.x` is changed.
34+
println!("cargo:rerun-if-changed=memory.x");
35+
// Ditto for `link_ram.x`.
36+
println!("cargo:rerun-if-changed=link_ram.x");
37+
38+
// println!("cargo:rustc-link-arg=-Tdefmt.x");
39+
}

examples/bootloader/memory.x

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* See: https://github.com/electro-smith/libDaisy/blob/master/core/STM32H750IB_sram.lds */
2+
3+
MEMORY
4+
{
5+
DTCMRAM (RWX) : ORIGIN = 0x20000000, LENGTH = 128K
6+
SRAM (RWX) : ORIGIN = 0x24000000, LENGTH = 512K - 32K
7+
RAM_D2_DMA (RWX) : ORIGIN = 0x30000000, LENGTH = 32K
8+
RAM_D2 (RWX) : ORIGIN = 0x30008000, LENGTH = 256K
9+
RAM_D3 (RWX) : ORIGIN = 0x38000000, LENGTH = 64K
10+
BACKUP_SRAM (RWX) : ORIGIN = 0x38800000, LENGTH = 4K
11+
ITCMRAM (RWX) : ORIGIN = 0x00000000, LENGTH = 64K
12+
SDRAM (RWX) : ORIGIN = 0xc0000000, LENGTH = 64M
13+
QSPIFLASH (RX) : ORIGIN = 0x90040000, LENGTH = 7936K
14+
}
15+
16+
REGION_ALIAS(RAM, DTCMRAM);
17+
REGION_ALIAS("FLASH", SRAM);

examples/bootloader/src/main.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//! Example of basic interaction with the board.
2+
3+
#![no_main]
4+
#![no_std]
5+
6+
use cortex_m_rt::entry;
7+
8+
use {defmt_rtt as _, panic_probe as _};
9+
10+
#[entry]
11+
fn main() -> ! {
12+
// Get device peripherals and the board abstraction.
13+
let dp = daisy::pac::Peripherals::take().unwrap();
14+
let board = daisy::Board::take().unwrap();
15+
16+
// Configure board's peripherals.
17+
let ccdr = daisy::board_freeze_clocks!(board, dp);
18+
let pins = daisy::board_split_gpios!(board, ccdr, dp);
19+
let mut led_user = daisy::board_split_leds!(pins).USER;
20+
21+
// Blink every second.
22+
let one_second = ccdr.clocks.sys_ck().to_Hz();
23+
loop {
24+
led_user.toggle();
25+
cortex_m::asm::delay(one_second);
26+
defmt::info!("Tick");
27+
}
28+
}

0 commit comments

Comments
 (0)