Merge remote-tracking branch 'upstream/main' into remove-bootloader-partitions

This commit is contained in:
Rasmus Melchior Jacobsen 2023-05-30 13:24:15 +02:00
commit 392ed64f6f
65 changed files with 659 additions and 555 deletions

1
.github/ci/build.sh vendored
View File

@ -9,6 +9,7 @@ export CARGO_HOME=/ci/cache/cargo
export CARGO_TARGET_DIR=/ci/cache/target export CARGO_TARGET_DIR=/ci/cache/target
if [ -f /ci/secrets/teleprobe-token.txt ]; then if [ -f /ci/secrets/teleprobe-token.txt ]; then
echo Got teleprobe token! echo Got teleprobe token!
export TELEPROBE_HOST=https://teleprobe.embassy.dev
export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt) export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt)
fi fi

4
.github/ci/test.sh vendored
View File

@ -21,7 +21,9 @@ cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly,ed25519-dalek cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly,ed25519-dalek
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly,ed25519-salty cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly,ed25519-salty
#cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --features nightly,nrf52840,time-driver-rtc1 ## broken doctests cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --features nightly,nrf52840,time-driver-rtc1,gpiote
cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features nightly,time-driver
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f429vg,exti,time-driver-any,exti cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f429vg,exti,time-driver-any,exti
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f732ze,exti,time-driver-any,exti cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f732ze,exti,time-driver-any,exti

3
.vscode/.gitignore vendored
View File

@ -1,3 +1,4 @@
*.cortex-debug.*.json *.cortex-debug.*.json
launch.json launch.json
tasks.json tasks.json
*.cfg

43
ci.sh
View File

@ -12,7 +12,7 @@ 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" BUILD_EXTRA="--- build --release --manifest-path examples/std/Cargo.toml --target $TARGET --out-dir out/examples/std"
fi fi
find . -name '*.rs' -not -path '*target*' | xargs rustfmt --check --skip-children --unstable-features --edition 2018 find . -name '*.rs' -not -path '*target*' | xargs rustfmt --check --skip-children --unstable-features --edition 2021
cargo batch \ 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 \
@ -127,45 +127,24 @@ cargo batch \
--- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \ --- 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/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \ --- 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/bluepill-stm32f103c8 \ --- 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/nucleo-stm32f429zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --out-dir out/tests/nucleo-stm32g491re \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --out-dir out/tests/stm32g491re \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/nucleo-stm32g071rb \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/stm32g071rb \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/nucleo-stm32c031c6 \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/stm32c031c6 \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/stm32h755zi \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ --- 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/nucleo-stm32h563zi \ --- 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/iot-stm32u585ai \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/stm32u585ai \
--- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ --- 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/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
--- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \ --- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \
$BUILD_EXTRA $BUILD_EXTRA
function run_elf {
echo Running target=$1 elf=$2
STATUSCODE=$(
curl \
-sS \
--output /dev/stderr \
--write-out "%{http_code}" \
-H "Authorization: Bearer $TELEPROBE_TOKEN" \
https://teleprobe.embassy.dev/targets/$1/run --data-binary @$2
)
echo
echo HTTP Status code: $STATUSCODE
test "$STATUSCODE" -eq 200
}
if [[ -z "${TELEPROBE_TOKEN-}" ]]; then if [[ -z "${TELEPROBE_TOKEN-}" ]]; then
echo No teleprobe token found, skipping running HIL tests echo No teleprobe token found, skipping running HIL tests
exit exit
fi fi
for board in $(ls out/tests); do teleprobe client run -r out/tests
echo Running tests for board: $board
for elf in $(ls out/tests/$board); do
run_elf $board out/tests/$board/$elf
done
done

View File

@ -7,8 +7,8 @@ license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
cortex-m = "0.7" cortex-m = "0.7"
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"], default-features = false } embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] }
embassy-executor = { version = "0.2.0", default-features = false, features = ["nightly", "arch-cortex-m", "executor-thread"] } embassy-executor = { version = "0.2.0", features = ["nightly", "arch-cortex-m", "executor-thread"] }
defmt = "0.3.0" defmt = "0.3.0"
defmt-rtt = "0.3.0" defmt-rtt = "0.3.0"

View File

@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
cortex-m = "0.7" cortex-m = "0.7"
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x"], default-features = false } embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x"] }
defmt = "0.3.0" defmt = "0.3.0"
defmt-rtt = "0.3.0" defmt-rtt = "0.3.0"

View File

@ -17,7 +17,7 @@ target = "thumbv7em-none-eabi"
defmt = { version = "0.3", optional = true } defmt = { version = "0.3", optional = true }
embassy-sync = { path = "../../embassy-sync" } embassy-sync = { path = "../../embassy-sync" }
embassy-nrf = { path = "../../embassy-nrf", default-features = false } embassy-nrf = { path = "../../embassy-nrf" }
embassy-boot = { path = "../boot", default-features = false } embassy-boot = { path = "../boot", default-features = false }
cortex-m = { version = "0.7.6" } cortex-m = { version = "0.7.6" }
cortex-m-rt = { version = "0.7" } cortex-m-rt = { version = "0.7" }

View File

@ -16,6 +16,18 @@ flavors = [
] ]
[features] [features]
default = [
"nrf52805-pac?/rt",
"nrf52810-pac?/rt",
"nrf52811-pac?/rt",
"nrf52820-pac?/rt",
"nrf52832-pac?/rt",
"nrf52833-pac?/rt",
"nrf52840-pac?/rt",
"nrf5340-app-pac?/rt",
"nrf5340-net-pac?/rt",
"nrf9160-pac?/rt",
]
time = ["dep:embassy-time"] time = ["dep:embassy-time"]
@ -103,13 +115,14 @@ embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.4.0", optional = true } embedded-storage-async = { version = "0.4.0", optional = true }
cfg-if = "1.0.0" cfg-if = "1.0.0"
nrf52805-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } nrf52805-pac = { version = "0.12.0", optional = true }
nrf52810-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } nrf52810-pac = { version = "0.12.0", optional = true }
nrf52811-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } nrf52811-pac = { version = "0.12.0", optional = true }
nrf52820-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } nrf52820-pac = { version = "0.12.0", optional = true }
nrf52832-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } nrf52832-pac = { version = "0.12.0", optional = true }
nrf52833-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } nrf52833-pac = { version = "0.12.0", optional = true }
nrf52840-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } nrf52840-pac = { version = "0.12.0", optional = true }
nrf5340-app-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } nrf5340-app-pac = { version = "0.12.0", optional = true }
nrf5340-net-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } nrf5340-net-pac = { version = "0.12.0", optional = true }
nrf9160-pac = { version = "0.12.0", optional = true, features = [ "rt" ] } nrf9160-pac = { version = "0.12.0", optional = true }

View File

@ -13,7 +13,7 @@ with peripherals. It takes care of sending/receiving data over a variety of bus
However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust However, EasyDMA requires the buffers used to transmit and receive data to reside in RAM. Unfortunately, Rust
slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen: slices will not always do so. The following example using the SPI peripheral shows a common situation where this might happen:
```no_run ```rust,ignore
// As we pass a slice to the function whose contents will not ever change, // As we pass a slice to the function whose contents will not ever change,
// the compiler writes it into the flash and thus the pointer to it will // the compiler writes it into the flash and thus the pointer to it will
// reference static memory. Since EasyDMA requires slices to reside in RAM, // reference static memory. Since EasyDMA requires slices to reside in RAM,

View File

