mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-22 06:42:32 +00:00
Merge branch 'embassy-rs:main' into reset-at-after
This commit is contained in:
commit
7efe8e0005
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -15,6 +15,8 @@
|
||||
*.x text
|
||||
*.yml text
|
||||
|
||||
.vscode/*.json linguist-language=JSON-with-Comments
|
||||
|
||||
*.raw binary
|
||||
*.bin binary
|
||||
*.png binary
|
||||
|
@ -7,6 +7,7 @@ set -euo pipefail
|
||||
export RUSTUP_HOME=/ci/cache/rustup
|
||||
export CARGO_HOME=/ci/cache/cargo
|
||||
export CARGO_TARGET_DIR=/ci/cache/target
|
||||
mv rust-toolchain-nightly.toml rust-toolchain.toml
|
||||
|
||||
# needed for "dumb HTTP" transport support
|
||||
# used when pointing stm32-metapac to a CI-built one.
|
||||
@ -21,9 +22,7 @@ fi
|
||||
hashtime restore /ci/cache/filetime.json || true
|
||||
hashtime save /ci/cache/filetime.json
|
||||
|
||||
sed -i 's/channel.*/channel = "beta"/g' rust-toolchain.toml
|
||||
|
||||
./ci_stable.sh
|
||||
./ci-nightly.sh
|
||||
|
||||
# Save lockfiles
|
||||
echo Saving lockfiles...
|
35
.github/ci/doc.sh
vendored
35
.github/ci/doc.sh
vendored
@ -1,13 +1,14 @@
|
||||
#!/bin/bash
|
||||
## on push branch=main
|
||||
|
||||
set -euo pipefail
|
||||
set -euxo pipefail
|
||||
|
||||
export RUSTUP_HOME=/ci/cache/rustup
|
||||
export CARGO_HOME=/ci/cache/cargo
|
||||
export CARGO_TARGET_DIR=/ci/cache/target
|
||||
export BUILDER_THREADS=4
|
||||
export BUILDER_COMPRESS=true
|
||||
mv rust-toolchain-nightly.toml rust-toolchain.toml
|
||||
|
||||
# force rustup to download the toolchain before starting building.
|
||||
# Otherwise, the docs builder is running multiple instances of cargo rustdoc concurrently.
|
||||
@ -15,29 +16,37 @@ export BUILDER_COMPRESS=true
|
||||
# which makes rustup very sad
|
||||
rustc --version > /dev/null
|
||||
|
||||
docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup
|
||||
docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup
|
||||
docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup
|
||||
docserver-builder -i ./embassy-boot/stm32 -o webroot/crates/embassy-boot-stm32/git.zup
|
||||
docserver-builder -i ./embassy-boot -o webroot/crates/embassy-boot/git.zup
|
||||
docserver-builder -i ./embassy-boot-nrf -o webroot/crates/embassy-boot-nrf/git.zup
|
||||
docserver-builder -i ./embassy-boot-rp -o webroot/crates/embassy-boot-rp/git.zup
|
||||
docserver-builder -i ./embassy-boot-stm32 -o webroot/crates/embassy-boot-stm32/git.zup
|
||||
docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup
|
||||
docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup
|
||||
docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup
|
||||
docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup
|
||||
docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup
|
||||
docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup
|
||||
docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup
|
||||
docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup
|
||||
docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup
|
||||
docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup
|
||||
docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup
|
||||
docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup
|
||||
docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup
|
||||
docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup
|
||||
docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup
|
||||
docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
|
||||
|
||||
docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup
|
||||
docserver-builder -i ./embassy-time-driver -o webroot/crates/embassy-time-driver/git.zup
|
||||
docserver-builder -i ./embassy-time-queue-driver -o webroot/crates/embassy-time-queue-driver/git.zup
|
||||
|
||||
docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup
|
||||
docserver-builder -i ./embassy-usb-dfu -o webroot/crates/embassy-usb-dfu/git.zup
|
||||
docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup
|
||||
docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup
|
||||
|
||||
docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup
|
||||
docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup
|
||||
docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup
|
||||
docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup
|
||||
docserver-builder -i ./embassy-net-ppp -o webroot/crates/embassy-net-ppp/git.zup
|
||||
docserver-builder -i ./embassy-net-tuntap -o webroot/crates/embassy-net-tuntap/git.zup
|
||||
docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup
|
||||
docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup
|
||||
docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
|
||||
docserver-builder -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup
|
||||
|
||||
export KUBECONFIG=/ci/secrets/kubeconfig.yml
|
||||
|
12
.github/ci/rustfmt.sh
vendored
Executable file
12
.github/ci/rustfmt.sh
vendored
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
## on push branch~=gh-readonly-queue/main/.*
|
||||
## on pull_request
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
export RUSTUP_HOME=/ci/cache/rustup
|
||||
export CARGO_HOME=/ci/cache/cargo
|
||||
export CARGO_TARGET_DIR=/ci/cache/target
|
||||
mv rust-toolchain-nightly.toml rust-toolchain.toml
|
||||
|
||||
find . -name '*.rs' -not -path '*target*' | xargs rustfmt --check --skip-children --unstable-features --edition 2021
|
13
.github/ci/test-nightly.sh
vendored
Executable file
13
.github/ci/test-nightly.sh
vendored
Executable file
@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
## on push branch~=gh-readonly-queue/main/.*
|
||||
## on pull_request
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
export RUSTUP_HOME=/ci/cache/rustup
|
||||
export CARGO_HOME=/ci/cache/cargo
|
||||
export CARGO_TARGET_DIR=/ci/cache/target
|
||||
mv rust-toolchain-nightly.toml rust-toolchain.toml
|
||||
|
||||
MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml
|
||||
MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml --features nightly
|
14
.github/ci/test.sh
vendored
14
.github/ci/test.sh
vendored
@ -4,17 +4,19 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml
|
||||
MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml --features nightly
|
||||
export RUSTUP_HOME=/ci/cache/rustup
|
||||
export CARGO_HOME=/ci/cache/cargo
|
||||
export CARGO_TARGET_DIR=/ci/cache/target
|
||||
|
||||
cargo test --manifest-path ./embassy-sync/Cargo.toml
|
||||
cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
|
||||
cargo test --manifest-path ./embassy-hal-internal/Cargo.toml
|
||||
cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue
|
||||
cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue,mock-driver
|
||||
cargo test --manifest-path ./embassy-time-driver/Cargo.toml
|
||||
|
||||
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml
|
||||
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features ed25519-dalek
|
||||
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features ed25519-salty
|
||||
cargo test --manifest-path ./embassy-boot/Cargo.toml
|
||||
cargo test --manifest-path ./embassy-boot/Cargo.toml --features ed25519-dalek
|
||||
cargo test --manifest-path ./embassy-boot/Cargo.toml --features ed25519-salty
|
||||
|
||||
cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --features nrf52840,time-driver-rtc1,gpiote
|
||||
|
||||
|
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
@ -1,5 +1,4 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"[toml]": {
|
||||
"editor.formatOnSave": false
|
||||
},
|
||||
@ -16,13 +15,15 @@
|
||||
"rust-analyzer.cargo.target": "thumbv7em-none-eabi",
|
||||
//"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
|
||||
"rust-analyzer.cargo.features": [
|
||||
// Uncomment if the example has a "nightly" feature.
|
||||
"nightly",
|
||||
"stm32f103c8",
|
||||
"time-driver-any",
|
||||
"unstable-pac",
|
||||
"exti",
|
||||
],
|
||||
"rust-analyzer.linkedProjects": [
|
||||
// Uncomment ONE line for the chip you want to work on.
|
||||
// This makes rust-analyzer work on the example crate and all its dependencies.
|
||||
"examples/nrf52840/Cargo.toml",
|
||||
"embassy-stm32/Cargo.toml",
|
||||
// "examples/nrf52840-rtic/Cargo.toml",
|
||||
// "examples/nrf5340/Cargo.toml",
|
||||
// "examples/nrf-rtos-trace/Cargo.toml",
|
||||
|
@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2019-2022 Embassy project contributors
|
||||
Copyright (c) Embassy project contributors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2019-2022 Embassy project contributors
|
||||
Copyright (c) Embassy project contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
|
23
README.md
23
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
Embassy is the next-generation framework for embedded applications. Write safe, correct and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries.
|
||||
|
||||
## <a href="https://embassy.dev/dev/index.html">Documentation</a> - <a href="https://docs.embassy.dev/">API reference</a> - <a href="https://embassy.dev/">Website</a> - <a href="https://matrix.to/#/#embassy-rs:matrix.org">Chat</a>
|
||||
## <a href="https://embassy.dev/book/dev/index.html">Documentation</a> - <a href="https://docs.embassy.dev/">API reference</a> - <a href="https://embassy.dev/">Website</a> - <a href="https://matrix.to/#/#embassy-rs:matrix.org">Chat</a>
|
||||
## Rust + async ❤️ embedded
|
||||
|
||||
The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system.
|
||||
@ -35,8 +35,7 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac
|
||||
The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
|
||||
The <a href="https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan">embassy-stm32-wpan</a> crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers.
|
||||
|
||||
- **LoRa** -
|
||||
<a href="hthttps://github.com/lora-rs/lora-rs">The lora-rs project</a> provides an async LoRa and LoRaWAN stack that works well on Embassy.
|
||||
- **LoRa** - The <a href="https://github.com/lora-rs/lora-rs">lora-rs</a> project provides an async LoRa and LoRaWAN stack that works well on Embassy.
|
||||
|
||||
- **USB** -
|
||||
<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
||||
@ -137,14 +136,8 @@ please refer to the `.vscode/settings.json` file's `rust-analyzer.linkedProjects
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
||||
|
||||
Several features require nightly:
|
||||
|
||||
- The `#[embassy_executor::main]` and `#[embassy_executor::task]` attribute macros.
|
||||
- Async traits
|
||||
|
||||
These are enabled by activating the `nightly` Cargo feature. If you do so, Embassy is guaranteed to compile on the exact nightly version specified in `rust-toolchain.toml`. It might compile with older or newer nightly versions, but that may change in any new patch release.
|
||||
Embassy is guaranteed to compile on stable Rust 1.75 and up. It *might*
|
||||
compile with older versions but that may change in any new patch release.
|
||||
|
||||
## Why the name?
|
||||
|
||||
@ -152,7 +145,7 @@ EMBedded ASYnc! :)
|
||||
|
||||
## License
|
||||
|
||||
This work is licensed under either of
|
||||
Embassy is licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||
@ -160,5 +153,11 @@ This work is licensed under either of
|
||||
|
||||
at your option.
|
||||
|
||||
## Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
|
||||
[1]: https://github.com/embassy-rs/embassy/wiki/Getting-Started
|
||||
[2]: https://github.com/embassy-rs/embassy/wiki/Running-the-Examples
|
||||
|
33
ci-nightly.sh
Executable file
33
ci-nightly.sh
Executable file
@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
export RUSTFLAGS=-Dwarnings
|
||||
export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
|
||||
if [[ -z "${CARGO_TARGET_DIR}" ]]; then
|
||||
export CARGO_TARGET_DIR=target_ci
|
||||
fi
|
||||
|
||||
cargo batch \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path examples/nrf52840-rtic/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840-rtic \
|
||||
|
||||
cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-unknown-gnu-atmega328 -Z build-std=core,alloc --features nightly,arch-avr,avr-device/atmega328p
|
||||
cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-unknown-gnu-atmega328 -Z build-std=core,alloc --features nightly,arch-avr,integrated-timers,avr-device/atmega328p
|
||||
|
159
ci.sh
159
ci.sh
@ -15,29 +15,30 @@ if [ $TARGET = "x86_64-unknown-linux-gnu" ]; then
|
||||
BUILD_EXTRA="--- build --release --manifest-path examples/std/Cargo.toml --target $TARGET --out-dir out/examples/std"
|
||||
fi
|
||||
|
||||
find . -name '*.rs' -not -path '*target*' | xargs rustfmt --check --skip-children --unstable-features --edition 2021
|
||||
|
||||
# CI intentionally does not use -eabihf on thumbv7em to minimize dep compile time.
|
||||
cargo batch \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,rtos-trace \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,integrated-timers,rtos-trace \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32 \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
||||
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,generic-queue-8,mock-driver \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,igmp,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,dhcpv4-hostname \
|
||||
@ -49,24 +50,34 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52820,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832,gpiote,time-driver-rtc1,reset-pin-as-gpio \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833,gpiote,time-driver-rtc1,nfc-pins-as-gpio \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-s,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-ns,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-s,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-ns,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-net,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,gpiote,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52820,gpiote,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832,gpiote,time,time-driver-rtc1,reset-pin-as-gpio \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833,gpiote,time,time-driver-rtc1,nfc-pins-as-gpio \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-s,gpiote,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-ns,gpiote,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-s,gpiote,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-ns,gpiote,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-net,gpiote,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,log,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,defmt,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features log \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features intrinsics \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features qspi-as-gpio \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,log,time \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,log,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,log,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,defmt,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,time \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,defmt \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,log \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,intrinsics \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,qspi-as-gpio \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time \
|
||||
@ -77,6 +88,16 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f038f6,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030c6,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f058t8,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030r8,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f031k6,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f030rc,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f070f6,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f078vb,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f042g4,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f072c8,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f401ve,defmt,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f405zg,defmt,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f407zg,defmt,exti,time-driver-any \
|
||||
@ -91,38 +112,42 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f417zg,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f423zh,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f427zi,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,embedded-sdmmc,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f437zi,log,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f439zi,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446ze,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f469zi,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f479zi,defmt,exti,time-driver-any,embedded-sdmmc,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f479zi,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f730i8,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h735zg,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h725re,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l431cb,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l422cb,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb15cc,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l041f6,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l051k8,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073cz,defmt,exti,time-driver-any,low-power,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f398ve,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f378cc,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303c8,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f398ve,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f378cc,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g0c1ve,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,low-power,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32wl54jc-cm0p,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wle5jb,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g474pe,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f107vc,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103re,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f100c4,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32h503rb,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32h562ag,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\
|
||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \
|
||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \
|
||||
@ -130,39 +155,40 @@ cargo batch \
|
||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs' \
|
||||
--- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features '' \
|
||||
--- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'overclock' \
|
||||
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
|
||||
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
|
||||
--- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||
--- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
|
||||
--- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
|
||||
--- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
|
||||
--- build --release --manifest-path embassy-boot-rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||
--- build --release --manifest-path embassy-boot-stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
|
||||
--- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \
|
||||
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \
|
||||
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \
|
||||
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml --target thumbv7em-none-eabi \
|
||||
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \
|
||||
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840 \
|
||||
--- build --release --manifest-path examples/nrf52840-rtic/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840-rtic \
|
||||
--- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf5340 \
|
||||
--- build --release --manifest-path examples/nrf9160/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9160 \
|
||||
--- build --release --manifest-path examples/nrf51/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/nrf51 \
|
||||
--- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/rp \
|
||||
--- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32f0 \
|
||||
--- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \
|
||||
--- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \
|
||||
--- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \
|
||||
--- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f334 \
|
||||
--- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f3 \
|
||||
--- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f334 \
|
||||
--- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \
|
||||
--- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \
|
||||
--- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f7 \
|
||||
--- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \
|
||||
--- build --release --manifest-path examples/stm32g0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32g0 \
|
||||
--- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32g4 \
|
||||
--- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h5 \
|
||||
--- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32h5 \
|
||||
--- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \
|
||||
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \
|
||||
--- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \
|
||||
--- build --release --manifest-path examples/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32l4 \
|
||||
--- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \
|
||||
--- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \
|
||||
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \
|
||||
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32wb \
|
||||
--- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32wba \
|
||||
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \
|
||||
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32wl \
|
||||
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --out-dir out/examples/boot/nrf52840 \
|
||||
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --out-dir out/examples/boot/nrf9160 \
|
||||
--- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/rp \
|
||||
@ -172,11 +198,14 @@ cargo batch \
|
||||
--- build --release --manifest-path examples/boot/application/stm32l0/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/stm32l0 \
|
||||
--- build --release --manifest-path examples/boot/application/stm32l1/Cargo.toml --target thumbv7m-none-eabi --features skip-include --out-dir out/examples/boot/stm32l1 \
|
||||
--- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32l4 \
|
||||
--- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --features skip-include --out-dir out/examples/boot/stm32wl \
|
||||
--- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32wl \
|
||||
--- build --release --manifest-path examples/boot/application/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32wb-dfu \
|
||||
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
|
||||
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
|
||||
--- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||
--- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
|
||||
--- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wb55rg \
|
||||
--- build --release --manifest-path examples/boot/bootloader/stm32-dual-bank/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32h747xi-cm7 \
|
||||
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
|
||||
@ -188,10 +217,10 @@ cargo batch \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi --out-dir out/tests/stm32h753zi \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7a3zi --out-dir out/tests/stm32h7a3zi \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/stm32wb55rg \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/stm32h563zi \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/stm32u585ai \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u5a5zj --out-dir out/tests/stm32u5a5zj \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wba52cg --out-dir out/tests/stm32wba52cg \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h563zi --out-dir out/tests/stm32h563zi \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u585ai --out-dir out/tests/stm32u585ai \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5a5zj --out-dir out/tests/stm32u5a5zj \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba52cg --out-dir out/tests/stm32wba52cg \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073rz --out-dir out/tests/stm32l073rz \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l152re --out-dir out/tests/stm32l152re \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4a6zg --out-dir out/tests/stm32l4a6zg \
|
||||
@ -202,8 +231,11 @@ cargo batch \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303ze --out-dir out/tests/stm32f303ze \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l496zg --out-dir out/tests/stm32l496zg \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55jc --out-dir out/tests/stm32wl55jc \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32f091rc --out-dir out/tests/stm32f091rc \
|
||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb --out-dir out/tests/stm32h503rb \
|
||||
--- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
|
||||
--- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
|
||||
--- build --release --manifest-path tests/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
|
||||
--- build --release --manifest-path tests/nrf51422/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/nrf51-dk \
|
||||
--- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \
|
||||
$BUILD_EXTRA
|
||||
|
||||
@ -211,9 +243,6 @@ cargo batch \
|
||||
rm out/tests/stm32wb55rg/wpan_mac
|
||||
rm out/tests/stm32wb55rg/wpan_ble
|
||||
|
||||
# not in CI yet.
|
||||
rm -rf out/tests/stm32f446re
|
||||
|
||||
# unstable, I think it's running out of RAM?
|
||||
rm out/tests/stm32f207zg/eth
|
||||
|
||||
|
77
ci_stable.sh
77
ci_stable.sh
@ -1,77 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
export RUSTFLAGS=-Dwarnings
|
||||
export DEFMT_LOG=trace
|
||||
|
||||
cargo batch \
|
||||
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
|
||||
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
|
||||
--- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||
--- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52820,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52832,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52833,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-s,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf9160-ns,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-s,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-ns,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-net,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,log,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,defmt,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features log \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features qspi-as-gpio \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55cc-cm4,defmt,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55cc-cm4,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \
|
||||
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf52840 --bin raw_spawn \
|
||||
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --no-default-features --out-dir out/examples/stm32l0 --bin raw_spawn \
|
@ -2,6 +2,12 @@
|
||||
name = "cyw43-pio"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "RP2040 PIO SPI implementation for cyw43"
|
||||
keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"]
|
||||
categories = ["embedded", "hardware-support", "no-std", "network-programming", "asynchronous"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/embassy-rs/embassy"
|
||||
documentation = "https://docs.embassy.dev/cyw43-pio"
|
||||
|
||||
[features]
|
||||
# If disabled, SPI runs at 31.25MHz
|
||||
|
3
cyw43-pio/README.md
Normal file
3
cyw43-pio/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# cyw43-pio
|
||||
|
||||
RP2040 PIO driver for the nonstandard half-duplex SPI used in the Pico W. The PIO driver offloads SPI communication with the WiFi chip and improves throughput.
|
@ -1,35 +1,38 @@
|
||||
#![no_std]
|
||||
#![allow(async_fn_in_trait)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use core::slice;
|
||||
|
||||
use cyw43::SpiBusCyw43;
|
||||
use embassy_rp::dma::Channel;
|
||||
use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate};
|
||||
use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine};
|
||||
use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef};
|
||||
use embassy_rp::gpio::{Drive, Level, Output, Pull, SlewRate};
|
||||
use embassy_rp::pio::{instr, Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine};
|
||||
use embassy_rp::{Peripheral, PeripheralRef};
|
||||
use fixed::FixedU32;
|
||||
use pio_proc::pio_asm;
|
||||
|
||||
pub struct PioSpi<'d, CS: Pin, PIO: Instance, const SM: usize, DMA> {
|
||||
cs: Output<'d, CS>,
|
||||
/// SPI comms driven by PIO.
|
||||
pub struct PioSpi<'d, PIO: Instance, const SM: usize, DMA> {
|
||||
cs: Output<'d>,
|
||||
sm: StateMachine<'d, PIO, SM>,
|
||||
irq: Irq<'d, PIO, 0>,
|
||||
dma: PeripheralRef<'d, DMA>,
|
||||
wrap_target: u8,
|
||||
}
|
||||
|
||||
impl<'d, CS, PIO, const SM: usize, DMA> PioSpi<'d, CS, PIO, SM, DMA>
|
||||
impl<'d, PIO, const SM: usize, DMA> PioSpi<'d, PIO, SM, DMA>
|
||||
where
|
||||
DMA: Channel,
|
||||
CS: Pin,
|
||||
PIO: Instance,
|
||||
{
|
||||
/// Create a new instance of PioSpi.
|
||||
pub fn new<DIO, CLK>(
|
||||
common: &mut Common<'d, PIO>,
|
||||
mut sm: StateMachine<'d, PIO, SM>,
|
||||
irq: Irq<'d, PIO, 0>,
|
||||
cs: Output<'d, CS>,
|
||||
cs: Output<'d>,
|
||||
dio: DIO,
|
||||
clk: CLK,
|
||||
dma: impl Peripheral<P = DMA> + 'd,
|
||||
@ -143,6 +146,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Write data to peripheral and return status.
|
||||
pub async fn write(&mut self, write: &[u32]) -> u32 {
|
||||
self.sm.set_enable(false);
|
||||
let write_bits = write.len() * 32 - 1;
|
||||
@ -152,10 +156,10 @@ where
|
||||
defmt::trace!("write={} read={}", write_bits, read_bits);
|
||||
|
||||
unsafe {
|
||||
pio_instr_util::set_x(&mut self.sm, write_bits as u32);
|
||||
pio_instr_util::set_y(&mut self.sm, read_bits as u32);
|
||||
pio_instr_util::set_pindir(&mut self.sm, 0b1);
|
||||
pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target);
|
||||
instr::set_x(&mut self.sm, write_bits as u32);
|
||||
instr::set_y(&mut self.sm, read_bits as u32);
|
||||
instr::set_pindir(&mut self.sm, 0b1);
|
||||
instr::exec_jmp(&mut self.sm, self.wrap_target);
|
||||
}
|
||||
|
||||
self.sm.set_enable(true);
|
||||
@ -170,6 +174,7 @@ where
|
||||
status
|
||||
}
|
||||
|
||||
/// Send command and read response into buffer.
|
||||
pub async fn cmd_read(&mut self, cmd: u32, read: &mut [u32]) -> u32 {
|
||||
self.sm.set_enable(false);
|
||||
let write_bits = 31;
|
||||
@ -179,10 +184,10 @@ where
|
||||
defmt::trace!("write={} read={}", write_bits, read_bits);
|
||||
|
||||
unsafe {
|
||||
pio_instr_util::set_y(&mut self.sm, read_bits as u32);
|
||||
pio_instr_util::set_x(&mut self.sm, write_bits as u32);
|
||||
pio_instr_util::set_pindir(&mut self.sm, 0b1);
|
||||
pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target);
|
||||
instr::set_y(&mut self.sm, read_bits as u32);
|
||||
instr::set_x(&mut self.sm, write_bits as u32);
|
||||
instr::set_pindir(&mut self.sm, 0b1);
|
||||
instr::exec_jmp(&mut self.sm, self.wrap_target);
|
||||
}
|
||||
|
||||
// self.cs.set_low();
|
||||
@ -200,9 +205,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, CS, PIO, const SM: usize, DMA> SpiBusCyw43 for PioSpi<'d, CS, PIO, SM, DMA>
|
||||
impl<'d, PIO, const SM: usize, DMA> SpiBusCyw43 for PioSpi<'d, PIO, SM, DMA>
|
||||
where
|
||||
CS: Pin,
|
||||
PIO: Instance,
|
||||
DMA: Channel,
|
||||
{
|
||||
|
@ -2,16 +2,22 @@
|
||||
name = "cyw43"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Rust driver for the CYW43439 WiFi chip, used in the Raspberry Pi Pico W."
|
||||
keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"]
|
||||
categories = ["embedded", "hardware-support", "no-std", "network-programming", "asynchronous"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/embassy-rs/embassy"
|
||||
documentation = "https://docs.embassy.dev/cyw43"
|
||||
|
||||
[features]
|
||||
defmt = ["dep:defmt"]
|
||||
defmt = ["dep:defmt", "heapless/defmt-03", "embassy-time/defmt"]
|
||||
log = ["dep:log"]
|
||||
|
||||
# Fetch console logs from the WiFi firmware and forward them to `log` or `defmt`.
|
||||
firmware-logs = []
|
||||
|
||||
[dependencies]
|
||||
embassy-time = { version = "0.2", path = "../embassy-time"}
|
||||
embassy-time = { version = "0.3.0", path = "../embassy-time"}
|
||||
embassy-sync = { version = "0.5.0", path = "../embassy-sync"}
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel"}
|
||||
@ -23,11 +29,16 @@ cortex-m = "0.7.6"
|
||||
cortex-m-rt = "0.7.0"
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
|
||||
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.2" }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
|
||||
num_enum = { version = "0.5.7", default-features = false }
|
||||
|
||||
heapless = "0.8.0"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-v$VERSION/cyw43/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43/src/"
|
||||
target = "thumbv6m-none-eabi"
|
||||
features = ["defmt", "firmware-logs"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["defmt", "firmware-logs"]
|
||||
|
@ -44,14 +44,3 @@ This example implements a TCP echo server on port 1234. You can try connecting t
|
||||
nc 192.168.0.250 1234
|
||||
```
|
||||
Send it some data, you should see it echoed back and printed in the firmware's logs.
|
||||
|
||||
## License
|
||||
|
||||
This work is licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||
|
||||
at your option.
|
||||
|
||||
|
@ -3,7 +3,7 @@ use core::iter::zip;
|
||||
|
||||
use embassy_net_driver_channel as ch;
|
||||
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
||||
use embassy_time::Timer;
|
||||
use embassy_time::{Duration, Timer};
|
||||
|
||||
use crate::consts::*;
|
||||
use crate::events::{Event, EventSubscriber, Events};
|
||||
@ -12,23 +12,66 @@ use crate::ioctl::{IoctlState, IoctlType};
|
||||
use crate::structs::*;
|
||||
use crate::{countries, events, PowerManagementMode};
|
||||
|
||||
/// Control errors.
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
/// Status code.
|
||||
pub status: u32,
|
||||
}
|
||||
|
||||
/// Multicast errors.
|
||||
#[derive(Debug)]
|
||||
pub enum AddMulticastAddressError {
|
||||
/// Not a multicast address.
|
||||
NotMulticast,
|
||||
/// No free address slots.
|
||||
NoFreeSlots,
|
||||
}
|
||||
|
||||
/// Control driver.
|
||||
pub struct Control<'a> {
|
||||
state_ch: ch::StateRunner<'a>,
|
||||
events: &'a Events,
|
||||
ioctl_state: &'a IoctlState,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum ScanType {
|
||||
Active,
|
||||
Passive,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct ScanOptions {
|
||||
pub ssid: Option<heapless::String<32>>,
|
||||
/// If set to `None`, all APs will be returned. If set to `Some`, only APs
|
||||
/// with the specified BSSID will be returned.
|
||||
pub bssid: Option<[u8; 6]>,
|
||||
/// Number of probes to send on each channel.
|
||||
pub nprobes: Option<u16>,
|
||||
/// Time to spend waiting on the home channel.
|
||||
pub home_time: Option<Duration>,
|
||||
/// Scan type: active or passive.
|
||||
pub scan_type: ScanType,
|
||||
/// Period of time to wait on each channel when passive scanning.
|
||||
pub dwell_time: Option<Duration>,
|
||||
}
|
||||
|
||||
impl Default for ScanOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ssid: None,
|
||||
bssid: None,
|
||||
nprobes: None,
|
||||
home_time: None,
|
||||
scan_type: ScanType::Passive,
|
||||
dwell_time: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Control<'a> {
|
||||
pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self {
|
||||
Self {
|
||||
@ -38,6 +81,7 @@ impl<'a> Control<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize WiFi controller.
|
||||
pub async fn init(&mut self, clm: &[u8]) {
|
||||
const CHUNK_SIZE: usize = 1024;
|
||||
|
||||
@ -154,6 +198,7 @@ impl<'a> Control<'a> {
|
||||
self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await;
|
||||
}
|
||||
|
||||
/// Set power management mode.
|
||||
pub async fn set_power_management(&mut self, mode: PowerManagementMode) {
|
||||
// power save mode
|
||||
let mode_num = mode.mode();
|
||||
@ -166,6 +211,7 @@ impl<'a> Control<'a> {
|
||||
self.ioctl_set_u32(86, 0, mode_num).await;
|
||||
}
|
||||
|
||||
/// Join an unprotected network with the provided ssid.
|
||||
pub async fn join_open(&mut self, ssid: &str) -> Result<(), Error> {
|
||||
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
|
||||
|
||||
@ -183,6 +229,7 @@ impl<'a> Control<'a> {
|
||||
self.wait_for_join(i).await
|
||||
}
|
||||
|
||||
/// Join an protected network with the provided ssid and passphrase.
|
||||
pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> {
|
||||
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
|
||||
|
||||
@ -250,16 +297,19 @@ impl<'a> Control<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set GPIO pin on WiFi chip.
|
||||
pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) {
|
||||
assert!(gpio_n < 3);
|
||||
self.set_iovar_u32x2("gpioout", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 })
|
||||
.await
|
||||
}
|
||||
|
||||
/// Start open access point.
|
||||
pub async fn start_ap_open(&mut self, ssid: &str, channel: u8) {
|
||||
self.start_ap(ssid, "", Security::OPEN, channel).await;
|
||||
}
|
||||
|
||||
/// Start WPA2 protected access point.
|
||||
pub async fn start_ap_wpa2(&mut self, ssid: &str, passphrase: &str, channel: u8) {
|
||||
self.start_ap(ssid, passphrase, Security::WPA2_AES_PSK, channel).await;
|
||||
}
|
||||
@ -458,22 +508,54 @@ impl<'a> Control<'a> {
|
||||
/// # Note
|
||||
/// Device events are currently implemented using a bounded queue.
|
||||
/// To not miss any events, you should make sure to always await the stream.
|
||||
pub async fn scan(&mut self) -> Scanner<'_> {
|
||||
pub async fn scan(&mut self, scan_opts: ScanOptions) -> Scanner<'_> {
|
||||
const SCANTYPE_ACTIVE: u8 = 0;
|
||||
const SCANTYPE_PASSIVE: u8 = 1;
|
||||
|
||||
let dwell_time = match scan_opts.dwell_time {
|
||||
None => !0,
|
||||
Some(t) => {
|
||||
let mut t = t.as_millis() as u32;
|
||||
if t == !0 {
|
||||
t = !0 - 1;
|
||||
}
|
||||
t
|
||||
}
|
||||
};
|
||||
|
||||
let mut active_time = !0;
|
||||
let mut passive_time = !0;
|
||||
let scan_type = match scan_opts.scan_type {
|
||||
ScanType::Active => {
|
||||
active_time = dwell_time;
|
||||
SCANTYPE_ACTIVE
|
||||
}
|
||||
ScanType::Passive => {
|
||||
passive_time = dwell_time;
|
||||
SCANTYPE_PASSIVE
|
||||
}
|
||||
};
|
||||
|
||||
let scan_params = ScanParams {
|
||||
version: 1,
|
||||
action: 1,
|
||||
sync_id: 1,
|
||||
ssid_len: 0,
|
||||
ssid: [0; 32],
|
||||
bssid: [0xff; 6],
|
||||
ssid_len: scan_opts.ssid.as_ref().map(|e| e.as_bytes().len() as u32).unwrap_or(0),
|
||||
ssid: scan_opts
|
||||
.ssid
|
||||
.map(|e| {
|
||||
let mut ssid = [0; 32];
|
||||
ssid[..e.as_bytes().len()].copy_from_slice(e.as_bytes());
|
||||
ssid
|
||||
})
|
||||
.unwrap_or([0; 32]),
|
||||
bssid: scan_opts.bssid.unwrap_or([0xff; 6]),
|
||||
bss_type: 2,
|
||||
scan_type: SCANTYPE_PASSIVE,
|
||||
nprobes: !0,
|
||||
active_time: !0,
|
||||
passive_time: !0,
|
||||
home_time: !0,
|
||||
scan_type,
|
||||
nprobes: scan_opts.nprobes.unwrap_or(!0).into(),
|
||||
active_time,
|
||||
passive_time,
|
||||
home_time: scan_opts.home_time.map(|e| e.as_millis() as u32).unwrap_or(!0),
|
||||
channel_num: 0,
|
||||
channel_list: [0; 1],
|
||||
};
|
||||
@ -494,13 +576,14 @@ impl<'a> Control<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// WiFi network scanner.
|
||||
pub struct Scanner<'a> {
|
||||
subscriber: EventSubscriber<'a>,
|
||||
events: &'a Events,
|
||||
}
|
||||
|
||||
impl Scanner<'_> {
|
||||
/// wait for the next found network
|
||||
/// Wait for the next found network.
|
||||
pub async fn next(&mut self) -> Option<BssInfo> {
|
||||
let event = self.subscriber.next_message_pure().await;
|
||||
if event.header.status != EStatus::PARTIAL {
|
||||
|
@ -311,13 +311,13 @@ pub struct Status {
|
||||
pub status: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Payload {
|
||||
None,
|
||||
BssInfo(BssInfo),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Copy, Clone)]
|
||||
|
||||
pub struct Message {
|
||||
pub header: Status,
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -2,6 +2,8 @@
|
||||
#![no_main]
|
||||
#![allow(async_fn_in_trait)]
|
||||
#![deny(unused_must_use)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
@ -102,6 +104,7 @@ const CHIP: Chip = Chip {
|
||||
chanspec_ctl_sb_mask: 0x0700,
|
||||
};
|
||||
|
||||
/// Driver state.
|
||||
pub struct State {
|
||||
ioctl_state: IoctlState,
|
||||
ch: ch::State<MTU, 4, 4>,
|
||||
@ -109,6 +112,7 @@ pub struct State {
|
||||
}
|
||||
|
||||
impl State {
|
||||
/// Create new driver state holder.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ioctl_state: IoctlState::new(),
|
||||
@ -118,6 +122,7 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
/// Power management modes.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PowerManagementMode {
|
||||
/// Custom, officially unsupported mode. Use at your own risk.
|
||||
@ -203,8 +208,13 @@ impl PowerManagementMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Embassy-net driver.
|
||||
pub type NetDriver<'a> = ch::Device<'a, MTU>;
|
||||
|
||||
/// Create a new instance of the CYW43 driver.
|
||||
///
|
||||
/// Returns a handle to the network device, control handle and a runner for driving the low level
|
||||
/// stack.
|
||||
pub async fn new<'a, PWR, SPI>(
|
||||
state: &'a mut State,
|
||||
pwr: PWR,
|
||||
|
@ -34,6 +34,7 @@ impl Default for LogState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Driver communicating with the WiFi chip.
|
||||
pub struct Runner<'a, PWR, SPI> {
|
||||
ch: ch::Runner<'a, MTU>,
|
||||
bus: Bus<PWR, SPI>,
|
||||
@ -222,6 +223,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the
|
||||
pub async fn run(mut self) -> ! {
|
||||
let mut buf = [0; 512];
|
||||
loop {
|
||||
@ -240,13 +242,12 @@ where
|
||||
cmd,
|
||||
iface,
|
||||
}) => {
|
||||
self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }).await;
|
||||
self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }, &mut buf).await;
|
||||
self.check_status(&mut buf).await;
|
||||
}
|
||||
Either3::Second(packet) => {
|
||||
trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
|
||||
|
||||
let mut buf = [0; 512];
|
||||
let buf8 = slice8_mut(&mut buf);
|
||||
|
||||
// There MUST be 2 bytes of padding between the SDPCM and BDC headers.
|
||||
@ -478,9 +479,8 @@ where
|
||||
self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0
|
||||
}
|
||||
|
||||
async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) {
|
||||
let mut buf = [0; 512];
|
||||
let buf8 = slice8_mut(&mut buf);
|
||||
async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8], buf: &mut [u32; 512]) {
|
||||
let buf8 = slice8_mut(buf);
|
||||
|
||||
let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len();
|
||||
|
||||
|
@ -4,13 +4,16 @@ use crate::fmt::Bytes;
|
||||
macro_rules! impl_bytes {
|
||||
($t:ident) => {
|
||||
impl $t {
|
||||
/// Bytes consumed by this type.
|
||||
pub const SIZE: usize = core::mem::size_of::<Self>();
|
||||
|
||||
/// Convert to byte array.
|
||||
#[allow(unused)]
|
||||
pub fn to_bytes(&self) -> [u8; Self::SIZE] {
|
||||
unsafe { core::mem::transmute(*self) }
|
||||
}
|
||||
|
||||
/// Create from byte array.
|
||||
#[allow(unused)]
|
||||
pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> &Self {
|
||||
let alignment = core::mem::align_of::<Self>();
|
||||
@ -23,6 +26,7 @@ macro_rules! impl_bytes {
|
||||
unsafe { core::mem::transmute(bytes) }
|
||||
}
|
||||
|
||||
/// Create from mutable byte array.
|
||||
#[allow(unused)]
|
||||
pub fn from_bytes_mut(bytes: &mut [u8; Self::SIZE]) -> &mut Self {
|
||||
let alignment = core::mem::align_of::<Self>();
|
||||
@ -204,6 +208,7 @@ pub struct EthernetHeader {
|
||||
}
|
||||
|
||||
impl EthernetHeader {
|
||||
/// Swap endianness.
|
||||
pub fn byteswap(&mut self) {
|
||||
self.ether_type = self.ether_type.to_be();
|
||||
}
|
||||
@ -472,19 +477,64 @@ impl ScanResults {
|
||||
#[repr(C, packed(2))]
|
||||
#[non_exhaustive]
|
||||
pub struct BssInfo {
|
||||
/// Version.
|
||||
pub version: u32,
|
||||
/// Length.
|
||||
pub length: u32,
|
||||
/// BSSID.
|
||||
pub bssid: [u8; 6],
|
||||
/// Beacon period.
|
||||
pub beacon_period: u16,
|
||||
/// Capability.
|
||||
pub capability: u16,
|
||||
/// SSID length.
|
||||
pub ssid_len: u8,
|
||||
/// SSID.
|
||||
pub ssid: [u8; 32],
|
||||
reserved1: [u8; 1],
|
||||
/// Number of rates in the rates field.
|
||||
pub rateset_count: u32,
|
||||
/// Rates in 500kpbs units.
|
||||
pub rates: [u8; 16],
|
||||
/// Channel specification.
|
||||
pub chanspec: u16,
|
||||
/// Announcement traffic indication message.
|
||||
pub atim_window: u16,
|
||||
/// Delivery traffic indication message.
|
||||
pub dtim_period: u8,
|
||||
reserved2: [u8; 1],
|
||||
/// Receive signal strength (in dbM).
|
||||
pub rssi: i16,
|
||||
/// Received noise (in dbM).
|
||||
pub phy_noise: i8,
|
||||
/// 802.11n capability.
|
||||
pub n_cap: u8,
|
||||
reserved3: [u8; 2],
|
||||
/// 802.11n BSS capabilities.
|
||||
pub nbss_cap: u32,
|
||||
/// 802.11n control channel number.
|
||||
pub ctl_ch: u8,
|
||||
reserved4: [u8; 3],
|
||||
reserved32: [u32; 1],
|
||||
/// Flags.
|
||||
pub flags: u8,
|
||||
/// VHT capability.
|
||||
pub vht_cap: u8,
|
||||
reserved5: [u8; 2],
|
||||
/// 802.11n BSS required MCS.
|
||||
pub basic_mcs: [u8; 16],
|
||||
/// Information Elements (IE) offset.
|
||||
pub ie_offset: u16,
|
||||
/// Length of Information Elements (IE) in bytes.
|
||||
pub ie_length: u32,
|
||||
/// Average signal-to-noise (SNR) ratio during frame reception.
|
||||
pub snr: i16,
|
||||
// there will be more stuff here
|
||||
}
|
||||
impl_bytes!(BssInfo);
|
||||
|
||||
impl BssInfo {
|
||||
pub fn parse(packet: &mut [u8]) -> Option<&mut Self> {
|
||||
pub(crate) fn parse(packet: &mut [u8]) -> Option<&mut Self> {
|
||||
if packet.len() < BssInfo::SIZE {
|
||||
return None;
|
||||
}
|
||||
|
4
docs/README.md
Normal file
4
docs/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# embassy docs
|
||||
|
||||
The documentation hosted at [https://embassy.dev/book](https://embassy.dev/book). Building the documentation requires
|
||||
cloning the [embassy-book](https://github.com/embassy-rs/embassy-book) repository and following the instructions.
|
@ -6,8 +6,8 @@ version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-executor = { version = "0.4.0", path = "../../../../../embassy-executor", features = ["defmt", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.2.0", path = "../../../../../embassy-time", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.5.0", path = "../../../../../embassy-executor", features = ["defmt", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.3.0", path = "../../../../../embassy-time", features = ["defmt"] }
|
||||
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] }
|
||||
|
||||
defmt = "0.3"
|
||||
|
@ -1,16 +1,14 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_nrf::gpio::{Level, Output, OutputDrive};
|
||||
use embassy_nrf::peripherals::P0_13;
|
||||
use embassy_time::{Duration, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _}; // global logger
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn blinker(mut led: Output<'static, P0_13>, interval: Duration) {
|
||||
async fn blinker(mut led: Output<'static>, interval: Duration) {
|
||||
loop {
|
||||
led.set_high();
|
||||
Timer::after(interval).await;
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
cortex-m = "0.7"
|
||||
cortex-m-rt = "0.7"
|
||||
embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] }
|
||||
embassy-executor = { version = "0.4.0", features = ["nightly", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-executor = { version = "0.5.0", features = ["arch-cortex-m", "executor-thread"] }
|
||||
|
||||
defmt = "0.3.0"
|
||||
defmt-rtt = "0.3.0"
|
||||
|
@ -1,17 +1,16 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::exti::ExtiInput;
|
||||
use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
|
||||
use embassy_stm32::gpio::{Level, Output, Pull, Speed};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_stm32::init(Default::default());
|
||||
let mut led = Output::new(p.PB14, Level::Low, Speed::VeryHigh);
|
||||
let mut button = ExtiInput::new(Input::new(p.PC13, Pull::Up), p.EXTI13);
|
||||
let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up);
|
||||
|
||||
loop {
|
||||
button.wait_for_any_edge().await;
|
||||
|
@ -6,13 +6,12 @@ use core::cell::RefCell;
|
||||
use cortex_m::interrupt::Mutex;
|
||||
use cortex_m::peripheral::NVIC;
|
||||
use cortex_m_rt::entry;
|
||||
use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed};
|
||||
use embassy_stm32::peripherals::{PB14, PC13};
|
||||
use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
|
||||
use embassy_stm32::{interrupt, pac};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
static BUTTON: Mutex<RefCell<Option<Input<'static, PC13>>>> = Mutex::new(RefCell::new(None));
|
||||
static LED: Mutex<RefCell<Option<Output<'static, PB14>>>> = Mutex::new(RefCell::new(None));
|
||||
static BUTTON: Mutex<RefCell<Option<Input<'static>>>> = Mutex::new(RefCell::new(None));
|
||||
static LED: Mutex<RefCell<Option<Output<'static>>>> = Mutex::new(RefCell::new(None));
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
@ -62,14 +61,14 @@ fn EXTI15_10() {
|
||||
|
||||
const PORT: u8 = 2;
|
||||
const PIN: usize = 13;
|
||||
fn check_interrupt<P: Pin>(_pin: &mut Input<'static, P>) -> bool {
|
||||
fn check_interrupt(_pin: &mut Input<'static>) -> bool {
|
||||
let exti = pac::EXTI;
|
||||
let pin = PIN;
|
||||
let lines = exti.pr(0).read();
|
||||
lines.line(pin)
|
||||
}
|
||||
|
||||
fn clear_interrupt<P: Pin>(_pin: &mut Input<'static, P>) {
|
||||
fn clear_interrupt(_pin: &mut Input<'static>) {
|
||||
let exti = pac::EXTI;
|
||||
let pin = PIN;
|
||||
let mut lines = exti.pr(0).read();
|
||||
@ -77,7 +76,7 @@ fn clear_interrupt<P: Pin>(_pin: &mut Input<'static, P>) {
|
||||
exti.pr(0).write_value(lines);
|
||||
}
|
||||
|
||||
fn enable_interrupt<P: Pin>(_pin: &mut Input<'static, P>) {
|
||||
fn enable_interrupt(_pin: &mut Input<'static>) {
|
||||
cortex_m::interrupt::free(|_| {
|
||||
let rcc = pac::RCC;
|
||||
rcc.apb2enr().modify(|w| w.set_syscfgen(true));
|
||||
|
@ -3,10 +3,11 @@
|
||||
** xref:project_structure.adoc[Project Structure]
|
||||
** xref:new_project.adoc[Starting a new Embassy project]
|
||||
** xref:best_practices.adoc[Best Practices]
|
||||
* xref:layer_by_layer.adoc[Bare metal to async]
|
||||
* xref:runtime.adoc[Executor]
|
||||
* xref:delaying_a_task.adoc[Delaying a Task]
|
||||
* xref::time_keeping.adoc[Time-keeping]
|
||||
* xref:sharing_peripherals.adoc[Sharing peripherals]
|
||||
* xref:hal.adoc[HAL]
|
||||
** xref:layer_by_layer.adoc[Anatomy of an async HAL]
|
||||
** xref:nrf.adoc[nRF]
|
||||
** xref:stm32.adoc[STM32]
|
||||
* xref:bootloader.adoc[Bootloader]
|
||||
|
@ -17,22 +17,13 @@ The first thing you’ll notice are two attributes at the top of the file. These
|
||||
include::example$basic/src/main.rs[lines="1..2"]
|
||||
----
|
||||
|
||||
=== Rust Nightly
|
||||
|
||||
The next declaration is a Rust Unstable feature, which means that Embassy requires Rust Nightly:
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
include::example$basic/src/main.rs[lines="3"]
|
||||
----
|
||||
|
||||
=== Dealing with errors
|
||||
|
||||
Then, what follows are some declarations on how to deal with panics and faults. During development, a good practice is to rely on `defmt-rtt` and `panic-probe` to print diagnostics to the terminal:
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
include::example$basic/src/main.rs[lines="10"]
|
||||
include::example$basic/src/main.rs[lines="8"]
|
||||
----
|
||||
|
||||
=== Task declaration
|
||||
@ -41,7 +32,7 @@ After a bit of import declaration, the tasks run by the application should be de
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
include::example$basic/src/main.rs[lines="12..20"]
|
||||
include::example$basic/src/main.rs[lines="10..18"]
|
||||
----
|
||||
|
||||
An embassy task must be declared `async`, and may NOT take generic arguments. In this case, we are handed the LED that should be blinked and the interval of the blinking.
|
||||
@ -56,7 +47,7 @@ We then initialize the HAL with a default config, which gives us a `Peripherals`
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
include::example$basic/src/main.rs[lines="22..-1"]
|
||||
include::example$basic/src/main.rs[lines="20..-1"]
|
||||
----
|
||||
|
||||
What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy_executor::main]` macro. The macro does the following:
|
||||
|
@ -3,8 +3,10 @@
|
||||
Over time, a couple of best practices have emerged. The following list should serve as a guideline for developers writing embedded software in _Rust_, especially in the context of the _Embassy_ framework.
|
||||
|
||||
== Passing Buffers by Reference
|
||||
It may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`], to a function or return one just like you would with a `std::Vec`. However, in most embedded applications you don't want to spend ressources on an allocator and end up placing buffers on the stack.
|
||||
This, however, can easily blow up your stack if you are not careful.
|
||||
It may be tempting to pass arrays or wrappers, like link:https://docs.rs/heapless/latest/heapless/[`heapless::Vec`],
|
||||
to a function or return one just like you would with a `std::Vec`. However, in most embedded applications you don't
|
||||
want to spend resources on an allocator and end up placing buffers on the stack. This, however, can easily blow up
|
||||
your stack if you are not careful.
|
||||
|
||||
Consider the following example:
|
||||
[,rust]
|
||||
|
@ -45,6 +45,8 @@ The BOOTLOADER_STATE partition must be big enough to store one word per page in
|
||||
|
||||
The 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.
|
||||
|
||||
NOTE: The linker scripts for the application and bootloader look similar, but the FLASH region must point to the BOOTLOADER partition for the bootloader, and the ACTIVE partition for the application.
|
||||
|
||||
=== FirmwareUpdater
|
||||
|
||||
The `FirmwareUpdater` is an object for conveniently flashing firmware to the DFU partition and subsequently marking it as being ready for swapping with the active partition on the next reset. Its principle methods are `write_firmware`, which is called once per the size of the flash "write block" (typically 4KiB), and `mark_updated`, which is the final call.
|
||||
|
@ -1,28 +0,0 @@
|
||||
= Delaying a Task
|
||||
|
||||
In an embedded program, delaying a task is one of the most common actions taken. In an event loop, delays will need to be inserted to ensure
|
||||
that other tasks have a chance to run before the next iteration of the loop is called, if no other I/O is performed. Embassy provides an abstraction
|
||||
to delay the current task for a specified interval of time.
|
||||
|
||||
Timing is serviced by the `embassy::time::Timer` struct, which provides two timing methods.
|
||||
|
||||
`Timer::at` creates a future that completes at the specified `Instant`, relative to the system boot time.
|
||||
`Timer::after` creates a future that completes after the specified `Duration`, relative to when the future was created.
|
||||
|
||||
An example of a delay is provided as follows:
|
||||
|
||||
[,rust]
|
||||
----
|
||||
use embassy::executor::{task, Executor};
|
||||
use embassy::time::{Duration, Timer};
|
||||
|
||||
#[task]
|
||||
/// Task that ticks periodically
|
||||
async fn tick_periodic() -> ! {
|
||||
loop {
|
||||
rprintln!("tick!");
|
||||
// async sleep primitive, suspends the task for 500ms.
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
}
|
||||
}
|
||||
----
|
@ -7,3 +7,5 @@ Here are known examples of real-world projects which make use of Embassy. Feel f
|
||||
* link:https://github.com/card-io-ecg/card-io-fw[Card/IO firmware] - firmware for an open source ECG device
|
||||
** Targets the ESP32-S3 or ESP32-C6 MCU
|
||||
* The link:https://github.com/lora-rs/lora-rs[lora-rs] project includes link:https://github.com/lora-rs/lora-rs/tree/main/examples/stm32l0/src/bin[various standalone examples] for NRF52840, RP2040, STM32L0 and STM32WL
|
||||
** link:https://github.com/matoushybl/air-force-one[Air force one: A simple air quality monitoring system]
|
||||
*** Targets nRF52 and uses nrf-softdevice
|
||||
|
@ -29,13 +29,12 @@ If you see an error like this:
|
||||
|
||||
You are likely missing some features of the `embassy-executor` crate.
|
||||
|
||||
For Cortex-M targets, consider making sure that ALL of the following features are active in your `Cargo.toml` for the `embassy-executor` crate:
|
||||
For Cortex-M targets, check whether ALL of the following features are enabled in your `Cargo.toml` for the `embassy-executor` crate:
|
||||
|
||||
* `arch-cortex-m`
|
||||
* `executor-thread`
|
||||
* `nightly`
|
||||
|
||||
For Xtensa ESP32, consider using the executors and `#[main]` macro provided by your appropriate link:https://crates.io/crates/esp-hal-common[HAL crate].
|
||||
For ESP32, consider using the executors and `#[main]` macro provided by your appropriate link:https://crates.io/crates/esp-hal-common[HAL crate].
|
||||
|
||||
== Why is my binary so big?
|
||||
|
||||
@ -44,11 +43,12 @@ The first step to managing your binary size is to set up your link:https://doc.r
|
||||
[source,toml]
|
||||
----
|
||||
[profile.release]
|
||||
debug = false
|
||||
lto = true
|
||||
opt-level = "s"
|
||||
incremental = false
|
||||
codegen-units = 1
|
||||
# note: debug = true is okay - debuginfo isn't flashed to the device!
|
||||
debug = true
|
||||
----
|
||||
|
||||
All of these flags are elaborated on in the Rust Book page linked above.
|
||||
@ -118,21 +118,31 @@ features = [
|
||||
]
|
||||
----
|
||||
|
||||
If you are in the early project setup phase and not using anything from the HAL, make sure the HAL is explicitly used to prevent the linker removing it as dead code by adding this line to your source:
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
use embassy_stm32 as _;
|
||||
----
|
||||
|
||||
== Error: `Only one package in the dependency graph may specify the same links value.`
|
||||
|
||||
You have multiple versions of the same crate in your dependency tree. This means that some of your
|
||||
embassy crates are coming from crates.io, and some from git, each of them pulling in a different set
|
||||
of dependencies.
|
||||
|
||||
To resolve this issue, make sure to only use a single source for all your embassy crates! To do this,
|
||||
you should patch your dependencies to use git sources using `[patch.crates.io]` and maybe `[patch.'https://github.com/embassy-rs/embassy.git']`.
|
||||
To resolve this issue, make sure to only use a single source for all your embassy crates!
|
||||
To do this, you should patch your dependencies to use git sources using `[patch.crates.io]`
|
||||
and maybe `[patch.'https://github.com/embassy-rs/embassy.git']`.
|
||||
|
||||
Example:
|
||||
|
||||
[source,toml]
|
||||
----
|
||||
[patch.crates-io]
|
||||
embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
|
||||
embassy-time-queue-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
|
||||
embassy-time-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
|
||||
# embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
|
||||
----
|
||||
|
||||
Note that the git revision should match any other embassy patches or git dependencies that you are using!
|
||||
@ -153,3 +163,48 @@ Note that the git revision should match any other embassy patches or git depende
|
||||
** disable `executor-thread`
|
||||
** make `main`` spawn everything, then enable link:https://docs.rs/cortex-m/latest/cortex_m/peripheral/struct.SCB.html#method.set_sleeponexit[SCB.SLEEPONEXIT] and `loop { cortex_m::asm::wfi() }`
|
||||
** *Note:* If you need 2 priority levels, using 2 interrupt executors is better than 1 thread executor + 1 interrupt executor.
|
||||
|
||||
== How do I set up the task arenas on stable?
|
||||
|
||||
When you aren't using the `nightly` feature of `embassy-executor`, the executor uses a bump allocator, which may require configuration.
|
||||
|
||||
Something like this error will occur at **compile time** if the task arena is *too large* for the target's RAM:
|
||||
|
||||
[source,plain]
|
||||
----
|
||||
rust-lld: error: section '.bss' will not fit in region 'RAM': overflowed by _ bytes
|
||||
rust-lld: error: section '.uninit' will not fit in region 'RAM': overflowed by _ bytes
|
||||
----
|
||||
|
||||
And this message will appear at **runtime** if the task arena is *too small* for the tasks running:
|
||||
|
||||
[source,plain]
|
||||
----
|
||||
ERROR panicked at 'embassy-executor: task arena is full. You must increase the arena size, see the documentation for details: https://docs.embassy.dev/embassy-executor/'
|
||||
----
|
||||
|
||||
NOTE: If all tasks are spawned at startup, this panic will occur immediately.
|
||||
|
||||
Check out link:https://docs.embassy.dev/embassy-executor/git/cortex-m/index.html#task-arena[Task Arena Documentation] for more details.
|
||||
|
||||
== Can I use manual ISRs alongside Embassy?
|
||||
|
||||
Yes! This can be useful if you need to respond to an event as fast as possible, and the latency caused by the usual “ISR, wake, return from ISR, context switch to woken task” flow is too much for your application. Simply define a `#[interrupt] fn INTERRUPT_NAME() {}` handler as you would link:https://docs.rust-embedded.org/book/start/interrupts.html[in any other embedded rust project].
|
||||
|
||||
== How can I measure resource usage (CPU, RAM, etc.)?
|
||||
|
||||
=== For CPU Usage:
|
||||
|
||||
There are a couple techniques that have been documented, generally you want to measure how long you are spending in the idle or low priority loop.
|
||||
|
||||
We need to document specifically how to do this in embassy, but link:https://blog.japaric.io/cpu-monitor/[this older post] describes the general process.
|
||||
|
||||
If you end up doing this, please update this section with more specific examples!
|
||||
|
||||
=== For Static Memory Usage
|
||||
|
||||
Tools like `cargo size` and `cargo nm` can tell you the size of any globals or other static usage. Specifically you will want to see the size of the `.data` and `.bss` sections, which together make up the total global/static memory usage.
|
||||
|
||||
=== For Max Stack Usage
|
||||
|
||||
Check out link:https://github.com/Dirbaio/cargo-call-stack/[`cargo-call-stack`] for statically calculating worst-case stack usage. There are some caveats and inaccuracies possible with this, but this is a good way to get the general idea. See link:https://github.com/dirbaio/cargo-call-stack#known-limitations[the README] for more details.
|
||||
|
@ -3,7 +3,7 @@
|
||||
So you want to try Embassy, great! To get started, there are a few tools you need to install:
|
||||
|
||||
* link:https://rustup.rs/[rustup] - the Rust toolchain is needed to compile Rust code.
|
||||
* link:https://crates.io/crates/probe-rs[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well.
|
||||
* link:https://probe.rs/[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well.
|
||||
|
||||
If you don't have any supported board, don't worry: you can also run embassy on your PC using the `std` examples.
|
||||
|
||||
@ -82,19 +82,19 @@ If everything worked correctly, you should see a blinking LED on your board, and
|
||||
└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:27
|
||||
----
|
||||
|
||||
NOTE: How does the `cargo run` command know how to connect to our board and program it? In each `examples` folder, there’s a `.cargo/config.toml` file which tells cargo to use link:https://probe.rs/[probe-rs] as the runner for ARM binaries in that folder. probe-rs handles communication with the debug probe and MCU. In order for this to work, probe-rs needs to know which chip it’s programming, so you’ll have to edit this file if you want to run examples on other chips.
|
||||
NOTE: How does the `+cargo run+` command know how to connect to our board and program it? In each `examples` folder, there’s a `.cargo/config.toml` file which tells cargo to use link:https://probe.rs/[probe-rs] as the runner for ARM binaries in that folder. probe-rs handles communication with the debug probe and MCU. In order for this to work, probe-rs needs to know which chip it’s programming, so you’ll have to edit this file if you want to run examples on other chips.
|
||||
|
||||
=== It didn’t work!
|
||||
|
||||
If you hare having issues when running `cargo run --release`, please check the following:
|
||||
If you hare having issues when running `+cargo run --release+`, please check the following:
|
||||
|
||||
* You are specifying the correct `--chip on the command line``, OR
|
||||
* You have set `.cargo/config.toml`'s run line to the correct chip, AND
|
||||
* You have changed `examples/Cargo.toml`'s HAL (e.g. embassy-stm32) dependency's feature to use the correct chip (replace the existing stm32xxxx feature)
|
||||
* You are specifying the correct `+--chip+` on the command line, OR
|
||||
* You have set `+.cargo/config.toml+`’s run line to the correct chip, AND
|
||||
* You have changed `+examples/Cargo.toml+`’s HAL (e.g. embassy-stm32) dependency's feature to use the correct chip (replace the existing stm32xxxx feature)
|
||||
|
||||
At this point the project should run. If you do not see a blinky LED for blinky, for example, be sure to check the code is toggling your board's LED pin.
|
||||
|
||||
If you are trying to run an example with `cargo run --release` and you see the following output:
|
||||
If you are trying to run an example with `+cargo run --release+` and you see the following output:
|
||||
[source]
|
||||
----
|
||||
0.000000 INFO Hello World!
|
||||
@ -115,6 +115,22 @@ To get rid of the frame-index error add the following to your `Cargo.toml`:
|
||||
debug = 2
|
||||
----
|
||||
|
||||
If you’re getting an extremely long error message containing something like the following:
|
||||
|
||||
[source]
|
||||
----
|
||||
error[E0463]: can't find crate for `std`
|
||||
|
|
||||
= note: the `thumbv6m-none-eabi` target may not support the standard library
|
||||
= note: `std` is required by `stable_deref_trait` because it does not declare `#![no_std]`
|
||||
----
|
||||
|
||||
Make sure that you didn’t accidentally run `+cargo add probe-rs+` (which adds it as a dependency) instead of link:https://probe.rs/docs/getting-started/installation/[correctly installing probe-rs].
|
||||
|
||||
If you’re using a raspberry pi pico-w, make sure you’re running `+cargo run --bin wifi_blinky --release+` rather than the regular blinky. The pico-w’s on-board LED is connected to the WiFi chip, which needs to be initialized before the LED can be blinked.
|
||||
|
||||
If you’re using an rp2040 debug probe (e.g. the pico probe) and are having issues after running `probe-rs info`, unplug and reconnect the probe, letting it power cycle. Running `probe-rs info` is link:https://github.com/probe-rs/probe-rs/issues/1849[known to put the pico probe into an unusable state].
|
||||
|
||||
If you’re still having problems, check the link:https://embassy.dev/book/dev/faq.html[FAQ], or ask for help in the link:https://matrix.to/#/#embassy-rs:matrix.org[Embassy Chat Room].
|
||||
|
||||
== What's next?
|
||||
@ -124,3 +140,4 @@ Congratulations, you have your first Embassy application running! Here are some
|
||||
* Read more about the xref:runtime.adoc[executor].
|
||||
* Read more about the xref:hal.adoc[HAL].
|
||||
* Start xref:basic_application.adoc[writing your application].
|
||||
* Learn how to xref:new_project.adoc[start a new embassy project by adapting an example].
|
||||
|
@ -41,7 +41,7 @@ The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implem
|
||||
The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
|
||||
|
||||
=== LoRa
|
||||
link:https://github.com/embassy-rs/lora-phy[lora-phy] and link:https://docs.embassy.dev/embassy-lora/[embassy-lora] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust link:https://github.com/ivajloip/rust-lorawan[LoRaWAN] implementation.
|
||||
link:https://github.com/lora-rs/lora-rs[lora-rs] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust LoRaWAN implementation. It provides four crates — lora-phy, lora-modulation, lorawan-encoding, and lorawan-device — and basic examples for various development boards. It has support for STM32WL wireless microcontrollers or Semtech SX127x transceivers, among others.
|
||||
|
||||
=== USB
|
||||
link:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
||||
|
@ -1,6 +1,17 @@
|
||||
= Starting a new Embassy project
|
||||
|
||||
Once you’ve successfully xref:getting_started.adoc[run some example projects], the next step is to make a standalone Embassy project. The easiest way to do this is to adapt an example for a similar chip to the one you’re targeting.
|
||||
Once you’ve successfully xref:getting_started.adoc[run some example projects], the next step is to make a standalone Embassy project.
|
||||
|
||||
There are some tools for generating Embassy projects: (WIP)
|
||||
|
||||
==== CLI
|
||||
- link:https://github.com/adinack/cargo-embassy[cargo-embassy] (STM32 and NRF)
|
||||
|
||||
==== cargo-generate
|
||||
- link:https://github.com/lulf/embassy-template[embassy-template] (STM32, NRF, and RP)
|
||||
- link:https://github.com/bentwire/embassy-rp2040-template[embassy-rp2040-template] (RP)
|
||||
|
||||
But if you want to start from scratch:
|
||||
|
||||
As an example, let’s create a new embassy project from scratch for a STM32G474. The same instructions are applicable for any supported chip with some minor changes.
|
||||
|
||||
|
@ -18,7 +18,7 @@ my-project
|
||||
|- rust-toolchain.toml
|
||||
----
|
||||
|
||||
=== .cargo/config.toml
|
||||
== .cargo/config.toml
|
||||
|
||||
This directory/file describes what platform you're on, and configures link:https://github.com/probe-rs/probe-rs[probe-rs] to deploy to your device.
|
||||
|
||||
@ -36,17 +36,22 @@ target = "thumbv6m-none-eabi" # <-change for your platform
|
||||
DEFMT_LOG = "trace" # <- can change to info, warn, or error
|
||||
----
|
||||
|
||||
=== build.rs
|
||||
== build.rs
|
||||
|
||||
This is the build script for your project. It links defmt (what is defmt?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example].
|
||||
This is the build script for your project. It links defmt (what is link:https://defmt.ferrous-systems.com[defmt]?) and the `memory.x` file if needed. This file is pretty specific for each chipset, just copy and paste from the corresponding link:https://github.com/embassy-rs/embassy/tree/main/examples[example].
|
||||
|
||||
=== Cargo.toml
|
||||
== Cargo.toml
|
||||
|
||||
This is your manifest file, where you can configure all of the embassy components to use the features you need.
|
||||
|
||||
TODO: someone should exhaustively describe every feature for every component!
|
||||
==== Features
|
||||
===== Time
|
||||
- tick-hz-x: Configures the tick rate of `embassy-time`. Higher tick rate means higher precision, and higher CPU wakes.
|
||||
- defmt-timestamp-uptime: defmt log entries will display the uptime in seconds.
|
||||
|
||||
=== memory.x
|
||||
...more to come
|
||||
|
||||
== memory.x
|
||||
|
||||
This file outlines the flash/ram usage of your program. It is especially useful when using link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] on an nRF5x.
|
||||
|
||||
@ -63,7 +68,7 @@ MEMORY
|
||||
}
|
||||
----
|
||||
|
||||
=== rust-toolchain.toml
|
||||
== rust-toolchain.toml
|
||||
|
||||
This file configures the rust version and configuration to use.
|
||||
|
||||
|
126
docs/modules/ROOT/pages/sharing_peripherals.adoc
Normal file
126
docs/modules/ROOT/pages/sharing_peripherals.adoc
Normal file
@ -0,0 +1,126 @@
|
||||
= Sharing peripherals between tasks
|
||||
|
||||
Often times, more than one task needs access to the same resource (pin, communication interface, etc.). Embassy provides many different synchronization primitives in the link:https://crates.io/crates/embassy-sync[embassy-sync] crate.
|
||||
|
||||
The following examples shows different ways to use the on-board LED on a Raspberry Pi Pico board by two tasks simultaneously.
|
||||
|
||||
== Sharing using a Mutex
|
||||
|
||||
Using mutual exclusion is the simplest way to share a peripheral.
|
||||
|
||||
[,rust]
|
||||
----
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio;
|
||||
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use gpio::{AnyPin, Level, Output};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
type LedType = Mutex<ThreadModeRawMutex, Option<Output<'static, AnyPin>>>;
|
||||
static LED: LedType = Mutex::new(None);
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
// set the content of the global LED reference to the real LED pin
|
||||
let led = Output::new(AnyPin::from(p.PIN_25), Level::High);
|
||||
// inner scope is so that once the mutex is written to, the MutexGuard is dropped, thus the
|
||||
// Mutex is released
|
||||
{
|
||||
*(LED.lock().await) = Some(led);
|
||||
}
|
||||
let dt = 100 * 1_000_000;
|
||||
let k = 1.003;
|
||||
|
||||
unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos(dt))));
|
||||
unwrap!(spawner.spawn(toggle_led(&LED, Duration::from_nanos((dt as f64 * k) as u64))));
|
||||
}
|
||||
|
||||
// A pool size of 2 means you can spawn two instances of this task.
|
||||
#[embassy_executor::task(pool_size = 2)]
|
||||
async fn toggle_led(led: &'static LedType, delay: Duration) {
|
||||
let mut ticker = Ticker::every(delay);
|
||||
loop {
|
||||
{
|
||||
let mut led_unlocked = led.lock().await;
|
||||
if let Some(pin_ref) = led_unlocked.as_mut() {
|
||||
pin_ref.toggle();
|
||||
}
|
||||
}
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The structure facilitating access to the resource is the defined `LedType`.
|
||||
|
||||
=== Why so complicated
|
||||
|
||||
Unwrapping the layers gives insight into why each one is needed.
|
||||
|
||||
==== `Mutex<RawMutexType, T>`
|
||||
|
||||
The mutex is there so if one task gets the resource first and begins modifying it, all other tasks wanting to write will have to wait (the `led.lock().await` will return immediately if no task has locked the mutex, and will block if it is accessed somewhere else).
|
||||
|
||||
==== `Option<T>`
|
||||
|
||||
The `LED` variable needs to be defined outside the main task as references accepted by tasks need to be `'static`. However, if it is outside the main task, it cannot be initialised to point to any pin, as the pins themselves are not initialised. Thus, it is set to `None`.
|
||||
|
||||
==== `Output<AnyPin>`
|
||||
|
||||
To indicate that the pin will be set to an Output. The `AnyPin` could have been `embassy_rp::peripherals::PIN_25`, however this option lets the `toggle_led` function be more generic.
|
||||
|
||||
== Sharing using a Channel
|
||||
|
||||
A channel is another way to ensure exclusive access to a resource. Using a channel is great in the cases where the access can happen at a later point in time, allowing you to enqueue operations and do other things.
|
||||
|
||||
[,rust]
|
||||
----
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio;
|
||||
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
|
||||
use embassy_sync::channel::{Channel, Sender};
|
||||
use embassy_time::{Duration, Ticker};
|
||||
use gpio::{AnyPin, Level, Output};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
enum LedState {
|
||||
Toggle,
|
||||
}
|
||||
static CHANNEL: Channel<ThreadModeRawMutex, LedState, 64> = Channel::new();
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
let mut led = Output::new(AnyPin::from(p.PIN_25), Level::High);
|
||||
|
||||
let dt = 100 * 1_000_000;
|
||||
let k = 1.003;
|
||||
|
||||
unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos(dt))));
|
||||
unwrap!(spawner.spawn(toggle_led(CHANNEL.sender(), Duration::from_nanos((dt as f64 * k) as u64))));
|
||||
|
||||
loop {
|
||||
match CHANNEL.receive().await {
|
||||
LedState::Toggle => led.toggle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A pool size of 2 means you can spawn two instances of this task.
|
||||
#[embassy_executor::task(pool_size = 2)]
|
||||
async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>, delay: Duration) {
|
||||
let mut ticker = Ticker::every(delay);
|
||||
loop {
|
||||
control.send(LedState::Toggle).await;
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
This example replaces the Mutex with a Channel, and uses another task (the main loop) to drive the LED. The advantage of this approach is that only a single task references the peripheral, separating concerns. However, using a Mutex has a lower overhead and might be necessary if you need to ensure
|
||||
that the operation is ecompleted before continuing to do other work in your task.
|
60
docs/modules/ROOT/pages/time_keeping.adoc
Normal file
60
docs/modules/ROOT/pages/time_keeping.adoc
Normal file
@ -0,0 +1,60 @@
|
||||
= Time-keeping
|
||||
|
||||
In an embedded program, delaying a task is one of the most common actions taken. In an event loop, delays will need to be inserted to ensure
|
||||
that other tasks have a chance to run before the next iteration of the loop is called, if no other I/O is performed. Embassy provides abstractions
|
||||
to delay the current task for a specified interval of time.
|
||||
|
||||
The interface for time-keeping in Embassy is handled by the link:https://crates.io/crates/embassy-time[embassy-time] crate. The types can be used with the internal
|
||||
timer queue in link:https://crates.io/crates/embassy-executor[embassy-executor] or a custom timer queue implementation.
|
||||
|
||||
== Timer
|
||||
|
||||
The `embassy::time::Timer` type provides two timing methods.
|
||||
|
||||
`Timer::at` creates a future that completes at the specified `Instant`, relative to the system boot time.
|
||||
`Timer::after` creates a future that completes after the specified `Duration`, relative to when the future was created.
|
||||
|
||||
An example of a delay is provided as follows:
|
||||
|
||||
[,rust]
|
||||
----
|
||||
use embassy::executor::{task, Executor};
|
||||
use embassy::time::{Duration, Timer};
|
||||
|
||||
#[task]
|
||||
/// Task that ticks periodically
|
||||
async fn tick_periodic() -> ! {
|
||||
loop {
|
||||
rprintln!("tick!");
|
||||
// async sleep primitive, suspends the task for 500ms.
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
== Delay
|
||||
|
||||
The `embassy::time::Delay` type provides an implementation of the link:https://docs.rs/embedded-hal/1.0.0/embedded_hal/delay/index.html[embedded-hal] and
|
||||
link:https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/index.html[embedded-hal-async] traits. This can be used for drivers
|
||||
that expect a generic delay implementation to be provided.
|
||||
|
||||
An example of how this can be used:
|
||||
|
||||
[,rust]
|
||||
----
|
||||
use embassy::executor::{task, Executor};
|
||||
|
||||
#[task]
|
||||
/// Task that ticks periodically
|
||||
async fn tick_periodic() -> ! {
|
||||
loop {
|
||||
rprintln!("tick!");
|
||||
// async sleep primitive, suspends the task for 500ms.
|
||||
generic_delay(embassy::time::Delay).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn generic_delay<D: embedded_hal_async::delay::DelayNs>(delay: D) {
|
||||
delay.delay_ms(500).await;
|
||||
}
|
||||
----
|
@ -1,13 +1,20 @@
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "embassy-boot-nrf"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
description = "Bootloader lib for nRF chips"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/embassy-rs/embassy"
|
||||
documentation = "https://docs.embassy.dev/embassy-boot-nrf"
|
||||
categories = [
|
||||
"embedded",
|
||||
"no-std",
|
||||
"asynchronous",
|
||||
]
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/nrf/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot/nrf/src/"
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot-nrf/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot-nrf/src/"
|
||||
features = ["embassy-nrf/nrf52840"]
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
||||
@ -16,16 +23,16 @@ target = "thumbv7em-none-eabi"
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
|
||||
embassy-sync = { version = "0.5.0", path = "../../embassy-sync" }
|
||||
embassy-nrf = { path = "../../embassy-nrf" }
|
||||
embassy-boot = { path = "../boot", default-features = false }
|
||||
embassy-sync = { version = "0.5.0", path = "../embassy-sync" }
|
||||
embassy-nrf = { version = "0.1.0", path = "../embassy-nrf", default-features = false }
|
||||
embassy-boot = { version = "0.2.0", path = "../embassy-boot" }
|
||||
cortex-m = { version = "0.7.6" }
|
||||
cortex-m-rt = { version = "0.7" }
|
||||
embedded-storage = "0.3.1"
|
||||
embedded-storage-async = { version = "0.4.1" }
|
||||
cfg-if = "1.0.0"
|
||||
|
||||
nrf-softdevice-mbr = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-softdevice.git", branch = "master", optional = true }
|
||||
nrf-softdevice-mbr = { version = "0.2.0", optional = true }
|
||||
|
||||
[features]
|
||||
defmt = [
|
11
embassy-boot-nrf/README.md
Normal file
11
embassy-boot-nrf/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# embassy-boot-nrf
|
||||
|
||||
An [Embassy](https://embassy.dev) project.
|
||||
|
||||
An adaptation of `embassy-boot` for nRF.
|
||||
|
||||
## Features
|
||||
|
||||
* Load applications with or without the softdevice.
|
||||
* Configure bootloader partitions based on linker script.
|
||||
* Using watchdog timer to detect application failure.
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
@ -4,8 +4,8 @@
|
||||
mod fmt;
|
||||
|
||||
pub use embassy_boot::{
|
||||
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareState, FirmwareUpdater,
|
||||
FirmwareUpdaterConfig,
|
||||
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootError, BootLoaderConfig, FirmwareState,
|
||||
FirmwareUpdater, FirmwareUpdaterConfig,
|
||||
};
|
||||
use embassy_nrf::nvmc::PAGE_SIZE;
|
||||
use embassy_nrf::peripherals::WDT;
|
||||
@ -16,14 +16,21 @@ use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
||||
pub struct BootLoader<const BUFFER_SIZE: usize = PAGE_SIZE>;
|
||||
|
||||
impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
|
||||
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware.
|
||||
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
|
||||
pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
|
||||
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
|
||||
) -> Self {
|
||||
Self::try_prepare::<ACTIVE, DFU, STATE>(config).expect("Boot prepare error")
|
||||
}
|
||||
|
||||
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
|
||||
pub fn try_prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
|
||||
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
|
||||
) -> Result<Self, BootError> {
|
||||
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
|
||||
let mut boot = embassy_boot::BootLoader::new(config);
|
||||
boot.prepare_boot(&mut aligned_buf.0).expect("Boot prepare error");
|
||||
Self
|
||||
let _state = boot.prepare_boot(aligned_buf.as_mut())?;
|
||||
Ok(Self)
|
||||
}
|
||||
|
||||
/// Boots the application without softdevice mechanisms.
|
@ -1,13 +1,20 @@
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "embassy-boot-rp"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
description = "Bootloader lib for RP2040 chips"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/embassy-rs/embassy"
|
||||
documentation = "https://docs.embassy.dev/embassy-boot-rp"
|
||||
categories = [
|
||||
"embedded",
|
||||
"no-std",
|
||||
"asynchronous",
|
||||
]
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-rp-v$VERSION/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot/rp/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot-rp/src/"
|
||||
target = "thumbv6m-none-eabi"
|
||||
|
||||
[lib]
|
||||
@ -17,10 +24,10 @@ defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
log = { version = "0.4", optional = true }
|
||||
|
||||
embassy-sync = { version = "0.5.0", path = "../../embassy-sync" }
|
||||
embassy-rp = { path = "../../embassy-rp", default-features = false }
|
||||
embassy-boot = { path = "../boot", default-features = false }
|
||||
embassy-time = { path = "../../embassy-time" }
|
||||
embassy-sync = { version = "0.5.0", path = "../embassy-sync" }
|
||||
embassy-rp = { version = "0.1.0", path = "../embassy-rp", default-features = false }
|
||||
embassy-boot = { version = "0.2.0", path = "../embassy-boot" }
|
||||
embassy-time = { version = "0.3.0", path = "../embassy-time" }
|
||||
|
||||
cortex-m = { version = "0.7.6" }
|
||||
cortex-m-rt = { version = "0.7" }
|
12
embassy-boot-rp/README.md
Normal file
12
embassy-boot-rp/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# embassy-boot-rp
|
||||
|
||||
An [Embassy](https://embassy.dev) project.
|
||||
|
||||
An adaptation of `embassy-boot` for RP2040.
|
||||
|
||||
NOTE: The applications using this bootloader should not link with the `link-rp.x` linker script.
|
||||
|
||||
## Features
|
||||
|
||||
* Configure bootloader partitions based on linker script.
|
||||
* Load applications from active partition.
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
@ -4,8 +4,8 @@
|
||||
mod fmt;
|
||||
|
||||
pub use embassy_boot::{
|
||||
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareState, FirmwareUpdater,
|
||||
FirmwareUpdaterConfig, State,
|
||||
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootError, BootLoaderConfig, FirmwareState,
|
||||
FirmwareUpdater, FirmwareUpdaterConfig, State,
|
||||
};
|
||||
use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE};
|
||||
use embassy_rp::peripherals::{FLASH, WATCHDOG};
|
||||
@ -21,10 +21,17 @@ impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
|
||||
pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
|
||||
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
|
||||
) -> Self {
|
||||
Self::try_prepare::<ACTIVE, DFU, STATE>(config).expect("Boot prepare error")
|
||||
}
|
||||
|
||||
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
|
||||
pub fn try_prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
|
||||
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
|
||||
) -> Result<Self, BootError> {
|
||||
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
|
||||
let mut boot = embassy_boot::BootLoader::new(config);
|
||||
boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
|
||||
Self
|
||||
let _state = boot.prepare_boot(aligned_buf.as_mut())?;
|
||||
Ok(Self)
|
||||
}
|
||||
|
||||
/// Boots the application.
|
@ -1,13 +1,20 @@
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "embassy-boot-stm32"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
description = "Bootloader lib for STM32 chips"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/embassy-rs/embassy"
|
||||
documentation = "https://docs.embassy.dev/embassy-boot-stm32"
|
||||
categories = [
|
||||
"embedded",
|
||||
"no-std",
|
||||
"asynchronous",
|
||||
]
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/stm32/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot/stm32/src/"
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot-stm32/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot-stm32/src/"
|
||||
features = ["embassy-stm32/stm32f429zi"]
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
||||
@ -18,9 +25,9 @@ defmt = { version = "0.3", optional = true }
|
||||
defmt-rtt = { version = "0.4", optional = true }
|
||||
log = { version = "0.4", optional = true }
|
||||
|
||||
embassy-sync = { version = "0.5.0", path = "../../embassy-sync" }
|
||||
embassy-stm32 = { path = "../../embassy-stm32", default-features = false }
|
||||
embassy-boot = { path = "../boot", default-features = false }
|
||||
embassy-sync = { version = "0.5.0", path = "../embassy-sync" }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false }
|
||||
embassy-boot = { version = "0.2.0", path = "../embassy-boot" }
|
||||
cortex-m = { version = "0.7.6" }
|
||||
cortex-m-rt = { version = "0.7" }
|
||||
embedded-storage = "0.3.1"
|
10
embassy-boot-stm32/README.md
Normal file
10
embassy-boot-stm32/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# embassy-boot-stm32
|
||||
|
||||
An [Embassy](https://embassy.dev) project.
|
||||
|
||||
An adaptation of `embassy-boot` for STM32.
|
||||
|
||||
## Features
|
||||
|
||||
* Configure bootloader partitions based on linker script.
|
||||
* Load applications from active partition.
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
@ -4,23 +4,33 @@
|
||||
mod fmt;
|
||||
|
||||
pub use embassy_boot::{
|
||||
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareState, FirmwareUpdater,
|
||||
FirmwareUpdaterConfig, State,
|
||||
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootError, BootLoaderConfig, FirmwareState,
|
||||
FirmwareUpdater, FirmwareUpdaterConfig, State,
|
||||
};
|
||||
use embedded_storage::nor_flash::NorFlash;
|
||||
|
||||
/// A bootloader for STM32 devices.
|
||||
pub struct BootLoader;
|
||||
pub struct BootLoader {
|
||||
/// The reported state of the bootloader after preparing for boot
|
||||
pub state: State,
|
||||
}
|
||||
|
||||
impl BootLoader {
|
||||
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
|
||||
pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>(
|
||||
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
|
||||
) -> Self {
|
||||
Self::try_prepare::<ACTIVE, DFU, STATE, BUFFER_SIZE>(config).expect("Boot prepare error")
|
||||
}
|
||||
|
||||
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
|
||||
pub fn try_prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>(
|
||||
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
|
||||
) -> Result<Self, BootError> {
|
||||
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
|
||||
let mut boot = embassy_boot::BootLoader::new(config);
|
||||
boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
|
||||
Self
|
||||
let state = boot.prepare_boot(aligned_buf.as_mut())?;
|
||||
Ok(Self { state })
|
||||
}
|
||||
|
||||
/// Boots the application.
|
@ -1,10 +1,11 @@
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "embassy-boot"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
description = "A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks."
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/embassy-rs/embassy"
|
||||
documentation = "https://docs.embassy.dev/embassy-boot"
|
||||
categories = [
|
||||
"embedded",
|
||||
"no-std",
|
||||
@ -12,8 +13,8 @@ categories = [
|
||||
]
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/boot/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot/boot/src/"
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot/src/"
|
||||
target = "thumbv7em-none-eabi"
|
||||
features = ["defmt"]
|
||||
|
||||
@ -26,25 +27,22 @@ features = ["defmt"]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
digest = "0.10"
|
||||
log = { version = "0.4", optional = true }
|
||||
ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true }
|
||||
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
|
||||
embassy-sync = { version = "0.5.0", path = "../../embassy-sync" }
|
||||
ed25519-dalek = { version = "2", default_features = false, features = ["digest"], optional = true }
|
||||
embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||
embassy-sync = { version = "0.5.0", path = "../embassy-sync" }
|
||||
embedded-storage = "0.3.1"
|
||||
embedded-storage-async = { version = "0.4.1" }
|
||||
salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true }
|
||||
signature = { version = "1.6.4", default-features = false }
|
||||
salty = { version = "0.3", optional = true }
|
||||
signature = { version = "2.0", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
log = "0.4"
|
||||
env_logger = "0.9"
|
||||
rand = "0.7" # ed25519-dalek v1.0.1 depends on this exact version
|
||||
rand = "0.8"
|
||||
futures = { version = "0.3", features = ["executor"] }
|
||||
sha1 = "0.10.5"
|
||||
critical-section = { version = "1.1.1", features = ["std"] }
|
||||
|
||||
[dev-dependencies.ed25519-dalek]
|
||||
default_features = false
|
||||
features = ["rand", "std", "u32_backend"]
|
||||
ed25519-dalek = { version = "2", default_features = false, features = ["std", "rand_core", "digest"] }
|
||||
|
||||
[features]
|
||||
ed25519-dalek = ["dep:ed25519-dalek", "_verify"]
|
35
embassy-boot/README.md
Normal file
35
embassy-boot/README.md
Normal file
@ -0,0 +1,35 @@
|
||||
# embassy-boot
|
||||
|
||||
An [Embassy](https://embassy.dev) project.
|
||||
|
||||
A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks.
|
||||
|
||||
The bootloader can be used either as a library or be flashed directly with the default configuration derived from linker scripts.
|
||||
|
||||
By 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.
|
||||
|
||||
## Overview
|
||||
|
||||
The bootloader divides the storage into 4 main partitions, configurable when creating the bootloader instance or via linker scripts:
|
||||
|
||||
* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash, but if you need to debug it and have space available, increasing this to 24kB will allow you to run the bootloader with probe-rs.
|
||||
* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. The minimum size required for this partition is the size of your application.
|
||||
* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. This partition must be at least 1 page bigger than the ACTIVE partition.
|
||||
* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped.
|
||||
|
||||
For any partition, the following preconditions are required:
|
||||
|
||||
* Partitions must be aligned on the page size.
|
||||
* Partitions must be a multiple of the page size.
|
||||
|
||||
The linker scripts for the application and bootloader look similar, but the FLASH region must point to the BOOTLOADER partition for the bootloader, and the ACTIVE partition for the application.
|
||||
|
||||
For more details on the bootloader, see [the documentation](https://embassy.dev/book/dev/bootloader.html).
|
||||
|
||||
## Hardware support
|
||||
|
||||
The bootloader supports different hardware in separate crates:
|
||||
|
||||
* `embassy-boot-nrf` - for the nRF microcontrollers.
|
||||
* `embassy-boot-rp` - for the RP2040 microcontrollers.
|
||||
* `embassy-boot-stm32` - for the STM32 microcontrollers.
|
@ -1,31 +0,0 @@
|
||||
# embassy-boot
|
||||
|
||||
An [Embassy](https://embassy.dev) project.
|
||||
|
||||
A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks.
|
||||
|
||||
The bootloader can be used either as a library or be flashed directly with the default configuration derived from linker scripts.
|
||||
|
||||
By 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.
|
||||
|
||||
## Hardware support
|
||||
|
||||
The bootloader supports different hardware in separate crates:
|
||||
|
||||
* `embassy-boot-nrf` - for the nRF microcontrollers.
|
||||
* `embassy-boot-rp` - for the RP2040 microcontrollers.
|
||||
* `embassy-boot-stm32` - for the STM32 microcontrollers.
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
`embassy-boot` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
This work is licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||
|
||||
at your option.
|
@ -1,26 +0,0 @@
|
||||
# embassy-boot-nrf
|
||||
|
||||
An [Embassy](https://embassy.dev) project.
|
||||
|
||||
An adaptation of `embassy-boot` for nRF.
|
||||
|
||||
## Features
|
||||
|
||||
* Load applications with or without the softdevice.
|
||||
* Configure bootloader partitions based on linker script.
|
||||
* Using watchdog timer to detect application failure.
|
||||
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
`embassy-boot-nrf` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
This work is licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||
|
||||
at your option.
|
@ -1,26 +0,0 @@
|
||||
# embassy-boot-rp
|
||||
|
||||
An [Embassy](https://embassy.dev) project.
|
||||
|
||||
An adaptation of `embassy-boot` for RP2040.
|
||||
|
||||
NOTE: The applications using this bootloader should not link with the `link-rp.x` linker script.
|
||||
|
||||
## Features
|
||||
|
||||
* Configure bootloader partitions based on linker script.
|
||||
* Load applications from active partition.
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
`embassy-boot-rp` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
This work is licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||
|
||||
at your option.
|
@ -5,7 +5,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
|
||||
|
||||
use crate::{State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
||||
use crate::{State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
||||
|
||||
/// Errors returned by bootloader
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
@ -49,16 +49,51 @@ pub struct BootLoaderConfig<ACTIVE, DFU, STATE> {
|
||||
pub state: STATE,
|
||||
}
|
||||
|
||||
impl<'a, FLASH: NorFlash>
|
||||
impl<'a, ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>
|
||||
BootLoaderConfig<
|
||||
BlockingPartition<'a, NoopRawMutex, FLASH>,
|
||||
BlockingPartition<'a, NoopRawMutex, FLASH>,
|
||||
BlockingPartition<'a, NoopRawMutex, FLASH>,
|
||||
BlockingPartition<'a, NoopRawMutex, ACTIVE>,
|
||||
BlockingPartition<'a, NoopRawMutex, DFU>,
|
||||
BlockingPartition<'a, NoopRawMutex, STATE>,
|
||||
>
|
||||
{
|
||||
/// Create a bootloader config from the flash and address symbols defined in the linkerfile
|
||||
/// Constructs a `BootLoaderConfig` instance from flash memory and address symbols defined in the linker file.
|
||||
///
|
||||
/// This method initializes `BlockingPartition` instances for the active, DFU (Device Firmware Update),
|
||||
/// and state partitions, leveraging start and end addresses specified by the linker. These partitions
|
||||
/// are critical for managing firmware updates, application state, and boot operations within the bootloader.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `active_flash`: A reference to a mutex-protected `RefCell` for the active partition's flash interface.
|
||||
/// - `dfu_flash`: A reference to a mutex-protected `RefCell` for the DFU partition's flash interface.
|
||||
/// - `state_flash`: A reference to a mutex-protected `RefCell` for the state partition's flash interface.
|
||||
///
|
||||
/// # Safety
|
||||
/// The method contains `unsafe` blocks for dereferencing raw pointers that represent the start and end addresses
|
||||
/// of the bootloader's partitions in flash memory. It is crucial that these addresses are accurately defined
|
||||
/// in the memory.x file to prevent undefined behavior.
|
||||
///
|
||||
/// The caller must ensure that the memory regions defined by these symbols are valid and that the flash memory
|
||||
/// interfaces provided are compatible with these regions.
|
||||
///
|
||||
/// # Returns
|
||||
/// A `BootLoaderConfig` instance with `BlockingPartition` instances for the active, DFU, and state partitions.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// // Assume `active_flash`, `dfu_flash`, and `state_flash` all share the same flash memory interface.
|
||||
/// let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();
|
||||
/// let flash = Mutex::new(RefCell::new(layout.bank1_region));
|
||||
///
|
||||
/// let config = BootLoaderConfig::from_linkerfile_blocking(&flash, &flash, &flash);
|
||||
/// // `config` can now be used to create a `BootLoader` instance for managing boot operations.
|
||||
/// ```
|
||||
/// Working examples can be found in the bootloader examples folder.
|
||||
// #[cfg(target_os = "none")]
|
||||
pub fn from_linkerfile_blocking(flash: &'a Mutex<NoopRawMutex, RefCell<FLASH>>) -> Self {
|
||||
pub fn from_linkerfile_blocking(
|
||||
active_flash: &'a Mutex<NoopRawMutex, RefCell<ACTIVE>>,
|
||||
dfu_flash: &'a Mutex<NoopRawMutex, RefCell<DFU>>,
|
||||
state_flash: &'a Mutex<NoopRawMutex, RefCell<STATE>>,
|
||||
) -> Self {
|
||||
extern "C" {
|
||||
static __bootloader_state_start: u32;
|
||||
static __bootloader_state_end: u32;
|
||||
@ -73,21 +108,21 @@ impl<'a, FLASH: NorFlash>
|
||||
let end = &__bootloader_active_end as *const u32 as u32;
|
||||
trace!("ACTIVE: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
BlockingPartition::new(flash, start, end - start)
|
||||
BlockingPartition::new(active_flash, start, end - start)
|
||||
};
|
||||
let dfu = unsafe {
|
||||
let start = &__bootloader_dfu_start as *const u32 as u32;
|
||||
let end = &__bootloader_dfu_end as *const u32 as u32;
|
||||
trace!("DFU: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
BlockingPartition::new(flash, start, end - start)
|
||||
BlockingPartition::new(dfu_flash, start, end - start)
|
||||
};
|
||||
let state = unsafe {
|
||||
let start = &__bootloader_state_start as *const u32 as u32;
|
||||
let end = &__bootloader_state_end as *const u32 as u32;
|
||||
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
BlockingPartition::new(flash, start, end - start)
|
||||
BlockingPartition::new(state_flash, start, end - start)
|
||||
};
|
||||
|
||||
Self { active, dfu, state }
|
||||
@ -135,51 +170,44 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
|
||||
/// The provided aligned_buf argument must satisfy any alignment requirements
|
||||
/// given by the partition flashes. All flash operations will use this buffer.
|
||||
///
|
||||
/// SWAPPING
|
||||
/// ## SWAPPING
|
||||
///
|
||||
/// Assume a flash size of 3 pages for the active partition, and 4 pages for the DFU partition.
|
||||
/// The swap index contains the copy progress, as to allow continuation of the copy process on
|
||||
/// power failure. The index counter is represented within 1 or more pages (depending on total
|
||||
/// flash size), where a page X is considered swapped if index at location (X + WRITE_SIZE)
|
||||
/// flash size), where a page X is considered swapped if index at location (`X + WRITE_SIZE`)
|
||||
/// contains a zero value. This ensures that index updates can be performed atomically and
|
||||
/// avoid a situation where the wrong index value is set (page write size is "atomic").
|
||||
///
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
///
|
||||
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
/// |-----------|------------|--------|--------|--------|--------|
|
||||
/// | Active | 0 | 1 | 2 | 3 | - |
|
||||
/// | DFU | 0 | 3 | 2 | 1 | X |
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
///
|
||||
/// The algorithm starts by copying 'backwards', and after the first step, the layout is
|
||||
/// as follows:
|
||||
///
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
/// |-----------|------------|--------|--------|--------|--------|
|
||||
/// | Active | 1 | 1 | 2 | 1 | - |
|
||||
/// | DFU | 1 | 3 | 2 | 1 | 3 |
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
///
|
||||
/// The next iteration performs the same steps
|
||||
///
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
/// |-----------|------------|--------|--------|--------|--------|
|
||||
/// | Active | 2 | 1 | 2 | 1 | - |
|
||||
/// | DFU | 2 | 3 | 2 | 2 | 3 |
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
///
|
||||
/// And again until we're done
|
||||
///
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
/// | Partition | Swap Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
/// |-----------|------------|--------|--------|--------|--------|
|
||||
/// | Active | 3 | 3 | 2 | 1 | - |
|
||||
/// | DFU | 3 | 3 | 1 | 2 | 3 |
|
||||
/// +-----------+------------+--------+--------+--------+--------+
|
||||
///
|
||||
/// REVERTING
|
||||
/// ## REVERTING
|
||||
///
|
||||
/// The reverting algorithm uses the swap index to discover that images were swapped, but that
|
||||
/// the application failed to mark the boot successful. In this case, the revert algorithm will
|
||||
@ -190,28 +218,21 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
|
||||
///
|
||||
/// The revert algorithm works forwards, by starting copying into the 'unused' DFU page at the start.
|
||||
///
|
||||
/// +-----------+--------------+--------+--------+--------+--------+
|
||||
/// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
//*/
|
||||
/// +-----------+--------------+--------+--------+--------+--------+
|
||||
/// |-----------|--------------|--------|--------|--------|--------|
|
||||
/// | Active | 3 | 1 | 2 | 1 | - |
|
||||
/// | DFU | 3 | 3 | 1 | 2 | 3 |
|
||||
/// +-----------+--------------+--------+--------+--------+--------+
|
||||
///
|
||||
///
|
||||
/// +-----------+--------------+--------+--------+--------+--------+
|
||||
/// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
/// +-----------+--------------+--------+--------+--------+--------+
|
||||
/// |-----------|--------------|--------|--------|--------|--------|
|
||||
/// | Active | 3 | 1 | 2 | 1 | - |
|
||||
/// | DFU | 3 | 3 | 2 | 2 | 3 |
|
||||
/// +-----------+--------------+--------+--------+--------+--------+
|
||||
///
|
||||
/// +-----------+--------------+--------+--------+--------+--------+
|
||||
/// | Partition | Revert Index | Page 0 | Page 1 | Page 3 | Page 4 |
|
||||
/// +-----------+--------------+--------+--------+--------+--------+
|
||||
/// |-----------|--------------|--------|--------|--------|--------|
|
||||
/// | Active | 3 | 1 | 2 | 3 | - |
|
||||
/// | DFU | 3 | 3 | 2 | 1 | 3 |
|
||||
/// +-----------+--------------+--------+--------+--------+--------+
|
||||
///
|
||||
pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> {
|
||||
// Ensure we have enough progress pages to store copy progress
|
||||
@ -224,6 +245,7 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
|
||||
assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE);
|
||||
assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE);
|
||||
|
||||
// Ensure our partitions are able to handle boot operations
|
||||
assert_partitions(&self.active, &self.dfu, &self.state, Self::PAGE_SIZE);
|
||||
|
||||
// Copy contents from partition N to active
|
||||
@ -384,6 +406,8 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
|
||||
|
||||
if !state_word.iter().any(|&b| b != SWAP_MAGIC) {
|
||||
Ok(State::Swap)
|
||||
} else if !state_word.iter().any(|&b| b != DFU_DETACH_MAGIC) {
|
||||
Ok(State::DfuDetach)
|
||||
} else {
|
||||
Ok(State::Boot)
|
||||
}
|
||||
@ -398,6 +422,7 @@ fn assert_partitions<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
|
||||
) {
|
||||
assert_eq!(active.capacity() as u32 % page_size, 0);
|
||||
assert_eq!(dfu.capacity() as u32 % page_size, 0);
|
||||
// DFU partition has to be bigger than ACTIVE partition to handle swap algorithm
|
||||
assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size);
|
||||
assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use digest::typenum::U64;
|
||||
use digest::{FixedOutput, HashMarker, OutputSizeUser, Update};
|
||||
use ed25519_dalek::Digest as _;
|
||||
use ed25519_dalek::Digest;
|
||||
|
||||
pub struct Sha512(ed25519_dalek::Sha512);
|
||||
|
||||
@ -12,7 +12,7 @@ impl Default for Sha512 {
|
||||
|
||||
impl Update for Sha512 {
|
||||
fn update(&mut self, data: &[u8]) {
|
||||
self.0.update(data)
|
||||
Digest::update(&mut self.0, data)
|
||||
}
|
||||
}
|
||||
|
@ -6,21 +6,25 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||
use embedded_storage_async::nor_flash::NorFlash;
|
||||
|
||||
use super::FirmwareUpdaterConfig;
|
||||
use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
||||
use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
||||
|
||||
/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
||||
/// 'mess up' the internal bootloader state
|
||||
pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
|
||||
dfu: DFU,
|
||||
state: FirmwareState<'d, STATE>,
|
||||
last_erased_dfu_sector_index: Option<usize>,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "none")]
|
||||
impl<'a, FLASH: NorFlash>
|
||||
FirmwareUpdaterConfig<Partition<'a, NoopRawMutex, FLASH>, Partition<'a, NoopRawMutex, FLASH>>
|
||||
impl<'a, DFU: NorFlash, STATE: NorFlash>
|
||||
FirmwareUpdaterConfig<Partition<'a, NoopRawMutex, DFU>, Partition<'a, NoopRawMutex, STATE>>
|
||||
{
|
||||
/// Create a firmware updater config from the flash and address symbols defined in the linkerfile
|
||||
pub fn from_linkerfile(flash: &'a embassy_sync::mutex::Mutex<NoopRawMutex, FLASH>) -> Self {
|
||||
pub fn from_linkerfile(
|
||||
dfu_flash: &'a embassy_sync::mutex::Mutex<NoopRawMutex, DFU>,
|
||||
state_flash: &'a embassy_sync::mutex::Mutex<NoopRawMutex, STATE>,
|
||||
) -> Self {
|
||||
extern "C" {
|
||||
static __bootloader_state_start: u32;
|
||||
static __bootloader_state_end: u32;
|
||||
@ -33,14 +37,14 @@ impl<'a, FLASH: NorFlash>
|
||||
let end = &__bootloader_dfu_end as *const u32 as u32;
|
||||
trace!("DFU: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
Partition::new(flash, start, end - start)
|
||||
Partition::new(dfu_flash, start, end - start)
|
||||
};
|
||||
let state = unsafe {
|
||||
let start = &__bootloader_state_start as *const u32 as u32;
|
||||
let end = &__bootloader_state_end as *const u32 as u32;
|
||||
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
Partition::new(flash, start, end - start)
|
||||
Partition::new(state_flash, start, end - start)
|
||||
};
|
||||
|
||||
Self { dfu, state }
|
||||
@ -53,6 +57,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
||||
Self {
|
||||
dfu: config.dfu,
|
||||
state: FirmwareState::new(config.state, aligned),
|
||||
last_erased_dfu_sector_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,7 +74,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
||||
/// proceed with updating the firmware as it must be signed with a
|
||||
/// corresponding private key (otherwise it could be malicious firmware).
|
||||
///
|
||||
/// Mark to trigger firmware swap on next boot if verify suceeds.
|
||||
/// Mark to trigger firmware swap on next boot if verify succeeds.
|
||||
///
|
||||
/// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
|
||||
/// been generated from a SHA-512 digest of the firmware bytes.
|
||||
@ -79,8 +84,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
||||
#[cfg(feature = "_verify")]
|
||||
pub async fn verify_and_mark_updated(
|
||||
&mut self,
|
||||
_public_key: &[u8],
|
||||
_signature: &[u8],
|
||||
_public_key: &[u8; 32],
|
||||
_signature: &[u8; 64],
|
||||
_update_len: u32,
|
||||
) -> Result<(), FirmwareUpdaterError> {
|
||||
assert!(_update_len <= self.dfu.capacity() as u32);
|
||||
@ -89,14 +94,14 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
||||
|
||||
#[cfg(feature = "ed25519-dalek")]
|
||||
{
|
||||
use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
|
||||
use ed25519_dalek::{Signature, SignatureError, Verifier, VerifyingKey};
|
||||
|
||||
use crate::digest_adapters::ed25519_dalek::Sha512;
|
||||
|
||||
let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
|
||||
|
||||
let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
||||
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
|
||||
let public_key = VerifyingKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
||||
let signature = Signature::from_bytes(_signature);
|
||||
|
||||
let mut chunk_buf = [0; 2];
|
||||
let mut message = [0; 64];
|
||||
@ -106,7 +111,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
||||
}
|
||||
#[cfg(feature = "ed25519-salty")]
|
||||
{
|
||||
use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
|
||||
use salty::{PublicKey, Signature};
|
||||
|
||||
use crate::digest_adapters::salty::Sha512;
|
||||
@ -115,10 +119,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
||||
FirmwareUpdaterError::Signature(signature::Error::default())
|
||||
}
|
||||
|
||||
let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
|
||||
let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
|
||||
let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
|
||||
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
|
||||
let public_key = PublicKey::try_from(_public_key).map_err(into_signature_error)?;
|
||||
let signature = Signature::try_from(_signature).map_err(into_signature_error)?;
|
||||
|
||||
let mut message = [0; 64];
|
||||
let mut chunk_buf = [0; 2];
|
||||
@ -161,26 +163,79 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
||||
self.state.mark_updated().await
|
||||
}
|
||||
|
||||
/// Mark to trigger USB DFU on next boot.
|
||||
pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||
self.state.verify_booted().await?;
|
||||
self.state.mark_dfu().await
|
||||
}
|
||||
|
||||
/// Mark firmware boot successful and stop rollback on reset.
|
||||
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||
self.state.mark_booted().await
|
||||
}
|
||||
|
||||
/// Write data to a flash page.
|
||||
/// Writes firmware data to the device.
|
||||
///
|
||||
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
|
||||
/// This function writes the given data to the firmware area starting at the specified offset.
|
||||
/// It handles sector erasures and data writes while verifying the device is in a proper state
|
||||
/// for firmware updates. The function ensures that only unerased sectors are erased before
|
||||
/// writing and efficiently handles the writing process across sector boundaries and in
|
||||
/// various configurations (data size, sector size, etc.).
|
||||
///
|
||||
/// # Safety
|
||||
/// # Arguments
|
||||
///
|
||||
/// Failing to meet alignment and size requirements may result in a panic.
|
||||
/// * `offset` - The starting offset within the firmware area where data writing should begin.
|
||||
/// * `data` - A slice of bytes representing the firmware data to be written. It must be a
|
||||
/// multiple of NorFlash WRITE_SIZE.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if:
|
||||
///
|
||||
/// - The device is not in a proper state to receive firmware updates (e.g., not booted).
|
||||
/// - There is a failure erasing a sector before writing.
|
||||
/// - There is a failure writing data to the device.
|
||||
pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
|
||||
assert!(data.len() >= DFU::ERASE_SIZE);
|
||||
|
||||
// Make sure we are running a booted firmware to avoid reverting to a bad state.
|
||||
self.state.verify_booted().await?;
|
||||
|
||||
self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?;
|
||||
// Initialize variables to keep track of the remaining data and the current offset.
|
||||
let mut remaining_data = data;
|
||||
let mut offset = offset;
|
||||
|
||||
self.dfu.write(offset as u32, data).await?;
|
||||
// Continue writing as long as there is data left to write.
|
||||
while !remaining_data.is_empty() {
|
||||
// Compute the current sector and its boundaries.
|
||||
let current_sector = offset / DFU::ERASE_SIZE;
|
||||
let sector_start = current_sector * DFU::ERASE_SIZE;
|
||||
let sector_end = sector_start + DFU::ERASE_SIZE;
|
||||
// Determine if the current sector needs to be erased before writing.
|
||||
let need_erase = self
|
||||
.last_erased_dfu_sector_index
|
||||
.map_or(true, |last_erased_sector| current_sector != last_erased_sector);
|
||||
|
||||
// If the sector needs to be erased, erase it and update the last erased sector index.
|
||||
if need_erase {
|
||||
self.dfu.erase(sector_start as u32, sector_end as u32).await?;
|
||||
self.last_erased_dfu_sector_index = Some(current_sector);
|
||||
}
|
||||
|
||||
// Calculate the size of the data chunk that can be written in the current iteration.
|
||||
let write_size = core::cmp::min(remaining_data.len(), sector_end - offset);
|
||||
// Split the data to get the current chunk to be written and the remaining data.
|
||||
let (data_chunk, rest) = remaining_data.split_at(write_size);
|
||||
|
||||
// Write the current data chunk.
|
||||
self.dfu.write(offset as u32, data_chunk).await?;
|
||||
|
||||
// Update the offset and remaining data for the next iteration.
|
||||
remaining_data = rest;
|
||||
offset += write_size;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -207,14 +262,24 @@ pub struct FirmwareState<'d, STATE> {
|
||||
}
|
||||
|
||||
impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
|
||||
/// Create a firmware state instance with a buffer for magic content and state partition.
|
||||
/// Create a firmware state instance from a FirmwareUpdaterConfig with a buffer for magic content and state partition.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
|
||||
/// and written to.
|
||||
pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
|
||||
Self::new(config.state, aligned)
|
||||
}
|
||||
|
||||
/// Create a firmware state instance with a buffer for magic content and state partition.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The `aligned` buffer must have a size of maximum of STATE::WRITE_SIZE and STATE::READ_SIZE,
|
||||
/// and follow the alignment rules for the flash being read from and written to.
|
||||
pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self {
|
||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE.max(STATE::READ_SIZE));
|
||||
Self { state, aligned }
|
||||
}
|
||||
|
||||
@ -247,6 +312,11 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
|
||||
self.set_magic(SWAP_MAGIC).await
|
||||
}
|
||||
|
||||
/// Mark to trigger USB DFU on next boot.
|
||||
pub async fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||
self.set_magic(DFU_DETACH_MAGIC).await
|
||||
}
|
||||
|
||||
/// Mark firmware boot successful and stop rollback on reset.
|
||||
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||
self.set_magic(BOOT_MAGIC).await
|
||||
@ -255,16 +325,25 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
|
||||
async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> {
|
||||
self.state.read(0, &mut self.aligned).await?;
|
||||
|
||||
if self.aligned.iter().any(|&b| b != magic) {
|
||||
if self.aligned[..STATE::WRITE_SIZE].iter().any(|&b| b != magic) {
|
||||
// Read progress validity
|
||||
if STATE::READ_SIZE <= 2 * STATE::WRITE_SIZE {
|
||||
self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?;
|
||||
} else {
|
||||
self.aligned.rotate_left(STATE::WRITE_SIZE);
|
||||
}
|
||||
|
||||
if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
|
||||
if self.aligned[..STATE::WRITE_SIZE]
|
||||
.iter()
|
||||
.any(|&b| b != STATE_ERASE_VALUE)
|
||||
{
|
||||
// The current progress validity marker is invalid
|
||||
} else {
|
||||
// Invalidate progress
|
||||
self.aligned.fill(!STATE_ERASE_VALUE);
|
||||
self.state.write(STATE::WRITE_SIZE as u32, &self.aligned).await?;
|
||||
self.state
|
||||
.write(STATE::WRITE_SIZE as u32, &self.aligned[..STATE::WRITE_SIZE])
|
||||
.await?;
|
||||
}
|
||||
|
||||
// Clear magic and progress
|
||||
@ -272,7 +351,7 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
|
||||
|
||||
// Set magic
|
||||
self.aligned.fill(magic);
|
||||
self.state.write(0, &self.aligned).await?;
|
||||
self.state.write(0, &self.aligned[..STATE::WRITE_SIZE]).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -308,4 +387,76 @@ mod tests {
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_verify_sha1_sector_bigger_than_chunk() {
|
||||
let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default());
|
||||
let state = Partition::new(&flash, 0, 4096);
|
||||
let dfu = Partition::new(&flash, 65536, 65536);
|
||||
let mut aligned = [0; 8];
|
||||
|
||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||
let mut to_write = [0; 4096];
|
||||
to_write[..7].copy_from_slice(update.as_slice());
|
||||
|
||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||
let mut offset = 0;
|
||||
for chunk in to_write.chunks(1024) {
|
||||
block_on(updater.write_firmware(offset, chunk)).unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut chunk_buf = [0; 2];
|
||||
let mut hash = [0; 20];
|
||||
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_verify_sha1_sector_smaller_than_chunk() {
|
||||
let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 1024, 8>::default());
|
||||
let state = Partition::new(&flash, 0, 4096);
|
||||
let dfu = Partition::new(&flash, 65536, 65536);
|
||||
let mut aligned = [0; 8];
|
||||
|
||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||
let mut to_write = [0; 4096];
|
||||
to_write[..7].copy_from_slice(update.as_slice());
|
||||
|
||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||
let mut offset = 0;
|
||||
for chunk in to_write.chunks(2048) {
|
||||
block_on(updater.write_firmware(offset, chunk)).unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut chunk_buf = [0; 2];
|
||||
let mut hash = [0; 20];
|
||||
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_verify_sha1_cross_sector_boundary() {
|
||||
let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 1024, 8>::default());
|
||||
let state = Partition::new(&flash, 0, 4096);
|
||||
let dfu = Partition::new(&flash, 65536, 65536);
|
||||
let mut aligned = [0; 8];
|
||||
|
||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||
let mut to_write = [0; 4096];
|
||||
to_write[..7].copy_from_slice(update.as_slice());
|
||||
|
||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||
let mut offset = 0;
|
||||
for chunk in to_write.chunks(896) {
|
||||
block_on(updater.write_firmware(offset, chunk)).unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut chunk_buf = [0; 2];
|
||||
let mut hash = [0; 20];
|
||||
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
}
|
@ -6,22 +6,54 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||
use embedded_storage::nor_flash::NorFlash;
|
||||
|
||||
use super::FirmwareUpdaterConfig;
|
||||
use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
||||
use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC};
|
||||
|
||||
/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
||||
/// 'mess up' the internal bootloader state
|
||||
pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
|
||||
dfu: DFU,
|
||||
state: BlockingFirmwareState<'d, STATE>,
|
||||
last_erased_dfu_sector_index: Option<usize>,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "none")]
|
||||
impl<'a, FLASH: NorFlash>
|
||||
FirmwareUpdaterConfig<BlockingPartition<'a, NoopRawMutex, FLASH>, BlockingPartition<'a, NoopRawMutex, FLASH>>
|
||||
impl<'a, DFU: NorFlash, STATE: NorFlash>
|
||||
FirmwareUpdaterConfig<BlockingPartition<'a, NoopRawMutex, DFU>, BlockingPartition<'a, NoopRawMutex, STATE>>
|
||||
{
|
||||
/// Create a firmware updater config from the flash and address symbols defined in the linkerfile
|
||||
/// Constructs a `FirmwareUpdaterConfig` instance from flash memory and address symbols defined in the linker file.
|
||||
///
|
||||
/// This method initializes `BlockingPartition` instances for the DFU (Device Firmware Update), and state
|
||||
/// partitions, leveraging start and end addresses specified by the linker. These partitions are critical
|
||||
/// for managing firmware updates, application state, and boot operations within the bootloader.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `dfu_flash`: A reference to a mutex-protected `RefCell` for the DFU partition's flash interface.
|
||||
/// - `state_flash`: A reference to a mutex-protected `RefCell` for the state partition's flash interface.
|
||||
///
|
||||
/// # Safety
|
||||
/// The method contains `unsafe` blocks for dereferencing raw pointers that represent the start and end addresses
|
||||
/// of the bootloader's partitions in flash memory. It is crucial that these addresses are accurately defined
|
||||
/// in the memory.x file to prevent undefined behavior.
|
||||
///
|
||||
/// The caller must ensure that the memory regions defined by these symbols are valid and that the flash memory
|
||||
/// interfaces provided are compatible with these regions.
|
||||
///
|
||||
/// # Returns
|
||||
/// A `FirmwareUpdaterConfig` instance with `BlockingPartition` instances for the DFU, and state partitions.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// // Assume `dfu_flash`, and `state_flash` share the same flash memory interface.
|
||||
/// let layout = Flash::new_blocking(p.FLASH).into_blocking_regions();
|
||||
/// let flash = Mutex::new(RefCell::new(layout.bank1_region));
|
||||
///
|
||||
/// let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash, &flash);
|
||||
/// // `config` can now be used to create a `FirmwareUpdater` instance for managing boot operations.
|
||||
/// ```
|
||||
/// Working examples can be found in the bootloader examples folder.
|
||||
pub fn from_linkerfile_blocking(
|
||||
flash: &'a embassy_sync::blocking_mutex::Mutex<NoopRawMutex, core::cell::RefCell<FLASH>>,
|
||||
dfu_flash: &'a embassy_sync::blocking_mutex::Mutex<NoopRawMutex, core::cell::RefCell<DFU>>,
|
||||
state_flash: &'a embassy_sync::blocking_mutex::Mutex<NoopRawMutex, core::cell::RefCell<STATE>>,
|
||||
) -> Self {
|
||||
extern "C" {
|
||||
static __bootloader_state_start: u32;
|
||||
@ -35,14 +67,14 @@ impl<'a, FLASH: NorFlash>
|
||||
let end = &__bootloader_dfu_end as *const u32 as u32;
|
||||
trace!("DFU: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
BlockingPartition::new(flash, start, end - start)
|
||||
BlockingPartition::new(dfu_flash, start, end - start)
|
||||
};
|
||||
let state = unsafe {
|
||||
let start = &__bootloader_state_start as *const u32 as u32;
|
||||
let end = &__bootloader_state_end as *const u32 as u32;
|
||||
trace!("STATE: 0x{:x} - 0x{:x}", start, end);
|
||||
|
||||
BlockingPartition::new(flash, start, end - start)
|
||||
BlockingPartition::new(state_flash, start, end - start)
|
||||
};
|
||||
|
||||
Self { dfu, state }
|
||||
@ -60,6 +92,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
||||
Self {
|
||||
dfu: config.dfu,
|
||||
state: BlockingFirmwareState::new(config.state, aligned),
|
||||
last_erased_dfu_sector_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,7 +109,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
||||
/// proceed with updating the firmware as it must be signed with a
|
||||
/// corresponding private key (otherwise it could be malicious firmware).
|
||||
///
|
||||
/// Mark to trigger firmware swap on next boot if verify suceeds.
|
||||
/// Mark to trigger firmware swap on next boot if verify succeeds.
|
||||
///
|
||||
/// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
|
||||
/// been generated from a SHA-512 digest of the firmware bytes.
|
||||
@ -86,8 +119,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
||||
#[cfg(feature = "_verify")]
|
||||
pub fn verify_and_mark_updated(
|
||||
&mut self,
|
||||
_public_key: &[u8],
|
||||
_signature: &[u8],
|
||||
_public_key: &[u8; 32],
|
||||
_signature: &[u8; 64],
|
||||
_update_len: u32,
|
||||
) -> Result<(), FirmwareUpdaterError> {
|
||||
assert!(_update_len <= self.dfu.capacity() as u32);
|
||||
@ -96,14 +129,14 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
||||
|
||||
#[cfg(feature = "ed25519-dalek")]
|
||||
{
|
||||
use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
|
||||
use ed25519_dalek::{Signature, SignatureError, Verifier, VerifyingKey};
|
||||
|
||||
use crate::digest_adapters::ed25519_dalek::Sha512;
|
||||
|
||||
let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
|
||||
|
||||
let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
||||
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
|
||||
let public_key = VerifyingKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
||||
let signature = Signature::from_bytes(_signature);
|
||||
|
||||
let mut message = [0; 64];
|
||||
let mut chunk_buf = [0; 2];
|
||||
@ -113,7 +146,6 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
||||
}
|
||||
#[cfg(feature = "ed25519-salty")]
|
||||
{
|
||||
use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
|
||||
use salty::{PublicKey, Signature};
|
||||
|
||||
use crate::digest_adapters::salty::Sha512;
|
||||
@ -122,10 +154,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
||||
FirmwareUpdaterError::Signature(signature::Error::default())
|
||||
}
|
||||
|
||||
let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
|
||||
let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
|
||||
let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
|
||||
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
|
||||
let public_key = PublicKey::try_from(_public_key).map_err(into_signature_error)?;
|
||||
let signature = Signature::try_from(_signature).map_err(into_signature_error)?;
|
||||
|
||||
let mut message = [0; 64];
|
||||
let mut chunk_buf = [0; 2];
|
||||
@ -168,25 +198,79 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
|
||||
self.state.mark_updated()
|
||||
}
|
||||
|
||||
/// Mark to trigger USB DFU device on next boot.
|
||||
pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||
self.state.verify_booted()?;
|
||||
self.state.mark_dfu()
|
||||
}
|
||||
|
||||
/// Mark firmware boot successful and stop rollback on reset.
|
||||
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||
self.state.mark_booted()
|
||||
}
|
||||
|
||||
/// Write data to a flash page.
|
||||
/// Writes firmware data to the device.
|
||||
///
|
||||
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
|
||||
/// This function writes the given data to the firmware area starting at the specified offset.
|
||||
/// It handles sector erasures and data writes while verifying the device is in a proper state
|
||||
/// for firmware updates. The function ensures that only unerased sectors are erased before
|
||||
/// writing and efficiently handles the writing process across sector boundaries and in
|
||||
/// various configurations (data size, sector size, etc.).
|
||||
///
|
||||
/// # Safety
|
||||
/// # Arguments
|
||||
///
|
||||
/// Failing to meet alignment and size requirements may result in a panic.
|
||||
/// * `offset` - The starting offset within the firmware area where data writing should begin.
|
||||
/// * `data` - A slice of bytes representing the firmware data to be written. It must be a
|
||||
/// multiple of NorFlash WRITE_SIZE.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result<(), FirmwareUpdaterError>` indicating the success or failure of the write operation.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if:
|
||||
///
|
||||
/// - The device is not in a proper state to receive firmware updates (e.g., not booted).
|
||||
/// - There is a failure erasing a sector before writing.
|
||||
/// - There is a failure writing data to the device.
|
||||
pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
|
||||
assert!(data.len() >= DFU::ERASE_SIZE);
|
||||
// Make sure we are running a booted firmware to avoid reverting to a bad state.
|
||||
self.state.verify_booted()?;
|
||||
|
||||
self.dfu.erase(offset as u32, (offset + data.len()) as u32)?;
|
||||
// Initialize variables to keep track of the remaining data and the current offset.
|
||||
let mut remaining_data = data;
|
||||
let mut offset = offset;
|
||||
|
||||
self.dfu.write(offset as u32, data)?;
|
||||
// Continue writing as long as there is data left to write.
|
||||
while !remaining_data.is_empty() {
|
||||
// Compute the current sector and its boundaries.
|
||||
let current_sector = offset / DFU::ERASE_SIZE;
|
||||
let sector_start = current_sector * DFU::ERASE_SIZE;
|
||||
let sector_end = sector_start + DFU::ERASE_SIZE;
|
||||
// Determine if the current sector needs to be erased before writing.
|
||||
let need_erase = self
|
||||
.last_erased_dfu_sector_index
|
||||
.map_or(true, |last_erased_sector| current_sector != last_erased_sector);
|
||||
|
||||
// If the sector needs to be erased, erase it and update the last erased sector index.
|
||||
if need_erase {
|
||||
self.dfu.erase(sector_start as u32, sector_end as u32)?;
|
||||
self.last_erased_dfu_sector_index = Some(current_sector);
|
||||
}
|
||||
|
||||
// Calculate the size of the data chunk that can be written in the current iteration.
|
||||
let write_size = core::cmp::min(remaining_data.len(), sector_end - offset);
|
||||
// Split the data to get the current chunk to be written and the remaining data.
|
||||
let (data_chunk, rest) = remaining_data.split_at(write_size);
|
||||
|
||||
// Write the current data chunk.
|
||||
self.dfu.write(offset as u32, data_chunk)?;
|
||||
|
||||
// Update the offset and remaining data for the next iteration.
|
||||
remaining_data = rest;
|
||||
offset += write_size;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -213,6 +297,16 @@ pub struct BlockingFirmwareState<'d, STATE> {
|
||||
}
|
||||
|
||||
impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
||||
/// Creates a firmware state instance from a FirmwareUpdaterConfig, with a buffer for magic content and state partition.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
|
||||
/// and written to.
|
||||
pub fn from_config<DFU: NorFlash>(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
|
||||
Self::new(config.state, aligned)
|
||||
}
|
||||
|
||||
/// Create a firmware state instance with a buffer for magic content and state partition.
|
||||
///
|
||||
/// # Safety
|
||||
@ -226,7 +320,7 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
||||
|
||||
// Make sure we are running a booted firmware to avoid reverting to a bad state.
|
||||
fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||
if self.get_state()? == State::Boot {
|
||||
if self.get_state()? == State::Boot || self.get_state()? == State::DfuDetach {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(FirmwareUpdaterError::BadState)
|
||||
@ -243,6 +337,8 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
||||
|
||||
if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) {
|
||||
Ok(State::Swap)
|
||||
} else if !self.aligned.iter().any(|&b| b != DFU_DETACH_MAGIC) {
|
||||
Ok(State::DfuDetach)
|
||||
} else {
|
||||
Ok(State::Boot)
|
||||
}
|
||||
@ -253,6 +349,11 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
||||
self.set_magic(SWAP_MAGIC)
|
||||
}
|
||||
|
||||
/// Mark to trigger USB DFU on next boot.
|
||||
pub fn mark_dfu(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||
self.set_magic(DFU_DETACH_MAGIC)
|
||||
}
|
||||
|
||||
/// Mark firmware boot successful and stop rollback on reset.
|
||||
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||
self.set_magic(BOOT_MAGIC)
|
||||
@ -317,4 +418,82 @@ mod tests {
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_verify_sha1_sector_bigger_than_chunk() {
|
||||
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));
|
||||
let state = BlockingPartition::new(&flash, 0, 4096);
|
||||
let dfu = BlockingPartition::new(&flash, 65536, 65536);
|
||||
let mut aligned = [0; 8];
|
||||
|
||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||
let mut to_write = [0; 4096];
|
||||
to_write[..7].copy_from_slice(update.as_slice());
|
||||
|
||||
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||
let mut offset = 0;
|
||||
for chunk in to_write.chunks(1024) {
|
||||
updater.write_firmware(offset, chunk).unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut chunk_buf = [0; 2];
|
||||
let mut hash = [0; 20];
|
||||
updater
|
||||
.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_verify_sha1_sector_smaller_than_chunk() {
|
||||
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 1024, 8>::default()));
|
||||
let state = BlockingPartition::new(&flash, 0, 4096);
|
||||
let dfu = BlockingPartition::new(&flash, 65536, 65536);
|
||||
let mut aligned = [0; 8];
|
||||
|
||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||
let mut to_write = [0; 4096];
|
||||
to_write[..7].copy_from_slice(update.as_slice());
|
||||
|
||||
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||
let mut offset = 0;
|
||||
for chunk in to_write.chunks(2048) {
|
||||
updater.write_firmware(offset, chunk).unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut chunk_buf = [0; 2];
|
||||
let mut hash = [0; 20];
|
||||
updater
|
||||
.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_verify_sha1_cross_sector_boundary() {
|
||||
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 1024, 8>::default()));
|
||||
let state = BlockingPartition::new(&flash, 0, 4096);
|
||||
let dfu = BlockingPartition::new(&flash, 65536, 65536);
|
||||
let mut aligned = [0; 8];
|
||||
|
||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||
let mut to_write = [0; 4096];
|
||||
to_write[..7].copy_from_slice(update.as_slice());
|
||||
|
||||
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||
let mut offset = 0;
|
||||
for chunk in to_write.chunks(896) {
|
||||
updater.write_firmware(offset, chunk).unwrap();
|
||||
offset += chunk.len();
|
||||
}
|
||||
let mut chunk_buf = [0; 2];
|
||||
let mut hash = [0; 20];
|
||||
updater
|
||||
.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(Sha1::digest(update).as_slice(), hash);
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
|
||||
/// Firmware updater flash configuration holding the two flashes used by the updater
|
||||
///
|
||||
/// If only a single flash is actually used, then that flash should be partitioned into two partitions before use.
|
||||
/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition
|
||||
/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile_blocking`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition
|
||||
/// the provided flash according to symbols defined in the linkerfile.
|
||||
pub struct FirmwareUpdaterConfig<DFU, STATE> {
|
||||
/// The dfu flash partition
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
@ -23,6 +23,7 @@ pub use firmware_updater::{
|
||||
|
||||
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
|
||||
pub(crate) const SWAP_MAGIC: u8 = 0xF0;
|
||||
pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0;
|
||||
|
||||
/// The state of the bootloader after running prepare.
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
@ -32,6 +33,8 @@ pub enum State {
|
||||
Boot,
|
||||
/// Bootloader has swapped the active partition with the dfu partition and will attempt boot.
|
||||
Swap,
|
||||
/// Application has received a request to reboot into DFU mode to apply an update.
|
||||
DfuDetach,
|
||||
}
|
||||
|
||||
/// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot.
|
||||
@ -272,21 +275,19 @@ mod tests {
|
||||
// The following key setup is based on:
|
||||
// https://docs.rs/ed25519-dalek/latest/ed25519_dalek/#example
|
||||
|
||||
use ed25519_dalek::Keypair;
|
||||
use ed25519_dalek::{Digest, Sha512, Signature, Signer, SigningKey, VerifyingKey};
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
let mut csprng = OsRng {};
|
||||
let keypair: Keypair = Keypair::generate(&mut csprng);
|
||||
let keypair = SigningKey::generate(&mut csprng);
|
||||
|
||||
use ed25519_dalek::{Digest, Sha512, Signature, Signer};
|
||||
let firmware: &[u8] = b"This are bytes that would otherwise be firmware bytes for DFU.";
|
||||
let mut digest = Sha512::new();
|
||||
digest.update(&firmware);
|
||||
let message = digest.finalize();
|
||||
let signature: Signature = keypair.sign(&message);
|
||||
|
||||
use ed25519_dalek::PublicKey;
|
||||
let public_key: PublicKey = keypair.public;
|
||||
let public_key = keypair.verifying_key();
|
||||
|
||||
// Setup flash
|
||||
let flash = BlockingTestFlash::new(BootLoaderConfig {
|
@ -1,24 +0,0 @@
|
||||
# embassy-boot-stm32
|
||||
|
||||
An [Embassy](https://embassy.dev) project.
|
||||
|
||||
An adaptation of `embassy-boot` for STM32.
|
||||
|
||||
## Features
|
||||
|
||||
* Configure bootloader partitions based on linker script.
|
||||
* Load applications from active partition.
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
`embassy-boot-stm32` is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
This work is licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||
|
||||
at your option.
|
@ -3,7 +3,14 @@ name = "embassy-embedded-hal"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
description = "Collection of utilities to use `embedded-hal` and `embedded-storage` traits with Embassy."
|
||||
repository = "https://github.com/embassy-rs/embassy"
|
||||
documentation = "https://docs.embassy.dev/embassy-embedded-hal"
|
||||
categories = [
|
||||
"embedded",
|
||||
"no-std",
|
||||
"asynchronous",
|
||||
]
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-embedded-hal-v$VERSION/embassy-embedded-hal/src/"
|
||||
@ -11,6 +18,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-embed
|
||||
features = ["std"]
|
||||
target = "x86_64-unknown-linux-gnu"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["std"]
|
||||
|
||||
[features]
|
||||
std = []
|
||||
time = ["dep:embassy-time"]
|
||||
@ -19,12 +29,12 @@ default = ["time"]
|
||||
[dependencies]
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
embassy-sync = { version = "0.5.0", path = "../embassy-sync" }
|
||||
embassy-time = { version = "0.2", path = "../embassy-time", optional = true }
|
||||
embassy-time = { version = "0.3.0", path = "../embassy-time", optional = true }
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
||||
"unproven",
|
||||
] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.2" }
|
||||
embedded-hal-async = { version = "=1.0.0-rc.2" }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
|
||||
embedded-hal-async = { version = "1.0" }
|
||||
embedded-storage = "0.3.1"
|
||||
embedded-storage-async = { version = "0.4.1" }
|
||||
nb = "1.0.0"
|
||||
|
12
embassy-embedded-hal/README.md
Normal file
12
embassy-embedded-hal/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# embassy-embedded-hal
|
||||
|
||||
Collection of utilities to use `embedded-hal` and `embedded-storage` traits with Embassy.
|
||||
|
||||
- Shared SPI and I2C buses, both blocking and async, with a `SetConfig` trait allowing changing bus configuration (e.g. frequency) between devices on the same bus.
|
||||
- Async utilities
|
||||
- Adapters to convert from blocking to (fake) async.
|
||||
- Adapters to insert yields on trait operations.
|
||||
- Flash utilities
|
||||
- Split a flash memory into smaller partitions.
|
||||
- Concatenate flash memories together.
|
||||
- Simulated in-memory flash.
|
@ -1,18 +0,0 @@
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc"));
|
||||
|
||||
let output = Command::new(rustc)
|
||||
.arg("--version")
|
||||
.output()
|
||||
.expect("failed to run `rustc --version`");
|
||||
|
||||
if String::from_utf8_lossy(&output.stdout).contains("nightly") {
|
||||
println!("cargo:rustc-cfg=nightly");
|
||||
}
|
||||
}
|
@ -104,8 +104,10 @@ where
|
||||
}
|
||||
|
||||
/// NOR flash wrapper
|
||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
||||
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
||||
use embedded_storage::nor_flash::{ErrorType, MultiwriteNorFlash, NorFlash, ReadNorFlash};
|
||||
use embedded_storage_async::nor_flash::{
|
||||
MultiwriteNorFlash as AsyncMultiwriteNorFlash, NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash,
|
||||
};
|
||||
|
||||
impl<T> ErrorType for BlockingAsync<T>
|
||||
where
|
||||
@ -143,3 +145,5 @@ where
|
||||
self.wrapped.capacity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsyncMultiwriteNorFlash for BlockingAsync<T> where T: MultiwriteNorFlash {}
|
||||
|
@ -1,10 +1,7 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(nightly, feature(async_fn_in_trait, impl_trait_projections))]
|
||||
#![cfg_attr(nightly, allow(stable_features, unknown_lints))]
|
||||
#![allow(async_fn_in_trait)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
//! Utilities to use `embedded-hal` traits with Embassy.
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
pub mod adapter;
|
||||
pub mod flash;
|
||||
|
@ -67,9 +67,11 @@ where
|
||||
}
|
||||
|
||||
fn transaction<'a>(&mut self, address: u8, operations: &mut [Operation<'a>]) -> Result<(), Self::Error> {
|
||||
let _ = address;
|
||||
let _ = operations;
|
||||
todo!()
|
||||
self.bus.lock(|bus| {
|
||||
bus.borrow_mut()
|
||||
.transaction(address, operations)
|
||||
.map_err(I2cDeviceError::I2c)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,8 +173,10 @@ where
|
||||
}
|
||||
|
||||
fn transaction<'a>(&mut self, address: u8, operations: &mut [Operation<'a>]) -> Result<(), Self::Error> {
|
||||
let _ = address;
|
||||
let _ = operations;
|
||||
todo!()
|
||||
self.bus.lock(|bus| {
|
||||
let mut bus = bus.borrow_mut();
|
||||
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||
bus.transaction(address, operations).map_err(I2cDeviceError::I2c)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
[package]
|
||||
name = "embassy-executor-macros"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "macros for creating the entry point and tasks for embassy-executor"
|
||||
repository = "https://github.com/embassy-rs/embassy"
|
||||
documentation = "https://docs.embassy.dev/embassy-executor-macros"
|
||||
categories = [
|
||||
"embedded",
|
||||
"no-std",
|
||||
|
@ -3,13 +3,3 @@
|
||||
An [Embassy](https://embassy.dev) project.
|
||||
|
||||
NOTE: Do not use this crate directly. The macros are re-exported by `embassy-executor`.
|
||||
|
||||
## License
|
||||
|
||||
This work is licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||
|
||||
at your option.
|
||||
|
@ -62,6 +62,13 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
task::run(&args.meta, f).unwrap_or_else(|x| x).into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn main_avr(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let args = syn::parse_macro_input!(args as Args);
|
||||
let f = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
main::run(&args.meta, f, main::avr()).unwrap_or_else(|x| x).into()
|
||||
}
|
||||
|
||||
/// Creates a new `executor` instance and declares an application entry point for Cortex-M spawning the corresponding function body as an async task.
|
||||
///
|
||||
/// The following restrictions apply:
|
||||
|
@ -12,6 +12,20 @@ struct Args {
|
||||
entry: Option<String>,
|
||||
}
|
||||
|
||||
pub fn avr() -> TokenStream {
|
||||
quote! {
|
||||
#[avr_device::entry]
|
||||
fn main() -> ! {
|
||||
let mut executor = ::embassy_executor::Executor::new();
|
||||
let executor = unsafe { __make_static(&mut executor) };
|
||||
|
||||
executor.run(|spawner| {
|
||||
spawner.must_spawn(__embassy_main(spawner));
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn riscv(args: &[NestedMeta]) -> TokenStream {
|
||||
let maybe_entry = match Args::from_list(args) {
|
||||
Ok(args) => args.entry,
|
||||
|
@ -49,7 +49,7 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre
|
||||
},
|
||||
}
|
||||
|
||||
let mut arg_names = Vec::new();
|
||||
let mut args = Vec::new();
|
||||
let mut fargs = f.sig.inputs.clone();
|
||||
|
||||
for arg in fargs.iter_mut() {
|
||||
@ -59,8 +59,8 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre
|
||||
}
|
||||
syn::FnArg::Typed(t) => match t.pat.as_mut() {
|
||||
syn::Pat::Ident(id) => {
|
||||
arg_names.push(id.ident.clone());
|
||||
id.mutability = None;
|
||||
args.push((id.clone(), t.attrs.clone()));
|
||||
}
|
||||
_ => {
|
||||
ctxt.error_spanned_by(arg, "pattern matching in task arguments is not yet supported");
|
||||
@ -79,13 +79,35 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre
|
||||
task_inner.vis = syn::Visibility::Inherited;
|
||||
task_inner.sig.ident = task_inner_ident.clone();
|
||||
|
||||
// assemble the original input arguments,
|
||||
// including any attributes that may have
|
||||
// been applied previously
|
||||
let mut full_args = Vec::new();
|
||||
for (arg, cfgs) in args {
|
||||
full_args.push(quote!(
|
||||
#(#cfgs)*
|
||||
#arg
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
let mut task_outer: ItemFn = parse_quote! {
|
||||
#visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> {
|
||||
type Fut = impl ::core::future::Future + 'static;
|
||||
trait _EmbassyInternalTaskTrait {
|
||||
type Fut: ::core::future::Future + 'static;
|
||||
fn construct(#fargs) -> Self::Fut;
|
||||
}
|
||||
|
||||
impl _EmbassyInternalTaskTrait for () {
|
||||
type Fut = impl core::future::Future + 'static;
|
||||
fn construct(#fargs) -> Self::Fut {
|
||||
#task_inner_ident(#(#full_args,)*)
|
||||
}
|
||||
}
|
||||
|
||||
const POOL_SIZE: usize = #pool_size;
|
||||
static POOL: ::embassy_executor::raw::TaskPool<Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new();
|
||||
unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) }
|
||||
static POOL: ::embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new();
|
||||
unsafe { POOL._spawn_async_fn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) }
|
||||
}
|
||||
};
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
@ -93,7 +115,7 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre
|
||||
#visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> {
|
||||
const POOL_SIZE: usize = #pool_size;
|
||||
static POOL: ::embassy_executor::_export::TaskPoolRef = ::embassy_executor::_export::TaskPoolRef::new();
|
||||
unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) }
|
||||
unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) }
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,6 @@ use std::thread;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn;
|
||||
|
||||
/// A type to collect errors together and format them.
|
||||
///
|
||||
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 0.5.0 - 2024-01-11
|
||||
|
||||
- Updated to `embassy-time-driver 0.1`, `embassy-time-queue-driver 0.1`, compatible with `embassy-time v0.3` and higher.
|
||||
|
||||
## 0.4.0 - 2023-12-05
|
||||
|
||||
- Removed `arch-xtensa`. Use the executor provided by the HAL crate (`esp-hal`, `esp32s3-hal`, etc...) instead.
|
||||
|
@ -1,10 +1,11 @@
|
||||
[package]
|
||||
name = "embassy-executor"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "async/await executor designed for embedded usage"
|
||||
repository = "https://github.com/embassy-rs/embassy"
|
||||
documentation = "https://docs.embassy.dev/embassy-executor"
|
||||
categories = [
|
||||
"embedded",
|
||||
"no-std",
|
||||
@ -14,7 +15,7 @@ categories = [
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
|
||||
features = ["nightly", "defmt"]
|
||||
features = ["defmt"]
|
||||
flavors = [
|
||||
{ name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] },
|
||||
{ name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] },
|
||||
@ -25,7 +26,7 @@ flavors = [
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "thumbv7em-none-eabi"
|
||||
targets = ["thumbv7em-none-eabi"]
|
||||
features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]
|
||||
features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3", optional = true }
|
||||
@ -33,11 +34,13 @@ log = { version = "0.4.14", optional = true }
|
||||
rtos-trace = { version = "0.1.2", optional = true }
|
||||
|
||||
embassy-executor-macros = { version = "0.4.0", path = "../embassy-executor-macros" }
|
||||
embassy-time = { version = "0.2", path = "../embassy-time", optional = true}
|
||||
embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver", optional = true }
|
||||
embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver", optional = true }
|
||||
critical-section = "1.1"
|
||||
|
||||
# needed for riscv
|
||||
# remove when https://github.com/rust-lang/rust/pull/114499 is merged
|
||||
document-features = "0.2.7"
|
||||
|
||||
# needed for AVR
|
||||
portable-atomic = { version = "1.5", optional = true }
|
||||
|
||||
# arch-cortex-m dependencies
|
||||
@ -47,72 +50,137 @@ cortex-m = { version = "0.7.6", optional = true }
|
||||
wasm-bindgen = { version = "0.2.82", optional = true }
|
||||
js-sys = { version = "0.3", optional = true }
|
||||
|
||||
# arch-avr dependencies
|
||||
avr-device = { version = "0.5.3", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
critical-section = { version = "1.1", features = ["std"] }
|
||||
|
||||
|
||||
[features]
|
||||
|
||||
# Architecture
|
||||
_arch = [] # some arch was picked
|
||||
arch-std = ["_arch", "critical-section/std"]
|
||||
arch-cortex-m = ["_arch", "dep:cortex-m"]
|
||||
arch-riscv32 = ["_arch", "dep:portable-atomic"]
|
||||
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
|
||||
|
||||
# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
|
||||
executor-thread = []
|
||||
# Enable the interrupt-mode executor (available in Cortex-M only)
|
||||
executor-interrupt = []
|
||||
|
||||
# Enable nightly-only features
|
||||
## Enable nightly-only features
|
||||
nightly = ["embassy-executor-macros/nightly"]
|
||||
|
||||
# Enables turbo wakers, which requires patching core. Not surfaced in the docs by default due to
|
||||
# being an complicated advanced and undocumented feature.
|
||||
# See: https://github.com/embassy-rs/embassy/pull/1263
|
||||
turbowakers = []
|
||||
|
||||
integrated-timers = ["dep:embassy-time"]
|
||||
## Use the executor-integrated `embassy-time` timer queue.
|
||||
integrated-timers = ["dep:embassy-time-driver", "dep:embassy-time-queue-driver"]
|
||||
|
||||
#! ### Architecture
|
||||
_arch = [] # some arch was picked
|
||||
## std
|
||||
arch-std = ["_arch", "critical-section/std"]
|
||||
## Cortex-M
|
||||
arch-cortex-m = ["_arch", "dep:cortex-m"]
|
||||
## RISC-V 32
|
||||
arch-riscv32 = ["_arch"]
|
||||
## WASM
|
||||
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
|
||||
## AVR
|
||||
arch-avr = ["_arch", "dep:portable-atomic", "dep:avr-device"]
|
||||
|
||||
#! ### Executor
|
||||
|
||||
## Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
|
||||
executor-thread = []
|
||||
## Enable the interrupt-mode executor (available in Cortex-M only)
|
||||
executor-interrupt = []
|
||||
|
||||
#! ### Task Arena Size
|
||||
#! Sets the [task arena](#task-arena) size. Necessary if you’re not using `nightly`.
|
||||
#!
|
||||
#! <details>
|
||||
#! <summary>Preconfigured Task Arena Sizes:</summary>
|
||||
#! <!-- rustdoc requires the following blank line for the feature list to render correctly! -->
|
||||
#!
|
||||
|
||||
# BEGIN AUTOGENERATED CONFIG FEATURES
|
||||
# Generated by gen_config.py. DO NOT EDIT.
|
||||
## 64
|
||||
task-arena-size-64 = []
|
||||
## 128
|
||||
task-arena-size-128 = []
|
||||
## 192
|
||||
task-arena-size-192 = []
|
||||
## 256
|
||||
task-arena-size-256 = []
|
||||
## 320
|
||||
task-arena-size-320 = []
|
||||
## 384
|
||||
task-arena-size-384 = []
|
||||
## 512
|
||||
task-arena-size-512 = []
|
||||
## 640
|
||||
task-arena-size-640 = []
|
||||
## 768
|
||||
task-arena-size-768 = []
|
||||
## 1024
|
||||
task-arena-size-1024 = []
|
||||
## 1280
|
||||
task-arena-size-1280 = []
|
||||
## 1536
|
||||
task-arena-size-1536 = []
|
||||
## 2048
|
||||
task-arena-size-2048 = []
|
||||
## 2560
|
||||
task-arena-size-2560 = []
|
||||
## 3072
|
||||
task-arena-size-3072 = []
|
||||
## 4096 (default)
|
||||
task-arena-size-4096 = [] # Default
|
||||
## 5120
|
||||
task-arena-size-5120 = []
|
||||
## 6144
|
||||
task-arena-size-6144 = []
|
||||
## 8192
|
||||
task-arena-size-8192 = []
|
||||
## 10240
|
||||
task-arena-size-10240 = []
|
||||
## 12288
|
||||
task-arena-size-12288 = []
|
||||
## 16384
|
||||
task-arena-size-16384 = []
|
||||
## 20480
|
||||
task-arena-size-20480 = []
|
||||
## 24576
|
||||
task-arena-size-24576 = []
|
||||
## 32768
|
||||
task-arena-size-32768 = []
|
||||
## 40960
|
||||
task-arena-size-40960 = []
|
||||
## 49152
|
||||
task-arena-size-49152 = []
|
||||
## 65536
|
||||
task-arena-size-65536 = []
|
||||
## 81920
|
||||
task-arena-size-81920 = []
|
||||
## 98304
|
||||
task-arena-size-98304 = []
|
||||
## 131072
|
||||
task-arena-size-131072 = []
|
||||
## 163840
|
||||
task-arena-size-163840 = []
|
||||
## 196608
|
||||
task-arena-size-196608 = []
|
||||
## 262144
|
||||
task-arena-size-262144 = []
|
||||
## 327680
|
||||
task-arena-size-327680 = []
|
||||
## 393216
|
||||
task-arena-size-393216 = []
|
||||
## 524288
|
||||
task-arena-size-524288 = []
|
||||
## 655360
|
||||
task-arena-size-655360 = []
|
||||
## 786432
|
||||
task-arena-size-786432 = []
|
||||
## 1048576
|
||||
task-arena-size-1048576 = []
|
||||
|
||||
# END AUTOGENERATED CONFIG FEATURES
|
||||
|
||||
#! </details>
|
||||
|
@ -22,7 +22,7 @@ Tasks are allocated from the arena when spawned for the first time. If the task
|
||||
The arena size can be configured in two ways:
|
||||
|
||||
- Via Cargo features: enable a Cargo feature like `task-arena-size-8192`. Only a selection of values
|
||||
is available, check `Cargo.toml` for the list.
|
||||
is available, see [Task Area Sizes](#task-arena-size) for reference.
|
||||
- Via environment variables at build time: set the variable named `EMBASSY_EXECUTOR_TASK_ARENA_SIZE`. For example
|
||||
`EMBASSY_EXECUTOR_TASK_ARENA_SIZE=4321 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`.
|
||||
Any value can be set, unlike with Cargo features.
|
||||
|
@ -45,6 +45,12 @@ things = ""
|
||||
for f in features:
|
||||
name = f["name"].replace("_", "-")
|
||||
for val in f["vals"]:
|
||||
things += f"## {val}"
|
||||
if val == f["default"]:
|
||||
things += " (default)\n"
|
||||
else:
|
||||
things += "\n"
|
||||
|
||||
things += f"{name}-{val} = []"
|
||||
if val == f["default"]:
|
||||
things += " # Default"
|
||||
|
72
embassy-executor/src/arch/avr.rs
Normal file
72
embassy-executor/src/arch/avr.rs
Normal file
@ -0,0 +1,72 @@
|
||||
#[cfg(feature = "executor-interrupt")]
|
||||
compile_error!("`executor-interrupt` is not supported with `arch-avr`.");
|
||||
|
||||
#[cfg(feature = "executor-thread")]
|
||||
pub use thread::*;
|
||||
#[cfg(feature = "executor-thread")]
|
||||
mod thread {
|
||||
use core::marker::PhantomData;
|
||||
|
||||
pub use embassy_executor_macros::main_avr as main;
|
||||
use portable_atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::{raw, Spawner};
|
||||
|
||||
static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[export_name = "__pender"]
|
||||
fn __pender(_context: *mut ()) {
|
||||
SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// avr Executor
|
||||
pub struct Executor {
|
||||
inner: raw::Executor,
|
||||
not_send: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
impl Executor {
|
||||
/// Create a new Executor.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: raw::Executor::new(core::ptr::null_mut()),
|
||||
not_send: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the executor.
|
||||
///
|
||||
/// The `init` closure is called with a [`Spawner`] that spawns tasks on
|
||||
/// this executor. Use it to spawn the initial task(s). After `init` returns,
|
||||
/// the executor starts running the tasks.
|
||||
///
|
||||
/// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
|
||||
/// for example by passing it as an argument to the initial tasks.
|
||||
///
|
||||
/// This function requires `&'static mut self`. This means you have to store the
|
||||
/// Executor instance in a place where it'll live forever and grants you mutable
|
||||
/// access. There's a few ways to do this:
|
||||
///
|
||||
/// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
|
||||
/// - a `static mut` (unsafe)
|
||||
/// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
|
||||
///
|
||||
/// This function never returns.
|
||||
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
|
||||
init(self.inner.spawner());
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
avr_device::interrupt::disable();
|
||||
if !SIGNAL_WORK_THREAD_MODE.swap(false, Ordering::SeqCst) {
|
||||
avr_device::interrupt::enable();
|
||||
avr_device::asm::sleep();
|
||||
} else {
|
||||
avr_device::interrupt::enable();
|
||||
self.inner.poll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,9 +6,9 @@ pub use thread::*;
|
||||
#[cfg(feature = "executor-thread")]
|
||||
mod thread {
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
pub use embassy_executor_macros::main_riscv as main;
|
||||
use portable_atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::{raw, Spawner};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused)]
|
||||
|
||||
use core::fmt::{Debug, Display, LowerHex};
|
||||
|
||||
@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||
|
||||
impl<'a> Debug for Bytes<'a> {
|
||||
|
@ -3,6 +3,9 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
//! ## Feature flags
|
||||
#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
||||
@ -20,9 +23,10 @@ macro_rules! check_at_most_one {
|
||||
check_at_most_one!(@amo [$($f)*] [$($f)*] []);
|
||||
};
|
||||
}
|
||||
check_at_most_one!("arch-cortex-m", "arch-riscv32", "arch-std", "arch-wasm",);
|
||||
check_at_most_one!("arch-avr", "arch-cortex-m", "arch-riscv32", "arch-std", "arch-wasm",);
|
||||
|
||||
#[cfg(feature = "_arch")]
|
||||
#[cfg_attr(feature = "arch-avr", path = "arch/avr.rs")]
|
||||
#[cfg_attr(feature = "arch-cortex-m", path = "arch/cortex_m.rs")]
|
||||
#[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")]
|
||||
#[cfg_attr(feature = "arch-std", path = "arch/std.rs")]
|
||||
|
@ -30,9 +30,7 @@ use core::ptr::NonNull;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
use embassy_time::driver::{self, AlarmHandle};
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
use embassy_time::Instant;
|
||||
use embassy_time_driver::AlarmHandle;
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
use rtos_trace::trace;
|
||||
|
||||
@ -50,7 +48,7 @@ pub(crate) struct TaskHeader {
|
||||
poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
pub(crate) expires_at: SyncUnsafeCell<Instant>,
|
||||
pub(crate) expires_at: SyncUnsafeCell<u64>,
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
pub(crate) timer_queue_item: timer_queue::TimerQueueItem,
|
||||
}
|
||||
@ -123,7 +121,7 @@ impl<F: Future + 'static> TaskStorage<F> {
|
||||
poll_fn: SyncUnsafeCell::new(None),
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
expires_at: SyncUnsafeCell::new(Instant::from_ticks(0)),
|
||||
expires_at: SyncUnsafeCell::new(0),
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
timer_queue_item: timer_queue::TimerQueueItem::new(),
|
||||
},
|
||||
@ -164,7 +162,7 @@ impl<F: Future + 'static> TaskStorage<F> {
|
||||
this.raw.state.despawn();
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
this.raw.expires_at.set(Instant::MAX);
|
||||
this.raw.expires_at.set(u64::MAX);
|
||||
}
|
||||
Poll::Pending => {}
|
||||
}
|
||||
@ -328,7 +326,7 @@ pub(crate) struct SyncExecutor {
|
||||
impl SyncExecutor {
|
||||
pub(crate) fn new(pender: Pender) -> Self {
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
let alarm = unsafe { unwrap!(driver::allocate_alarm()) };
|
||||
let alarm = unsafe { unwrap!(embassy_time_driver::allocate_alarm()) };
|
||||
|
||||
Self {
|
||||
run_queue: RunQueue::new(),
|
||||
@ -377,18 +375,19 @@ impl SyncExecutor {
|
||||
/// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created.
|
||||
pub(crate) unsafe fn poll(&'static self) {
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ());
|
||||
embassy_time_driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ());
|
||||
|
||||
#[allow(clippy::never_loop)]
|
||||
loop {
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
self.timer_queue.dequeue_expired(Instant::now(), wake_task_no_pend);
|
||||
self.timer_queue
|
||||
.dequeue_expired(embassy_time_driver::now(), wake_task_no_pend);
|
||||
|
||||
self.run_queue.dequeue_all(|p| {
|
||||
let task = p.header();
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
task.expires_at.set(Instant::MAX);
|
||||
task.expires_at.set(u64::MAX);
|
||||
|
||||
if !task.state.run_dequeue() {
|
||||
// If task is not running, ignore it. This can happen in the following scenario:
|
||||
@ -418,7 +417,7 @@ impl SyncExecutor {
|
||||
// If this is already in the past, set_alarm might return false
|
||||
// In that case do another poll loop iteration.
|
||||
let next_expiration = self.timer_queue.next_expiration();
|
||||
if driver::set_alarm(self.alarm, next_expiration.as_ticks()) {
|
||||
if embassy_time_driver::set_alarm(self.alarm, next_expiration) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -568,8 +567,8 @@ pub fn wake_task_no_pend(task: TaskRef) {
|
||||
struct TimerQueue;
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
impl embassy_time::queue::TimerQueue for TimerQueue {
|
||||
fn schedule_wake(&'static self, at: Instant, waker: &core::task::Waker) {
|
||||
impl embassy_time_queue_driver::TimerQueue for TimerQueue {
|
||||
fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) {
|
||||
let task = waker::task_from_waker(waker);
|
||||
let task = task.header();
|
||||
unsafe {
|
||||
@ -580,7 +579,16 @@ impl embassy_time::queue::TimerQueue for TimerQueue {
|
||||
}
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
embassy_time::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue);
|
||||
embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue);
|
||||
|
||||
#[cfg(all(feature = "rtos-trace", feature = "integrated-timers"))]
|
||||
const fn gcd(a: u64, b: u64) -> u64 {
|
||||
if b == 0 {
|
||||
a
|
||||
} else {
|
||||
gcd(b, a % b)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
impl rtos_trace::RtosTraceOSCallbacks for Executor {
|
||||
@ -589,7 +597,8 @@ impl rtos_trace::RtosTraceOSCallbacks for Executor {
|
||||
}
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
fn time() -> u64 {
|
||||
Instant::now().as_micros()
|
||||
const GCD_1M: u64 = gcd(embassy_time_driver::TICK_HZ, 1_000_000);
|
||||
embassy_time_driver::now() * (1_000_000 / GCD_1M) / (embassy_time_driver::TICK_HZ / GCD_1M)
|
||||
}
|
||||
#[cfg(not(feature = "integrated-timers"))]
|
||||
fn time() -> u64 {
|
||||
|
@ -1,7 +1,5 @@
|
||||
use core::cmp::min;
|
||||
|
||||
use embassy_time::Instant;
|
||||
|
||||
use super::TaskRef;
|
||||
use crate::raw::util::SyncUnsafeCell;
|
||||
|
||||
@ -30,7 +28,7 @@ impl TimerQueue {
|
||||
|
||||
pub(crate) unsafe fn update(&self, p: TaskRef) {
|
||||
let task = p.header();
|
||||
if task.expires_at.get() != Instant::MAX {
|
||||
if task.expires_at.get() != u64::MAX {
|
||||
if task.state.timer_enqueue() {
|
||||
task.timer_queue_item.next.set(self.head.get());
|
||||
self.head.set(Some(p));
|
||||
@ -38,18 +36,18 @@ impl TimerQueue {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn next_expiration(&self) -> Instant {
|
||||
let mut res = Instant::MAX;
|
||||
pub(crate) unsafe fn next_expiration(&self) -> u64 {
|
||||
let mut res = u64::MAX;
|
||||
self.retain(|p| {
|
||||
let task = p.header();
|
||||
let expires = task.expires_at.get();
|
||||
res = min(res, expires);
|
||||
expires != Instant::MAX
|
||||
expires != u64::MAX
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn dequeue_expired(&self, now: Instant, on_task: impl Fn(TaskRef)) {
|
||||
pub(crate) unsafe fn dequeue_expired(&self, now: u64, on_task: impl Fn(TaskRef)) {
|
||||
self.retain(|p| {
|
||||
let task = p.header();
|
||||
if task.expires_at.get() <= now {
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
|
||||
|
||||
use std::boxed::Box;
|
||||
use std::future::poll_fn;
|
||||
@ -135,3 +135,17 @@ fn executor_task_self_wake_twice() {
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn executor_task_cfg_args() {
|
||||
// simulate cfg'ing away argument c
|
||||
#[task]
|
||||
async fn task1(a: u32, b: u32, #[cfg(any())] c: u32) {
|
||||
let (_, _) = (a, b);
|
||||
}
|
||||
|
||||
#[task]
|
||||
async fn task2(a: u32, b: u32, #[cfg(all())] c: u32) {
|
||||
let (_, _, _) = (a, b, c);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ version = "0.1.1"
|
||||
edition = "2021"
|
||||
description = "no-std, no-alloc utilities for working with futures"
|
||||
repository = "https://github.com/embassy-rs/embassy"
|
||||
documentation = "https://docs.embassy.dev/embassy-futures"
|
||||
readme = "README.md"
|
||||
license = "MIT OR Apache-2.0"
|
||||
categories = [
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user