aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-04-27 13:43:15 +0000
committerGitHub <[email protected]>2022-04-27 13:43:15 +0000
commit663642eabbe7c8b0a66a4c1d00d1cc681c1d497e (patch)
treea2821e2a58f7b58eef8595ce69f87db31ed5f32f
parent9c283cd44504d6d9d6f9e352e4c7a8d043bd673f (diff)
parent93c17be32e150d0afffdf25fddab47767c779c3f (diff)
Merge #724
724: STM32 Flash + Bootloader r=Dirbaio a=lulf Not working. Co-authored-by: Ulf Lilleengen <[email protected]> Co-authored-by: Ulf Lilleengen <[email protected]>
-rwxr-xr-xci.sh7
-rw-r--r--docs/modules/ROOT/nav.adoc1
-rw-r--r--docs/modules/ROOT/pages/bootloader.adoc32
-rw-r--r--embassy-boot/boot/src/lib.rs222
-rw-r--r--embassy-boot/nrf/src/lib.rs32
-rw-r--r--embassy-boot/nrf/src/main.rs5
-rw-r--r--embassy-boot/stm32/Cargo.toml61
-rw-r--r--embassy-boot/stm32/README.md11
-rw-r--r--embassy-boot/stm32/build.rs27
-rw-r--r--embassy-boot/stm32/memory.x18
-rw-r--r--embassy-boot/stm32/src/fmt.rs225
-rw-r--r--embassy-boot/stm32/src/lib.rs77
-rw-r--r--embassy-boot/stm32/src/main.rs48
-rw-r--r--embassy-stm32/Cargo.toml5
-rw-r--r--embassy-stm32/src/flash/mod.rs359
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/rcc/wl.rs156
-rw-r--r--examples/boot/Cargo.toml19
-rw-r--r--examples/boot/nrf/.cargo/config.toml6
-rw-r--r--examples/boot/nrf/Cargo.toml19
-rw-r--r--examples/boot/nrf/README.md (renamed from examples/boot/README.md)8
-rw-r--r--examples/boot/nrf/build.rs (renamed from examples/boot/build.rs)0
-rw-r--r--examples/boot/nrf/memory.x (renamed from examples/boot/memory.x)0
-rw-r--r--examples/boot/nrf/src/bin/a.rs (renamed from examples/boot/src/bin/a.rs)4
-rw-r--r--examples/boot/nrf/src/bin/b.rs (renamed from examples/boot/src/bin/b.rs)0
-rw-r--r--examples/boot/stm32l0/.cargo/config.toml6
-rw-r--r--examples/boot/stm32l0/Cargo.toml26
-rw-r--r--examples/boot/stm32l0/README.md29
-rw-r--r--examples/boot/stm32l0/build.rs37
-rw-r--r--examples/boot/stm32l0/memory.x15
-rw-r--r--examples/boot/stm32l0/src/bin/a.rs48
-rw-r--r--examples/boot/stm32l0/src/bin/b.rs25
-rw-r--r--examples/boot/stm32l1/.cargo/config.toml6
-rw-r--r--examples/boot/stm32l1/Cargo.toml26
-rw-r--r--examples/boot/stm32l1/README.md29
-rw-r--r--examples/boot/stm32l1/build.rs37
-rw-r--r--examples/boot/stm32l1/memory.x15
-rw-r--r--examples/boot/stm32l1/src/bin/a.rs48
-rw-r--r--examples/boot/stm32l1/src/bin/b.rs25
-rw-r--r--examples/boot/stm32l4/.cargo/config.toml6
-rw-r--r--examples/boot/stm32l4/Cargo.toml26
-rw-r--r--examples/boot/stm32l4/README.md29
-rw-r--r--examples/boot/stm32l4/build.rs37
-rw-r--r--examples/boot/stm32l4/memory.x15
-rw-r--r--examples/boot/stm32l4/src/bin/a.rs44
-rw-r--r--examples/boot/stm32l4/src/bin/b.rs25
-rw-r--r--examples/boot/stm32wl/.cargo/config.toml6
-rw-r--r--examples/boot/stm32wl/Cargo.toml26
-rw-r--r--examples/boot/stm32wl/README.md29
-rw-r--r--examples/boot/stm32wl/build.rs37
-rw-r--r--examples/boot/stm32wl/memory.x15
-rw-r--r--examples/boot/stm32wl/src/bin/a.rs47
-rw-r--r--examples/boot/stm32wl/src/bin/b.rs25
-rw-r--r--examples/stm32l0/Cargo.toml2
-rw-r--r--examples/stm32l0/src/bin/flash.rs43
-rw-r--r--examples/stm32l1/Cargo.toml1
-rw-r--r--examples/stm32l1/src/bin/flash.rs43
-rw-r--r--examples/stm32wl/Cargo.toml3
-rw-r--r--examples/stm32wl/src/bin/flash.rs43
m---------stm32-data0
-rw-r--r--stm32-metapac-gen/src/data.rs8
-rw-r--r--stm32-metapac-gen/src/lib.rs34
-rw-r--r--stm32-metapac/src/metadata.rs8
63 files changed, 2118 insertions, 150 deletions
diff --git a/ci.sh b/ci.sh
index 69fa08047..c6fe9c4aa 100755
--- a/ci.sh
+++ b/ci.sh
@@ -58,6 +58,8 @@ cargo batch \
58 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \ 58 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \
59 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits \ 59 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits \
60 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5ub,defmt,exti,time-driver-any,unstable-traits \ 60 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5ub,defmt,exti,time-driver-any,unstable-traits \
61 --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
62 --- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
61 --- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \ 63 --- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \
62 --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \ 64 --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \
63 --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \ 65 --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \
@@ -81,6 +83,11 @@ cargo batch \
81 --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \ 83 --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \
82 --- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \ 84 --- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \
83 --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \ 85 --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \
86 --- build --release --manifest-path examples/boot/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/nrf --bin b \
87 --- build --release --manifest-path examples/boot/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/boot/stm32l0 --bin b \
88 --- build --release --manifest-path examples/boot/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/boot/stm32l1 --bin b \
89 --- build --release --manifest-path examples/boot/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32l4 --bin b \
90 --- build --release --manifest-path examples/boot/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/boot/stm32wl --bin b \
84 --- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \ 91 --- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \
85 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/bluepill-stm32f103c8 \ 92 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/bluepill-stm32f103c8 \
86 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/nucleo-stm32f429zi \ 93 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/nucleo-stm32f429zi \
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index 3aa2eb6bc..a45da1958 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -3,6 +3,7 @@
3* xref:hal.adoc[Hardware Abstraction Layer] 3* xref:hal.adoc[Hardware Abstraction Layer]
4** xref:nrf.adoc[nRF] 4** xref:nrf.adoc[nRF]
5** xref:stm32.adoc[STM32] 5** xref:stm32.adoc[STM32]
6* xref:bootloader.adoc[Bootloader]
6* xref:getting_started.adoc[Getting started] 7* xref:getting_started.adoc[Getting started]
7** xref:basic_application.adoc[Basic application] 8** xref:basic_application.adoc[Basic application]
8** xref:layer_by_layer.adoc[Layer by Layer] 9** xref:layer_by_layer.adoc[Layer by Layer]
diff --git a/docs/modules/ROOT/pages/bootloader.adoc b/docs/modules/ROOT/pages/bootloader.adoc
new file mode 100644
index 000000000..1a984d6dc
--- /dev/null
+++ b/docs/modules/ROOT/pages/bootloader.adoc
@@ -0,0 +1,32 @@
1= Bootloader
2
3`embassy-boot` a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.
4
5The bootloader can be used either as a library or be flashed directly if you are happy with the default configuration and capabilities.
6
7By design, the bootloader does not provide any network capabilities. Networking capabilities for fetching new firmware can be provided by the user application, using the bootloader as a library for updating the firmware, or by using the bootloader as a library and adding this capability yourself.
8
9The bootloader supports both internal and external flash by relying on the `embedded-storage` traits.
10
11
12== Hardware support
13
14The bootloader supports
15
16* nRF52 with and without softdevice
17* STM32 L4, WB, WL, L1 and L0
18
19In general, the bootloader works on any platform that implements the `embedded-storage` traits for its internal flash, but may require custom initialization code to work.
20
21== Design
22
23The bootloader divides the storage into 4 main partitions, configured by a linker script:
24
25* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash.
26* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader.
27* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application.
28* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a flag is set to instruct the bootloader that the partitions should be swapped.
29
30The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash, but they have to support compatible page sizes.
31
32The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice.
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs
index 0d33ad1a6..554709250 100644
--- a/embassy-boot/boot/src/lib.rs
+++ b/embassy-boot/boot/src/lib.rs
@@ -1,5 +1,7 @@
1#![feature(type_alias_impl_trait)] 1#![feature(type_alias_impl_trait)]
2#![feature(generic_associated_types)] 2#![feature(generic_associated_types)]
3#![feature(generic_const_exprs)]
4#![allow(incomplete_features)]
3#![no_std] 5#![no_std]
4///! embassy-boot is a bootloader and firmware updater for embedded devices with flash 6///! embassy-boot is a bootloader and firmware updater for embedded devices with flash
5///! storage implemented using embedded-storage 7///! storage implemented using embedded-storage
@@ -17,8 +19,8 @@ mod fmt;
17use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; 19use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
18use embedded_storage_async::nor_flash::AsyncNorFlash; 20use embedded_storage_async::nor_flash::AsyncNorFlash;
19 21
20pub const BOOT_MAGIC: u32 = 0xD00DF00D; 22const BOOT_MAGIC: u8 = 0xD0;
21pub const SWAP_MAGIC: u32 = 0xF00FDAAD; 23const SWAP_MAGIC: u8 = 0xF0;
22 24
23#[derive(Copy, Clone, Debug)] 25#[derive(Copy, Clone, Debug)]
24#[cfg_attr(feature = "defmt", derive(defmt::Format))] 26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -80,12 +82,12 @@ pub trait FlashProvider {
80} 82}
81 83
82/// BootLoader works with any flash implementing embedded_storage and can also work with 84/// BootLoader works with any flash implementing embedded_storage and can also work with
83/// different page sizes. 85/// different page sizes and flash write sizes.
84pub struct BootLoader<const PAGE_SIZE: usize> { 86pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8> {
85 // Page with current state of bootloader. The state partition has the following format: 87 // Page with current state of bootloader. The state partition has the following format:
86 // | Range | Description | 88 // | Range | Description |
87 // | 0 - 4 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | 89 // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
88 // | 4 - N | Progress index used while swapping or reverting | 90 // | WRITE_SIZE - N | Progress index used while swapping or reverting |
89 state: Partition, 91 state: Partition,
90 // Location of the partition which will be booted from 92 // Location of the partition which will be booted from
91 active: Partition, 93 active: Partition,
@@ -93,14 +95,16 @@ pub struct BootLoader<const PAGE_SIZE: usize> {
93 dfu: Partition, 95 dfu: Partition,
94} 96}
95 97
96impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> { 98impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
99 BootLoader<PAGE_SIZE, WRITE_SIZE, ERASE_VALUE>
100{
97 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { 101 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
98 assert_eq!(active.len() % PAGE_SIZE, 0); 102 assert_eq!(active.len() % PAGE_SIZE, 0);
99 assert_eq!(dfu.len() % PAGE_SIZE, 0); 103 assert_eq!(dfu.len() % PAGE_SIZE, 0);
100 // DFU partition must have an extra page 104 // DFU partition must have an extra page
101 assert!(dfu.len() - active.len() >= PAGE_SIZE); 105 assert!(dfu.len() - active.len() >= PAGE_SIZE);
102 // Ensure we have enough progress pages to store copy progress 106 // Ensure we have enough progress pages to store copy progress
103 assert!(active.len() / PAGE_SIZE >= (state.len() - 4) / PAGE_SIZE); 107 assert!(active.len() / PAGE_SIZE >= (state.len() - WRITE_SIZE) / PAGE_SIZE);
104 Self { active, dfu, state } 108 Self { active, dfu, state }
105 } 109 }
106 110
@@ -203,15 +207,18 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
203 if !self.is_swapped(p.state())? { 207 if !self.is_swapped(p.state())? {
204 trace!("Swapping"); 208 trace!("Swapping");
205 self.swap(p)?; 209 self.swap(p)?;
210 trace!("Swapping done");
206 } else { 211 } else {
207 trace!("Reverting"); 212 trace!("Reverting");
208 self.revert(p)?; 213 self.revert(p)?;
209 214
210 // Overwrite magic and reset progress 215 // Overwrite magic and reset progress
211 let fstate = p.state().flash(); 216 let fstate = p.state().flash();
212 fstate.write(self.state.from as u32, &[0, 0, 0, 0])?; 217 let aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]);
218 fstate.write(self.state.from as u32, &aligned.0)?;
213 fstate.erase(self.state.from as u32, self.state.to as u32)?; 219 fstate.erase(self.state.from as u32, self.state.to as u32)?;
214 fstate.write(self.state.from as u32, &BOOT_MAGIC.to_le_bytes())?; 220 let aligned = Aligned([BOOT_MAGIC; WRITE_SIZE]);
221 fstate.write(self.state.from as u32, &aligned.0)?;
215 } 222 }
216 } 223 }
217 _ => {} 224 _ => {}
@@ -227,12 +234,15 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
227 } 234 }
228 235
229 fn current_progress<P: FlashConfig>(&mut self, p: &mut P) -> Result<usize, BootError> { 236 fn current_progress<P: FlashConfig>(&mut self, p: &mut P) -> Result<usize, BootError> {
230 let max_index = ((self.state.len() - 4) / 4) - 1; 237 let max_index = ((self.state.len() - WRITE_SIZE) / WRITE_SIZE) - 1;
231 let flash = p.flash(); 238 let flash = p.flash();
239 let mut aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]);
232 for i in 0..max_index { 240 for i in 0..max_index {
233 let mut buf: [u8; 4] = [0; 4]; 241 flash.read(
234 flash.read((self.state.from + 4 + i * 4) as u32, &mut buf)?; 242 (self.state.from + WRITE_SIZE + i * WRITE_SIZE) as u32,
235 if buf == [0xFF, 0xFF, 0xFF, 0xFF] { 243 &mut aligned.0,
244 )?;
245 if aligned.0 == [ERASE_VALUE; WRITE_SIZE] {
236 return Ok(i); 246 return Ok(i);
237 } 247 }
238 } 248 }
@@ -241,8 +251,9 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
241 251
242 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P) -> Result<(), BootError> { 252 fn update_progress<P: FlashConfig>(&mut self, idx: usize, p: &mut P) -> Result<(), BootError> {
243 let flash = p.flash(); 253 let flash = p.flash();
244 let w = self.state.from + 4 + idx * 4; 254 let w = self.state.from + WRITE_SIZE + idx * WRITE_SIZE;
245 flash.write(w as u32, &[0, 0, 0, 0])?; 255 let aligned = Aligned([!ERASE_VALUE; WRITE_SIZE]);
256 flash.write(w as u32, &aligned.0)?;
246 Ok(()) 257 Ok(())
247 } 258 }
248 259
@@ -314,18 +325,19 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
314 325
315 fn swap<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> { 326 fn swap<P: FlashProvider>(&mut self, p: &mut P) -> Result<(), BootError> {
316 let page_count = self.active.len() / PAGE_SIZE; 327 let page_count = self.active.len() / PAGE_SIZE;
317 // trace!("Page count: {}", page_count); 328 trace!("Page count: {}", page_count);
318 for page in 0..page_count { 329 for page in 0..page_count {
330 trace!("COPY PAGE {}", page);
319 // Copy active page to the 'next' DFU page. 331 // Copy active page to the 'next' DFU page.
320 let active_page = self.active_addr(page_count - 1 - page); 332 let active_page = self.active_addr(page_count - 1 - page);
321 let dfu_page = self.dfu_addr(page_count - page); 333 let dfu_page = self.dfu_addr(page_count - page);
322 info!("Copy active {} to dfu {}", active_page, dfu_page); 334 //trace!("Copy active {} to dfu {}", active_page, dfu_page);
323 self.copy_page_once_to_dfu(page * 2, active_page, dfu_page, p)?; 335 self.copy_page_once_to_dfu(page * 2, active_page, dfu_page, p)?;
324 336
325 // Copy DFU page to the active page 337 // Copy DFU page to the active page
326 let active_page = self.active_addr(page_count - 1 - page); 338 let active_page = self.active_addr(page_count - 1 - page);
327 let dfu_page = self.dfu_addr(page_count - 1 - page); 339 let dfu_page = self.dfu_addr(page_count - 1 - page);
328 info!("Copy dfy {} to active {}", dfu_page, active_page); 340 //trace!("Copy dfy {} to active {}", dfu_page, active_page);
329 self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?; 341 self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?;
330 } 342 }
331 343
@@ -350,13 +362,14 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
350 } 362 }
351 363
352 fn read_state<P: FlashConfig>(&mut self, p: &mut P) -> Result<State, BootError> { 364 fn read_state<P: FlashConfig>(&mut self, p: &mut P) -> Result<State, BootError> {
353 let mut magic: [u8; 4] = [0; 4]; 365 let mut magic: [u8; WRITE_SIZE] = [0; WRITE_SIZE];
354 let flash = p.flash(); 366 let flash = p.flash();
355 flash.read(self.state.from as u32, &mut magic)?; 367 flash.read(self.state.from as u32, &mut magic)?;
356 368
357 match u32::from_le_bytes(magic) { 369 if magic == [SWAP_MAGIC; WRITE_SIZE] {
358 SWAP_MAGIC => Ok(State::Swap), 370 Ok(State::Swap)
359 _ => Ok(State::Boot), 371 } else {
372 Ok(State::Boot)
360 } 373 }
361 } 374 }
362} 375}
@@ -424,6 +437,38 @@ pub struct FirmwareUpdater {
424 dfu: Partition, 437 dfu: Partition,
425} 438}
426 439
440// NOTE: Aligned to the largest write size supported by flash
441#[repr(align(32))]
442pub struct Aligned<const N: usize>([u8; N]);
443
444impl Default for FirmwareUpdater {
445 fn default() -> Self {
446 extern "C" {
447 static __bootloader_state_start: u32;
448 static __bootloader_state_end: u32;
449 static __bootloader_dfu_start: u32;
450 static __bootloader_dfu_end: u32;
451 }
452
453 let dfu = unsafe {
454 Partition::new(
455 &__bootloader_dfu_start as *const u32 as usize,
456 &__bootloader_dfu_end as *const u32 as usize,
457 )
458 };
459 let state = unsafe {
460 Partition::new(
461 &__bootloader_state_start as *const u32 as usize,
462 &__bootloader_state_end as *const u32 as usize,
463 )
464 };
465
466 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
467 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
468 FirmwareUpdater::new(dfu, state)
469 }
470}
471
427impl FirmwareUpdater { 472impl FirmwareUpdater {
428 pub const fn new(dfu: Partition, state: Partition) -> Self { 473 pub const fn new(dfu: Partition, state: Partition) -> Self {
429 Self { dfu, state } 474 Self { dfu, state }
@@ -435,53 +480,51 @@ impl FirmwareUpdater {
435 } 480 }
436 481
437 /// Instruct bootloader that DFU should commence at next boot. 482 /// Instruct bootloader that DFU should commence at next boot.
438 pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> { 483 /// Must be provided with an aligned buffer to use for reading and writing magic;
439 #[repr(align(4))] 484 pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error>
440 struct Aligned([u8; 4]); 485 where
441 486 [(); F::WRITE_SIZE]:,
442 let mut magic = Aligned([0; 4]); 487 {
443 flash.read(self.state.from as u32, &mut magic.0).await?; 488 let mut aligned = Aligned([0; { F::WRITE_SIZE }]);
444 let magic = u32::from_le_bytes(magic.0); 489 self.set_magic(&mut aligned.0, SWAP_MAGIC, flash).await
445
446 if magic != SWAP_MAGIC {
447 flash
448 .write(self.state.from as u32, &Aligned([0; 4]).0)
449 .await?;
450 flash
451 .erase(self.state.from as u32, self.state.to as u32)
452 .await?;
453 trace!(
454 "Setting swap magic at {} to 0x{:x}, LE: 0x{:x}",
455 self.state.from,
456 &SWAP_MAGIC,
457 &SWAP_MAGIC.to_le_bytes()
458 );
459 flash
460 .write(self.state.from as u32, &SWAP_MAGIC.to_le_bytes())
461 .await?;
462 }
463 Ok(())
464 } 490 }
465 491
466 /// Mark firmware boot successfully 492 /// Mark firmware boot successfully
467 pub async fn mark_booted<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> { 493 pub async fn mark_booted<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error>
468 #[repr(align(4))] 494 where
469 struct Aligned([u8; 4]); 495 [(); F::WRITE_SIZE]:,
496 {
497 let mut aligned = Aligned([0; { F::WRITE_SIZE }]);
498 self.set_magic(&mut aligned.0, BOOT_MAGIC, flash).await
499 }
470 500
471 let mut magic = Aligned([0; 4]); 501 async fn set_magic<F: AsyncNorFlash>(
472 flash.read(self.state.from as u32, &mut magic.0).await?; 502 &mut self,
473 let magic = u32::from_le_bytes(magic.0); 503 aligned: &mut [u8],
504 magic: u8,
505 flash: &mut F,
506 ) -> Result<(), F::Error> {
507 flash.read(self.state.from as u32, aligned).await?;
474 508
475 if magic != BOOT_MAGIC { 509 let mut is_set = true;
476 flash 510 for b in 0..aligned.len() {
477 .write(self.state.from as u32, &Aligned([0; 4]).0) 511 if aligned[b] != magic {
478 .await?; 512 is_set = false;
513 }
514 }
515 if !is_set {
516 for i in 0..aligned.len() {
517 aligned[i] = 0;
518 }
519 flash.write(self.state.from as u32, aligned).await?;
479 flash 520 flash
480 .erase(self.state.from as u32, self.state.to as u32) 521 .erase(self.state.from as u32, self.state.to as u32)
481 .await?; 522 .await?;
482 flash 523
483 .write(self.state.from as u32, &BOOT_MAGIC.to_le_bytes()) 524 for i in 0..aligned.len() {
484 .await?; 525 aligned[i] = magic;
526 }
527 flash.write(self.state.from as u32, aligned).await?;
485 } 528 }
486 Ok(()) 529 Ok(())
487 } 530 }
@@ -545,6 +588,7 @@ mod tests {
545 use super::*; 588 use super::*;
546 use core::convert::Infallible; 589 use core::convert::Infallible;
547 use core::future::Future; 590 use core::future::Future;
591 use embedded_storage::nor_flash::ErrorType;
548 use embedded_storage_async::nor_flash::AsyncReadNorFlash; 592 use embedded_storage_async::nor_flash::AsyncReadNorFlash;
549 use futures::executor::block_on; 593 use futures::executor::block_on;
550 594
@@ -552,9 +596,11 @@ mod tests {
552 const ACTIVE: Partition = Partition::new(4096, 61440); 596 const ACTIVE: Partition = Partition::new(4096, 61440);
553 const DFU: Partition = Partition::new(61440, 122880); 597 const DFU: Partition = Partition::new(61440, 122880);
554 598
599 /*
555 #[test] 600 #[test]
556 fn test_bad_magic() { 601 fn test_bad_magic() {
557 let mut flash = MemFlash([0xff; 131072]); 602 let mut flash = MemFlash([0xff; 131072]);
603 let mut flash = SingleFlashProvider::new(&mut flash);
558 604
559 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); 605 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE);
560 606
@@ -563,13 +609,15 @@ mod tests {
563 Err(BootError::BadMagic) 609 Err(BootError::BadMagic)
564 ); 610 );
565 } 611 }
612 */
566 613
567 #[test] 614 #[test]
568 fn test_boot_state() { 615 fn test_boot_state() {
569 let mut flash = MemFlash([0xff; 131072]); 616 let mut flash = MemFlash([0xff; 131072]);
570 flash.0[0..4].copy_from_slice(&BOOT_MAGIC.to_le_bytes()); 617 flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
618 let mut flash = SingleFlashProvider::new(&mut flash);
571 619
572 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); 620 let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE);
573 621
574 assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap()); 622 assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap());
575 } 623 }
@@ -586,21 +634,21 @@ mod tests {
586 flash.0[i] = original[i - ACTIVE.from]; 634 flash.0[i] = original[i - ACTIVE.from];
587 } 635 }
588 636
589 let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); 637 let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE);
590 let mut updater = FirmwareUpdater::new(DFU, STATE); 638 let mut updater = FirmwareUpdater::new(DFU, STATE);
591 for i in (DFU.from..DFU.to).step_by(4) { 639 let mut offset = 0;
592 let base = i - DFU.from; 640 for chunk in update.chunks(4096) {
593 let data: [u8; 4] = [ 641 block_on(updater.write_firmware(offset, &chunk, &mut flash, 4096)).unwrap();
594 update[base], 642 offset += chunk.len();
595 update[base + 1],
596 update[base + 2],
597 update[base + 3],
598 ];
599 block_on(updater.write_firmware(i - DFU.from, &data, &mut flash)).unwrap();
600 } 643 }
601 block_on(updater.mark_update(&mut flash)).unwrap(); 644 block_on(updater.mark_update(&mut flash)).unwrap();
602 645
603 assert_eq!(State::Swap, bootloader.prepare_boot(&mut flash).unwrap()); 646 assert_eq!(
647 State::Swap,
648 bootloader
649 .prepare_boot(&mut SingleFlashProvider::new(&mut flash))
650 .unwrap()
651 );
604 652
605 for i in ACTIVE.from..ACTIVE.to { 653 for i in ACTIVE.from..ACTIVE.to {
606 assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i); 654 assert_eq!(flash.0[i], update[i - ACTIVE.from], "Index {}", i);
@@ -612,7 +660,12 @@ mod tests {
612 } 660 }
613 661
614 // Running again should cause a revert 662 // Running again should cause a revert
615 assert_eq!(State::Swap, bootloader.prepare_boot(&mut flash).unwrap()); 663 assert_eq!(
664 State::Swap,
665 bootloader
666 .prepare_boot(&mut SingleFlashProvider::new(&mut flash))
667 .unwrap()
668 );
616 669
617 for i in ACTIVE.from..ACTIVE.to { 670 for i in ACTIVE.from..ACTIVE.to {
618 assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i); 671 assert_eq!(flash.0[i], original[i - ACTIVE.from], "Index {}", i);
@@ -625,7 +678,12 @@ mod tests {
625 678
626 // Mark as booted 679 // Mark as booted
627 block_on(updater.mark_booted(&mut flash)).unwrap(); 680 block_on(updater.mark_booted(&mut flash)).unwrap();
628 assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap()); 681 assert_eq!(
682 State::Boot,
683 bootloader
684 .prepare_boot(&mut SingleFlashProvider::new(&mut flash))
685 .unwrap()
686 );
629 } 687 }
630 688
631 struct MemFlash([u8; 131072]); 689 struct MemFlash([u8; 131072]);
@@ -656,9 +714,12 @@ mod tests {
656 } 714 }
657 } 715 }
658 716
717 impl ErrorType for MemFlash {
718 type Error = Infallible;
719 }
720
659 impl ReadNorFlash for MemFlash { 721 impl ReadNorFlash for MemFlash {
660 const READ_SIZE: usize = 4; 722 const READ_SIZE: usize = 4;
661 type Error = Infallible;
662 723
663 fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> { 724 fn read(&mut self, offset: u32, buf: &mut [u8]) -> Result<(), Self::Error> {
664 let len = buf.len(); 725 let len = buf.len();
@@ -673,10 +734,9 @@ mod tests {
673 734
674 impl AsyncReadNorFlash for MemFlash { 735 impl AsyncReadNorFlash for MemFlash {
675 const READ_SIZE: usize = 4; 736 const READ_SIZE: usize = 4;
676 type Error = Infallible;
677 737
678 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a; 738 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a;
679 fn read<'a>(&'a mut self, offset: usize, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { 739 fn read<'a>(&'a mut self, offset: u32, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
680 async move { 740 async move {
681 let len = buf.len(); 741 let len = buf.len();
682 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]); 742 buf[..].copy_from_slice(&self.0[offset as usize..offset as usize + len]);
diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs
index 785cb67e8..c12899d77 100644
--- a/embassy-boot/nrf/src/lib.rs
+++ b/embassy-boot/nrf/src/lib.rs
@@ -4,9 +4,7 @@
4 4
5mod fmt; 5mod fmt;
6 6
7pub use embassy_boot::{ 7pub use embassy_boot::{FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider};
8 FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider, State, BOOT_MAGIC,
9};
10use embassy_nrf::{ 8use embassy_nrf::{
11 nvmc::{Nvmc, PAGE_SIZE}, 9 nvmc::{Nvmc, PAGE_SIZE},
12 peripherals::WDT, 10 peripherals::WDT,
@@ -15,7 +13,7 @@ use embassy_nrf::{
15use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; 13use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
16 14
17pub struct BootLoader { 15pub struct BootLoader {
18 boot: embassy_boot::BootLoader<PAGE_SIZE>, 16 boot: embassy_boot::BootLoader<PAGE_SIZE, 4, 0xFF>,
19} 17}
20 18
21impl BootLoader { 19impl BootLoader {
@@ -184,29 +182,3 @@ impl<'d> ReadNorFlash for WatchdogFlash<'d> {
184 self.flash.capacity() 182 self.flash.capacity()
185 } 183 }
186} 184}
187
188pub mod updater {
189 use super::*;
190 pub fn new() -> embassy_boot::FirmwareUpdater {
191 extern "C" {
192 static __bootloader_state_start: u32;
193 static __bootloader_state_end: u32;
194 static __bootloader_dfu_start: u32;
195 static __bootloader_dfu_end: u32;
196 }
197
198 let dfu = unsafe {
199 Partition::new(
200 &__bootloader_dfu_start as *const u32 as usize,
201 &__bootloader_dfu_end as *const u32 as usize,
202 )
203 };
204 let state = unsafe {
205 Partition::new(
206 &__bootloader_state_start as *const u32 as usize,
207 &__bootloader_state_end as *const u32 as usize,
208 )
209 };
210 embassy_boot::FirmwareUpdater::new(dfu, state)
211 }
212}
diff --git a/embassy-boot/nrf/src/main.rs b/embassy-boot/nrf/src/main.rs
index 63de7c869..0ccd3774d 100644
--- a/embassy-boot/nrf/src/main.rs
+++ b/embassy-boot/nrf/src/main.rs
@@ -46,8 +46,5 @@ unsafe fn DefaultHandler(_: i16) -> ! {
46 46
47#[panic_handler] 47#[panic_handler]
48fn panic(_info: &core::panic::PanicInfo) -> ! { 48fn panic(_info: &core::panic::PanicInfo) -> ! {
49 unsafe { 49 cortex_m::asm::udf();
50 cortex_m::asm::udf();
51 core::hint::unreachable_unchecked();
52 }
53} 50}
diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml
new file mode 100644
index 000000000..a706e4c06
--- /dev/null
+++ b/embassy-boot/stm32/Cargo.toml
@@ -0,0 +1,61 @@
1[package]
2authors = [
3 "Ulf Lilleengen <[email protected]>",
4]
5edition = "2018"
6name = "embassy-boot-stm32"
7version = "0.1.0"
8description = "Bootloader for STM32 chips"
9
10[dependencies]
11defmt = { version = "0.3", optional = true }
12defmt-rtt = { version = "0.3", optional = true }
13
14embassy = { path = "../../embassy", default-features = false }
15embassy-stm32 = { path = "../../embassy-stm32", default-features = false, features = ["nightly"] }
16embassy-boot = { path = "../boot", default-features = false }
17cortex-m = { version = "0.7" }
18cortex-m-rt = { version = "0.7" }
19embedded-storage = "0.3.0"
20embedded-storage-async = "0.3.0"
21cfg-if = "1.0.0"
22
23[features]
24defmt = [
25 "dep:defmt",
26 "embassy-boot/defmt",
27 "embassy-stm32/defmt",
28]
29debug = ["defmt-rtt"]
30thumbv6 = []
31
32[profile.dev]
33debug = 2
34debug-assertions = true
35incremental = false
36opt-level = 'z'
37overflow-checks = true
38
39[profile.release]
40codegen-units = 1
41debug = 2
42debug-assertions = false
43incremental = false
44lto = 'fat'
45opt-level = 'z'
46overflow-checks = false
47
48# do not optimize proc-macro crates = faster builds from scratch
49[profile.dev.build-override]
50codegen-units = 8
51debug = false
52debug-assertions = false
53opt-level = 0
54overflow-checks = false
55
56[profile.release.build-override]
57codegen-units = 8
58debug = false
59debug-assertions = false
60opt-level = 0
61overflow-checks = false
diff --git a/embassy-boot/stm32/README.md b/embassy-boot/stm32/README.md
new file mode 100644
index 000000000..a82b730b9
--- /dev/null
+++ b/embassy-boot/stm32/README.md
@@ -0,0 +1,11 @@
1# Bootloader for STM32
2
3The bootloader uses `embassy-boot` to interact with the flash.
4
5# Usage
6
7Flash the bootloader
8
9```
10cargo flash --features embassy-stm32/stm32wl55jc-cm4 --release --chip STM32WLE5JCIx
11```
diff --git a/embassy-boot/stm32/build.rs b/embassy-boot/stm32/build.rs
new file mode 100644
index 000000000..fd605991f
--- /dev/null
+++ b/embassy-boot/stm32/build.rs
@@ -0,0 +1,27 @@
1use std::env;
2use std::fs::File;
3use std::io::Write;
4use std::path::PathBuf;
5
6fn main() {
7 // Put `memory.x` in our output directory and ensure it's
8 // on the linker search path.
9 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
10 File::create(out.join("memory.x"))
11 .unwrap()
12 .write_all(include_bytes!("memory.x"))
13 .unwrap();
14 println!("cargo:rustc-link-search={}", out.display());
15
16 // By default, Cargo will re-run a build script whenever
17 // any file in the project changes. By specifying `memory.x`
18 // here, we ensure the build script is only re-run when
19 // `memory.x` is changed.
20 println!("cargo:rerun-if-changed=memory.x");
21
22 println!("cargo:rustc-link-arg-bins=--nmagic");
23 println!("cargo:rustc-link-arg-bins=-Tlink.x");
24 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
25 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
26 }
27}
diff --git a/embassy-boot/stm32/memory.x b/embassy-boot/stm32/memory.x
new file mode 100644
index 000000000..110c23259
--- /dev/null
+++ b/embassy-boot/stm32/memory.x
@@ -0,0 +1,18 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 FLASH : ORIGIN = 0x08000000, LENGTH = 24K
5 BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
6 ACTIVE : ORIGIN = 0x08008000, LENGTH = 32K
7 DFU : ORIGIN = 0x08010000, LENGTH = 36K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 16K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);
13
14__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);
15__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);
16
17__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);
18__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);
diff --git a/embassy-boot/stm32/src/fmt.rs b/embassy-boot/stm32/src/fmt.rs
new file mode 100644
index 000000000..066970813
--- /dev/null
+++ b/embassy-boot/stm32/src/fmt.rs
@@ -0,0 +1,225 @@
1#![macro_use]
2#![allow(unused_macros)]
3
4#[cfg(all(feature = "defmt", feature = "log"))]
5compile_error!("You may not enable both `defmt` and `log` features.");
6
7macro_rules! assert {
8 ($($x:tt)*) => {
9 {
10 #[cfg(not(feature = "defmt"))]
11 ::core::assert!($($x)*);
12 #[cfg(feature = "defmt")]
13 ::defmt::assert!($($x)*);
14 }
15 };
16}
17
18macro_rules! assert_eq {
19 ($($x:tt)*) => {
20 {
21 #[cfg(not(feature = "defmt"))]
22 ::core::assert_eq!($($x)*);
23 #[cfg(feature = "defmt")]
24 ::defmt::assert_eq!($($x)*);
25 }
26 };
27}
28
29macro_rules! assert_ne {
30 ($($x:tt)*) => {
31 {
32 #[cfg(not(feature = "defmt"))]
33 ::core::assert_ne!($($x)*);
34 #[cfg(feature = "defmt")]
35 ::defmt::assert_ne!($($x)*);
36 }
37 };
38}
39
40macro_rules! debug_assert {
41 ($($x:tt)*) => {
42 {
43 #[cfg(not(feature = "defmt"))]
44 ::core::debug_assert!($($x)*);
45 #[cfg(feature = "defmt")]
46 ::defmt::debug_assert!($($x)*);
47 }
48 };
49}
50
51macro_rules! debug_assert_eq {
52 ($($x:tt)*) => {
53 {
54 #[cfg(not(feature = "defmt"))]
55 ::core::debug_assert_eq!($($x)*);
56 #[cfg(feature = "defmt")]
57 ::defmt::debug_assert_eq!($($x)*);
58 }
59 };
60}
61
62macro_rules! debug_assert_ne {
63 ($($x:tt)*) => {
64 {
65 #[cfg(not(feature = "defmt"))]
66 ::core::debug_assert_ne!($($x)*);
67 #[cfg(feature = "defmt")]
68 ::defmt::debug_assert_ne!($($x)*);
69 }
70 };
71}
72
73macro_rules! todo {
74 ($($x:tt)*) => {
75 {
76 #[cfg(not(feature = "defmt"))]
77 ::core::todo!($($x)*);
78 #[cfg(feature = "defmt")]
79 ::defmt::todo!($($x)*);
80 }
81 };
82}
83
84macro_rules! unreachable {
85 ($($x:tt)*) => {
86 {
87 #[cfg(not(feature = "defmt"))]
88 ::core::unreachable!($($x)*);
89 #[cfg(feature = "defmt")]
90 ::defmt::unreachable!($($x)*);
91 }
92 };
93}
94
95macro_rules! panic {
96 ($($x:tt)*) => {
97 {
98 #[cfg(not(feature = "defmt"))]
99 ::core::panic!($($x)*);
100 #[cfg(feature = "defmt")]
101 ::defmt::panic!($($x)*);
102 }
103 };
104}
105
106macro_rules! trace {
107 ($s:literal $(, $x:expr)* $(,)?) => {
108 {
109 #[cfg(feature = "log")]
110 ::log::trace!($s $(, $x)*);
111 #[cfg(feature = "defmt")]
112 ::defmt::trace!($s $(, $x)*);
113 #[cfg(not(any(feature = "log", feature="defmt")))]
114 let _ = ($( & $x ),*);
115 }
116 };
117}
118
119macro_rules! debug {
120 ($s:literal $(, $x:expr)* $(,)?) => {
121 {
122 #[cfg(feature = "log")]
123 ::log::debug!($s $(, $x)*);
124 #[cfg(feature = "defmt")]
125 ::defmt::debug!($s $(, $x)*);
126 #[cfg(not(any(feature = "log", feature="defmt")))]
127 let _ = ($( & $x ),*);
128 }
129 };
130}
131
132macro_rules! info {
133 ($s:literal $(, $x:expr)* $(,)?) => {
134 {
135 #[cfg(feature = "log")]
136 ::log::info!($s $(, $x)*);
137 #[cfg(feature = "defmt")]
138 ::defmt::info!($s $(, $x)*);
139 #[cfg(not(any(feature = "log", feature="defmt")))]
140 let _ = ($( & $x ),*);
141 }
142 };
143}
144
145macro_rules! warn {
146 ($s:literal $(, $x:expr)* $(,)?) => {
147 {
148 #[cfg(feature = "log")]
149 ::log::warn!($s $(, $x)*);
150 #[cfg(feature = "defmt")]
151 ::defmt::warn!($s $(, $x)*);
152 #[cfg(not(any(feature = "log", feature="defmt")))]
153 let _ = ($( & $x ),*);
154 }
155 };
156}
157
158macro_rules! error {
159 ($s:literal $(, $x:expr)* $(,)?) => {
160 {
161 #[cfg(feature = "log")]
162 ::log::error!($s $(, $x)*);
163 #[cfg(feature = "defmt")]
164 ::defmt::error!($s $(, $x)*);
165 #[cfg(not(any(feature = "log", feature="defmt")))]
166 let _ = ($( & $x ),*);
167 }
168 };
169}
170
171#[cfg(feature = "defmt")]
172macro_rules! unwrap {
173 ($($x:tt)*) => {
174 ::defmt::unwrap!($($x)*)
175 };
176}
177
178#[cfg(not(feature = "defmt"))]
179macro_rules! unwrap {
180 ($arg:expr) => {
181 match $crate::fmt::Try::into_result($arg) {
182 ::core::result::Result::Ok(t) => t,
183 ::core::result::Result::Err(e) => {
184 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
185 }
186 }
187 };
188 ($arg:expr, $($msg:expr),+ $(,)? ) => {
189 match $crate::fmt::Try::into_result($arg) {
190 ::core::result::Result::Ok(t) => t,
191 ::core::result::Result::Err(e) => {
192 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
193 }
194 }
195 }
196}
197
198#[derive(Debug, Copy, Clone, Eq, PartialEq)]
199pub struct NoneError;
200
201pub trait Try {
202 type Ok;
203 type Error;
204 fn into_result(self) -> Result<Self::Ok, Self::Error>;
205}
206
207impl<T> Try for Option<T> {
208 type Ok = T;
209 type Error = NoneError;
210
211 #[inline]
212 fn into_result(self) -> Result<T, NoneError> {
213 self.ok_or(NoneError)
214 }
215}
216
217impl<T, E> Try for Result<T, E> {
218 type Ok = T;
219 type Error = E;
220
221 #[inline]
222 fn into_result(self) -> Self {
223 self
224 }
225}
diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs
new file mode 100644
index 000000000..68220780c
--- /dev/null
+++ b/embassy-boot/stm32/src/lib.rs
@@ -0,0 +1,77 @@
1#![no_std]
2#![feature(generic_associated_types)]
3#![feature(type_alias_impl_trait)]
4
5mod fmt;
6
7pub use embassy_boot::{FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider, State};
8use embassy_stm32::flash::{ERASE_SIZE, ERASE_VALUE, WRITE_SIZE};
9
10pub struct BootLoader {
11 boot: embassy_boot::BootLoader<ERASE_SIZE, WRITE_SIZE, ERASE_VALUE>,
12}
13
14impl BootLoader {
15 /// Create a new bootloader instance using parameters from linker script
16 pub fn default() -> Self {
17 extern "C" {
18 static __bootloader_state_start: u32;
19 static __bootloader_state_end: u32;
20 static __bootloader_active_start: u32;
21 static __bootloader_active_end: u32;
22 static __bootloader_dfu_start: u32;
23 static __bootloader_dfu_end: u32;
24 }
25
26 let active = unsafe {
27 Partition::new(
28 &__bootloader_active_start as *const u32 as usize,
29 &__bootloader_active_end as *const u32 as usize,
30 )
31 };
32 let dfu = unsafe {
33 Partition::new(
34 &__bootloader_dfu_start as *const u32 as usize,
35 &__bootloader_dfu_end as *const u32 as usize,
36 )
37 };
38 let state = unsafe {
39 Partition::new(
40 &__bootloader_state_start as *const u32 as usize,
41 &__bootloader_state_end as *const u32 as usize,
42 )
43 };
44
45 trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to);
46 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
47 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
48
49 Self::new(active, dfu, state)
50 }
51
52 /// Create a new bootloader instance using the supplied partitions for active, dfu and state.
53 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
54 Self {
55 boot: embassy_boot::BootLoader::new(active, dfu, state),
56 }
57 }
58
59 /// Boots the application
60 pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize {
61 match self.boot.prepare_boot(flash) {
62 Ok(_) => self.boot.boot_address(),
63 Err(_) => panic!("boot prepare error!"),
64 }
65 }
66
67 pub unsafe fn load(&mut self, start: usize) -> ! {
68 trace!("Loading app at 0x{:x}", start);
69 #[allow(unused_mut)]
70 let mut p = cortex_m::Peripherals::steal();
71 #[cfg(not(feature = "thumbv6"))]
72 p.SCB.invalidate_icache();
73 p.SCB.vtor.write(start as u32);
74
75 cortex_m::asm::bootload(start as *const u32)
76 }
77}
diff --git a/embassy-boot/stm32/src/main.rs b/embassy-boot/stm32/src/main.rs
new file mode 100644
index 000000000..563bc55d3
--- /dev/null
+++ b/embassy-boot/stm32/src/main.rs
@@ -0,0 +1,48 @@
1#![no_std]
2#![no_main]
3
4use cortex_m_rt::{entry, exception};
5
6#[cfg(feature = "defmt")]
7use defmt_rtt as _;
8
9use embassy_boot_stm32::*;
10use embassy_stm32::flash::Flash;
11
12#[entry]
13fn main() -> ! {
14 let p = embassy_stm32::init(Default::default());
15
16 // Uncomment this if you are debugging the bootloader with debugger/RTT attached,
17 // as it prevents a hard fault when accessing flash 'too early' after boot.
18 /*
19 for i in 0..10000000 {
20 cortex_m::asm::nop();
21 }
22 */
23
24 let mut bl = BootLoader::default();
25 let mut flash = Flash::unlock(p.FLASH);
26 let start = bl.prepare(&mut SingleFlashProvider::new(&mut flash));
27 core::mem::drop(flash);
28 unsafe { bl.load(start) }
29}
30
31#[no_mangle]
32#[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
33unsafe extern "C" fn HardFault() {
34 cortex_m::peripheral::SCB::sys_reset();
35}
36
37#[exception]
38unsafe fn DefaultHandler(_: i16) -> ! {
39 const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;
40 let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16;
41
42 panic!("DefaultHandler #{:?}", irqn);
43}
44
45#[panic_handler]
46fn panic(_info: &core::panic::PanicInfo) -> ! {
47 cortex_m::asm::udf();
48}
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 1c9616b71..8152b07d4 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -42,6 +42,9 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["un
42embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} 42embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
43embedded-hal-async = { version = "0.1.0-alpha.0", optional = true} 43embedded-hal-async = { version = "0.1.0-alpha.0", optional = true}
44 44
45embedded-storage = "0.3.0"
46embedded-storage-async = { version = "0.3.0", optional = true }
47
45defmt = { version = "0.3", optional = true } 48defmt = { version = "0.3", optional = true }
46log = { version = "0.4.14", optional = true } 49log = { version = "0.4.14", optional = true }
47cortex-m-rt = ">=0.6.15,<0.8" 50cortex-m-rt = ">=0.6.15,<0.8"
@@ -87,7 +90,7 @@ time-driver-tim12 = ["_time-driver"]
87time-driver-tim15 = ["_time-driver"] 90time-driver-tim15 = ["_time-driver"]
88 91
89# Enable nightly-only features 92# Enable nightly-only features
90nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async"] 93nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async"]
91 94
92# Reexport stm32-metapac at `embassy_stm32::pac`. 95# Reexport stm32-metapac at `embassy_stm32::pac`.
93# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. 96# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version.
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs
new file mode 100644
index 000000000..cff3119fd
--- /dev/null
+++ b/embassy-stm32/src/flash/mod.rs
@@ -0,0 +1,359 @@
1use crate::pac;
2use crate::peripherals::FLASH;
3use core::convert::TryInto;
4use core::marker::PhantomData;
5use core::ptr::write_volatile;
6use embassy::util::Unborrow;
7use embassy_hal_common::unborrow;
8
9use embedded_storage::nor_flash::{
10 ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash,
11};
12
13pub use crate::pac::ERASE_SIZE;
14pub use crate::pac::ERASE_VALUE;
15pub use crate::pac::FLASH_BASE;
16pub use crate::pac::FLASH_SIZE;
17pub use crate::pac::WRITE_SIZE;
18const FLASH_END: usize = FLASH_BASE + FLASH_SIZE;
19
20pub struct Flash<'d> {
21 _inner: FLASH,
22 _phantom: PhantomData<&'d mut FLASH>,
23}
24
25impl<'d> Flash<'d> {
26 pub fn new(p: impl Unborrow<Target = FLASH>) -> Self {
27 unborrow!(p);
28 Self {
29 _inner: p,
30 _phantom: PhantomData,
31 }
32 }
33
34 pub fn unlock(p: impl Unborrow<Target = FLASH>) -> Self {
35 let flash = Self::new(p);
36 #[cfg(any(flash_wl, flash_wb, flash_l4))]
37 unsafe {
38 pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123));
39 pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB));
40 }
41
42 #[cfg(any(flash_l0))]
43 unsafe {
44 pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x89ABCDEF));
45 pac::FLASH.pekeyr().write(|w| w.set_pekeyr(0x02030405));
46
47 pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x8C9DAEBF));
48 pac::FLASH.prgkeyr().write(|w| w.set_prgkeyr(0x13141516));
49 }
50 flash
51 }
52
53 pub fn lock(&mut self) {
54 #[cfg(any(flash_wl, flash_wb, flash_l4))]
55 unsafe {
56 pac::FLASH.cr().modify(|w| w.set_lock(true));
57 }
58
59 #[cfg(any(flash_l0))]
60 unsafe {
61 pac::FLASH.pecr().modify(|w| {
62 w.set_optlock(true);
63 w.set_prglock(true);
64 w.set_pelock(true);
65 });
66 }
67 }
68
69 pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
70 let offset = FLASH_BASE as u32 + offset;
71 if offset as usize >= FLASH_END || offset as usize + bytes.len() > FLASH_END {
72 return Err(Error::Size);
73 }
74
75 let flash_data = unsafe { core::slice::from_raw_parts(offset as *const u8, bytes.len()) };
76 bytes.copy_from_slice(flash_data);
77 Ok(())
78 }
79
80 pub fn blocking_write(&mut self, offset: u32, buf: &[u8]) -> Result<(), Error> {
81 let offset = FLASH_BASE as u32 + offset;
82 if offset as usize + buf.len() > FLASH_END {
83 return Err(Error::Size);
84 }
85 if offset as usize % WRITE_SIZE != 0 || buf.len() as usize % WRITE_SIZE != 0 {
86 return Err(Error::Unaligned);
87 }
88 trace!("Writing {} bytes at 0x{:x}", buf.len(), offset);
89
90 self.clear_all_err();
91
92 #[cfg(any(flash_wl, flash_wb, flash_l4))]
93 unsafe {
94 pac::FLASH.cr().write(|w| w.set_pg(true))
95 }
96
97 let mut ret: Result<(), Error> = Ok(());
98 let mut offset = offset;
99 for chunk in buf.chunks(WRITE_SIZE) {
100 for val in chunk.chunks(4) {
101 unsafe {
102 write_volatile(
103 offset as *mut u32,
104 u32::from_le_bytes(val[0..4].try_into().unwrap()),
105 );
106 }
107 offset += val.len() as u32;
108 }
109
110 ret = self.blocking_wait_ready();
111 if ret.is_err() {
112 break;
113 }
114 }
115
116 #[cfg(any(flash_wl, flash_wb, flash_l4))]
117 unsafe {
118 pac::FLASH.cr().write(|w| w.set_pg(false))
119 }
120
121 ret
122 }
123
124 pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
125 let from = FLASH_BASE as u32 + from;
126 let to = FLASH_BASE as u32 + to;
127 if to < from || to as usize > FLASH_END {
128 return Err(Error::Size);
129 }
130 if from as usize % ERASE_SIZE != 0 || to as usize % ERASE_SIZE != 0 {
131 return Err(Error::Unaligned);
132 }
133
134 self.clear_all_err();
135
136 for page in (from..to).step_by(ERASE_SIZE) {
137 #[cfg(any(flash_l0, flash_l1))]
138 unsafe {
139 pac::FLASH.pecr().modify(|w| {
140 w.set_erase(true);
141 w.set_prog(true);
142 });
143
144 write_volatile(page as *mut u32, 0xFFFFFFFF);
145 }
146
147 #[cfg(any(flash_wl, flash_wb, flash_l4))]
148 unsafe {
149 let idx = page / ERASE_SIZE as u32;
150
151 pac::FLASH.cr().modify(|w| {
152 w.set_per(true);
153 w.set_pnb(idx as u8);
154 #[cfg(any(flash_wl, flash_wb))]
155 w.set_strt(true);
156 #[cfg(any(flash_l4))]
157 w.set_start(true);
158 });
159 }
160
161 let ret: Result<(), Error> = self.blocking_wait_ready();
162
163 #[cfg(any(flash_wl, flash_wb, flash_l4))]
164 unsafe {
165 pac::FLASH.cr().modify(|w| w.set_per(false));
166 }
167
168 #[cfg(any(flash_l0, flash_l1))]
169 unsafe {
170 pac::FLASH.pecr().modify(|w| {
171 w.set_erase(false);
172 w.set_prog(false);
173 });
174 }
175
176 self.clear_all_err();
177 if ret.is_err() {
178 return ret;
179 }
180 }
181
182 Ok(())
183 }
184
185 fn blocking_wait_ready(&self) -> Result<(), Error> {
186 loop {
187 let sr = unsafe { pac::FLASH.sr().read() };
188
189 if !sr.bsy() {
190 #[cfg(any(flash_wl, flash_wb, flash_l4))]
191 if sr.progerr() {
192 return Err(Error::Prog);
193 }
194
195 if sr.wrperr() {
196 return Err(Error::Protected);
197 }
198
199 if sr.pgaerr() {
200 return Err(Error::Unaligned);
201 }
202
203 if sr.sizerr() {
204 return Err(Error::Size);
205 }
206
207 #[cfg(any(flash_wl, flash_wb, flash_l4))]
208 if sr.miserr() {
209 return Err(Error::Miss);
210 }
211
212 #[cfg(any(flash_wl, flash_wb, flash_l4))]
213 if sr.pgserr() {
214 return Err(Error::Seq);
215 }
216 return Ok(());
217 }
218 }
219 }
220
221 fn clear_all_err(&mut self) {
222 unsafe {
223 pac::FLASH.sr().modify(|w| {
224 #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l0))]
225 if w.rderr() {
226 w.set_rderr(false);
227 }
228 #[cfg(any(flash_wl, flash_wb, flash_l4))]
229 if w.fasterr() {
230 w.set_fasterr(false);
231 }
232 #[cfg(any(flash_wl, flash_wb, flash_l4))]
233 if w.miserr() {
234 w.set_miserr(false);
235 }
236 #[cfg(any(flash_wl, flash_wb, flash_l4))]
237 if w.pgserr() {
238 w.set_pgserr(false);
239 }
240 if w.sizerr() {
241 w.set_sizerr(false);
242 }
243 if w.pgaerr() {
244 w.set_pgaerr(false);
245 }
246 if w.wrperr() {
247 w.set_wrperr(false);
248 }
249 #[cfg(any(flash_wl, flash_wb, flash_l4))]
250 if w.progerr() {
251 w.set_progerr(false);
252 }
253 #[cfg(any(flash_wl, flash_wb, flash_l4))]
254 if w.operr() {
255 w.set_operr(false);
256 }
257 });
258 }
259 }
260}
261
262impl Drop for Flash<'_> {
263 fn drop(&mut self) {
264 self.lock();
265 }
266}
267
268#[derive(Debug, Copy, Clone, PartialEq, Eq)]
269#[cfg_attr(feature = "defmt", derive(defmt::Format))]
270pub enum Error {
271 Prog,
272 Size,
273 Miss,
274 Seq,
275 Protected,
276 Unaligned,
277}
278
279impl<'d> ErrorType for Flash<'d> {
280 type Error = Error;
281}
282
283impl NorFlashError for Error {
284 fn kind(&self) -> NorFlashErrorKind {
285 match self {
286 Self::Size => NorFlashErrorKind::OutOfBounds,
287 Self::Unaligned => NorFlashErrorKind::NotAligned,
288 _ => NorFlashErrorKind::Other,
289 }
290 }
291}
292
293impl<'d> ReadNorFlash for Flash<'d> {
294 const READ_SIZE: usize = WRITE_SIZE;
295
296 fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
297 self.blocking_read(offset, bytes)
298 }
299
300 fn capacity(&self) -> usize {
301 FLASH_SIZE
302 }
303}
304
305impl<'d> NorFlash for Flash<'d> {
306 const WRITE_SIZE: usize = WRITE_SIZE;
307 const ERASE_SIZE: usize = ERASE_SIZE;
308
309 fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
310 self.blocking_erase(from, to)
311 }
312
313 fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
314 self.blocking_write(offset, bytes)
315 }
316}
317
318/*
319cfg_if::cfg_if! {
320 if #[cfg(feature = "nightly")]
321 {
322 use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash};
323 use core::future::Future;
324
325 impl<'d> AsyncNorFlash for Flash<'d> {
326 const WRITE_SIZE: usize = <Self as NorFlash>::WRITE_SIZE;
327 const ERASE_SIZE: usize = <Self as NorFlash>::ERASE_SIZE;
328
329 type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
330 fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> {
331 async move {
332 todo!()
333 }
334 }
335
336 type EraseFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
337 fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> {
338 async move {
339 todo!()
340 }
341 }
342 }
343
344 impl<'d> AsyncReadNorFlash for Flash<'d> {
345 const READ_SIZE: usize = <Self as ReadNorFlash>::READ_SIZE;
346 type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
347 fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> {
348 async move {
349 todo!()
350 }
351 }
352
353 fn capacity(&self) -> usize {
354 FLASH_SIZE
355 }
356 }
357 }
358}
359*/
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 3417c5d9b..ba426128f 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -50,6 +50,8 @@ pub mod i2c;
50 50
51#[cfg(crc)] 51#[cfg(crc)]
52pub mod crc; 52pub mod crc;
53#[cfg(any(flash_l0, flash_l1, flash_wl, flash_wb, flash_l4))]
54pub mod flash;
53pub mod pwm; 55pub mod pwm;
54#[cfg(rng)] 56#[cfg(rng)]
55pub mod rng; 57pub mod rng;
diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs
index fb2dd9986..1f2fbaf87 100644
--- a/embassy-stm32/src/rcc/wl.rs
+++ b/embassy-stm32/src/rcc/wl.rs
@@ -1,4 +1,4 @@
1use crate::pac::RCC; 1use crate::pac::{FLASH, RCC};
2use crate::rcc::{set_freqs, Clocks}; 2use crate::rcc::{set_freqs, Clocks};
3use crate::time::U32Ext; 3use crate::time::U32Ext;
4 4
@@ -15,10 +15,101 @@ pub const HSE32_FREQ: u32 = 32_000_000;
15/// System clock mux source 15/// System clock mux source
16#[derive(Clone, Copy)] 16#[derive(Clone, Copy)]
17pub enum ClockSrc { 17pub enum ClockSrc {
18 MSI(MSIRange),
18 HSE32, 19 HSE32,
19 HSI16, 20 HSI16,
20} 21}
21 22
23#[derive(Clone, Copy, PartialOrd, PartialEq)]
24pub enum MSIRange {
25 /// Around 100 kHz
26 Range0,
27 /// Around 200 kHz
28 Range1,
29 /// Around 400 kHz
30 Range2,
31 /// Around 800 kHz
32 Range3,
33 /// Around 1 MHz
34 Range4,
35 /// Around 2 MHz
36 Range5,
37 /// Around 4 MHz (reset value)
38 Range6,
39 /// Around 8 MHz
40 Range7,
41 /// Around 16 MHz
42 Range8,
43 /// Around 24 MHz
44 Range9,
45 /// Around 32 MHz
46 Range10,
47 /// Around 48 MHz
48 Range11,
49}
50
51impl MSIRange {
52 fn freq(&self) -> u32 {
53 match self {
54 MSIRange::Range0 => 100_000,
55 MSIRange::Range1 => 200_000,
56 MSIRange::Range2 => 400_000,
57 MSIRange::Range3 => 800_000,
58 MSIRange::Range4 => 1_000_000,
59 MSIRange::Range5 => 2_000_000,
60 MSIRange::Range6 => 4_000_000,
61 MSIRange::Range7 => 8_000_000,
62 MSIRange::Range8 => 16_000_000,
63 MSIRange::Range9 => 24_000_000,
64 MSIRange::Range10 => 32_000_000,
65 MSIRange::Range11 => 48_000_000,
66 }
67 }
68
69 fn vos(&self) -> VoltageScale {
70 if self > &MSIRange::Range8 {
71 VoltageScale::Range1
72 } else {
73 VoltageScale::Range2
74 }
75 }
76}
77
78impl Default for MSIRange {
79 fn default() -> MSIRange {
80 MSIRange::Range6
81 }
82}
83
84impl Into<u8> for MSIRange {
85 fn into(self) -> u8 {
86 match self {
87 MSIRange::Range0 => 0b0000,
88 MSIRange::Range1 => 0b0001,
89 MSIRange::Range2 => 0b0010,
90 MSIRange::Range3 => 0b0011,
91 MSIRange::Range4 => 0b0100,
92 MSIRange::Range5 => 0b0101,
93 MSIRange::Range6 => 0b0110,
94 MSIRange::Range7 => 0b0111,
95 MSIRange::Range8 => 0b1000,
96 MSIRange::Range9 => 0b1001,
97 MSIRange::Range10 => 0b1010,
98 MSIRange::Range11 => 0b1011,
99 }
100 }
101}
102
103/// Voltage Scale
104///
105/// Represents the voltage range feeding the CPU core. The maximum core
106/// clock frequency depends on this value.
107#[derive(Copy, Clone, PartialEq)]
108pub enum VoltageScale {
109 Range1,
110 Range2,
111}
112
22/// AHB prescaler 113/// AHB prescaler
23#[derive(Clone, Copy, PartialEq)] 114#[derive(Clone, Copy, PartialEq)]
24pub enum AHBPrescaler { 115pub enum AHBPrescaler {
@@ -85,6 +176,7 @@ impl Into<u8> for AHBPrescaler {
85pub struct Config { 176pub struct Config {
86 pub mux: ClockSrc, 177 pub mux: ClockSrc,
87 pub ahb_pre: AHBPrescaler, 178 pub ahb_pre: AHBPrescaler,
179 pub shd_ahb_pre: AHBPrescaler,
88 pub apb1_pre: APBPrescaler, 180 pub apb1_pre: APBPrescaler,
89 pub apb2_pre: APBPrescaler, 181 pub apb2_pre: APBPrescaler,
90 pub enable_lsi: bool, 182 pub enable_lsi: bool,
@@ -94,8 +186,9 @@ impl Default for Config {
94 #[inline] 186 #[inline]
95 fn default() -> Config { 187 fn default() -> Config {
96 Config { 188 Config {
97 mux: ClockSrc::HSI16, 189 mux: ClockSrc::MSI(MSIRange::default()),
98 ahb_pre: AHBPrescaler::NotDivided, 190 ahb_pre: AHBPrescaler::NotDivided,
191 shd_ahb_pre: AHBPrescaler::NotDivided,
99 apb1_pre: APBPrescaler::NotDivided, 192 apb1_pre: APBPrescaler::NotDivided,
100 apb2_pre: APBPrescaler::NotDivided, 193 apb2_pre: APBPrescaler::NotDivided,
101 enable_lsi: false, 194 enable_lsi: false,
@@ -104,13 +197,13 @@ impl Default for Config {
104} 197}
105 198
106pub(crate) unsafe fn init(config: Config) { 199pub(crate) unsafe fn init(config: Config) {
107 let (sys_clk, sw) = match config.mux { 200 let (sys_clk, sw, vos) = match config.mux {
108 ClockSrc::HSI16 => { 201 ClockSrc::HSI16 => {
109 // Enable HSI16 202 // Enable HSI16
110 RCC.cr().write(|w| w.set_hsion(true)); 203 RCC.cr().write(|w| w.set_hsion(true));
111 while !RCC.cr().read().hsirdy() {} 204 while !RCC.cr().read().hsirdy() {}
112 205
113 (HSI_FREQ, 0x01) 206 (HSI_FREQ, 0x01, VoltageScale::Range2)
114 } 207 }
115 ClockSrc::HSE32 => { 208 ClockSrc::HSE32 => {
116 // Enable HSE32 209 // Enable HSE32
@@ -120,7 +213,17 @@ pub(crate) unsafe fn init(config: Config) {
120 }); 213 });
121 while !RCC.cr().read().hserdy() {} 214 while !RCC.cr().read().hserdy() {}
122 215
123 (HSE32_FREQ, 0x02) 216 (HSE32_FREQ, 0x02, VoltageScale::Range1)
217 }
218 ClockSrc::MSI(range) => {
219 RCC.cr().write(|w| {
220 w.set_msirange(range.into());
221 w.set_msion(true);
222 });
223
224 while !RCC.cr().read().msirdy() {}
225
226 (range.freq(), 0x00, range.vos())
124 } 227 }
125 }; 228 };
126 229
@@ -135,6 +238,14 @@ pub(crate) unsafe fn init(config: Config) {
135 w.set_ppre2(config.apb2_pre.into()); 238 w.set_ppre2(config.apb2_pre.into());
136 }); 239 });
137 240
241 RCC.extcfgr().modify(|w| {
242 if config.shd_ahb_pre == AHBPrescaler::NotDivided {
243 w.set_shdhpre(0);
244 } else {
245 w.set_shdhpre(config.shd_ahb_pre.into());
246 }
247 });
248
138 let ahb_freq: u32 = match config.ahb_pre { 249 let ahb_freq: u32 = match config.ahb_pre {
139 AHBPrescaler::NotDivided => sys_clk, 250 AHBPrescaler::NotDivided => sys_clk,
140 pre => { 251 pre => {
@@ -144,6 +255,15 @@ pub(crate) unsafe fn init(config: Config) {
144 } 255 }
145 }; 256 };
146 257
258 let shd_ahb_freq: u32 = match config.shd_ahb_pre {
259 AHBPrescaler::NotDivided => sys_clk,
260 pre => {
261 let pre: u8 = pre.into();
262 let pre = 1 << (pre as u32 - 7);
263 sys_clk / pre
264 }
265 };
266
147 let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { 267 let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
148 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 268 APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
149 pre => { 269 pre => {
@@ -164,8 +284,7 @@ pub(crate) unsafe fn init(config: Config) {
164 } 284 }
165 }; 285 };
166 286
167 // TODO: completely untested 287 let apb3_freq = shd_ahb_freq;
168 let apb3_freq = ahb_freq;
169 288
170 if config.enable_lsi { 289 if config.enable_lsi {
171 let csr = RCC.csr().read(); 290 let csr = RCC.csr().read();
@@ -175,11 +294,32 @@ pub(crate) unsafe fn init(config: Config) {
175 } 294 }
176 } 295 }
177 296
297 // Adjust flash latency
298 let flash_clk_src_freq: u32 = shd_ahb_freq;
299 let ws = match vos {
300 VoltageScale::Range1 => match flash_clk_src_freq {
301 0..=18_000_000 => 0b000,
302 18_000_001..=36_000_000 => 0b001,
303 _ => 0b010,
304 },
305 VoltageScale::Range2 => match flash_clk_src_freq {
306 0..=6_000_000 => 0b000,
307 6_000_001..=12_000_000 => 0b001,
308 _ => 0b010,
309 },
310 };
311
312 FLASH.acr().modify(|w| {
313 w.set_latency(ws);
314 });
315
316 while FLASH.acr().read().latency() != ws {}
317
178 set_freqs(Clocks { 318 set_freqs(Clocks {
179 sys: sys_clk.hz(), 319 sys: sys_clk.hz(),
180 ahb1: ahb_freq.hz(), 320 ahb1: ahb_freq.hz(),
181 ahb2: ahb_freq.hz(), 321 ahb2: ahb_freq.hz(),
182 ahb3: ahb_freq.hz(), 322 ahb3: shd_ahb_freq.hz(),
183 apb1: apb1_freq.hz(), 323 apb1: apb1_freq.hz(),
184 apb2: apb2_freq.hz(), 324 apb2: apb2_freq.hz(),
185 apb3: apb3_freq.hz(), 325 apb3: apb3_freq.hz(),
diff --git a/examples/boot/Cargo.toml b/examples/boot/Cargo.toml
deleted file mode 100644
index 2da659478..000000000
--- a/examples/boot/Cargo.toml
+++ /dev/null
@@ -1,19 +0,0 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2018"
4name = "embassy-boot-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../embassy", features = ["nightly"] }
9embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] }
10embassy-boot-nrf = { version = "0.1.0", path = "../../embassy-boot/nrf" }
11embassy-traits = { version = "0.1.0", path = "../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
diff --git a/examples/boot/nrf/.cargo/config.toml b/examples/boot/nrf/.cargo/config.toml
new file mode 100644
index 000000000..c75b5c539
--- /dev/null
+++ b/examples/boot/nrf/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace nRF82840_xxAA with your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip nRF52840_xxAA"
4
5[build]
6target = "thumbv7em-none-eabi"
diff --git a/examples/boot/nrf/Cargo.toml b/examples/boot/nrf/Cargo.toml
new file mode 100644
index 000000000..da8333b7c
--- /dev/null
+++ b/examples/boot/nrf/Cargo.toml
@@ -0,0 +1,19 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2018"
4name = "embassy-boot-nrf-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-nrf = { version = "0.1.0", path = "../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly", "nrf52840"] }
10embassy-boot-nrf = { version = "0.1.0", path = "../../../embassy-boot/nrf" }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
diff --git a/examples/boot/README.md b/examples/boot/nrf/README.md
index b97513a9d..453df7f31 100644
--- a/examples/boot/README.md
+++ b/examples/boot/nrf/README.md
@@ -17,15 +17,15 @@ application.
17 17
18``` 18```
19# Flash bootloader 19# Flash bootloader
20cargo flash --manifest-path ../../embassy-boot/nrf/Cargo.toml --release --features embassy-nrf/nrf52840 --chip nRF52840_xxAA 20cargo flash --manifest-path ../../../embassy-boot/nrf/Cargo.toml --features embassy-nrf/nrf52840 --release --chip nRF52840_xxAA
21# Build 'b' 21# Build 'b'
22cargo build --release --features embassy-nrf/nrf52840 --bin b 22cargo build --release --bin b
23# Generate binary for 'b' 23# Generate binary for 'b'
24cargo objcopy --release --features embassy-nrf/nrf52840 --bin b -- -O binary b.bin 24cargo objcopy --release --bin b -- -O binary b.bin
25``` 25```
26 26
27# Flash `a` (which includes b.bin) 27# Flash `a` (which includes b.bin)
28 28
29``` 29```
30cargo flash --release --features embassy-nrf/nrf52840 --bin a --chip nRF52840_xxAA 30cargo flash --release --bin a --chip nRF52840_xxAA
31``` 31```
diff --git a/examples/boot/build.rs b/examples/boot/nrf/build.rs
index cd1a264c4..cd1a264c4 100644
--- a/examples/boot/build.rs
+++ b/examples/boot/nrf/build.rs
diff --git a/examples/boot/memory.x b/examples/boot/nrf/memory.x
index dfb72103f..dfb72103f 100644
--- a/examples/boot/memory.x
+++ b/examples/boot/nrf/memory.x
diff --git a/examples/boot/src/bin/a.rs b/examples/boot/nrf/src/bin/a.rs
index d18b508cc..caf8140d8 100644
--- a/examples/boot/src/bin/a.rs
+++ b/examples/boot/nrf/src/bin/a.rs
@@ -4,7 +4,7 @@
4#![feature(generic_associated_types)] 4#![feature(generic_associated_types)]
5#![feature(type_alias_impl_trait)] 5#![feature(type_alias_impl_trait)]
6 6
7use embassy_boot_nrf::updater; 7use embassy_boot_nrf::FirmwareUpdater;
8use embassy_nrf::{ 8use embassy_nrf::{
9 gpio::{Input, Pull}, 9 gpio::{Input, Pull},
10 gpio::{Level, Output, OutputDrive}, 10 gpio::{Level, Output, OutputDrive},
@@ -26,10 +26,10 @@ async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
26 let nvmc = Nvmc::new(p.NVMC); 26 let nvmc = Nvmc::new(p.NVMC);
27 let mut nvmc = BlockingAsync::new(nvmc); 27 let mut nvmc = BlockingAsync::new(nvmc);
28 28
29 let mut updater = FirmwareUpdater::default();
29 loop { 30 loop {
30 button.wait_for_any_edge().await; 31 button.wait_for_any_edge().await;
31 if button.is_low() { 32 if button.is_low() {
32 let mut updater = updater::new();
33 let mut offset = 0; 33 let mut offset = 0;
34 for chunk in APP_B.chunks(4096) { 34 for chunk in APP_B.chunks(4096) {
35 let mut buf: [u8; 4096] = [0; 4096]; 35 let mut buf: [u8; 4096] = [0; 4096];
diff --git a/examples/boot/src/bin/b.rs b/examples/boot/nrf/src/bin/b.rs
index 18bb6330c..18bb6330c 100644
--- a/examples/boot/src/bin/b.rs
+++ b/examples/boot/nrf/src/bin/b.rs
diff --git a/examples/boot/stm32l0/.cargo/config.toml b/examples/boot/stm32l0/.cargo/config.toml
new file mode 100644
index 000000000..840faa62e
--- /dev/null
+++ b/examples/boot/stm32l0/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32L072CZTx"
4
5[build]
6target = "thumbv6m-none-eabi"
diff --git a/examples/boot/stm32l0/Cargo.toml b/examples/boot/stm32l0/Cargo.toml
new file mode 100644
index 000000000..5cb1add5b
--- /dev/null
+++ b/examples/boot/stm32l0/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2018"
4name = "embassy-boot-stm32l0-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32", features = ["thumbv6"] }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32l0/README.md b/examples/boot/stm32l0/README.md
new file mode 100644
index 000000000..b498fdc2d
--- /dev/null
+++ b/examples/boot/stm32l0/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32L0 demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32l072cz,thumbv6 --chip STM32L072CZTx
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32L072CZTx
29```
diff --git a/examples/boot/stm32l0/build.rs b/examples/boot/stm32l0/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32l0/build.rs
@@ -0,0 +1,37 @@
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
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.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 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32l0/memory.x b/examples/boot/stm32l0/memory.x
new file mode 100644
index 000000000..d0d2bd7bb
--- /dev/null
+++ b/examples/boot/stm32l0/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
5 BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
6 FLASH : ORIGIN = 0x08008000, LENGTH = 32K
7 DFU : ORIGIN = 0x08010000, LENGTH = 36K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 16K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
13
14__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
diff --git a/examples/boot/stm32l0/src/bin/a.rs b/examples/boot/stm32l0/src/bin/a.rs
new file mode 100644
index 000000000..7b9000c91
--- /dev/null
+++ b/examples/boot/stm32l0/src/bin/a.rs
@@ -0,0 +1,48 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::time::{Duration, Timer};
6use embassy_boot_stm32::FirmwareUpdater;
7use embassy_stm32::exti::ExtiInput;
8use embassy_stm32::flash::Flash;
9use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
10use embassy_stm32::Peripherals;
11use embassy_traits::adapter::BlockingAsync;
12use panic_reset as _;
13
14#[cfg(feature = "defmt-rtt")]
15use defmt_rtt::*;
16
17static APP_B: &[u8] = include_bytes!("../../b.bin");
18
19#[embassy::main]
20async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
21 let flash = Flash::unlock(p.FLASH);
22 let mut flash = BlockingAsync::new(flash);
23
24 let button = Input::new(p.PB2, Pull::Up);
25 let mut button = ExtiInput::new(button, p.EXTI2);
26
27 let mut led = Output::new(p.PB5, Level::Low, Speed::Low);
28
29 led.set_high();
30
31 let mut updater = FirmwareUpdater::default();
32 button.wait_for_falling_edge().await;
33 let mut offset = 0;
34 for chunk in APP_B.chunks(128) {
35 let mut buf: [u8; 128] = [0; 128];
36 buf[..chunk.len()].copy_from_slice(chunk);
37 updater
38 .write_firmware(offset, &buf, &mut flash, 128)
39 .await
40 .unwrap();
41 offset += chunk.len();
42 }
43
44 updater.mark_update(&mut flash).await.unwrap();
45 led.set_low();
46 Timer::after(Duration::from_secs(1)).await;
47 cortex_m::peripheral::SCB::sys_reset();
48}
diff --git a/examples/boot/stm32l0/src/bin/b.rs b/examples/boot/stm32l0/src/bin/b.rs
new file mode 100644
index 000000000..ed774fd70
--- /dev/null
+++ b/examples/boot/stm32l0/src/bin/b.rs
@@ -0,0 +1,25 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 let mut led = Output::new(p.PB6, Level::High, Speed::Low);
17
18 loop {
19 led.set_high();
20 Timer::after(Duration::from_millis(500)).await;
21
22 led.set_low();
23 Timer::after(Duration::from_millis(500)).await;
24 }
25}
diff --git a/examples/boot/stm32l1/.cargo/config.toml b/examples/boot/stm32l1/.cargo/config.toml
new file mode 100644
index 000000000..04985720b
--- /dev/null
+++ b/examples/boot/stm32l1/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32L151CBxxA"
4
5[build]
6target = "thumbv7m-none-eabi"
diff --git a/examples/boot/stm32l1/Cargo.toml b/examples/boot/stm32l1/Cargo.toml
new file mode 100644
index 000000000..9f97462f6
--- /dev/null
+++ b/examples/boot/stm32l1/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2018"
4name = "embassy-boot-stm32l1-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32l1/README.md b/examples/boot/stm32l1/README.md
new file mode 100644
index 000000000..0d4accbff
--- /dev/null
+++ b/examples/boot/stm32l1/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32L1 demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32l151cb-a --chip STM32L151CBxxA
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32L151CBxxA
29```
diff --git a/examples/boot/stm32l1/build.rs b/examples/boot/stm32l1/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32l1/build.rs
@@ -0,0 +1,37 @@
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
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.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 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32l1/memory.x b/examples/boot/stm32l1/memory.x
new file mode 100644
index 000000000..d0d2bd7bb
--- /dev/null
+++ b/examples/boot/stm32l1/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
5 BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
6 FLASH : ORIGIN = 0x08008000, LENGTH = 32K
7 DFU : ORIGIN = 0x08010000, LENGTH = 36K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 16K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
13
14__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
diff --git a/examples/boot/stm32l1/src/bin/a.rs b/examples/boot/stm32l1/src/bin/a.rs
new file mode 100644
index 000000000..7b9000c91
--- /dev/null
+++ b/examples/boot/stm32l1/src/bin/a.rs
@@ -0,0 +1,48 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::time::{Duration, Timer};
6use embassy_boot_stm32::FirmwareUpdater;
7use embassy_stm32::exti::ExtiInput;
8use embassy_stm32::flash::Flash;
9use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
10use embassy_stm32::Peripherals;
11use embassy_traits::adapter::BlockingAsync;
12use panic_reset as _;
13
14#[cfg(feature = "defmt-rtt")]
15use defmt_rtt::*;
16
17static APP_B: &[u8] = include_bytes!("../../b.bin");
18
19#[embassy::main]
20async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
21 let flash = Flash::unlock(p.FLASH);
22 let mut flash = BlockingAsync::new(flash);
23
24 let button = Input::new(p.PB2, Pull::Up);
25 let mut button = ExtiInput::new(button, p.EXTI2);
26
27 let mut led = Output::new(p.PB5, Level::Low, Speed::Low);
28
29 led.set_high();
30
31 let mut updater = FirmwareUpdater::default();
32 button.wait_for_falling_edge().await;
33 let mut offset = 0;
34 for chunk in APP_B.chunks(128) {
35 let mut buf: [u8; 128] = [0; 128];
36 buf[..chunk.len()].copy_from_slice(chunk);
37 updater
38 .write_firmware(offset, &buf, &mut flash, 128)
39 .await
40 .unwrap();
41 offset += chunk.len();
42 }
43
44 updater.mark_update(&mut flash).await.unwrap();
45 led.set_low();
46 Timer::after(Duration::from_secs(1)).await;
47 cortex_m::peripheral::SCB::sys_reset();
48}
diff --git a/examples/boot/stm32l1/src/bin/b.rs b/examples/boot/stm32l1/src/bin/b.rs
new file mode 100644
index 000000000..ed774fd70
--- /dev/null
+++ b/examples/boot/stm32l1/src/bin/b.rs
@@ -0,0 +1,25 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 let mut led = Output::new(p.PB6, Level::High, Speed::Low);
17
18 loop {
19 led.set_high();
20 Timer::after(Duration::from_millis(500)).await;
21
22 led.set_low();
23 Timer::after(Duration::from_millis(500)).await;
24 }
25}
diff --git a/examples/boot/stm32l4/.cargo/config.toml b/examples/boot/stm32l4/.cargo/config.toml
new file mode 100644
index 000000000..7b6c4c0ac
--- /dev/null
+++ b/examples/boot/stm32l4/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32L475VG"
4
5[build]
6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/stm32l4/Cargo.toml b/examples/boot/stm32l4/Cargo.toml
new file mode 100644
index 000000000..53424a666
--- /dev/null
+++ b/examples/boot/stm32l4/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2018"
4name = "embassy-boot-stm32l4-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32l4/README.md b/examples/boot/stm32l4/README.md
new file mode 100644
index 000000000..8966c2fb5
--- /dev/null
+++ b/examples/boot/stm32l4/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32L4 demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32l475vg --chip STM32L475VG
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32L475VG
29```
diff --git a/examples/boot/stm32l4/build.rs b/examples/boot/stm32l4/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32l4/build.rs
@@ -0,0 +1,37 @@
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
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.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 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32l4/memory.x b/examples/boot/stm32l4/memory.x
new file mode 100644
index 000000000..14b2a2c9f
--- /dev/null
+++ b/examples/boot/stm32l4/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
5 BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
6 FLASH : ORIGIN = 0x08008000, LENGTH = 32K
7 DFU : ORIGIN = 0x08010000, LENGTH = 36K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
13
14__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
diff --git a/examples/boot/stm32l4/src/bin/a.rs b/examples/boot/stm32l4/src/bin/a.rs
new file mode 100644
index 000000000..a5a9e2302
--- /dev/null
+++ b/examples/boot/stm32l4/src/bin/a.rs
@@ -0,0 +1,44 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy_boot_stm32::FirmwareUpdater;
6use embassy_stm32::exti::ExtiInput;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
9use embassy_stm32::Peripherals;
10use embassy_traits::adapter::BlockingAsync;
11use panic_reset as _;
12
13#[cfg(feature = "defmt-rtt")]
14use defmt_rtt::*;
15
16static APP_B: &[u8] = include_bytes!("../../b.bin");
17
18#[embassy::main]
19async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
20 let flash = Flash::unlock(p.FLASH);
21 let mut flash = BlockingAsync::new(flash);
22
23 let button = Input::new(p.PC13, Pull::Up);
24 let mut button = ExtiInput::new(button, p.EXTI13);
25
26 let mut led = Output::new(p.PB14, Level::Low, Speed::Low);
27 led.set_high();
28
29 let mut updater = FirmwareUpdater::default();
30 button.wait_for_falling_edge().await;
31 let mut offset = 0;
32 for chunk in APP_B.chunks(2048) {
33 let mut buf: [u8; 2048] = [0; 2048];
34 buf[..chunk.len()].copy_from_slice(chunk);
35 updater
36 .write_firmware(offset, &buf, &mut flash, 2048)
37 .await
38 .unwrap();
39 offset += chunk.len();
40 }
41 updater.mark_update(&mut flash).await.unwrap();
42 led.set_low();
43 cortex_m::peripheral::SCB::sys_reset();
44}
diff --git a/examples/boot/stm32l4/src/bin/b.rs b/examples/boot/stm32l4/src/bin/b.rs
new file mode 100644
index 000000000..814275988
--- /dev/null
+++ b/examples/boot/stm32l4/src/bin/b.rs
@@ -0,0 +1,25 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 let mut led = Output::new(p.PA5, Level::High, Speed::Low);
17
18 loop {
19 led.set_high();
20 Timer::after(Duration::from_millis(500)).await;
21
22 led.set_low();
23 Timer::after(Duration::from_millis(500)).await;
24 }
25}
diff --git a/examples/boot/stm32wl/.cargo/config.toml b/examples/boot/stm32wl/.cargo/config.toml
new file mode 100644
index 000000000..60076e06b
--- /dev/null
+++ b/examples/boot/stm32wl/.cargo/config.toml
@@ -0,0 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-run --list-chips`
3runner = "probe-run --chip STM32WLE5JCIx"
4
5[build]
6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/stm32wl/Cargo.toml b/examples/boot/stm32wl/Cargo.toml
new file mode 100644
index 000000000..fb64886e6
--- /dev/null
+++ b/examples/boot/stm32wl/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2authors = ["Ulf Lilleengen <[email protected]>"]
3edition = "2018"
4name = "embassy-boot-stm32wl-examples"
5version = "0.1.0"
6
7[dependencies]
8embassy = { version = "0.1.0", path = "../../../embassy", features = ["nightly"] }
9embassy-stm32 = { version = "0.1.0", path = "../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] }
10embassy-boot-stm32 = { version = "0.1.0", path = "../../../embassy-boot/stm32" }
11embassy-traits = { version = "0.1.0", path = "../../../embassy-traits" }
12
13defmt = { version = "0.3", optional = true }
14defmt-rtt = { version = "0.3", optional = true }
15panic-reset = { version = "0.1.1" }
16embedded-hal = { version = "0.2.6" }
17
18cortex-m = "0.7.3"
19cortex-m-rt = "0.7.0"
20
21[features]
22defmt = [
23 "dep:defmt",
24 "embassy-stm32/defmt",
25 "embassy-boot-stm32/defmt",
26]
diff --git a/examples/boot/stm32wl/README.md b/examples/boot/stm32wl/README.md
new file mode 100644
index 000000000..edcdacf91
--- /dev/null
+++ b/examples/boot/stm32wl/README.md
@@ -0,0 +1,29 @@
1# Examples using bootloader
2
3Example for STM32WL demonstrating the bootloader. The example consists of application binaries, 'a'
4which allows you to press a button to start the DFU process, and 'b' which is the updated
5application.
6
7
8## Prerequisites
9
10* `cargo-binutils`
11* `cargo-flash`
12* `embassy-boot-stm32`
13
14## Usage
15
16```
17# Flash bootloader
18cargo flash --manifest-path ../../../embassy-boot/stm32/Cargo.toml --release --features embassy-stm32/stm32wl55jc-cm4 --chip STM32WLE5JCIx
19# Build 'b'
20cargo build --release --bin b
21# Generate binary for 'b'
22cargo objcopy --release --bin b -- -O binary b.bin
23```
24
25# Flash `a` (which includes b.bin)
26
27```
28cargo flash --release --bin a --chip STM32WLE5JCIx
29```
diff --git a/examples/boot/stm32wl/build.rs b/examples/boot/stm32wl/build.rs
new file mode 100644
index 000000000..e1da69328
--- /dev/null
+++ b/examples/boot/stm32wl/build.rs
@@ -0,0 +1,37 @@
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
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.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 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 if env::var("CARGO_FEATURE_DEFMT").is_ok() {
35 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
36 }
37}
diff --git a/examples/boot/stm32wl/memory.x b/examples/boot/stm32wl/memory.x
new file mode 100644
index 000000000..14b2a2c9f
--- /dev/null
+++ b/examples/boot/stm32wl/memory.x
@@ -0,0 +1,15 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 BOOTLOADER : ORIGIN = 0x08000000, LENGTH = 24K
5 BOOTLOADER_STATE : ORIGIN = 0x08006000, LENGTH = 4K
6 FLASH : ORIGIN = 0x08008000, LENGTH = 32K
7 DFU : ORIGIN = 0x08010000, LENGTH = 36K
8 RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 32K
9}
10
11__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
12__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(BOOTLOADER);
13
14__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(BOOTLOADER);
15__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(BOOTLOADER);
diff --git a/examples/boot/stm32wl/src/bin/a.rs b/examples/boot/stm32wl/src/bin/a.rs
new file mode 100644
index 000000000..d01a72f2d
--- /dev/null
+++ b/examples/boot/stm32wl/src/bin/a.rs
@@ -0,0 +1,47 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy_boot_stm32::FirmwareUpdater;
6use embassy_stm32::exti::ExtiInput;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
9use embassy_stm32::Peripherals;
10use embassy_traits::adapter::BlockingAsync;
11use panic_reset as _;
12
13#[cfg(feature = "defmt-rtt")]
14use defmt_rtt::*;
15
16static APP_B: &[u8] = include_bytes!("../../b.bin");
17
18#[embassy::main]
19async fn main(_s: embassy::executor::Spawner, p: Peripherals) {
20 let flash = Flash::unlock(p.FLASH);
21 let mut flash = BlockingAsync::new(flash);
22
23 let button = Input::new(p.PA0, Pull::Up);
24 let mut button = ExtiInput::new(button, p.EXTI0);
25
26 let mut led = Output::new(p.PB9, Level::Low, Speed::Low);
27 led.set_high();
28
29 let mut updater = FirmwareUpdater::default();
30 button.wait_for_falling_edge().await;
31 //defmt::info!("Starting update");
32 let mut offset = 0;
33 for chunk in APP_B.chunks(2048) {
34 let mut buf: [u8; 2048] = [0; 2048];
35 buf[..chunk.len()].copy_from_slice(chunk);
36 // defmt::info!("Writing chunk at 0x{:x}", offset);
37 updater
38 .write_firmware(offset, &buf, &mut flash, 2048)
39 .await
40 .unwrap();
41 offset += chunk.len();
42 }
43 updater.mark_update(&mut flash).await.unwrap();
44 //defmt::info!("Marked as updated");
45 led.set_low();
46 cortex_m::peripheral::SCB::sys_reset();
47}
diff --git a/examples/boot/stm32wl/src/bin/b.rs b/examples/boot/stm32wl/src/bin/b.rs
new file mode 100644
index 000000000..ffe15b661
--- /dev/null
+++ b/examples/boot/stm32wl/src/bin/b.rs
@@ -0,0 +1,25 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use embassy::executor::Spawner;
6use embassy::time::{Duration, Timer};
7use embassy_stm32::gpio::{Level, Output, Speed};
8use embassy_stm32::Peripherals;
9use panic_reset as _;
10
11#[cfg(feature = "defmt-rtt")]
12use defmt_rtt::*;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 let mut led = Output::new(p.PB15, Level::High, Speed::Low);
17
18 loop {
19 led.set_high();
20 Timer::after(Duration::from_millis(500)).await;
21
22 led.set_low();
23 Timer::after(Duration::from_millis(500)).await;
24 }
25}
diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml
index 0b21f6742..1cdb2f374 100644
--- a/examples/stm32l0/Cargo.toml
+++ b/examples/stm32l0/Cargo.toml
@@ -21,6 +21,8 @@ lorawan = { version = "0.7.1", default-features = false, features = ["default-cr
21defmt = "0.3" 21defmt = "0.3"
22defmt-rtt = "0.3" 22defmt-rtt = "0.3"
23 23
24embedded-storage = "0.3.0"
25
24cortex-m = "0.7.3" 26cortex-m = "0.7.3"
25cortex-m-rt = "0.7.0" 27cortex-m-rt = "0.7.0"
26panic-probe = { version = "0.3", features = ["print-defmt"] } 28panic-probe = { version = "0.3", features = ["print-defmt"] }
diff --git a/examples/stm32l0/src/bin/flash.rs b/examples/stm32l0/src/bin/flash.rs
new file mode 100644
index 000000000..c2ccb5b69
--- /dev/null
+++ b/examples/stm32l0/src/bin/flash.rs
@@ -0,0 +1,43 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::Peripherals;
9use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
10
11use defmt_rtt as _; // global logger
12use panic_probe as _;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 info!("Hello Flash!");
17
18 const ADDR: u32 = 0x26000;
19
20 let mut f = Flash::unlock(p.FLASH);
21
22 info!("Reading...");
23 let mut buf = [0u8; 8];
24 unwrap!(f.read(ADDR, &mut buf));
25 info!("Read: {=[u8]:x}", buf);
26
27 info!("Erasing...");
28 unwrap!(f.erase(ADDR, ADDR + 128));
29
30 info!("Reading...");
31 let mut buf = [0u8; 8];
32 unwrap!(f.read(ADDR, &mut buf));
33 info!("Read after erase: {=[u8]:x}", buf);
34
35 info!("Writing...");
36 unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));
37
38 info!("Reading...");
39 let mut buf = [0u8; 8];
40 unwrap!(f.read(ADDR, &mut buf));
41 info!("Read: {=[u8]:x}", buf);
42 assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);
43}
diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml
index a7a6c228e..ce6b07729 100644
--- a/examples/stm32l1/Cargo.toml
+++ b/examples/stm32l1/Cargo.toml
@@ -18,3 +18,4 @@ embedded-hal = "0.2.6"
18panic-probe = { version = "0.3", features = ["print-defmt"] } 18panic-probe = { version = "0.3", features = ["print-defmt"] }
19futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 19futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
20heapless = { version = "0.7.5", default-features = false } 20heapless = { version = "0.7.5", default-features = false }
21embedded-storage = "0.3.0"
diff --git a/examples/stm32l1/src/bin/flash.rs b/examples/stm32l1/src/bin/flash.rs
new file mode 100644
index 000000000..eea838cba
--- /dev/null
+++ b/examples/stm32l1/src/bin/flash.rs
@@ -0,0 +1,43 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::Peripherals;
9use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
10
11use defmt_rtt as _; // global logger
12use panic_probe as _;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 info!("Hello Flash!");
17
18 const ADDR: u32 = 0x26000;
19
20 let mut f = Flash::unlock(p.FLASH);
21
22 info!("Reading...");
23 let mut buf = [0u8; 8];
24 unwrap!(f.read(ADDR, &mut buf));
25 info!("Read: {=[u8]:x}", buf);
26
27 info!("Erasing...");
28 unwrap!(f.erase(ADDR, ADDR + 256));
29
30 info!("Reading...");
31 let mut buf = [0u8; 8];
32 unwrap!(f.read(ADDR, &mut buf));
33 info!("Read after erase: {=[u8]:x}", buf);
34
35 info!("Writing...");
36 unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));
37
38 info!("Reading...");
39 let mut buf = [0u8; 8];
40 unwrap!(f.read(ADDR, &mut buf));
41 info!("Read: {=[u8]:x}", buf);
42 assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);
43}
diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml
index 919104b01..01f317039 100644
--- a/examples/stm32wl/Cargo.toml
+++ b/examples/stm32wl/Cargo.toml
@@ -8,7 +8,7 @@ resolver = "2"
8[dependencies] 8[dependencies]
9embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } 9embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
10embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] } 10embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "subghz", "unstable-pac", "exti"] }
11embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time"] } 11embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] }
12 12
13lorawan-device = { version = "0.7.1", default-features = false, features = ["async"] } 13lorawan-device = { version = "0.7.1", default-features = false, features = ["async"] }
14lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } 14lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] }
@@ -19,6 +19,7 @@ defmt-rtt = "0.3"
19cortex-m = "0.7.3" 19cortex-m = "0.7.3"
20cortex-m-rt = "0.7.0" 20cortex-m-rt = "0.7.0"
21embedded-hal = "0.2.6" 21embedded-hal = "0.2.6"
22embedded-storage = "0.3.0"
22panic-probe = { version = "0.3", features = ["print-defmt"] } 23panic-probe = { version = "0.3", features = ["print-defmt"] }
23futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 24futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
24heapless = { version = "0.7.5", default-features = false } 25heapless = { version = "0.7.5", default-features = false }
diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs
new file mode 100644
index 000000000..f84818224
--- /dev/null
+++ b/examples/stm32wl/src/bin/flash.rs
@@ -0,0 +1,43 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy_stm32::flash::Flash;
8use embassy_stm32::Peripherals;
9use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
10
11use defmt_rtt as _; // global logger
12use panic_probe as _;
13
14#[embassy::main]
15async fn main(_spawner: Spawner, p: Peripherals) {
16 info!("Hello Flash!");
17
18 const ADDR: u32 = 0x36000;
19
20 let mut f = Flash::unlock(p.FLASH);
21
22 info!("Reading...");
23 let mut buf = [0u8; 8];
24 unwrap!(f.read(ADDR, &mut buf));
25 info!("Read: {=[u8]:x}", buf);
26
27 info!("Erasing...");
28 unwrap!(f.erase(ADDR, ADDR + 2048));
29
30 info!("Reading...");
31 let mut buf = [0u8; 8];
32 unwrap!(f.read(ADDR, &mut buf));
33 info!("Read: {=[u8]:x}", buf);
34
35 info!("Writing...");
36 unwrap!(f.write(ADDR, &[1, 2, 3, 4, 5, 6, 7, 8]));
37
38 info!("Reading...");
39 let mut buf = [0u8; 8];
40 unwrap!(f.read(ADDR, &mut buf));
41 info!("Read: {=[u8]:x}", buf);
42 assert_eq!(&buf[..], &[1, 2, 3, 4, 5, 6, 7, 8]);
43}
diff --git a/stm32-data b/stm32-data
Subproject 472ee98e8fdb11312392e47b16568c9d02fe654 Subproject b71707e525e336f0afb9a74f3c436d3dd540ffc
diff --git a/stm32-metapac-gen/src/data.rs b/stm32-metapac-gen/src/data.rs
index a74c60ac0..17eccfe9a 100644
--- a/stm32-metapac-gen/src/data.rs
+++ b/stm32-metapac-gen/src/data.rs
@@ -16,6 +16,14 @@ pub struct MemoryRegion {
16 pub kind: MemoryRegionKind, 16 pub kind: MemoryRegionKind,
17 pub address: u32, 17 pub address: u32,
18 pub size: u32, 18 pub size: u32,
19 pub settings: Option<FlashSettings>,
20}
21
22#[derive(Debug, Eq, PartialEq, Clone, Deserialize)]
23pub struct FlashSettings {
24 pub erase_size: u32,
25 pub write_size: u32,
26 pub erase_value: u8,
19} 27}
20 28
21#[derive(Debug, Eq, PartialEq, Clone, Deserialize)] 29#[derive(Debug, Eq, PartialEq, Clone, Deserialize)]
diff --git a/stm32-metapac-gen/src/lib.rs b/stm32-metapac-gen/src/lib.rs
index c959e1a61..f5d61ca31 100644
--- a/stm32-metapac-gen/src/lib.rs
+++ b/stm32-metapac-gen/src/lib.rs
@@ -129,6 +129,40 @@ impl Gen {
129 ) 129 )
130 .unwrap(); 130 .unwrap();
131 131
132 let flash = chip.memory.iter().find(|r| r.name == "BANK_1").unwrap();
133 write!(
134 &mut extra,
135 "pub const FLASH_BASE: usize = {};\n",
136 flash.address,
137 )
138 .unwrap();
139 write!(
140 &mut extra,
141 "pub const FLASH_SIZE: usize = {};\n",
142 flash.size,
143 )
144 .unwrap();
145 if let Some(settings) = &flash.settings {
146 write!(
147 &mut extra,
148 "pub const ERASE_SIZE: usize = {};\n",
149 settings.erase_size,
150 )
151 .unwrap();
152 write!(
153 &mut extra,
154 "pub const WRITE_SIZE: usize = {};\n",
155 settings.write_size,
156 )
157 .unwrap();
158 write!(
159 &mut extra,
160 "pub const ERASE_VALUE: u8 = {};\n",
161 settings.erase_value,
162 )
163 .unwrap();
164 }
165
132 // Cleanups! 166 // Cleanups!
133 transform::sort::Sort {}.run(&mut ir).unwrap(); 167 transform::sort::Sort {}.run(&mut ir).unwrap();
134 transform::Sanitize {}.run(&mut ir).unwrap(); 168 transform::Sanitize {}.run(&mut ir).unwrap();
diff --git a/stm32-metapac/src/metadata.rs b/stm32-metapac/src/metadata.rs
index 23b759f6c..d05830e94 100644
--- a/stm32-metapac/src/metadata.rs
+++ b/stm32-metapac/src/metadata.rs
@@ -15,6 +15,14 @@ pub struct MemoryRegion {
15 pub kind: MemoryRegionKind, 15 pub kind: MemoryRegionKind,
16 pub address: u32, 16 pub address: u32,
17 pub size: u32, 17 pub size: u32,
18 pub settings: Option<FlashSettings>,
19}
20
21#[derive(Debug, Eq, PartialEq, Clone)]
22pub struct FlashSettings {
23 pub erase_size: u32,
24 pub write_size: u32,
25 pub erase_value: u8,
18} 26}
19 27
20#[derive(Debug, Eq, PartialEq, Clone)] 28#[derive(Debug, Eq, PartialEq, Clone)]