@ -154,10 +154,19 @@ impl<'d, T: Instance> Qdec<'d, T> {
/// # Example /// # Example
/// ///
/// ```no_run /// ```no_run
/// let irq = interrupt::take!(QDEC); /// use embassy_nrf::qdec::{self, Qdec};
/// use embassy_nrf::{bind_interrupts, peripherals};
///
/// bind_interrupts!(struct Irqs {
/// QDEC => qdec::InterruptHandler<peripherals::QDEC>;
/// });
///
/// # async {
/// # let p: embassy_nrf::Peripherals = todo!();
/// let config = qdec::Config::default(); /// let config = qdec::Config::default();
/// let mut q = Qdec::new(p.QDEC, p.P0_31, p.P0_30, config); /// let mut q = Qdec::new(p.QDEC, Irqs, p.P0_31, p.P0_30, config);
/// let delta = q.read().await; /// let delta = q.read().await;
/// # };
/// ``` /// ```
pub async fn read(&mut self) -> i16 { pub async fn read(&mut self) -> i16 {
let t = T::regs(); let t = T::regs();

View File

@ -56,8 +56,19 @@ impl<'d> Temp<'d> {
/// # Example /// # Example
/// ///
/// ```no_run /// ```no_run
/// let mut t = Temp::new(p.TEMP, interrupt::take!(TEMP)); /// use embassy_nrf::{bind_interrupts, temp};
/// use embassy_nrf::temp::Temp;
/// use embassy_time::{Duration, Timer};
///
/// bind_interrupts!(struct Irqs {
/// TEMP => temp::InterruptHandler;
/// });
///
/// # async {
/// # let p: embassy_nrf::Peripherals = todo!();
/// let mut t = Temp::new(p.TEMP, Irqs);
/// let v: u16 = t.read().await.to_num::<u16>(); /// let v: u16 = t.read().await.to_num::<u16>();
/// # };
/// ``` /// ```
pub async fn read(&mut self) -> I30F2 { pub async fn read(&mut self) -> I30F2 {
// In case the future is dropped, stop the task and reset events. // In case the future is dropped, stop the task and reset events.

View File

@ -13,6 +13,8 @@ flavors = [
] ]
[features] [features]
default = [ "rp-pac/rt" ]
defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-common/defmt"] defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-common/defmt"]
# critical section that is safe for multicore use # critical section that is safe for multicore use
@ -70,7 +72,7 @@ embedded-storage = { version = "0.3" }
rand_core = "0.6.4" rand_core = "0.6.4"
fixed = "1.23.1" fixed = "1.23.1"
rp-pac = { version = "4", features = ["rt"] } rp-pac = { version = "4" }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
@ -81,3 +83,7 @@ paste = "1.0"
pio-proc = {version= "0.2" } pio-proc = {version= "0.2" }
pio = {version= "0.2.1" } pio = {version= "0.2.1" }
rp2040-boot2 = "0.3" rp2040-boot2 = "0.3"
[dev-dependencies]
embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] }
static_cell = "1.0"

View File

@ -575,6 +575,7 @@ mod ram_helpers {
#[inline(never)] #[inline(never)]
#[link_section = ".data.ram_func"] #[link_section = ".data.ram_func"]
unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) { unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) {
#[cfg(target_arch = "arm")]
core::arch::asm!( core::arch::asm!(
"mov r10, r0", // cmd "mov r10, r0", // cmd
"mov r5, r1", // ptrs "mov r5, r1", // ptrs

View File

@ -61,16 +61,17 @@ macro_rules! intrinsics_aliases {
/// Like the compiler-builtins macro, it accepts a series of functions that /// Like the compiler-builtins macro, it accepts a series of functions that
/// looks like normal Rust code: /// looks like normal Rust code:
/// ///
/// intrinsics! { /// ```rust,ignore
/// extern "C" fn foo(a: i32) -> u32 { /// intrinsics! {
/// // ... /// extern "C" fn foo(a: i32) -> u32 {
/// } /// // ...
///
/// #[nonstandard_attribute]
/// extern "C" fn bar(a: i32) -> u32 {
/// // ...
/// }
/// } /// }
/// #[nonstandard_attribute]
/// extern "C" fn bar(a: i32) -> u32 {
/// // ...
/// }
/// }
/// ```
/// ///
/// Each function can also be decorated with nonstandard attributes to control /// Each function can also be decorated with nonstandard attributes to control
/// additional behaviour: /// additional behaviour:

View File

@ -9,22 +9,41 @@
//! the `embassy-sync` primitives and `CriticalSectionRawMutex`. //! the `embassy-sync` primitives and `CriticalSectionRawMutex`.
//! //!
//! # Usage //! # Usage
//!
//! ```no_run //! ```no_run
//! # #![feature(type_alias_impl_trait)]
//! use embassy_rp::multicore::Stack;
//! use static_cell::StaticCell;
//! use embassy_executor::Executor;
//!
//! static mut CORE1_STACK: Stack<4096> = Stack::new(); //! static mut CORE1_STACK: Stack<4096> = Stack::new();
//! static EXECUTOR0: StaticCell<Executor> = StaticCell::new(); //! static EXECUTOR0: StaticCell<Executor> = StaticCell::new();
//! static EXECUTOR1: StaticCell<Executor> = StaticCell::new(); //! static EXECUTOR1: StaticCell<Executor> = StaticCell::new();
//! //!
//! # // workaround weird error: `main` function not found in crate `rust_out`
//! # let _ = ();
//!
//! #[embassy_executor::task]
//! async fn core0_task() {
//! // ...
//! }
//!
//! #[embassy_executor::task]
//! async fn core1_task() {
//! // ...
//! }
//!
//! #[cortex_m_rt::entry] //! #[cortex_m_rt::entry]
//! fn main() -> ! { //! fn main() -> ! {
//! let p = embassy_rp::init(Default::default()); //! let p = embassy_rp::init(Default::default());
//! //!
//! spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { //! embassy_rp::multicore::spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || {
//! let executor1 = EXECUTOR1.init(Executor::new()); //! let executor1 = EXECUTOR1.init(Executor::new());
//! executor1.run(|spawner| unwrap!(spawner.spawn(core1_task()))); //! executor1.run(|spawner| spawner.spawn(core1_task()).unwrap());
//! }); //! });
//! //!
//! let executor0 = EXECUTOR0.init(Executor::new()); //! let executor0 = EXECUTOR0.init(Executor::new());
//! executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); //! executor0.run(|spawner| spawner.spawn(core0_task()).unwrap())
//! } //! }
//! ``` //! ```

View File

@ -121,7 +121,7 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
/// # #[cfg(not(feature = "chrono"))] /// # #[cfg(not(feature = "chrono"))]
/// # fn main() { /// # fn main() {
/// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter}; /// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter};
/// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() }; /// # let mut real_time_clock: RealTimeClock<embassy_rp::peripherals::RTC> = unsafe { core::mem::zeroed() };
/// let now = real_time_clock.now().unwrap(); /// let now = real_time_clock.now().unwrap();
/// real_time_clock.schedule_alarm( /// real_time_clock.schedule_alarm(
/// DateTimeFilter::default() /// DateTimeFilter::default()

View File

@ -111,24 +111,18 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: usize, index
panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num); panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num);
} }
let mut wake = false;
if isr.htif(channel_num) && cr.read().htie() { if isr.htif(channel_num) && cr.read().htie() {
// Acknowledge half transfer complete interrupt // Acknowledge half transfer complete interrupt
dma.ifcr().write(|w| w.set_htif(channel_num, true)); dma.ifcr().write(|w| w.set_htif(channel_num, true));
wake = true; } else if isr.tcif(channel_num) && cr.read().tcie() {
}
if isr.tcif(channel_num) && cr.read().tcie() {
// Acknowledge transfer complete interrupt // Acknowledge transfer complete interrupt
dma.ifcr().write(|w| w.set_tcif(channel_num, true)); dma.ifcr().write(|w| w.set_tcif(channel_num, true));
STATE.complete_count[index].fetch_add(1, Ordering::Release); STATE.complete_count[index].fetch_add(1, Ordering::Release);
wake = true; } else {
return;
} }
if wake { STATE.ch_wakers[index].wake();
STATE.ch_wakers[index].wake();
}
} }
#[cfg(any(bdma_v2, dmamux))] #[cfg(any(bdma_v2, dmamux))]
@ -371,7 +365,7 @@ impl<'a, C: Channel> Future for Transfer<'a, C> {
struct DmaCtrlImpl<'a, C: Channel>(PeripheralRef<'a, C>); struct DmaCtrlImpl<'a, C: Channel>(PeripheralRef<'a, C>);
impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
fn ndtr(&self) -> usize { fn get_remaining_transfers(&self) -> usize {
let ch = self.0.regs().ch(self.0.num()); let ch = self.0.regs().ch(self.0.num());
unsafe { ch.ndtr().read() }.ndt() as usize unsafe { ch.ndtr().read() }.ndt() as usize
} }
@ -457,21 +451,17 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
} }
/// Read bytes from the ring buffer /// Read bytes from the ring buffer
/// Return a tuple of the length read and the length remaining in the buffer
/// If not all of the bytes were read, then there will be some bytes in the buffer remaining
/// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller. /// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
pub fn read(&mut self, buf: &mut [W]) -> Result<usize, OverrunError> { pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf)
} }
pub fn is_empty(&self) -> bool { /// The capacity of the ringbuffer
self.ringbuf.is_empty() pub fn cap(&self) -> usize {
} self.ringbuf.cap()
pub fn len(&self) -> usize {
self.ringbuf.len()
}
pub fn capacity(&self) -> usize {
self.ringbuf.dma_buf.len()
} }
pub fn set_waker(&mut self, waker: &Waker) { pub fn set_waker(&mut self, waker: &Waker) {
@ -506,12 +496,6 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
let ch = self.channel.regs().ch(self.channel.num()); let ch = self.channel.regs().ch(self.channel.num());
unsafe { ch.cr().read() }.en() unsafe { ch.cr().read() }.en()
} }
/// Synchronize the position of the ring buffer to the actual DMA controller position
pub fn reload_position(&mut self) {
let ch = self.channel.regs().ch(self.channel.num());
self.ringbuf.ndtr = unsafe { ch.ndtr().read() }.ndt() as usize;
}
} }
impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> { impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> {

View File

@ -187,24 +187,18 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: usize, index:
panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num); panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num);
} }
let mut wake = false;
if isr.htif(channel_num % 4) && cr.read().htie() { if isr.htif(channel_num % 4) && cr.read().htie() {
// Acknowledge half transfer complete interrupt // Acknowledge half transfer complete interrupt
dma.ifcr(channel_num / 4).write(|w| w.set_htif(channel_num % 4, true)); dma.ifcr(channel_num / 4).write(|w| w.set_htif(channel_num % 4, true));
wake = true; } else if isr.tcif(channel_num % 4) && cr.read().tcie() {
}
if isr.tcif(channel_num % 4) && cr.read().tcie() {
// Acknowledge transfer complete interrupt // Acknowledge transfer complete interrupt
dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true)); dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true));
STATE.complete_count[index].fetch_add(1, Ordering::Release); STATE.complete_count[index].fetch_add(1, Ordering::Release);
wake = true; } else {
return;
} }
if wake { STATE.ch_wakers[index].wake();
STATE.ch_wakers[index].wake();
}
} }
#[cfg(any(dma_v2, dmamux))] #[cfg(any(dma_v2, dmamux))]
@ -612,7 +606,7 @@ impl<'a, C: Channel, W: Word> Drop for DoubleBuffered<'a, C, W> {
struct DmaCtrlImpl<'a, C: Channel>(PeripheralRef<'a, C>); struct DmaCtrlImpl<'a, C: Channel>(PeripheralRef<'a, C>);
impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
fn ndtr(&self) -> usize { fn get_remaining_transfers(&self) -> usize {
let ch = self.0.regs().st(self.0.num()); let ch = self.0.regs().st(self.0.num());
unsafe { ch.ndtr().read() }.ndt() as usize unsafe { ch.ndtr().read() }.ndt() as usize
} }
@ -713,21 +707,17 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
} }
/// Read bytes from the ring buffer /// Read bytes from the ring buffer
/// Return a tuple of the length read and the length remaining in the buffer
/// If not all of the bytes were read, then there will be some bytes in the buffer remaining
/// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller. /// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
pub fn read(&mut self, buf: &mut [W]) -> Result<usize, OverrunError> { pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> {
self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf)
} }
pub fn is_empty(&self) -> bool { // The capacity of the ringbuffer
self.ringbuf.is_empty() pub fn cap(&self) -> usize {
} self.ringbuf.cap()
pub fn len(&self) -> usize {
self.ringbuf.len()
}
pub fn capacity(&self) -> usize {
self.ringbuf.dma_buf.len()
} }
pub fn set_waker(&mut self, waker: &Waker) { pub fn set_waker(&mut self, waker: &Waker) {
@ -766,12 +756,6 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
let ch = self.channel.regs().st(self.channel.num()); let ch = self.channel.regs().st(self.channel.num());
unsafe { ch.cr().read() }.en() unsafe { ch.cr().read() }.en()
} }
/// Synchronize the position of the ring buffer to the actual DMA controller position
pub fn reload_position(&mut self) {
let ch = self.channel.regs().st(self.channel.num());
self.ringbuf.ndtr = unsafe { ch.ndtr().read() }.ndt() as usize;
}
} }
impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> { impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> {

View File

@ -25,14 +25,13 @@ use super::word::Word;
/// +-----------------------------------------+ +-----------------------------------------+ /// +-----------------------------------------+ +-----------------------------------------+
/// ^ ^ ^ ^ ^ ^ /// ^ ^ ^ ^ ^ ^
/// | | | | | | /// | | | | | |
/// +- first --+ | +- end ------+ | /// +- start --+ | +- end ------+ |
/// | | | | /// | | | |
/// +- end --------------------+ +- first ----------------+ /// +- end --------------------+ +- start ----------------+
/// ``` /// ```
pub struct DmaRingBuffer<'a, W: Word> { pub struct DmaRingBuffer<'a, W: Word> {
pub(crate) dma_buf: &'a mut [W], pub(crate) dma_buf: &'a mut [W],
first: usize, start: usize,
pub ndtr: usize,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -41,7 +40,7 @@ pub struct OverrunError;
pub trait DmaCtrl { pub trait DmaCtrl {
/// Get the NDTR register value, i.e. the space left in the underlying /// Get the NDTR register value, i.e. the space left in the underlying
/// buffer until the dma writer wraps. /// buffer until the dma writer wraps.
fn ndtr(&self) -> usize; fn get_remaining_transfers(&self) -> usize;
/// Get the transfer completed counter. /// Get the transfer completed counter.
/// This counter is incremented by the dma controller when NDTR is reloaded, /// This counter is incremented by the dma controller when NDTR is reloaded,
@ -54,151 +53,131 @@ pub trait DmaCtrl {
impl<'a, W: Word> DmaRingBuffer<'a, W> { impl<'a, W: Word> DmaRingBuffer<'a, W> {
pub fn new(dma_buf: &'a mut [W]) -> Self { pub fn new(dma_buf: &'a mut [W]) -> Self {
let ndtr = dma_buf.len(); Self { dma_buf, start: 0 }
Self {
dma_buf,
first: 0,
ndtr,
}
} }
/// Reset the ring buffer to its initial state /// Reset the ring buffer to its initial state
pub fn clear(&mut self, mut dma: impl DmaCtrl) { pub fn clear(&mut self, mut dma: impl DmaCtrl) {
self.first = 0; self.start = 0;
self.ndtr = self.dma_buf.len();
dma.reset_complete_count(); dma.reset_complete_count();
} }
/// The buffer end position /// The capacity of the ringbuffer
fn end(&self) -> usize { pub const fn cap(&self) -> usize {
self.dma_buf.len() - self.ndtr self.dma_buf.len()
} }
/// Returns whether the buffer is empty /// The current position of the ringbuffer
pub fn is_empty(&self) -> bool { fn pos(&self, remaining_transfers: usize) -> usize {
self.first == self.end() self.cap() - remaining_transfers
}
/// The current number of bytes in the buffer
/// This may change at any time if dma is currently active
pub fn len(&self) -> usize {
// Read out a stable end (the dma periheral can change it at anytime)
let end = self.end();
if self.first <= end {
// No wrap
end - self.first
} else {
self.dma_buf.len() - self.first + end
}
} }
/// Read bytes from the ring buffer /// Read bytes from the ring buffer
/// Return a tuple of the length read and the length remaining in the buffer
/// If not all of the bytes were read, then there will be some bytes in the buffer remaining
/// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read
/// OverrunError is returned if the portion to be read was overwritten by the DMA controller. /// OverrunError is returned if the portion to be read was overwritten by the DMA controller.
pub fn read(&mut self, mut dma: impl DmaCtrl, buf: &mut [W]) -> Result<usize, OverrunError> { pub fn read(&mut self, mut dma: impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> {
let end = self.end(); /*
This algorithm is optimistic: we assume we haven't overrun more than a full buffer and then check
after we've done our work to see we have. This is because on stm32, an interrupt is not guaranteed
to fire in the same clock cycle that a register is read, so checking get_complete_count early does
not yield relevant information.
compiler_fence(Ordering::SeqCst); Therefore, the only variable we really need to know is ndtr. If the dma has overrun by more than a full
buffer, we will do a bit more work than we have to, but algorithms should not be optimized for error
conditions.
if self.first == end { After we've done our work, we confirm that we haven't overrun more than a full buffer, and also that
// The buffer is currently empty the dma has not overrun within the data we could have copied. We check the data we could have copied
rather than the data we actually copied because it costs nothing and confirms an error condition
if dma.get_complete_count() > 0 { earlier.
// The DMA has written such that the ring buffer wraps at least once */
self.ndtr = dma.ndtr(); let end = self.pos(dma.get_remaining_transfers());
if self.end() > self.first || dma.get_complete_count() > 1 { if self.start == end && dma.get_complete_count() == 0 {
return Err(OverrunError); // No bytes are available in the buffer
} Ok((0, self.cap()))
} } else if self.start < end {
Ok(0)
} else if self.first < end {
// The available, unread portion in the ring buffer DOES NOT wrap // The available, unread portion in the ring buffer DOES NOT wrap
if dma.get_complete_count() > 1 {
return Err(OverrunError);
}
// Copy out the bytes from the dma buffer // Copy out the bytes from the dma buffer
let len = self.copy_to(buf, self.first..end); let len = self.copy_to(buf, self.start..end);
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
match dma.get_complete_count() { /*
0 => { first, check if the dma has wrapped at all if it's after end
// The DMA writer has not wrapped before nor after the copy or more than once if it's before start
}
1 => {
// The DMA writer has written such that the ring buffer now wraps
self.ndtr = dma.ndtr();
if self.end() > self.first || dma.get_complete_count() > 1 {
// The bytes that we have copied out have overflowed
// as the writer has now both wrapped and is currently writing
// within the region that we have just copied out
return Err(OverrunError);
}
}
_ => {
return Err(OverrunError);
}
}
self.first = (self.first + len) % self.dma_buf.len(); this is in a critical section to try to reduce mushy behavior.
Ok(len) it's not ideal but it's the best we can do
then, get the current position of of the dma write and check
if it's inside data we could have copied
*/
let (pos, complete_count) =
critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count()));
if (pos >= self.start && pos < end) || (complete_count > 0 && pos >= end) || complete_count > 1 {
Err(OverrunError)
} else {
self.start = (self.start + len) % self.cap();
Ok((len, self.cap() - self.start))
}
} else if self.start + buf.len() < self.cap() {
// The available, unread portion in the ring buffer DOES wrap
// The DMA writer has wrapped since we last read and is currently
// writing (or the next byte added will be) in the beginning of the ring buffer.
// The provided read buffer is not large enough to include all bytes from the tail of the dma buffer.
// Copy out from the dma buffer
let len = self.copy_to(buf, self.start..self.cap());
compiler_fence(Ordering::SeqCst);
/*
first, check if the dma has wrapped around more than once
then, get the current position of of the dma write and check
if it's inside data we could have copied
*/
let pos = self.pos(dma.get_remaining_transfers());
if pos > self.start || pos < end || dma.get_complete_count() > 1 {
Err(OverrunError)
} else {
self.start = (self.start + len) % self.cap();
Ok((len, self.start + end))
}
} else { } else {
// The available, unread portion in the ring buffer DOES wrap // The available, unread portion in the ring buffer DOES wrap
// The DMA writer has wrapped since we last read and is currently // The DMA writer has wrapped since we last read and is currently
// writing (or the next byte added will be) in the beginning of the ring buffer. // writing (or the next byte added will be) in the beginning of the ring buffer.
let complete_count = dma.get_complete_count(); // The provided read buffer is large enough to include all bytes from the tail of the dma buffer,
if complete_count > 1 { // so the next read will not have any unread tail bytes in the ring buffer.
return Err(OverrunError);
}
// If the unread portion wraps then the writer must also have wrapped // Copy out from the dma buffer
assert!(complete_count == 1); let tail = self.copy_to(buf, self.start..self.cap());
let head = self.copy_to(&mut buf[tail..], 0..end);
if self.first + buf.len() < self.dma_buf.len() { compiler_fence(Ordering::SeqCst);
// The provided read buffer is not large enough to include all bytes from the tail of the dma buffer.
// Copy out from the dma buffer /*
let len = self.copy_to(buf, self.first..self.dma_buf.len()); first, check if the dma has wrapped around more than once
compiler_fence(Ordering::SeqCst); then, get the current position of of the dma write and check
if it's inside data we could have copied
// We have now copied out the data from dma_buf */
// Make sure that the just read part was not overwritten during the copy let pos = self.pos(dma.get_remaining_transfers());
self.ndtr = dma.ndtr(); if pos > self.start || pos < end || dma.reset_complete_count() > 1 {
if self.end() > self.first || dma.get_complete_count() > 1 { Err(OverrunError)
// The writer has entered the data that we have just read since we read out `end` in the beginning and until now.
return Err(OverrunError);
}
self.first = (self.first + len) % self.dma_buf.len();
Ok(len)
} else { } else {
// The provided read buffer is large enough to include all bytes from the tail of the dma buffer, self.start = head;
// so the next read will not have any unread tail bytes in the ring buffer. Ok((tail + head, self.cap() - self.start))
// Copy out from the dma buffer
let tail = self.copy_to(buf, self.first..self.dma_buf.len());
let head = self.copy_to(&mut buf[tail..], 0..end);
compiler_fence(Ordering::SeqCst);
// We have now copied out the data from dma_buf
// Reset complete counter and make sure that the just read part was not overwritten during the copy
self.ndtr = dma.ndtr();
let complete_count = dma.reset_complete_count();
if self.end() > self.first || complete_count > 1 {
return Err(OverrunError);
}
self.first = head;
Ok(tail + head)
} }
} }
} }
/// Copy from the dma buffer at `data_range` into `buf` /// Copy from the dma buffer at `data_range` into `buf`
fn copy_to(&mut self, buf: &mut [W], data_range: Range<usize>) -> usize { fn copy_to(&mut self, buf: &mut [W], data_range: Range<usize>) -> usize {
// Limit the number of bytes that can be copied // Limit the number of bytes that can be copied
@ -218,203 +197,289 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> {
length length
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use core::array; use core::array;
use core::cell::RefCell; use std::{cell, vec};
use super::*; use super::*;
struct TestCtrl { #[allow(dead_code)]
next_ndtr: RefCell<Option<usize>>, #[derive(PartialEq, Debug)]
complete_count: usize, enum TestCircularTransferRequest {
GetCompleteCount(usize),
ResetCompleteCount(usize),
PositionRequest(usize),
} }
impl TestCtrl { struct TestCircularTransfer {
pub const fn new() -> Self { len: usize,
Self { requests: cell::RefCell<vec::Vec<TestCircularTransferRequest>>,
next_ndtr: RefCell::new(None), }
complete_count: 0,
impl DmaCtrl for &mut TestCircularTransfer {
fn get_remaining_transfers(&self) -> usize {
match self.requests.borrow_mut().pop().unwrap() {
TestCircularTransferRequest::PositionRequest(pos) => {
let len = self.len;
assert!(len >= pos);
len - pos
}
_ => unreachable!(),
} }
} }
pub fn set_next_ndtr(&mut self, ndtr: usize) {
self.next_ndtr.borrow_mut().replace(ndtr);
}
}
impl DmaCtrl for &mut TestCtrl {
fn ndtr(&self) -> usize {
self.next_ndtr.borrow_mut().unwrap()
}
fn get_complete_count(&self) -> usize { fn get_complete_count(&self) -> usize {
self.complete_count match self.requests.borrow_mut().pop().unwrap() {
TestCircularTransferRequest::GetCompleteCount(complete_count) => complete_count,
_ => unreachable!(),
}
} }
fn reset_complete_count(&mut self) -> usize { fn reset_complete_count(&mut self) -> usize {
let old = self.complete_count; match self.requests.get_mut().pop().unwrap() {
self.complete_count = 0; TestCircularTransferRequest::ResetCompleteCount(complete_count) => complete_count,
old _ => unreachable!(),
}
}
}
impl TestCircularTransfer {
pub fn new(len: usize) -> Self {
Self {
requests: cell::RefCell::new(vec![]),
len: len,
}
}
pub fn setup(&self, mut requests: vec::Vec<TestCircularTransferRequest>) {
requests.reverse();
self.requests.replace(requests);
} }
} }
#[test] #[test]
fn empty() { fn empty_and_read_not_started() {
let mut dma_buf = [0u8; 16]; let mut dma_buf = [0u8; 16];
let ringbuf = DmaRingBuffer::new(&mut dma_buf); let ringbuf = DmaRingBuffer::new(&mut dma_buf);
assert!(ringbuf.is_empty()); assert_eq!(0, ringbuf.start);
assert_eq!(0, ringbuf.len());
} }
#[test] #[test]
fn can_read() { fn can_read() {
let mut dma = TestCircularTransfer::new(16);
let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15
let mut ctrl = TestCtrl::new();
let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf);
ringbuf.ndtr = 6;
assert!(!ringbuf.is_empty()); assert_eq!(0, ringbuf.start);
assert_eq!(10, ringbuf.len()); assert_eq!(16, ringbuf.cap());
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(8),
TestCircularTransferRequest::PositionRequest(10),
TestCircularTransferRequest::GetCompleteCount(0),
]);
let mut buf = [0; 2]; let mut buf = [0; 2];
assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0);
assert_eq!([0, 1], buf); assert_eq!([0, 1], buf);
assert_eq!(8, ringbuf.len()); assert_eq!(2, ringbuf.start);
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(10),
TestCircularTransferRequest::PositionRequest(12),
TestCircularTransferRequest::GetCompleteCount(0),
]);
let mut buf = [0; 2]; let mut buf = [0; 2];
assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0);
assert_eq!([2, 3], buf); assert_eq!([2, 3], buf);
assert_eq!(6, ringbuf.len()); assert_eq!(4, ringbuf.start);
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(12),
TestCircularTransferRequest::PositionRequest(14),
TestCircularTransferRequest::GetCompleteCount(0),
]);
let mut buf = [0; 8]; let mut buf = [0; 8];
assert_eq!(6, ringbuf.read(&mut ctrl, &mut buf).unwrap()); assert_eq!(8, ringbuf.read(&mut dma, &mut buf).unwrap().0);
assert_eq!([4, 5, 6, 7, 8, 9], buf[..6]); assert_eq!([4, 5, 6, 7, 8, 9], buf[..6]);
assert_eq!(0, ringbuf.len()); assert_eq!(12, ringbuf.start);
let mut buf = [0; 2];
assert_eq!(0, ringbuf.read(&mut ctrl, &mut buf).unwrap());
} }
#[test] #[test]
fn can_read_with_wrap() { fn can_read_with_wrap() {
let mut dma = TestCircularTransfer::new(16);
let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15
let mut ctrl = TestCtrl::new();
let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf);
ringbuf.first = 12;
ringbuf.ndtr = 10;
// The dma controller has written 4 + 6 bytes and has reloaded NDTR assert_eq!(0, ringbuf.start);
ctrl.complete_count = 1; assert_eq!(16, ringbuf.cap());
ctrl.set_next_ndtr(10);
assert!(!ringbuf.is_empty()); /*
assert_eq!(6 + 4, ringbuf.len()); Read to close to the end of the buffer
*/
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(14),
TestCircularTransferRequest::PositionRequest(16),
TestCircularTransferRequest::GetCompleteCount(0),
]);
let mut buf = [0; 14];
assert_eq!(14, ringbuf.read(&mut dma, &mut buf).unwrap().0);
assert_eq!(14, ringbuf.start);
let mut buf = [0; 2]; /*
assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); Now, read around the buffer
assert_eq!([12, 13], buf); */
assert_eq!(6 + 2, ringbuf.len()); dma.setup(vec![
TestCircularTransferRequest::PositionRequest(6),
let mut buf = [0; 4]; TestCircularTransferRequest::PositionRequest(8),
assert_eq!(4, ringbuf.read(&mut ctrl, &mut buf).unwrap()); TestCircularTransferRequest::ResetCompleteCount(1),
assert_eq!([14, 15, 0, 1], buf); ]);
assert_eq!(4, ringbuf.len()); let mut buf = [0; 6];
assert_eq!(6, ringbuf.read(&mut dma, &mut buf).unwrap().0);
assert_eq!(4, ringbuf.start);
} }
#[test] #[test]
fn can_read_when_dma_writer_is_wrapped_and_read_does_not_wrap() { fn can_read_when_dma_writer_is_wrapped_and_read_does_not_wrap() {
let mut dma = TestCircularTransfer::new(16);
let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15
let mut ctrl = TestCtrl::new();
let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf);
ringbuf.first = 2;
ringbuf.ndtr = 6;
// The dma controller has written 6 + 2 bytes and has reloaded NDTR assert_eq!(0, ringbuf.start);
ctrl.complete_count = 1; assert_eq!(16, ringbuf.cap());
ctrl.set_next_ndtr(14);
/*
Read to close to the end of the buffer
*/
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(14),
TestCircularTransferRequest::PositionRequest(16),
TestCircularTransferRequest::GetCompleteCount(0),
]);
let mut buf = [0; 14];
assert_eq!(14, ringbuf.read(&mut dma, &mut buf).unwrap().0);
assert_eq!(14, ringbuf.start);
/*
Now, read to the end of the buffer
*/
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(6),
TestCircularTransferRequest::PositionRequest(8),
TestCircularTransferRequest::ResetCompleteCount(1),
]);
let mut buf = [0; 2]; let mut buf = [0; 2];
assert_eq!(2, ringbuf.read(&mut ctrl, &mut buf).unwrap()); assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0);
assert_eq!([2, 3], buf); assert_eq!(0, ringbuf.start);
assert_eq!(1, ctrl.complete_count); // The interrupt flag IS NOT cleared
} }
#[test] #[test]
fn can_read_when_dma_writer_is_wrapped_and_read_wraps() { fn can_read_when_dma_writer_wraps_once_with_same_ndtr() {
let mut dma = TestCircularTransfer::new(16);
let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15
let mut ctrl = TestCtrl::new();
let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf);
ringbuf.first = 12;
ringbuf.ndtr = 10;
// The dma controller has written 6 + 2 bytes and has reloaded NDTR assert_eq!(0, ringbuf.start);
ctrl.complete_count = 1; assert_eq!(16, ringbuf.cap());
ctrl.set_next_ndtr(14);
let mut buf = [0; 10]; /*
assert_eq!(10, ringbuf.read(&mut ctrl, &mut buf).unwrap()); Read to about the middle of the buffer
assert_eq!([12, 13, 14, 15, 0, 1, 2, 3, 4, 5], buf); */
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(6),
TestCircularTransferRequest::PositionRequest(6),
TestCircularTransferRequest::GetCompleteCount(0),
]);
let mut buf = [0; 6];
assert_eq!(6, ringbuf.read(&mut dma, &mut buf).unwrap().0);
assert_eq!(6, ringbuf.start);
assert_eq!(0, ctrl.complete_count); // The interrupt flag IS cleared /*
} Now, wrap the DMA controller around
*/
#[test] dma.setup(vec![
fn cannot_read_when_dma_writer_wraps_with_same_ndtr() { TestCircularTransferRequest::PositionRequest(6),
let mut dma_buf = [0u8; 16]; TestCircularTransferRequest::GetCompleteCount(1),
let mut ctrl = TestCtrl::new(); TestCircularTransferRequest::PositionRequest(6),
let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); TestCircularTransferRequest::GetCompleteCount(1),
ringbuf.first = 6; ]);
ringbuf.ndtr = 10; let mut buf = [0; 6];
ctrl.set_next_ndtr(9); assert_eq!(6, ringbuf.read(&mut dma, &mut buf).unwrap().0);
assert_eq!(12, ringbuf.start);
assert!(ringbuf.is_empty()); // The ring buffer thinks that it is empty
// The dma controller has written exactly 16 bytes
ctrl.complete_count = 1;
let mut buf = [0; 2];
assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf));
assert_eq!(1, ctrl.complete_count); // The complete counter is not reset
} }
#[test] #[test]
fn cannot_read_when_dma_writer_overwrites_during_not_wrapping_read() { fn cannot_read_when_dma_writer_overwrites_during_not_wrapping_read() {
let mut dma = TestCircularTransfer::new(16);
let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15
let mut ctrl = TestCtrl::new();
let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf);
ringbuf.first = 2;
ringbuf.ndtr = 6;
// The dma controller has written 6 + 3 bytes and has reloaded NDTR assert_eq!(0, ringbuf.start);
ctrl.complete_count = 1; assert_eq!(16, ringbuf.cap());
ctrl.set_next_ndtr(13);
let mut buf = [0; 2]; /*
assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); Read a few bytes
*/
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(2),
TestCircularTransferRequest::PositionRequest(2),
TestCircularTransferRequest::GetCompleteCount(0),
]);
let mut buf = [0; 6];
assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0);
assert_eq!(2, ringbuf.start);
assert_eq!(1, ctrl.complete_count); // The complete counter is not reset /*
Now, overtake the reader
*/
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(4),
TestCircularTransferRequest::PositionRequest(6),
TestCircularTransferRequest::GetCompleteCount(1),
]);
let mut buf = [0; 6];
assert_eq!(OverrunError, ringbuf.read(&mut dma, &mut buf).unwrap_err());
} }
#[test] #[test]
fn cannot_read_when_dma_writer_overwrites_during_wrapping_read() { fn cannot_read_when_dma_writer_overwrites_during_wrapping_read() {
let mut dma = TestCircularTransfer::new(16);
let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15
let mut ctrl = TestCtrl::new();
let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); let mut ringbuf = DmaRingBuffer::new(&mut dma_buf);
ringbuf.first = 12;
ringbuf.ndtr = 10;
// The dma controller has written 6 + 13 bytes and has reloaded NDTR assert_eq!(0, ringbuf.start);
ctrl.complete_count = 1; assert_eq!(16, ringbuf.cap());
ctrl.set_next_ndtr(3);
let mut buf = [0; 2]; /*
assert_eq!(Err(OverrunError), ringbuf.read(&mut ctrl, &mut buf)); Read to close to the end of the buffer
*/
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(14),
TestCircularTransferRequest::PositionRequest(16),
TestCircularTransferRequest::GetCompleteCount(0),
]);
let mut buf = [0; 14];
assert_eq!(14, ringbuf.read(&mut dma, &mut buf).unwrap().0);
assert_eq!(14, ringbuf.start);
assert_eq!(1, ctrl.complete_count); // The complete counter is not reset /*
Now, overtake the reader
*/
dma.setup(vec![
TestCircularTransferRequest::PositionRequest(8),
TestCircularTransferRequest::PositionRequest(10),
TestCircularTransferRequest::ResetCompleteCount(2),
]);
let mut buf = [0; 6];
assert_eq!(OverrunError, ringbuf.read(&mut dma, &mut buf).unwrap_err());
} }
} }

View File

@ -1,4 +1,4 @@
#![no_std] #![cfg_attr(not(test), no_std)]
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
// This must go FIRST so that all the other modules see its macros. // This must go FIRST so that all the other modules see its macros.

View File

@ -13,6 +13,12 @@ use futures::future::{select, Either};
use crate::dma::{NoDma, Transfer}; use crate::dma::{NoDma, Transfer};
use crate::gpio::sealed::AFType; use crate::gpio::sealed::AFType;
#[cfg(not(any(usart_v1, usart_v2)))] #[cfg(not(any(usart_v1, usart_v2)))]
#[allow(unused_imports)]
use crate::pac::usart::regs::Isr as Sr;
#[cfg(any(usart_v1, usart_v2))]
#[allow(unused_imports)]
use crate::pac::usart::regs::Sr;
#[cfg(not(any(usart_v1, usart_v2)))]
use crate::pac::usart::Lpuart as Regs; use crate::pac::usart::Lpuart as Regs;
#[cfg(any(usart_v1, usart_v2))] #[cfg(any(usart_v1, usart_v2))]
use crate::pac::usart::Usart as Regs; use crate::pac::usart::Usart as Regs;
@ -32,7 +38,6 @@ impl<T: BasicInstance> interrupt::Handler<T::Interrupt> for InterruptHandler<T>
let (sr, cr1, cr3) = unsafe { (sr(r).read(), r.cr1().read(), r.cr3().read()) }; let (sr, cr1, cr3) = unsafe { (sr(r).read(), r.cr1().read(), r.cr3().read()) };
let mut wake = false;
let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie()); let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie());
if has_errors { if has_errors {
// clear all interrupts and DMA Rx Request // clear all interrupts and DMA Rx Request
@ -52,35 +57,24 @@ impl<T: BasicInstance> interrupt::Handler<T::Interrupt> for InterruptHandler<T>
w.set_dmar(false); w.set_dmar(false);
}); });
} }
} else if cr1.idleie() && sr.idle() {
// IDLE detected: no more data will come
unsafe {
r.cr1().modify(|w| {
// disable idle line detection
w.set_idleie(false);
});
}
} else if cr1.rxneie() {
// We cannot check the RXNE flag as it is auto-cleared by the DMA controller
wake = true; // It is up to the listener to determine if this in fact was a RX event and disable the RXNE detection
} else { } else {
if cr1.idleie() && sr.idle() { return;
// IDLE detected: no more data will come
unsafe {
r.cr1().modify(|w| {
// disable idle line detection
w.set_idleie(false);
});
}
wake = true;
}
if cr1.rxneie() {
// We cannot check the RXNE flag as it is auto-cleared by the DMA controller
// It is up to the listener to determine if this in fact was a RX event and disable the RXNE detection
wake = true;
}
} }
if wake { compiler_fence(Ordering::SeqCst);
compiler_fence(Ordering::SeqCst); s.rx_waker.wake();
s.rx_waker.wake();
}
} }
} }
@ -1109,9 +1103,9 @@ pub use crate::usart::buffered::InterruptHandler as BufferedInterruptHandler;
mod buffered; mod buffered;
#[cfg(not(gpdma))] #[cfg(not(gpdma))]
mod rx_ringbuffered; mod ringbuffered;
#[cfg(not(gpdma))] #[cfg(not(gpdma))]
pub use rx_ringbuffered::RingBufferedUartRx; pub use ringbuffered::RingBufferedUartRx;
use self::sealed::Kind; use self::sealed::Kind;

View File

@ -2,13 +2,12 @@ use core::future::poll_fn;
use core::sync::atomic::{compiler_fence, Ordering}; use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll; use core::task::Poll;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::PeripheralRef; use embassy_hal_common::PeripheralRef;
use futures::future::{select, Either}; use futures::future::{select, Either};
use super::{clear_interrupt_flags, rdr, sr, BasicInstance, Error, UartRx}; use super::{clear_interrupt_flags, rdr, sr, BasicInstance, Error, UartRx};
use crate::dma::ringbuffer::OverrunError;
use crate::dma::RingBuffer; use crate::dma::RingBuffer;
use crate::usart::{Regs, Sr};
pub struct RingBufferedUartRx<'d, T: BasicInstance, RxDma: super::RxDma<T>> { pub struct RingBufferedUartRx<'d, T: BasicInstance, RxDma: super::RxDma<T>> {
_peri: PeripheralRef<'d, T>, _peri: PeripheralRef<'d, T>,
@ -24,7 +23,9 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> UartRx<'d, T, RxDma> {
let request = self.rx_dma.request(); let request = self.rx_dma.request();
let opts = Default::default(); let opts = Default::default();
let ring_buf = unsafe { RingBuffer::new_read(self.rx_dma, request, rdr(T::regs()), dma_buf, opts) }; let ring_buf = unsafe { RingBuffer::new_read(self.rx_dma, request, rdr(T::regs()), dma_buf, opts) };
RingBufferedUartRx { RingBufferedUartRx {
_peri: self._peri, _peri: self._peri,
ring_buf, ring_buf,
@ -42,11 +43,18 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxD
Ok(()) Ok(())
} }
fn stop(&mut self, err: Error) -> Result<usize, Error> {
self.teardown_uart();
Err(err)
}
/// Start uart background receive /// Start uart background receive
fn setup_uart(&mut self) { fn setup_uart(&mut self) {
// fence before starting DMA. // fence before starting DMA.
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
// start the dma controller
self.ring_buf.start(); self.ring_buf.start();
let r = T::regs(); let r = T::regs();
@ -58,8 +66,8 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxD
w.set_rxneie(false); w.set_rxneie(false);
// enable parity interrupt if not ParityNone // enable parity interrupt if not ParityNone
w.set_peie(w.pce()); w.set_peie(w.pce());
// disable idle line interrupt // enable idle line interrupt
w.set_idleie(false); w.set_idleie(true);
}); });
r.cr3().modify(|w| { r.cr3().modify(|w| {
// enable Error Interrupt: (Frame error, Noise error, Overrun error) // enable Error Interrupt: (Frame error, Noise error, Overrun error)
@ -72,6 +80,8 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxD
/// Stop uart background receive /// Stop uart background receive
fn teardown_uart(&mut self) { fn teardown_uart(&mut self) {
self.ring_buf.request_stop();
let r = T::regs(); let r = T::regs();
// clear all interrupts and DMA Rx Request // clear all interrupts and DMA Rx Request
// SAFETY: only clears Rx related flags // SAFETY: only clears Rx related flags
@ -93,9 +103,6 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxD
} }
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
self.ring_buf.request_stop();
while self.ring_buf.is_running() {}
} }
/// Read bytes that are readily available in the ring buffer. /// Read bytes that are readily available in the ring buffer.
@ -111,96 +118,49 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxD
// Start background receive if it was not already started // Start background receive if it was not already started
// SAFETY: read only // SAFETY: read only
let is_started = unsafe { r.cr3().read().dmar() }; match unsafe { r.cr3().read().dmar() } {
if !is_started { false => self.start()?,
self.start()?; _ => {}
} };
// SAFETY: read only and we only use Rx related flags check_for_errors(clear_idle_flag(T::regs()))?;
let s = unsafe { sr(r).read() };
let has_errors = s.pe() || s.fe() || s.ne() || s.ore();
if has_errors {
self.teardown_uart();
if s.pe() {
return Err(Error::Parity);
} else if s.fe() {
return Err(Error::Framing);
} else if s.ne() {
return Err(Error::Noise);
} else {
return Err(Error::Overrun);
}
}
self.ring_buf.reload_position();
match self.ring_buf.read(buf) {
Ok(len) if len == 0 => {}
Ok(len) => {
assert!(len > 0);
return Ok(len);
}
Err(OverrunError) => {
// Stop any transfer from now on
// The user must re-start to receive any more data
self.teardown_uart();
return Err(Error::Overrun);
}
}
loop { loop {
self.wait_for_data_or_idle().await?; match self.ring_buf.read(buf) {
Ok((0, _)) => {}
Ok((len, _)) => {
return Ok(len);
}
Err(_) => {
return self.stop(Error::Overrun);
}
}
self.ring_buf.reload_position(); match self.wait_for_data_or_idle().await {
if !self.ring_buf.is_empty() { Ok(_) => {}
break; Err(err) => {
return self.stop(err);
}
} }
} }
let len = self.ring_buf.read(buf).map_err(|_err| Error::Overrun)?;
assert!(len > 0);
Ok(len)
} }
/// Wait for uart idle or dma half-full or full /// Wait for uart idle or dma half-full or full
async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> { async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> {
let r = T::regs();
// make sure USART state is restored to neutral state
let _on_drop = OnDrop::new(move || {
// SAFETY: only clears Rx related flags
unsafe {
r.cr1().modify(|w| {
// disable idle line interrupt
w.set_idleie(false);
});
}
});
// SAFETY: only sets Rx related flags
unsafe {
r.cr1().modify(|w| {
// enable idle line interrupt
w.set_idleie(true);
});
}
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
let mut dma_init = false;
// Future which completes when there is dma is half full or full // Future which completes when there is dma is half full or full
let dma = poll_fn(|cx| { let dma = poll_fn(|cx| {
self.ring_buf.set_waker(cx.waker()); self.ring_buf.set_waker(cx.waker());
compiler_fence(Ordering::SeqCst); let status = match dma_init {
false => Poll::Pending,
true => Poll::Ready(()),
};
self.ring_buf.reload_position(); dma_init = true;
if !self.ring_buf.is_empty() { status
// Some data is now available
Poll::Ready(())
} else {
Poll::Pending
}
}); });
// Future which completes when idle line is detected // Future which completes when idle line is detected
@ -210,28 +170,11 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxD
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
// SAFETY: read only and we only use Rx related flags // Critical section is needed so that IDLE isn't set after
let sr = unsafe { sr(r).read() }; // our read but before we clear it.
let sr = critical_section::with(|_| clear_idle_flag(T::regs()));
// SAFETY: only clears Rx related flags check_for_errors(sr)?;
unsafe {
// This read also clears the error and idle interrupt flags on v1.
rdr(r).read_volatile();
clear_interrupt_flags(r, sr);
}
let has_errors = sr.pe() || sr.fe() || sr.ne() || sr.ore();
if has_errors {
if sr.pe() {
return Poll::Ready(Err(Error::Parity));
} else if sr.fe() {
return Poll::Ready(Err(Error::Framing));
} else if sr.ne() {
return Poll::Ready(Err(Error::Noise));
} else {
return Poll::Ready(Err(Error::Overrun));
}
}
if sr.idle() { if sr.idle() {
// Idle line is detected // Idle line is detected
@ -243,11 +186,7 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxD
match select(dma, uart).await { match select(dma, uart).await {
Either::Left(((), _)) => Ok(()), Either::Left(((), _)) => Ok(()),
Either::Right((Ok(()), _)) => Ok(()), Either::Right((result, _)) => result,
Either::Right((Err(e), _)) => {
self.teardown_uart();
Err(e)
}
} }
} }
} }
@ -257,6 +196,37 @@ impl<T: BasicInstance, RxDma: super::RxDma<T>> Drop for RingBufferedUartRx<'_, T
self.teardown_uart(); self.teardown_uart();
} }
} }
/// Return an error result if the Sr register has errors
fn check_for_errors(s: Sr) -> Result<(), Error> {
if s.pe() {
Err(Error::Parity)
} else if s.fe() {
Err(Error::Framing)
} else if s.ne() {
Err(Error::Noise)
} else if s.ore() {
Err(Error::Overrun)
} else {
Ok(())
}
}
/// Clear IDLE and return the Sr register
fn clear_idle_flag(r: Regs) -> Sr {
unsafe {
// SAFETY: read only and we only use Rx related flags
let sr = sr(r).read();
// This read also clears the error and idle interrupt flags on v1.
rdr(r).read_volatile();
clear_interrupt_flags(r, sr);
r.cr1().modify(|w| w.set_idleie(true));
sr
}
}
#[cfg(all(feature = "unstable-traits", feature = "nightly"))] #[cfg(all(feature = "unstable-traits", feature = "nightly"))]
mod eio { mod eio {

View File

@ -9,8 +9,8 @@ license = "MIT OR Apache-2.0"
defmt = { version = "0.3", optional = true } defmt = { version = "0.3", optional = true }
defmt-rtt = { version = "0.4", optional = true } defmt-rtt = { version = "0.4", optional = true }
embassy-nrf = { path = "../../../../embassy-nrf", default-features = false, features = ["nightly"] } embassy-nrf = { path = "../../../../embassy-nrf", features = ["nightly"] }
embassy-boot-nrf = { path = "../../../../embassy-boot/nrf", default-features = false } embassy-boot-nrf = { path = "../../../../embassy-boot/nrf" }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = { version = "0.7" } cortex-m-rt = { version = "0.7" }
cfg-if = "1.0.0" cfg-if = "1.0.0"

View File

@ -9,8 +9,8 @@ license = "MIT OR Apache-2.0"
defmt = { version = "0.3", optional = true } defmt = { version = "0.3", optional = true }
defmt-rtt = { version = "0.4", optional = true } defmt-rtt = { version = "0.4", optional = true }
embassy-rp = { path = "../../../../embassy-rp", default-features = false, features = ["nightly"] } embassy-rp = { path = "../../../../embassy-rp", features = ["nightly"] }
embassy-boot-rp = { path = "../../../../embassy-boot/rp", default-features = false } embassy-boot-rp = { path = "../../../../embassy-boot/rp" }
embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } embassy-time = { path = "../../../../embassy-time", features = ["nightly"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }

View File

@ -9,8 +9,8 @@ license = "MIT OR Apache-2.0"
defmt = { version = "0.3", optional = true } defmt = { version = "0.3", optional = true }
defmt-rtt = { version = "0.4", optional = true } defmt-rtt = { version = "0.4", optional = true }
embassy-stm32 = { path = "../../../../embassy-stm32", default-features = false, features = ["nightly"] } embassy-stm32 = { path = "../../../../embassy-stm32", features = ["nightly"] }
embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features = false } embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32" }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = { version = "0.7" } cortex-m-rt = { version = "0.7" }
embedded-storage = "0.3.0" embedded-storage = "0.3.0"

View File

@ -1,6 +1,6 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))'] [target.'cfg(all(target_arch = "arm", target_os = "none"))']
#runner = "teleprobe local run --chip nRF52840_xxAA --elf" #runner = "teleprobe local run --chip nRF52840_xxAA --elf"
runner = "teleprobe client run --target nrf52840-dk --elf" runner = "teleprobe client run"
[build] [build]
target = "thumbv7em-none-eabi" target = "thumbv7em-none-eabi"

View File

@ -5,6 +5,8 @@ version = "0.1.0"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
teleprobe-meta = "1"
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt", "nightly"] }
embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] }

View File

@ -11,6 +11,7 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("cargo:rustc-link-arg-bins=--nmagic"); println!("cargo:rustc-link-arg-bins=--nmagic");
println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); println!("cargo:rustc-link-arg-bins=-Tlink_ram.x");
println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
println!("cargo:rustc-link-arg-bins=-Tteleprobe.x");
Ok(()) Ok(())
} }

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{assert_eq, *}; use defmt::{assert_eq, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use core::mem; use core::mem;
use core::ptr::NonNull; use core::ptr::NonNull;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{assert, info}; use defmt::{assert, info};
use embassy_executor::Spawner; use embassy_executor::Spawner;

1
tests/nrf/src/common.rs Normal file
View File

@ -0,0 +1 @@
teleprobe_meta::target!(b"nrf52840-dk");

View File

@ -5,8 +5,8 @@
#build-std-features = ["panic_immediate_abort"] #build-std-features = ["panic_immediate_abort"]
[target.'cfg(all(target_arch = "arm", target_os = "none"))'] [target.'cfg(all(target_arch = "arm", target_os = "none"))']
#runner = "teleprobe client run --target rpi-pico --elf" runner = "teleprobe client run"
runner = "teleprobe local run --chip RP2040 --elf" #runner = "teleprobe local run --chip RP2040 --elf"
rustflags = [ rustflags = [
# Code-size optimizations. # Code-size optimizations.

View File

@ -5,6 +5,8 @@ version = "0.1.0"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
teleprobe-meta = "1"
embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] }

View File

@ -11,6 +11,7 @@ fn main() -> Result<(), Box<dyn Error>> {
println!("cargo:rustc-link-arg-bins=--nmagic"); println!("cargo:rustc-link-arg-bins=--nmagic");
println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); println!("cargo:rustc-link-arg-bins=-Tlink_ram.x");
println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
println!("cargo:rustc-link-arg-bins=-Tteleprobe.x");
Ok(()) Ok(())
} }

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{assert_eq, *}; use defmt::{assert_eq, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{assert, *}; use defmt::{assert, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{assert, *}; use defmt::{assert, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{info, unwrap}; use defmt::{info, unwrap};
use embassy_executor::Executor; use embassy_executor::Executor;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{info, unwrap}; use defmt::{info, unwrap};
use embassy_executor::Executor; use embassy_executor::Executor;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{assert, assert_eq, assert_ne, *}; use defmt::{assert, assert_eq, assert_ne, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{assert_eq, *}; use defmt::{assert_eq, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;

View File

@ -4,6 +4,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{assert_eq, *}; use defmt::{assert_eq, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{assert_eq, *}; use defmt::{assert_eq, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{assert_eq, panic, *}; use defmt::{assert_eq, panic, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{assert_eq, *}; use defmt::{assert_eq, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;

View File

@ -1,6 +1,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{assert_eq, *}; use defmt::{assert_eq, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;

1
tests/rp/src/common.rs Normal file
View File

@ -0,0 +1 @@
teleprobe_meta::target!(b"rpi-pico");

View File

@ -3,7 +3,7 @@ build-std = ["core"]
build-std-features = ["panic_immediate_abort"] build-std-features = ["panic_immediate_abort"]
[target.'cfg(all(target_arch = "arm", target_os = "none"))'] [target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "teleprobe client run --target bluepill-stm32f103c8 --elf" runner = "teleprobe client run"
#runner = "teleprobe local run --chip STM32F103C8 --elf" #runner = "teleprobe local run --chip STM32F103C8 --elf"
rustflags = [ rustflags = [

View File

@ -22,6 +22,8 @@ ble = []
not-gpdma = [] not-gpdma = []
[dependencies] [dependencies]
teleprobe-meta = "1"
embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] }

View File

@ -26,6 +26,7 @@ fn main() -> Result<(), Box<dyn Error>> {
} }
println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
println!("cargo:rustc-link-arg-bins=-Tteleprobe.x");
Ok(()) Ok(())
} }

View File

@ -1,13 +1,13 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
#[path = "../example_common.rs"] use common::*;
mod example_common;
use defmt::assert; use defmt::assert;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::gpio::{Flex, Input, Level, Output, OutputOpenDrain, Pull, Speed}; use embassy_stm32::gpio::{Flex, Input, Level, Output, OutputOpenDrain, Pull, Speed};
use example_common::*;
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {

View File

@ -1,18 +1,16 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
// required-features: chrono
#[path = "../example_common.rs"]
mod example_common;
use chrono::{NaiveDate, NaiveDateTime}; use chrono::{NaiveDate, NaiveDateTime};
use common::*;
use defmt::assert; use defmt::assert;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::pac; use embassy_stm32::pac;
use embassy_stm32::rtc::{Rtc, RtcConfig}; use embassy_stm32::rtc::{Rtc, RtcConfig};
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use example_common::*;
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {

View File

@ -2,6 +2,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
use defmt::{assert_eq, *}; use defmt::{assert_eq, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;

View File

@ -1,15 +1,15 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
#[path = "../example_common.rs"] use common::*;
mod example_common;
use defmt::assert_eq; use defmt::assert_eq;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::dma::NoDma; use embassy_stm32::dma::NoDma;
use embassy_stm32::spi::{self, Spi}; use embassy_stm32::spi::{self, Spi};
use embassy_stm32::time::Hertz; use embassy_stm32::time::Hertz;
use example_common::*;
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {

View File

@ -1,14 +1,14 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
#[path = "../example_common.rs"] use common::*;
mod example_common;
use defmt::assert_eq; use defmt::assert_eq;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::spi::{self, Spi}; use embassy_stm32::spi::{self, Spi};
use embassy_stm32::time::Hertz; use embassy_stm32::time::Hertz;
use example_common::*;
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {

View File

@ -1,13 +1,13 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
#[path = "../example_common.rs"] use common::*;
mod example_common;
use defmt::assert; use defmt::assert;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Timer}; use embassy_time::{Duration, Instant, Timer};
use example_common::*;
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {

View File

@ -1,16 +1,16 @@
// required-features: ble
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
// required-features: ble use common::*;
#[path = "../example_common.rs"]
mod example_common;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::tl_mbox::{Config, TlMbox}; use embassy_stm32::tl_mbox::{Config, TlMbox};
use embassy_stm32::{bind_interrupts, tl_mbox}; use embassy_stm32::{bind_interrupts, tl_mbox};
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use example_common::*;
bind_interrupts!(struct Irqs{ bind_interrupts!(struct Irqs{
IPCC_C1_RX => tl_mbox::ReceiveInterruptHandler; IPCC_C1_RX => tl_mbox::ReceiveInterruptHandler;

View File

@ -1,16 +1,16 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
#[path = "../example_common.rs"] use common::*;
mod example_common;
use defmt::assert_eq; use defmt::assert_eq;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::dma::NoDma; use embassy_stm32::dma::NoDma;
use embassy_stm32::usart::{Config, Error, Uart}; use embassy_stm32::usart::{Config, Error, Uart};
use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_stm32::{bind_interrupts, peripherals, usart};
use embassy_time::{Duration, Instant}; use embassy_time::{Duration, Instant};
use example_common::*;
#[cfg(any( #[cfg(any(
feature = "stm32f103c8", feature = "stm32f103c8",

View File

@ -1,15 +1,15 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
#[path = "../example_common.rs"] use common::*;
mod example_common;
use defmt::assert_eq; use defmt::assert_eq;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_futures::join::join; use embassy_futures::join::join;
use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::usart::{Config, Uart};
use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_stm32::{bind_interrupts, peripherals, usart};
use example_common::*;
#[cfg(any( #[cfg(any(
feature = "stm32f103c8", feature = "stm32f103c8",

View File

@ -3,15 +3,15 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
#[path = "../common.rs"]
mod common;
#[path = "../example_common.rs"] use common::*;
mod example_common;
use defmt::{assert_eq, panic}; use defmt::{assert_eq, panic};
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::usart::{Config, DataBits, Parity, RingBufferedUartRx, StopBits, Uart, UartTx}; use embassy_stm32::usart::{Config, DataBits, Parity, RingBufferedUartRx, StopBits, Uart, UartTx};
use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_stm32::{bind_interrupts, peripherals, usart};
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use example_common::*;
use rand_chacha::ChaCha8Rng; use rand_chacha::ChaCha8Rng;
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};

44
tests/stm32/src/common.rs Normal file
View File

@ -0,0 +1,44 @@
#![macro_use]
pub use defmt::*;
#[allow(unused)]
use embassy_stm32::time::Hertz;
use embassy_stm32::Config;
use {defmt_rtt as _, panic_probe as _};
#[cfg(feature = "stm32f103c8")]
teleprobe_meta::target!(b"bluepill-stm32f103c8");
#[cfg(feature = "stm32g491re")]
teleprobe_meta::target!(b"nucleo-stm32g491re");
#[cfg(feature = "stm32g071rb")]
teleprobe_meta::target!(b"nucleo-stm32g071rb");
#[cfg(feature = "stm32f429zi")]
teleprobe_meta::target!(b"nucleo-stm32f429zi");
#[cfg(feature = "stm32wb55rg")]
teleprobe_meta::target!(b"nucleo-stm32wb55rg");
#[cfg(feature = "stm32h755zi")]
teleprobe_meta::target!(b"nucleo-stm32h755zi");
#[cfg(feature = "stm32u585ai")]
teleprobe_meta::target!(b"iot-stm32u585ai");
#[cfg(feature = "stm32h563zi")]
teleprobe_meta::target!(b"nucleo-stm32h563zi");
#[cfg(feature = "stm32c031c6")]
teleprobe_meta::target!(b"nucleo-stm32c031c6");
pub fn config() -> Config {
#[allow(unused_mut)]
let mut config = Config::default();
#[cfg(feature = "stm32h755zi")]
{
config.rcc.sys_ck = Some(Hertz(400_000_000));
config.rcc.pll1.q_ck = Some(Hertz(100_000_000));
}
#[cfg(feature = "stm32u585ai")]
{
config.rcc.mux = embassy_stm32::rcc::ClockSrc::MSI(embassy_stm32::rcc::MSIRange::Range48mhz);
}
config
}

View File

@ -1,25 +0,0 @@
#![macro_use]
pub use defmt::*;
#[allow(unused)]
use embassy_stm32::time::Hertz;
use embassy_stm32::Config;
use {defmt_rtt as _, panic_probe as _};
pub fn config() -> Config {
#[allow(unused_mut)]
let mut config = Config::default();
#[cfg(feature = "stm32h755zi")]
{
config.rcc.sys_ck = Some(Hertz(400_000_000));
config.rcc.pll1.q_ck = Some(Hertz(100_000_000));
}
#[cfg(feature = "stm32u585ai")]
{
config.rcc.mux = embassy_stm32::rcc::ClockSrc::MSI(embassy_stm32::rcc::MSIRange::Range48mhz);
}
config
}