From ea5f2c71e063aff9fdc0bde04656f36a3883178d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Sep 2022 12:05:58 +0200 Subject: [PATCH 01/42] sync/signal: wake old waker on overflow instead of panicking. This makes behavior consistent with `WakerRegistration`. It allows canceling `wait` in one task and then calling `wait` in another. If two tasks are `wait`ing concurrently the signal will be received by only one of them, randomly. --- embassy-sync/src/signal.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index f6ebeb9b..34201d03 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -79,7 +79,11 @@ impl Signal { Poll::Pending } State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending, - State::Waiting(_) => panic!("waker overflow"), + State::Waiting(w) => { + let w = mem::replace(w, cx.waker().clone()); + w.wake(); + Poll::Pending + } State::Signaled(_) => match mem::replace(state, State::None) { State::Signaled(res) => Poll::Ready(res), _ => unreachable!(), From c4d5c047d7311e506ee1c0c9322cbf4e72dd6bcc Mon Sep 17 00:00:00 2001 From: Vincent Stakenburg Date: Thu, 15 Sep 2022 12:34:17 +0200 Subject: [PATCH 02/42] make `State::new()` const, consistent with others --- embassy-stm32/src/eth/v1/mod.rs | 2 +- embassy-stm32/src/eth/v2/mod.rs | 2 +- embassy-stm32/src/usart/buffered.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 1ab0438a..38629a93 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -29,7 +29,7 @@ use super::*; pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage>); impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index d67c3c5e..a81ee118 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -19,7 +19,7 @@ use super::*; pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage>); impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index a7fa4389..5f6dabb3 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -11,7 +11,7 @@ use super::*; pub struct State<'d, T: BasicInstance>(StateStorage>); impl<'d, T: BasicInstance> State<'d, T> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } From 70a3b85acc3b87abab5a66b1a02da033789b5e1a Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Fri, 16 Sep 2022 10:32:43 +0200 Subject: [PATCH 03/42] Add .into_inner() and .get_mut() to Mutex --- embassy-sync/src/mutex.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 75a6e8dd..a792cf07 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -111,6 +111,20 @@ where Ok(MutexGuard { mutex: self }) } + + /// Consumes this mutex, returning the underlying data. + pub fn into_inner(self) -> T + where T: Sized { + self.inner.into_inner() + } + + /// Returns a mutable reference to the underlying data. + /// + /// Since this call borrows the Mutex mutably, no actual locking needs to + /// take place -- the mutable borrow statically guarantees no locks exist. + pub fn get_mut(&mut self) -> &mut T { + self.inner.get_mut() + } } /// Async mutex guard. From 79654510b71290632ee659dd2ae1851f33f48374 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Fri, 16 Sep 2022 10:44:33 +0200 Subject: [PATCH 04/42] Make rustfmt happy --- embassy-sync/src/mutex.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index a792cf07..92101c6b 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -114,7 +114,9 @@ where /// Consumes this mutex, returning the underlying data. pub fn into_inner(self) -> T - where T: Sized { + where + T: Sized, + { self.inner.into_inner() } From f7267d493fe9ed63b02f82267d29a10a4b1cf515 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 12:45:03 +0200 Subject: [PATCH 05/42] Feature-gate time-driver in embassy-rp --- ci.sh | 2 +- embassy-rp/Cargo.toml | 2 ++ embassy-rp/src/lib.rs | 2 ++ examples/rp/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ci.sh b/ci.sh index fa24d526..ae1b4428 100755 --- a/ci.sh +++ b/ci.sh @@ -120,7 +120,7 @@ cargo batch \ --- 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 stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ --- 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/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 --features embassy-rp/time-driver --out-dir out/tests/rpi-pico \ $BUILD_EXTRA diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ccb2f652..1e26f7dd 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -20,6 +20,8 @@ defmt = ["dep:defmt", "embassy-usb?/defmt"] # There are no plans to make this stable. unstable-pac = [] +time-driver = [] + # Enable nightly-only features nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index aebbbf56..8dcefece 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -8,6 +8,7 @@ pub mod dma; pub mod gpio; pub mod interrupt; pub mod spi; +#[cfg(feature = "time-driver")] pub mod timer; pub mod uart; #[cfg(feature = "nightly")] @@ -108,6 +109,7 @@ pub fn init(_config: config::Config) -> Peripherals { unsafe { clocks::init(); + #[cfg(feature = "time-driver")] timer::init(); dma::init(); } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 18a92b09..17393322 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,7 +8,7 @@ version = "0.1.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } From feead3ae89d57e9f0ff7d7a264136d3e89aaebcf Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 06:45:27 +0200 Subject: [PATCH 06/42] Implement RealTimeClock for embassy-rp --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/clocks.rs | 2 +- embassy-rp/src/lib.rs | 3 + embassy-rp/src/rtc/datetime_chrono.rs | 62 ++++++++ embassy-rp/src/rtc/datetime_no_deps.rs | 127 +++++++++++++++++ embassy-rp/src/rtc/filter.rs | 100 +++++++++++++ embassy-rp/src/rtc/mod.rs | 188 +++++++++++++++++++++++++ 7 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 embassy-rp/src/rtc/datetime_chrono.rs create mode 100644 embassy-rp/src/rtc/datetime_no_deps.rs create mode 100644 embassy-rp/src/rtc/filter.rs create mode 100644 embassy-rp/src/rtc/mod.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index ccb2f652..bc6f4466 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -44,6 +44,7 @@ cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" critical-section = "1.1" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +chrono = { version = "0.4", default-features = false, optional = true } rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 3ad1e5d8..1c446f38 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -122,7 +122,7 @@ pub(crate) fn clk_peri_freq() -> u32 { 125_000_000 } -pub(crate) fn _clk_rtc_freq() -> u32 { +pub(crate) fn clk_rtc_freq() -> u32 { 46875 } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index aebbbf56..73035455 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -7,6 +7,7 @@ pub(crate) mod fmt; pub mod dma; pub mod gpio; pub mod interrupt; +pub mod rtc; pub mod spi; pub mod timer; pub mod uart; @@ -84,6 +85,8 @@ embassy_hal_common::peripherals! { DMA_CH11, USB, + + RTC, } #[link_section = ".boot2"] diff --git a/embassy-rp/src/rtc/datetime_chrono.rs b/embassy-rp/src/rtc/datetime_chrono.rs new file mode 100644 index 00000000..b3c78dd4 --- /dev/null +++ b/embassy-rp/src/rtc/datetime_chrono.rs @@ -0,0 +1,62 @@ +use chrono::{Datelike, Timelike}; + +use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1}; + +/// Alias for [`chrono::NaiveDateTime`] +pub type DateTime = chrono::NaiveDateTime; +/// Alias for [`chrono::Weekday`] +pub type DayOfWeek = chrono::Weekday; + +/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs. +/// +/// [`DateTimeFilter`]: struct.DateTimeFilter.html +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Error { + /// The [DateTime] has an invalid year. The year must be between 0 and 4095. + InvalidYear, + /// The [DateTime] contains an invalid date. + InvalidDate, + /// The [DateTime] contains an invalid time. + InvalidTime, +} + +pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { + dotw.num_days_from_sunday() as u8 +} + +pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> { + if dt.year() < 0 || dt.year() > 4095 { + // rp2040 can't hold these years + Err(Error::InvalidYear) + } else { + // The rest of the chrono date is assumed to be valid + Ok(()) + } +} + +pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) { + w.set_year(dt.year() as u16); + w.set_month(dt.month() as u8); + w.set_day(dt.day() as u8); +} + +pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) { + w.set_dotw(dt.weekday().num_days_from_sunday() as u8); + w.set_hour(dt.hour() as u8); + w.set_min(dt.minute() as u8); + w.set_sec(dt.second() as u8); +} + +pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result { + let year = rtc_1.year() as i32; + let month = rtc_1.month() as u32; + let day = rtc_1.day() as u32; + + let hour = rtc_0.hour() as u32; + let minute = rtc_0.min() as u32; + let second = rtc_0.sec() as u32; + + let date = chrono::NaiveDate::from_ymd_opt(year, month, day).ok_or(Error::InvalidDate)?; + let time = chrono::NaiveTime::from_hms_opt(hour, minute, second).ok_or(Error::InvalidTime)?; + Ok(DateTime::new(date, time)) +} diff --git a/embassy-rp/src/rtc/datetime_no_deps.rs b/embassy-rp/src/rtc/datetime_no_deps.rs new file mode 100644 index 00000000..92770e98 --- /dev/null +++ b/embassy-rp/src/rtc/datetime_no_deps.rs @@ -0,0 +1,127 @@ +use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1}; + +/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs. +/// +/// [`DateTimeFilter`]: struct.DateTimeFilter.html +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Error { + /// The [DateTime] contains an invalid year value. Must be between `0..=4095`. + InvalidYear, + /// The [DateTime] contains an invalid month value. Must be between `1..=12`. + InvalidMonth, + /// The [DateTime] contains an invalid day value. Must be between `1..=31`. + InvalidDay, + /// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday. + InvalidDayOfWeek( + /// The value of the DayOfWeek that was given. + u8, + ), + /// The [DateTime] contains an invalid hour value. Must be between `0..=23`. + InvalidHour, + /// The [DateTime] contains an invalid minute value. Must be between `0..=59`. + InvalidMinute, + /// The [DateTime] contains an invalid second value. Must be between `0..=59`. + InvalidSecond, +} + +/// Structure containing date and time information +pub struct DateTime { + /// 0..4095 + pub year: u16, + /// 1..12, 1 is January + pub month: u8, + /// 1..28,29,30,31 depending on month + pub day: u8, + /// + pub day_of_week: DayOfWeek, + /// 0..23 + pub hour: u8, + /// 0..59 + pub minute: u8, + /// 0..59 + pub second: u8, +} + +/// A day of the week +#[repr(u8)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] +#[allow(missing_docs)] +pub enum DayOfWeek { + Sunday = 0, + Monday = 1, + Tuesday = 2, + Wednesday = 3, + Thursday = 4, + Friday = 5, + Saturday = 6, +} + +fn day_of_week_from_u8(v: u8) -> Result { + Ok(match v { + 0 => DayOfWeek::Sunday, + 1 => DayOfWeek::Monday, + 2 => DayOfWeek::Tuesday, + 3 => DayOfWeek::Wednesday, + 4 => DayOfWeek::Thursday, + 5 => DayOfWeek::Friday, + 6 => DayOfWeek::Saturday, + x => return Err(Error::InvalidDayOfWeek(x)), + }) +} + +pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 { + dotw as u8 +} + +pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> { + if dt.year > 4095 { + Err(Error::InvalidYear) + } else if dt.month < 1 || dt.month > 12 { + Err(Error::InvalidMonth) + } else if dt.day < 1 || dt.day > 31 { + Err(Error::InvalidDay) + } else if dt.hour > 23 { + Err(Error::InvalidHour) + } else if dt.minute > 59 { + Err(Error::InvalidMinute) + } else if dt.second > 59 { + Err(Error::InvalidSecond) + } else { + Ok(()) + } +} + +pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) { + w.set_year(dt.year); + w.set_month(dt.month); + w.set_day(dt.day); +} + +pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) { + w.set_dotw(dt.day_of_week as u8); + w.set_hour(dt.hour); + w.set_min(dt.minute); + w.set_sec(dt.second); +} + +pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result { + let year = rtc_1.year(); + let month = rtc_1.month(); + let day = rtc_1.day(); + + let day_of_week = rtc_0.dotw(); + let hour = rtc_0.hour(); + let minute = rtc_0.min(); + let second = rtc_0.sec(); + + let day_of_week = day_of_week_from_u8(day_of_week)?; + Ok(DateTime { + year, + month, + day, + day_of_week, + hour, + minute, + second, + }) +} diff --git a/embassy-rp/src/rtc/filter.rs b/embassy-rp/src/rtc/filter.rs new file mode 100644 index 00000000..d4a3bab2 --- /dev/null +++ b/embassy-rp/src/rtc/filter.rs @@ -0,0 +1,100 @@ +use super::DayOfWeek; +use crate::pac::rtc::regs::{IrqSetup0, IrqSetup1}; + +/// A filter used for [`RealTimeClock::schedule_alarm`]. +/// +/// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm +#[derive(Default)] +pub struct DateTimeFilter { + /// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value. + pub year: Option, + /// The month that this alarm should trigger on, `None` if the RTC alarm should not trigger on a month value. + pub month: Option, + /// The day that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day value. + pub day: Option, + /// The day of week that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day of week value. + pub day_of_week: Option, + /// The hour that this alarm should trigger on, `None` if the RTC alarm should not trigger on a hour value. + pub hour: Option, + /// The minute that this alarm should trigger on, `None` if the RTC alarm should not trigger on a minute value. + pub minute: Option, + /// The second that this alarm should trigger on, `None` if the RTC alarm should not trigger on a second value. + pub second: Option, +} + +impl DateTimeFilter { + /// Set a filter on the given year + pub fn year(mut self, year: u16) -> Self { + self.year = Some(year); + self + } + /// Set a filter on the given month + pub fn month(mut self, month: u8) -> Self { + self.month = Some(month); + self + } + /// Set a filter on the given day + pub fn day(mut self, day: u8) -> Self { + self.day = Some(day); + self + } + /// Set a filter on the given day of the week + pub fn day_of_week(mut self, day_of_week: DayOfWeek) -> Self { + self.day_of_week = Some(day_of_week); + self + } + /// Set a filter on the given hour + pub fn hour(mut self, hour: u8) -> Self { + self.hour = Some(hour); + self + } + /// Set a filter on the given minute + pub fn minute(mut self, minute: u8) -> Self { + self.minute = Some(minute); + self + } + /// Set a filter on the given second + pub fn second(mut self, second: u8) -> Self { + self.second = Some(second); + self + } +} + +// register helper functions +impl DateTimeFilter { + pub(super) fn write_setup_0(&self, w: &mut IrqSetup0) { + if let Some(year) = self.year { + w.set_year_ena(true); + + w.set_year(year); + } + if let Some(month) = self.month { + w.set_month_ena(true); + w.set_month(month); + } + if let Some(day) = self.day { + w.set_day_ena(true); + w.set_day(day); + } + } + pub(super) fn write_setup_1(&self, w: &mut IrqSetup1) { + if let Some(day_of_week) = self.day_of_week { + w.set_dotw_ena(true); + let bits = super::datetime::day_of_week_to_u8(day_of_week); + + w.set_dotw(bits); + } + if let Some(hour) = self.hour { + w.set_hour_ena(true); + w.set_hour(hour); + } + if let Some(minute) = self.minute { + w.set_min_ena(true); + w.set_min(minute); + } + if let Some(second) = self.second { + w.set_sec_ena(true); + w.set_sec(second); + } + } +} diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs new file mode 100644 index 00000000..7f3bbbe7 --- /dev/null +++ b/embassy-rp/src/rtc/mod.rs @@ -0,0 +1,188 @@ +mod filter; + +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; + +pub use self::filter::DateTimeFilter; + +#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")] +#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")] +mod datetime; + +pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; +use crate::clocks::clk_rtc_freq; + +/// A reference to the real time clock of the system +pub struct RealTimeClock<'d, T: Instance> { + inner: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> RealTimeClock<'d, T> { + /// Create a new instance of the real time clock, with the given date as an initial value. + /// + /// # Errors + /// + /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. + pub fn new(inner: impl Peripheral

+ 'd, initial_date: DateTime) -> Result { + into_ref!(inner); + + // Set the RTC divider + unsafe { + inner + .regs() + .clkdiv_m1() + .write(|w| w.set_clkdiv_m1(clk_rtc_freq() as u16 - 1)) + }; + + let mut result = Self { inner }; + result.set_leap_year_check(true); // should be on by default, make sure this is the case. + result.set_datetime(initial_date)?; + Ok(result) + } + + /// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check. + /// + /// Leap year checking is enabled by default. + pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) { + unsafe { + self.inner + .regs() + .ctrl() + .modify(|w| w.set_force_notleapyear(!leap_year_check_enabled)) + }; + } + + /// Checks to see if this RealTimeClock is running + pub fn is_running(&self) -> bool { + unsafe { self.inner.regs().ctrl().read().rtc_active() } + } + + /// Set the datetime to a new value. + /// + /// # Errors + /// + /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. + pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> { + self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?; + + // disable RTC while we configure it + unsafe { + self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false)); + while self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); + } + + self.inner.regs().setup_0().write(|w| { + self::datetime::write_setup_0(&t, w); + }); + self.inner.regs().setup_1().write(|w| { + self::datetime::write_setup_1(&t, w); + }); + + // Load the new datetime and re-enable RTC + self.inner.regs().ctrl().write(|w| w.set_load(true)); + self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true)); + while !self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); + } + } + Ok(()) + } + + /// Return the current datetime. + /// + /// # Errors + /// + /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. + pub fn now(&self) -> Result { + if !self.is_running() { + return Err(RtcError::NotRunning); + } + + let rtc_0 = unsafe { self.inner.regs().rtc_0().read() }; + let rtc_1 = unsafe { self.inner.regs().rtc_1().read() }; + + self::datetime::datetime_from_registers(rtc_0, rtc_1).map_err(RtcError::InvalidDateTime) + } + + /// Disable the alarm that was scheduled with [`schedule_alarm`]. + /// + /// [`schedule_alarm`]: #method.schedule_alarm + pub fn disable_alarm(&mut self) { + unsafe { + self.inner.regs().irq_setup_0().modify(|s| s.set_match_ena(false)); + + while self.inner.regs().irq_setup_0().read().match_active() { + core::hint::spin_loop(); + } + } + } + + /// Schedule an alarm. The `filter` determines at which point in time this alarm is set. + /// + /// Keep in mind that the filter only triggers on the specified time. If you want to schedule this alarm every minute, you have to call: + /// ```no_run + /// # #[cfg(feature = "chrono")] + /// # fn main() { } + /// # #[cfg(not(feature = "chrono"))] + /// # fn main() { + /// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter}; + /// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() }; + /// let now = real_time_clock.now().unwrap(); + /// real_time_clock.schedule_alarm( + /// DateTimeFilter::default() + /// .minute(if now.minute == 59 { 0 } else { now.minute + 1 }) + /// ); + /// # } + /// ``` + pub fn schedule_alarm(&mut self, filter: DateTimeFilter) { + self.disable_alarm(); + + unsafe { + self.inner.regs().irq_setup_0().write(|w| { + filter.write_setup_0(w); + }); + self.inner.regs().irq_setup_1().write(|w| { + filter.write_setup_1(w); + }); + + // Set the enable bit and check if it is set + self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true)); + while !self.inner.regs().irq_setup_0().read().match_active() { + core::hint::spin_loop(); + } + } + } + + /// Clear the interrupt. This should be called every time the `RTC_IRQ` interrupt is triggered, + /// or the next [`schedule_alarm`] will never fire. + /// + /// [`schedule_alarm`]: #method.schedule_alarm + pub fn clear_interrupt(&mut self) { + self.disable_alarm(); + } +} + +/// Errors that can occur on methods on [RtcClock] +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RtcError { + /// An invalid DateTime was given or stored on the hardware. + InvalidDateTime(DateTimeError), + + /// The RTC clock is not running + NotRunning, +} + +mod sealed { + pub trait Instance { + fn regs(&self) -> crate::pac::rtc::Rtc; + } +} + +pub trait Instance: sealed::Instance {} + +impl sealed::Instance for crate::peripherals::RTC { + fn regs(&self) -> crate::pac::rtc::Rtc { + crate::pac::RTC + } +} +impl Instance for crate::peripherals::RTC {} From 1c657d2d5532979f326d89a0d40e8aa4bc96d0c2 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 16 Sep 2022 16:45:59 +0200 Subject: [PATCH 07/42] Add time-driver feature to docs --- embassy-rp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 1e26f7dd..7315d673 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/" -features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] +features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver"] flavors = [ { name = "rp2040", target = "thumbv6m-none-eabi" }, ] From ab1a6889a62e86a80af6fc572ffa992cfb9ef960 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 12:02:05 -0700 Subject: [PATCH 08/42] rp: fix async SPI read and write --- embassy-rp/src/dma.rs | 38 ++++++++++++++++++++++++++++ embassy-rp/src/spi.rs | 59 ++++++++++++++++++++++++++++++------------- 2 files changed, 79 insertions(+), 18 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index acf33822..b256cc2f 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -56,6 +56,25 @@ pub unsafe fn read<'a, C: Channel, W: Word>( ) } +pub unsafe fn read_repeated<'a, C: Channel, W: Word>( + ch: impl Peripheral

+ 'a, + from: *const W, + len: usize, + dreq: u8, +) -> Transfer<'a, C> { + let mut dummy: u32 = 0; + copy_inner( + ch, + from as *const u32, + &mut dummy as *mut u32, + len, + W::size(), + false, + false, + dreq, + ) +} + pub unsafe fn write<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: *const [W], @@ -75,6 +94,25 @@ pub unsafe fn write<'a, C: Channel, W: Word>( ) } +pub unsafe fn write_repeated<'a, C: Channel, W: Word>( + ch: impl Peripheral

+ 'a, + to: *mut W, + len: usize, + dreq: u8, +) -> Transfer<'a, C> { + let dummy: u32 = 0; + copy_inner( + ch, + &dummy as *const u32, + to as *mut u32, + len, + W::size(), + false, + false, + dreq, + ) +} + pub unsafe fn copy<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: &[W], diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 74f0b04d..e7cd9992 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -325,30 +325,53 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - let ch = self.tx_dma.as_mut().unwrap(); - let transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { + unsafe { + self.inner.regs().dmacr().write(|reg| { + reg.set_rxdmae(true); reg.set_txdmae(true); - }); + }) + }; + let tx_ch = self.tx_dma.as_mut().unwrap(); + let tx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::write(ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) + crate::dma::write(tx_ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; - transfer.await; + let rx_ch = self.rx_dma.as_mut().unwrap(); + let rx_transfer = unsafe { + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read_repeated( + rx_ch, + self.inner.regs().dr().ptr() as *const u8, + buffer.len(), + T::RX_DREQ, + ) + }; + join(tx_transfer, rx_transfer).await; Ok(()) } pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let ch = self.rx_dma.as_mut().unwrap(); - let transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { + unsafe { + self.inner.regs().dmacr().write(|reg| { reg.set_rxdmae(true); - }); + reg.set_txdmae(true); + }) + }; + let tx_ch = self.tx_dma.as_mut().unwrap(); + let tx_transfer = unsafe { // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - crate::dma::read(ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) + crate::dma::write_repeated(tx_ch, self.inner.regs().dr().ptr() as *mut u8, buffer.len(), T::TX_DREQ) }; - transfer.await; + let rx_ch = self.rx_dma.as_mut().unwrap(); + let rx_transfer = unsafe { + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) + }; + join(tx_transfer, rx_transfer).await; Ok(()) } @@ -364,20 +387,20 @@ impl<'d, T: Instance> Spi<'d, T, Async> { let (_, from_len) = crate::dma::slice_ptr_parts(tx_ptr); let (_, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr); assert_eq!(from_len, to_len); + unsafe { + self.inner.regs().dmacr().write(|reg| { + reg.set_rxdmae(true); + reg.set_txdmae(true); + }) + }; let tx_ch = self.tx_dma.as_mut().unwrap(); let tx_transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { - reg.set_txdmae(true); - }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::write(tx_ch, tx_ptr, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; let rx_ch = self.rx_dma.as_mut().unwrap(); let rx_transfer = unsafe { - self.inner.regs().dmacr().modify(|reg| { - reg.set_rxdmae(true); - }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ) From 295cc997ae8b1468617dd9f430be4e502901f4f2 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 12:23:17 -0700 Subject: [PATCH 09/42] rp: let SPI RX overflow during async write --- embassy-rp/src/dma.rs | 19 ------------------- embassy-rp/src/spi.rs | 35 +++++++++++++++++------------------ 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index b256cc2f..7ad1a6bf 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -56,25 +56,6 @@ pub unsafe fn read<'a, C: Channel, W: Word>( ) } -pub unsafe fn read_repeated<'a, C: Channel, W: Word>( - ch: impl Peripheral

+ 'a, - from: *const W, - len: usize, - dreq: u8, -) -> Transfer<'a, C> { - let mut dummy: u32 = 0; - copy_inner( - ch, - from as *const u32, - &mut dummy as *mut u32, - len, - W::size(), - false, - false, - dreq, - ) -} - pub unsafe fn write<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, from: *const [W], diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index e7cd9992..3cf82357 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -325,30 +325,29 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - unsafe { - self.inner.regs().dmacr().write(|reg| { - reg.set_rxdmae(true); - reg.set_txdmae(true); - }) - }; let tx_ch = self.tx_dma.as_mut().unwrap(); let tx_transfer = unsafe { + self.inner.regs().dmacr().modify(|reg| { + reg.set_txdmae(true); + }); // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. crate::dma::write(tx_ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) }; - let rx_ch = self.rx_dma.as_mut().unwrap(); - let rx_transfer = unsafe { - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - crate::dma::read_repeated( - rx_ch, - self.inner.regs().dr().ptr() as *const u8, - buffer.len(), - T::RX_DREQ, - ) - }; - join(tx_transfer, rx_transfer).await; + tx_transfer.await; + + let p = self.inner.regs(); + unsafe { + while p.sr().read().bsy() {} + + // clear RX FIFO contents to prevent stale reads + while p.sr().read().rne() { + let _: u16 = p.dr().read().data(); + } + // clear RX overrun interrupt + p.icr().write(|w| w.set_roric(true)); + } + Ok(()) } From 0c6933fefb749aa8857ade8cf3d52cb38a398d55 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 18 Sep 2022 14:54:24 -0700 Subject: [PATCH 10/42] rp: remove extraneous newlines in logs --- embassy-rp/src/gpio.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index a0328302..9b9a0811 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -159,7 +159,7 @@ unsafe fn IO_IRQ_BANK0() { w.set_edge_low(pin_group, false); } InterruptTrigger::LevelHigh => { - debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered\n", pin); + debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin); w.set_level_high(pin_group, false); } InterruptTrigger::LevelLow => { @@ -198,7 +198,7 @@ impl<'d, T: Pin> InputFuture<'d, T> { critical_section::with(|_| { pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level { InterruptTrigger::LevelHigh => { - debug!("InputFuture::new enable LevelHigh for pin {} \n", pin.pin()); + debug!("InputFuture::new enable LevelHigh for pin {}", pin.pin()); w.set_level_high(pin_group, true); } InterruptTrigger::LevelLow => { @@ -245,45 +245,45 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> { // the pin and if it has been disabled that means it was done by the // interrupt service routine, so we then know that the event/trigger // happened and Poll::Ready will be returned. - debug!("{:?} for pin {}\n", self.level, self.pin.pin()); + debug!("{:?} for pin {}", self.level, self.pin.pin()); match self.level { InterruptTrigger::AnyEdge => { if !inte.edge_high(pin_group) && !inte.edge_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::LevelHigh => { if !inte.level_high(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::LevelLow => { if !inte.level_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::EdgeHigh => { if !inte.edge_high(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } InterruptTrigger::EdgeLow => { if !inte.edge_low(pin_group) { #[rustfmt::skip] - debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin()); + debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); return Poll::Ready(()); } } } - debug!("InputFuture::poll return Poll::Pending\n"); + debug!("InputFuture::poll return Poll::Pending"); Poll::Pending } } From d0fe654c82b548d65f49213ad50fc2edc5b3d71e Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 20 Sep 2022 09:42:40 +0200 Subject: [PATCH 11/42] Remove BootFlash borrow Compiler will infer a different lifetime for BootFlash than for the borrowed flash, which makes it require more type annotations than if it was just owning the type. Since it doesn't really matter if it owns or borrows in practical use, change it to own so that it simplifies usage. --- embassy-boot/boot/src/lib.rs | 24 +++++++++++----------- examples/boot/bootloader/nrf/src/main.rs | 2 +- examples/boot/bootloader/stm32/src/main.rs | 7 +++---- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 4a2b112a..015dd58d 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -447,24 +447,24 @@ where } /// A flash wrapper implementing the Flash and embedded_storage traits. -pub struct BootFlash<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8 = 0xFF> +pub struct BootFlash where F: NorFlash + ReadNorFlash, { - flash: &'a mut F, + flash: F, } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl BootFlash where F: NorFlash + ReadNorFlash, { /// Create a new instance of a bootable flash - pub fn new(flash: &'a mut F) -> Self { + pub fn new(flash: F) -> Self { Self { flash } } } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> Flash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl Flash for BootFlash where F: NorFlash + ReadNorFlash, { @@ -472,14 +472,14 @@ where const ERASE_VALUE: u8 = ERASE_VALUE; } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ErrorType for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl ErrorType for BootFlash where F: ReadNorFlash + NorFlash, { type Error = F::Error; } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> NorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl NorFlash for BootFlash where F: ReadNorFlash + NorFlash, { @@ -487,26 +487,26 @@ where const ERASE_SIZE: usize = F::ERASE_SIZE; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - F::erase(self.flash, from, to) + F::erase(&mut self.flash, from, to) } fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - F::write(self.flash, offset, bytes) + F::write(&mut self.flash, offset, bytes) } } -impl<'a, F, const BLOCK_SIZE: usize, const ERASE_VALUE: u8> ReadNorFlash for BootFlash<'a, F, BLOCK_SIZE, ERASE_VALUE> +impl ReadNorFlash for BootFlash where F: ReadNorFlash + NorFlash, { const READ_SIZE: usize = F::READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - F::read(self.flash, offset, bytes) + F::read(&mut self.flash, offset, bytes) } fn capacity(&self) -> usize { - F::capacity(self.flash) + F::capacity(&self.flash) } } diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 9031997c..8266206b 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -21,7 +21,7 @@ fn main() -> ! { let mut bl = BootLoader::default(); let start = bl.prepare(&mut SingleFlashConfig::new(&mut BootFlash::<_, 4096>::new( - &mut WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5), + WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, 5), ))); unsafe { bl.load(start) } } diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index bb5d3e53..294464d1 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -20,10 +20,9 @@ fn main() -> ! { */ let mut bl: BootLoader = BootLoader::default(); - let mut flash = Flash::unlock(p.FLASH); - let start = bl.prepare(&mut SingleFlashConfig::new( - &mut BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(&mut flash), - )); + let flash = Flash::unlock(p.FLASH); + let mut flash = BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(flash); + let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash)); core::mem::drop(flash); unsafe { bl.load(start) } } From b418c0e4d620db0332d02c16fbbd455e7b8805a9 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 20 Sep 2022 14:03:04 +0200 Subject: [PATCH 12/42] Take into account size of revert index Fixes a bug in the partition assertions that ensures that the state page(s) have enough space for 2x active partition range. Add unit test to verify that panic is observed. --- embassy-boot/boot/src/lib.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 015dd58d..3d359533 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -222,10 +222,7 @@ impl BootLoader { page: &mut [u8], ) -> Result { // Ensure we have enough progress pages to store copy progress - assert_eq!(self.active.len() % page.len(), 0); - assert_eq!(self.dfu.len() % page.len(), 0); - assert!(self.dfu.len() - self.active.len() >= page.len()); - assert!(self.active.len() / page.len() <= (self.state.len() - P::STATE::WRITE_SIZE) / P::STATE::WRITE_SIZE); + assert_partitions(self.active, self.dfu, self.state, page.len(), P::STATE::WRITE_SIZE); assert_eq!(magic.len(), P::STATE::WRITE_SIZE); // Copy contents from partition N to active @@ -409,6 +406,13 @@ impl BootLoader { } } +fn assert_partitions(active: Partition, dfu: Partition, state: Partition, page_size: usize, write_size: usize) { + assert_eq!(active.len() % page_size, 0); + assert_eq!(dfu.len() % page_size, 0); + assert!(dfu.len() - active.len() >= page_size); + assert!(2 * (active.len() / page_size) <= (state.len() - write_size) / write_size); +} + /// Convenience provider that uses a single flash for all partitions. pub struct SingleFlashConfig<'a, F> where @@ -919,6 +923,15 @@ mod tests { } } + #[test] + #[should_panic] + fn test_range_asserts() { + const ACTIVE: Partition = Partition::new(4096, 4194304); + const DFU: Partition = Partition::new(4194304, 2 * 4194304); + const STATE: Partition = Partition::new(0, 4096); + assert_partitions(ACTIVE, DFU, STATE, 4096, 4); + } + struct MemFlash([u8; SIZE]); impl NorFlash From 3d708a459c9c5f18ddd5c63a06f272371b6225c8 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Wed, 21 Sep 2022 10:47:49 +0200 Subject: [PATCH 13/42] Implement proper `Drop` for `BufferedUarte` --- embassy-nrf/src/buffered_uarte.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index c3cba247..84ef86c9 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -27,7 +27,7 @@ use futures::future::poll_fn; // Re-export SVD variants to allow user to directly set values pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; -use crate::gpio::Pin as GpioPin; +use crate::gpio::{self, Pin as GpioPin}; use crate::interrupt::InterruptExt; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; @@ -427,23 +427,26 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { fn drop(&mut self) { + debug!("oh no, dropping uarte"); let r = U::regs(); // TODO this probably deadlocks. do like Uarte instead. + r.inten.reset(); + r.events_rxto.reset(); + r.tasks_stoprx.write(|w| w.tasks_stoprx().set_bit()); - self.timer.stop(); - if let RxState::Receiving = self.rx_state { - r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); - } - if let TxState::Transmitting(_) = self.tx_state { - r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); - } - if let RxState::Receiving = self.rx_state { - low_power_wait_until(|| r.events_endrx.read().bits() == 1); - } - if let TxState::Transmitting(_) = self.tx_state { - low_power_wait_until(|| r.events_endtx.read().bits() == 1); - } + r.events_txstopped.reset(); + r.tasks_stoptx.write(|w| w.tasks_stoptx().set_bit()); + while !r.events_txstopped.read().events_txstopped().bit_is_set() {} + + while !r.events_rxto.read().events_rxto().bit_is_set() {} + + r.enable.write(|w| w.enable().disabled()); + + gpio::deconfigure_pin(r.psel.rxd.read().bits()); + gpio::deconfigure_pin(r.psel.txd.read().bits()); + gpio::deconfigure_pin(r.psel.rts.read().bits()); + gpio::deconfigure_pin(r.psel.cts.read().bits()); } } From 0f55f5a73d356dc991dbc3c4bc102e7d652c5fc5 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Wed, 21 Sep 2022 11:06:06 +0200 Subject: [PATCH 14/42] Remove left-in comments and logs --- embassy-nrf/src/buffered_uarte.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 84ef86c9..3ee3e947 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -427,10 +427,8 @@ impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { fn drop(&mut self) { - debug!("oh no, dropping uarte"); let r = U::regs(); - // TODO this probably deadlocks. do like Uarte instead. r.inten.reset(); r.events_rxto.reset(); r.tasks_stoprx.write(|w| w.tasks_stoprx().set_bit()); From 15b4f9db9000cfb402357c5ac2c84876a3ad27c3 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Wed, 21 Sep 2022 11:19:47 +0200 Subject: [PATCH 15/42] Remove unused function --- embassy-nrf/src/buffered_uarte.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 3ee3e947..e212e989 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -550,13 +550,3 @@ impl<'a, U: UarteInstance, T: TimerInstance> PeripheralState for StateInner<'a, trace!("irq: end"); } } - -/// Low power blocking wait loop using WFE/SEV. -fn low_power_wait_until(mut condition: impl FnMut() -> bool) { - while !condition() { - // WFE might "eat" an event that would have otherwise woken the executor. - cortex_m::asm::wfe(); - } - // Retrigger an event to be transparent to the executor. - cortex_m::asm::sev(); -} From 5f7e0eb2aea6f7f6e23d9a5b7400b29377e57d8e Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Wed, 21 Sep 2022 14:06:56 +0200 Subject: [PATCH 16/42] Fix builds on other nrf pacs --- embassy-nrf/src/buffered_uarte.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index e212e989..eb0b1b0c 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -431,13 +431,13 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { r.inten.reset(); r.events_rxto.reset(); - r.tasks_stoprx.write(|w| w.tasks_stoprx().set_bit()); + r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); r.events_txstopped.reset(); - r.tasks_stoptx.write(|w| w.tasks_stoptx().set_bit()); - while !r.events_txstopped.read().events_txstopped().bit_is_set() {} + r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); + while r.events_txstopped.read().bits() == 0 {} - while !r.events_rxto.read().events_rxto().bit_is_set() {} + while r.events_rxto.read().bits() == 0 {} r.enable.write(|w| w.enable().disabled()); From 5914d80968a6aca99f0018148e4b4ed7c4e06bf0 Mon Sep 17 00:00:00 2001 From: Andrew Ealovega Date: Wed, 21 Sep 2022 22:29:57 -0400 Subject: [PATCH 17/42] Add non blocking Bxcan constructor. Signed-off-by: Andrew Ealovega --- embassy-stm32/src/can/bxcan.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index c0bd44e0..bd92b35a 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -12,6 +12,7 @@ pub struct Can<'d, T: Instance> { } impl<'d, T: Instance> Can<'d, T> { + /// Creates a new Bxcan instance, blocking for 11 recessive bits to sync with the CAN bus. pub fn new( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, @@ -31,6 +32,28 @@ impl<'d, T: Instance> Can<'d, T> { can: bxcan::Can::builder(BxcanInstance(peri)).enable(), } } + + /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. + /// You must call [Can::enable_non_blocking] to use the peripheral. + pub fn new_disabled( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + ) -> Self { + into_ref!(peri, rx, tx); + + unsafe { + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } + + T::enable(); + T::reset(); + + Self { + can: bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(), + } + } } impl<'d, T: Instance> Drop for Can<'d, T> { From 897b72c872183221e088611aa6f30989800afd2b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:28:56 +0200 Subject: [PATCH 18/42] Update Rust nightly. Removes feature(generic_associated_types) --- embassy-boot/boot/src/lib.rs | 1 - embassy-boot/nrf/src/lib.rs | 1 - embassy-boot/stm32/src/lib.rs | 1 - embassy-embedded-hal/src/lib.rs | 2 +- embassy-lora/src/lib.rs | 1 - embassy-net/src/lib.rs | 2 +- embassy-nrf/src/lib.rs | 2 +- embassy-rp/src/lib.rs | 2 +- embassy-stm32/src/lib.rs | 2 +- embassy-sync/src/lib.rs | 2 +- embassy-time/src/lib.rs | 2 +- embassy-usb-hid/src/lib.rs | 1 - embassy-usb-serial/src/lib.rs | 1 - embassy-usb/src/lib.rs | 1 - examples/boot/application/nrf/src/bin/a.rs | 1 - examples/boot/application/nrf/src/bin/b.rs | 1 - examples/nrf/src/bin/usb_ethernet.rs | 1 - examples/nrf/src/bin/usb_hid_keyboard.rs | 1 - examples/nrf/src/bin/usb_hid_mouse.rs | 1 - examples/nrf/src/bin/usb_serial.rs | 1 - examples/nrf/src/bin/usb_serial_multitask.rs | 1 - examples/rp/src/bin/usb_ethernet.rs | 1 - examples/rp/src/bin/usb_serial.rs | 1 - examples/stm32l0/src/bin/lorawan.rs | 1 - examples/stm32l5/src/bin/usb_ethernet.rs | 1 - examples/stm32l5/src/bin/usb_hid_mouse.rs | 1 - examples/stm32wl/src/bin/lorawan.rs | 1 - examples/stm32wl/src/bin/subghz.rs | 1 - rust-toolchain.toml | 2 +- 29 files changed, 8 insertions(+), 29 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 3d359533..96878ace 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -1,5 +1,4 @@ #![feature(type_alias_impl_trait)] -#![feature(generic_associated_types)] #![no_std] #![warn(missing_docs)] #![doc = include_str!("../../README.md")] diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 0c14781a..385e089f 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] #![warn(missing_docs)] #![doc = include_str!("../../README.md")] diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 39f08051..edba39cc 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] #![warn(missing_docs)] #![doc = include_str!("../../README.md")] diff --git a/embassy-embedded-hal/src/lib.rs b/embassy-embedded-hal/src/lib.rs index 0c6f2786..a12a3a3a 100644 --- a/embassy-embedded-hal/src/lib.rs +++ b/embassy-embedded-hal/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![warn(missing_docs)] //! Utilities to use `embedded-hal` traits with Embassy. diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 2483dcb2..90ba0d1d 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -1,6 +1,5 @@ #![no_std] #![feature(type_alias_impl_trait)] -#![feature(generic_associated_types)] //! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device //! crate's async LoRaWAN MAC implementation. diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 8eebc798..4d30550d 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index f3b3ca0c..d7bd2170 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -43,7 +43,7 @@ //! mutable slices always reside in RAM. #![no_std] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #[cfg(not(any( feature = "nrf51", diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index c3976d26..9ce09064 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 30ff02d5..0392e808 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] // This must go FIRST so that all the other modules see its macros. pub mod fmt; diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index 25150e8a..80bb907a 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![allow(clippy::new_without_default)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 5b262098..4edc883f 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] -#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] #![doc = include_str!("../README.md")] #![allow(clippy::new_without_default)] #![warn(missing_docs)] diff --git a/embassy-usb-hid/src/lib.rs b/embassy-usb-hid/src/lib.rs index 5fee60bb..8b181aec 100644 --- a/embassy-usb-hid/src/lib.rs +++ b/embassy-usb-hid/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] //! Implements HID functionality for a usb-device device. diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb-serial/src/lib.rs index f3de2ec1..27b536a6 100644 --- a/embassy-usb-serial/src/lib.rs +++ b/embassy-usb-serial/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] // This mod MUST go first, so that the others see its macros. diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index b165804e..ca7dde62 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -1,5 +1,4 @@ #![no_std] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] // This mod MUST go first, so that the others see its macros. diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 133a3e67..7a404a91 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] #![macro_use] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_boot_nrf::FirmwareUpdater; diff --git a/examples/boot/application/nrf/src/bin/b.rs b/examples/boot/application/nrf/src/bin/b.rs index 5394bf0c..1373f277 100644 --- a/examples/boot/application/nrf/src/bin/b.rs +++ b/examples/boot/application/nrf/src/bin/b.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] #![macro_use] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index 352660b5..33ca380f 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 7fdb0b68..4761fcf6 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index 7cd2ece1..f1b57a17 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index a68edb32..f9526cbc 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf/src/bin/usb_serial_multitask.rs index d62d7e52..c646c0bb 100644 --- a/examples/nrf/src/bin/usb_serial_multitask.rs +++ b/examples/nrf/src/bin/usb_serial_multitask.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::mem; diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 2df7f62f..166ffe17 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index 74be1f59..00cf3e93 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use defmt::{info, panic}; diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs index 00ff67f3..27d7c29c 100644 --- a/examples/stm32l0/src/bin/lorawan.rs +++ b/examples/stm32l0/src/bin/lorawan.rs @@ -3,7 +3,6 @@ #![no_main] #![macro_use] #![allow(dead_code)] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 2c8706e4..c96a83ea 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index 7d763e7f..0aca6f1c 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use defmt::*; diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index 9143e64d..7f34dd30 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -2,7 +2,6 @@ #![no_main] #![macro_use] #![allow(dead_code)] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use embassy_executor::Spawner; diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs index 8f674d79..3c60a8de 100644 --- a/examples/stm32wl/src/bin/subghz.rs +++ b/examples/stm32wl/src/bin/subghz.rs @@ -2,7 +2,6 @@ #![no_main] #![macro_use] #![allow(dead_code)] -#![feature(generic_associated_types)] #![feature(type_alias_impl_trait)] use defmt::*; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f5e342ed..1ec19e58 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,7 +1,7 @@ # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly-2022-08-16" +channel = "nightly-2022-09-22" components = [ "rust-src", "rustfmt" ] targets = [ "thumbv7em-none-eabi", From a0487380da42a71ab7532e2bc1befd1039c18a78 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:42:49 +0200 Subject: [PATCH 19/42] Replace futures::future::poll_fn -> core::future::poll_fn. --- embassy-executor/src/spawner.rs | 3 +-- embassy-lora/src/stm32wl/mod.rs | 3 +-- embassy-net/src/stack.rs | 3 +-- embassy-net/src/tcp.rs | 6 +++--- embassy-net/src/udp.rs | 2 +- embassy-nrf/src/buffered_uarte.rs | 3 +-- embassy-nrf/src/gpiote.rs | 3 +-- embassy-nrf/src/qdec.rs | 2 +- embassy-nrf/src/qspi.rs | 2 +- embassy-nrf/src/rng.rs | 2 +- embassy-nrf/src/saadc.rs | 2 +- embassy-nrf/src/spim.rs | 2 +- embassy-nrf/src/temp.rs | 2 +- embassy-nrf/src/timer.rs | 2 +- embassy-nrf/src/twim.rs | 3 +-- embassy-nrf/src/uarte.rs | 2 +- embassy-nrf/src/usb.rs | 3 +-- embassy-rp/src/dma.rs | 2 +- embassy-rp/src/usb.rs | 3 +-- embassy-stm32/src/dcmi.rs | 2 +- embassy-stm32/src/i2c/v2.rs | 2 +- embassy-stm32/src/rng.rs | 2 +- embassy-stm32/src/sdmmc/mod.rs | 2 +- embassy-stm32/src/usart/buffered.rs | 3 +-- embassy-stm32/src/usb/usb.rs | 3 +-- embassy-sync/src/mutex.rs | 3 +-- embassy-sync/src/signal.rs | 4 ++-- examples/nrf-rtos-trace/src/bin/rtos_trace.rs | 3 ++- examples/nrf/src/bin/executor_fairness_test.rs | 3 ++- 29 files changed, 34 insertions(+), 43 deletions(-) diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 25a0d7db..400d973f 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -1,10 +1,9 @@ +use core::future::poll_fn; use core::marker::PhantomData; use core::mem; use core::ptr::NonNull; use core::task::Poll; -use futures_util::future::poll_fn; - use super::raw; /// Token to spawn a newly-created task in an executor. diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 4d11244b..e28fa2c1 100644 --- a/embassy-lora/src/stm32wl/mod.rs +++ b/embassy-lora/src/stm32wl/mod.rs @@ -1,5 +1,5 @@ //! A radio driver integration for the radio found on STM32WL family devices. -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::Poll; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; @@ -11,7 +11,6 @@ use embassy_stm32::subghz::{ Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams, }; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig}; use lorawan_device::async_device::Timings; diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 8d2dd4bc..3a761075 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -1,10 +1,9 @@ use core::cell::UnsafeCell; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; -use futures::future::poll_fn; use futures::pin_mut; use heapless::Vec; #[cfg(feature = "dhcpv4")] diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 0fa87360..f8fff3e2 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -1,8 +1,8 @@ use core::cell::UnsafeCell; +use core::future::poll_fn; use core::mem; use core::task::Poll; -use futures::future::poll_fn; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::tcp; use smoltcp::time::Duration; @@ -103,7 +103,7 @@ impl<'a> TcpSocket<'a> { Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute), } - futures::future::poll_fn(|cx| unsafe { + poll_fn(|cx| unsafe { self.io.with_mut(|s, _| match s.state() { tcp::State::Closed | tcp::State::TimeWait => Poll::Ready(Err(ConnectError::ConnectionReset)), tcp::State::Listen => unreachable!(), @@ -128,7 +128,7 @@ impl<'a> TcpSocket<'a> { Err(tcp::ListenError::Unaddressable) => return Err(AcceptError::InvalidPort), } - futures::future::poll_fn(|cx| unsafe { + poll_fn(|cx| unsafe { self.io.with_mut(|s, _| match s.state() { tcp::State::Listen | tcp::State::SynSent | tcp::State::SynReceived => { s.register_send_waker(cx.waker()); diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 78b09a49..f2e33493 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -1,8 +1,8 @@ use core::cell::UnsafeCell; +use core::future::poll_fn; use core::mem; use core::task::Poll; -use futures::future::poll_fn; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::udp::{self, PacketMetadata}; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index c3cba247..fec875cb 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -15,7 +15,7 @@ use core::cell::RefCell; use core::cmp::min; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -23,7 +23,6 @@ use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorag use embassy_hal_common::ring_buffer::RingBuffer; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::WakerRegistration; -use futures::future::poll_fn; // Re-export SVD variants to allow user to directly set values pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index d99f592b..b418be9d 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -1,10 +1,9 @@ use core::convert::Infallible; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin}; diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 762e0971..253c85c3 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -1,10 +1,10 @@ //! Quadrature decoder interface +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index c97cb165..ea0a1703 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -1,11 +1,11 @@ #![macro_use] +use core::future::poll_fn; use core::ptr; use core::task::Poll; use embassy_hal_common::drop::DropBomb; use embassy_hal_common::{into_ref, PeripheralRef}; -use futures::future::poll_fn; use crate::gpio::{self, Pin as GpioPin}; use crate::interrupt::{Interrupt, InterruptExt}; diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index 42da51d0..e0caeaae 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -1,3 +1,4 @@ +use core::future::poll_fn; use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Poll; @@ -5,7 +6,6 @@ use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::interrupt::InterruptExt; use crate::peripherals::RNG; diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 9bc89eb3..d1c82423 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -1,12 +1,12 @@ #![macro_use] +use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use pac::{saadc, SAADC}; use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A}; // We treat the positive and negative channels with the same enum values to keep our type tidy and given they are the same diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 2955182e..51cd73a4 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -1,12 +1,12 @@ #![macro_use] +use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; use embassy_hal_common::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; -use futures::future::poll_fn; pub use pac::spim0::frequency::FREQUENCY_A as Frequency; use crate::chip::FORCE_COPY_BUFFER_SIZE; diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index d520fd68..7a7f61b5 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -1,12 +1,12 @@ //! Temperature sensor interface. +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::I30F2; -use futures::future::poll_fn; use crate::interrupt::InterruptExt; use crate::peripherals::TEMP; diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 3de5a896..bc871064 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -1,12 +1,12 @@ #![macro_use] +use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::interrupt::{Interrupt, InterruptExt}; use crate::ppi::{Event, Task}; diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 3d4af753..7c6ca1d3 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -6,7 +6,7 @@ //! //! - nRF52832: Section 33 //! - nRF52840: Section 6.31 -use core::future::Future; +use core::future::{poll_fn, Future}; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; @@ -16,7 +16,6 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; -use futures::future::poll_fn; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index c250e24c..5f9c4f17 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -13,12 +13,12 @@ //! memory may be used given that buffers are passed in directly to its read and write //! methods. +use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use futures::future::poll_fn; use pac::uarte0::RegisterBlock; // Re-export SVD variants to allow user to directly set values. pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 688326e9..0685d419 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -1,5 +1,6 @@ #![macro_use] +use core::future::{poll_fn, Future}; use core::marker::PhantomData; use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; @@ -11,8 +12,6 @@ use embassy_sync::waitqueue::AtomicWaker; pub use embassy_usb; use embassy_usb::driver::{self, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; -use futures::future::poll_fn; -use futures::Future; use pac::usbd::RegisterBlock; use crate::interrupt::{Interrupt, InterruptExt}; diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 7ad1a6bf..410c4866 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -1,3 +1,4 @@ +use core::future::Future; use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; @@ -5,7 +6,6 @@ use core::task::{Context, Poll}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::Future; use pac::dma::vals::DataSize; use crate::pac::dma::vals; diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 82eafdef..a7ec5fb7 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -1,3 +1,4 @@ +use core::future::{poll_fn, Future}; use core::marker::PhantomData; use core::slice; use core::sync::atomic::Ordering; @@ -8,8 +9,6 @@ use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; -use futures::future::poll_fn; -use futures::Future; use crate::interrupt::{Interrupt, InterruptExt}; use crate::{pac, peripherals, Peripheral, RegExt}; diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index fb9dc9d0..ff9157d5 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -1,8 +1,8 @@ +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::gpio::sealed::AFType; use crate::gpio::Speed; diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index db492446..b7c89931 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1,4 +1,5 @@ use core::cmp; +use core::future::poll_fn; use core::task::Poll; use atomic_polyfill::{AtomicUsize, Ordering}; @@ -6,7 +7,6 @@ use embassy_embedded_hal::SetConfig; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use crate::dma::NoDma; use crate::gpio::sealed::AFType; diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 520f2ab9..10fc4a75 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -1,10 +1,10 @@ #![macro_use] +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use rand_core::{CryptoRng, RngCore}; use crate::{pac, peripherals, Peripheral}; diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 67758c49..a8bc6385 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1,12 +1,12 @@ #![macro_use] use core::default::Default; +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use futures::future::poll_fn; use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR}; use crate::dma::NoDma; diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 5f6dabb3..46c49a99 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,11 +1,10 @@ -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::Poll; use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; use embassy_hal_common::ring_buffer::RingBuffer; use embassy_sync::waitqueue::WakerRegistration; -use futures::future::poll_fn; use super::*; diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index db965824..e5ee1181 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -1,5 +1,6 @@ #![macro_use] +use core::future::{poll_fn, Future}; use core::marker::PhantomData; use core::sync::atomic::Ordering; use core::task::Poll; @@ -10,8 +11,6 @@ use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{block_for, Duration}; use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; -use futures::future::poll_fn; -use futures::Future; use pac::common::{Reg, RW}; use pac::usb::vals::{EpType, Stat}; diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 92101c6b..fcf056d3 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -2,11 +2,10 @@ //! //! This module provides a mutex that can be used to synchronize data between asynchronous tasks. use core::cell::{RefCell, UnsafeCell}; +use core::future::poll_fn; use core::ops::{Deref, DerefMut}; use core::task::Poll; -use futures_util::future::poll_fn; - use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex as BlockingMutex; use crate::waitqueue::WakerRegistration; diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index 34201d03..b4d99513 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -1,6 +1,6 @@ //! A synchronization primitive for passing the latest value to a task. use core::cell::UnsafeCell; -use core::future::Future; +use core::future::{poll_fn, Future}; use core::mem; use core::task::{Context, Poll, Waker}; @@ -94,7 +94,7 @@ impl Signal { /// Future that completes when this Signal has been signaled. pub fn wait(&self) -> impl Future + '_ { - futures_util::future::poll_fn(move |cx| self.poll_wait(cx)) + poll_fn(move |cx| self.poll_wait(cx)) } /// non-blocking method to check whether this signal has been signaled. diff --git a/examples/nrf-rtos-trace/src/bin/rtos_trace.rs b/examples/nrf-rtos-trace/src/bin/rtos_trace.rs index 7d1ad87c..cf8b2f80 100644 --- a/examples/nrf-rtos-trace/src/bin/rtos_trace.rs +++ b/examples/nrf-rtos-trace/src/bin/rtos_trace.rs @@ -2,6 +2,7 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::future::poll_fn; use core::task::Poll; use embassy_executor::Spawner; @@ -46,7 +47,7 @@ async fn run2() { #[embassy_executor::task] async fn run3() { - futures::future::poll_fn(|cx| { + poll_fn(|cx| { cx.waker().wake_by_ref(); Poll::<()>::Pending }) diff --git a/examples/nrf/src/bin/executor_fairness_test.rs b/examples/nrf/src/bin/executor_fairness_test.rs index 9ae030d0..2a28f276 100644 --- a/examples/nrf/src/bin/executor_fairness_test.rs +++ b/examples/nrf/src/bin/executor_fairness_test.rs @@ -2,6 +2,7 @@ #![no_main] #![feature(type_alias_impl_trait)] +use core::future::poll_fn; use core::task::Poll; use defmt::{info, unwrap}; @@ -26,7 +27,7 @@ async fn run2() { #[embassy_executor::task] async fn run3() { - futures::future::poll_fn(|cx| { + poll_fn(|cx| { cx.waker().wake_by_ref(); Poll::<()>::Pending }) From 10d1ad2343825388277dc54db649c9349a0b6de8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 22 Sep 2022 16:48:35 +0200 Subject: [PATCH 20/42] Replace futures::future::join -> embassy_futures::join::join. --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/spi.rs | 2 +- embassy-stm32/Cargo.toml | 1 + embassy-stm32/src/dcmi.rs | 4 ++-- embassy-stm32/src/spi/mod.rs | 2 +- examples/nrf/src/bin/usb_hid_keyboard.rs | 2 +- examples/nrf/src/bin/usb_hid_mouse.rs | 2 +- examples/nrf/src/bin/usb_serial.rs | 2 +- examples/rp/Cargo.toml | 1 + examples/rp/src/bin/usb_serial.rs | 2 +- examples/stm32f1/Cargo.toml | 1 + examples/stm32f1/src/bin/usb_serial.rs | 2 +- examples/stm32f3/Cargo.toml | 1 + examples/stm32f3/src/bin/usb_serial.rs | 2 +- examples/stm32l5/Cargo.toml | 1 + examples/stm32l5/src/bin/usb_hid_mouse.rs | 2 +- examples/stm32l5/src/bin/usb_serial.rs | 2 +- tests/rp/Cargo.toml | 1 + tests/rp/src/bin/gpio_async.rs | 2 +- 19 files changed, 20 insertions(+), 13 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 63997c99..885a4746 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -33,6 +33,7 @@ unstable-traits = ["embedded-hal-1"] embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 3cf82357..03293e06 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -1,9 +1,9 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; +use embassy_futures::join::join; use embassy_hal_common::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Phase, Polarity}; -use futures::future::join; use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin as _; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b4c19f32..484496f2 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -34,6 +34,7 @@ flavors = [ embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embassy-executor = { version = "0.1.0", path = "../embassy-executor" } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index ff9157d5..20e1a407 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -429,7 +429,7 @@ where } }); - let (_, result) = futures::future::join(dma_read, result).await; + let (_, result) = embassy_futures::join::join(dma_read, result).await; unsafe { Self::toggle(false) }; @@ -537,7 +537,7 @@ where unsafe { Self::toggle(true) }; - let (_, result) = futures::future::join(dma_result, result).await; + let (_, result) = embassy_futures::join::join(dma_result, result).await; unsafe { Self::toggle(false) }; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 02e6020b..556d1230 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -3,9 +3,9 @@ use core::ptr; use embassy_embedded_hal::SetConfig; +use embassy_futures::join::join; use embassy_hal_common::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; -use futures::future::join; use self::sealed::WordSize; use crate::dma::{slice_ptr_parts, NoDma, Transfer}; diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 4761fcf6..70318b78 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -7,6 +7,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use defmt::*; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_futures::select::{select, Either}; use embassy_nrf::gpio::{Input, Pin, Pull}; use embassy_nrf::usb::{Driver, PowerUsb}; @@ -15,7 +16,6 @@ use embassy_sync::signal::Signal; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, DeviceStateHandler}; use embassy_usb_hid::{HidReaderWriter, ReportId, RequestHandler, State}; -use futures::future::join; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index f1b57a17..65fbda1c 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -6,13 +6,13 @@ use core::mem; use defmt::*; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; use embassy_time::{Duration, Timer}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config}; use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; -use futures::future::join; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index f9526cbc..a740b4e0 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -6,12 +6,12 @@ use core::mem; use defmt::{info, panic}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; use embassy_nrf::{interrupt, pac}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 17393322..24c3cdd6 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -13,6 +13,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index 00cf3e93..bf92a163 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -4,12 +4,12 @@ use defmt::{info, panic}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_rp::interrupt; use embassy_rp::usb::{Driver, Instance}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 33ac63db..895e043d 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -10,6 +10,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index a9c46068..a14e728b 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -4,6 +4,7 @@ use defmt::{panic, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; @@ -12,7 +13,6 @@ use embassy_time::{Duration, Timer}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 208f3908..27f5c260 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -11,6 +11,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.3" diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index d3702fc3..b9fd20e2 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -4,6 +4,7 @@ use defmt::{panic, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::mhz; use embassy_stm32::usb::{Driver, Instance}; @@ -12,7 +13,6 @@ use embassy_time::{Duration, Timer}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index c451bd22..05945f6b 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -15,6 +15,7 @@ embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", fea embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.6.0" defmt = "0.3" diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index 0aca6f1c..fa92ceae 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::rcc::*; use embassy_stm32::usb::Driver; use embassy_stm32::{interrupt, Config}; @@ -11,7 +12,6 @@ use embassy_time::{Duration, Timer}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; -use futures::future::join; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index b576a735..7484dc83 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -4,13 +4,13 @@ use defmt::{panic, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_stm32::rcc::*; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; use embassy_usb_serial::{CdcAcmClass, State}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 4d6877cc..11ecb916 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,6 +8,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" defmt-rtt = "0.3.0" diff --git a/tests/rp/src/bin/gpio_async.rs b/tests/rp/src/bin/gpio_async.rs index 1eeaac1f..f20b8fcb 100644 --- a/tests/rp/src/bin/gpio_async.rs +++ b/tests/rp/src/bin/gpio_async.rs @@ -4,9 +4,9 @@ use defmt::{assert, *}; use embassy_executor::Spawner; +use embassy_futures::join::join; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_time::{Duration, Instant, Timer}; -use futures::future::join; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] From 2e7916c5feed13893921936ce26dab44c8a617a9 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 23 Sep 2022 06:38:47 +0200 Subject: [PATCH 21/42] Add RP2040 ROM functions and intrinsics aliases --- embassy-rp/Cargo.toml | 4 + embassy-rp/src/intrinsics.rs | 273 +++++++++++++ embassy-rp/src/lib.rs | 3 + embassy-rp/src/rom_data.rs | 730 +++++++++++++++++++++++++++++++++++ 4 files changed, 1010 insertions(+) create mode 100644 embassy-rp/src/intrinsics.rs create mode 100644 embassy-rp/src/rom_data.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 885a4746..df0af8df 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -22,6 +22,10 @@ unstable-pac = [] time-driver = [] +rom-func-cache = [] +disable-intrinsics = [] +rom-v2-intrinsics = [] + # Enable nightly-only features nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs new file mode 100644 index 00000000..9e6624cf --- /dev/null +++ b/embassy-rp/src/intrinsics.rs @@ -0,0 +1,273 @@ +#![macro_use] + +/// Generate a series of aliases for an intrinsic function. +macro_rules! intrinsics_aliases { + ( + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + ) => {}; + ( + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + ) => {}; + + ( + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + $alias:ident + $($rest:ident)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + intrinsics! { + extern $abi fn $alias( $($argname: $ty),* ) -> $ret { + $name($($argname),*) + } + } + + intrinsics_aliases! { + extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($rest)* + } + }; + + ( + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty, + $alias:ident + $($rest:ident)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + intrinsics! { + unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret { + $name($($argname),*) + } + } + + intrinsics_aliases! { + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($rest)* + } + }; +} + +/// The macro used to define overridden intrinsics. +/// +/// This is heavily inspired by the macro used by compiler-builtins. The idea +/// is to abstract anything special that needs to be done to override an +/// intrinsic function. Intrinsic generation is disabled for non-ARM targets +/// so things like CI and docs generation do not have problems. Additionally +/// they can be disabled with the crate feature `disable-intrinsics` for +/// testing or comparing performance. +/// +/// Like the compiler-builtins macro, it accepts a series of functions that +/// looks like normal Rust code: +/// +/// intrinsics! { +/// extern "C" fn foo(a: i32) -> u32 { +/// // ... +/// } +/// +/// #[nonstandard_attribute] +/// extern "C" fn bar(a: i32) -> u32 { +/// // ... +/// } +/// } +/// +/// Each function can also be decorated with nonstandard attributes to control +/// additional behaviour: +/// +/// * `slower_than_default` - indicates that the override is slower than the +/// default implementation. Currently this just disables the override +/// entirely. +/// * `bootrom_v2` - indicates that the override is only available +/// on a V2 bootrom or higher. Only enabled when the feature +/// `rom-v2-intrinsics` is set. +/// * `alias` - accepts a list of names to alias the intrinsic to. +/// * `aeabi` - accepts a list of ARM EABI names to alias to. +/// +macro_rules! intrinsics { + () => {}; + + ( + #[slower_than_default] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + // Not exported, but defined so the actual implementation is + // considered used + #[allow(dead_code)] + fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + intrinsics!($($rest)*); + }; + + ( + #[bootrom_v2] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + // Not exported, but defined so the actual implementation is + // considered used + #[cfg(not(feature = "rom-v2-intrinsics"))] + #[allow(dead_code)] + fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + #[cfg(feature = "rom-v2-intrinsics")] + intrinsics! { + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics!($($rest)*); + }; + + ( + #[alias = $($alias:ident),*] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + intrinsics! { + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics_aliases! { + extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($alias) * + } + + intrinsics!($($rest)*); + }; + + ( + #[alias = $($alias:ident),*] + $(#[$($attr:tt)*])* + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + intrinsics! { + $(#[$($attr)*])* + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics_aliases! { + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret, + $($alias) * + } + + intrinsics!($($rest)*); + }; + + ( + #[aeabi = $($alias:ident),*] + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + intrinsics! { + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + } + + intrinsics_aliases! { + extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret, + $($alias) * + } + + intrinsics!($($rest)*); + }; + + ( + $(#[$($attr:tt)*])* + extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + $(#[$($attr)*])* + extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + mod $name { + #[no_mangle] + $(#[$($attr)*])* + pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { + super::$name($($argname),*) + } + } + + // Not exported, but defined so the actual implementation is + // considered used + #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[allow(dead_code)] + fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + intrinsics!($($rest)*); + }; + + ( + $(#[$($attr:tt)*])* + unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty { + $($body:tt)* + } + + $($rest:tt)* + ) => { + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + $(#[$($attr)*])* + unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + mod $name { + #[no_mangle] + $(#[$($attr)*])* + pub unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { + super::$name($($argname),*) + } + } + + // Not exported, but defined so the actual implementation is + // considered used + #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[allow(dead_code)] + unsafe fn $name( $($argname: $ty),* ) -> $ret { + $($body)* + } + + intrinsics!($($rest)*); + }; +} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9ce09064..9ac98d22 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -4,9 +4,12 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +mod intrinsics; + pub mod dma; pub mod gpio; pub mod interrupt; +pub mod rom_data; pub mod rtc; pub mod spi; #[cfg(feature = "time-driver")] diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs new file mode 100644 index 00000000..93a3632a --- /dev/null +++ b/embassy-rp/src/rom_data.rs @@ -0,0 +1,730 @@ +//! Functions and data from the RPI Bootrom. +//! +//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1: +//! +//! > The Bootrom contains a number of public functions that provide useful +//! > RP2040 functionality that might be needed in the absence of any other code +//! > on the device, as well as highly optimized versions of certain key +//! > functionality that would otherwise have to take up space in most user +//! > binaries. + +/// A bootrom function table code. +pub type RomFnTableCode = [u8; 2]; + +/// This function searches for (table) +type RomTableLookupFn = unsafe extern "C" fn(*const u16, u32) -> T; + +/// The following addresses are described at `2.8.2. Bootrom Contents` +/// Pointer to the lookup table function supplied by the rom. +const ROM_TABLE_LOOKUP_PTR: *const u16 = 0x0000_0018 as _; + +/// Pointer to helper functions lookup table. +const FUNC_TABLE: *const u16 = 0x0000_0014 as _; + +/// Pointer to the public data lookup table. +const DATA_TABLE: *const u16 = 0x0000_0016 as _; + +/// Address of the version number of the ROM. +const VERSION_NUMBER: *const u8 = 0x0000_0013 as _; + +/// Retrive rom content from a table using a code. +fn rom_table_lookup(table: *const u16, tag: RomFnTableCode) -> T { + unsafe { + let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR); + let rom_table_lookup: RomTableLookupFn = core::mem::transmute(rom_table_lookup_ptr); + rom_table_lookup(rom_hword_as_ptr(table) as *const u16, u16::from_le_bytes(tag) as u32) + } +} + +/// To save space, the ROM likes to store memory pointers (which are 32-bit on +/// the Cortex-M0+) using only the bottom 16-bits. The assumption is that the +/// values they point at live in the first 64 KiB of ROM, and the ROM is mapped +/// to address `0x0000_0000` and so 16-bits are always sufficient. +/// +/// This functions grabs a 16-bit value from ROM and expands it out to a full 32-bit pointer. +unsafe fn rom_hword_as_ptr(rom_address: *const u16) -> *const u32 { + let ptr: u16 = *rom_address; + ptr as *const u32 +} + +macro_rules! declare_rom_function { + ( + $(#[$outer:meta])* + fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty + $lookup:block + ) => { + #[doc = r"Additional access for the `"] + #[doc = stringify!($name)] + #[doc = r"` ROM function."] + pub mod $name { + /// Retrieve a function pointer. + #[cfg(not(feature = "rom-func-cache"))] + pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { + let p: *const u32 = $lookup; + unsafe { + let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + + /// Retrieve a function pointer. + #[cfg(feature = "rom-func-cache")] + pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{AtomicU16, Ordering}; + + // All pointers in the ROM fit in 16 bits, so we don't need a + // full width word to store the cached value. + static CACHED_PTR: AtomicU16 = AtomicU16::new(0); + // This is safe because the lookup will always resolve + // to the same value. So even if an interrupt or another + // core starts at the same time, it just repeats some + // work and eventually writes back the correct value. + let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { + 0 => { + let raw: *const u32 = $lookup; + CACHED_PTR.store(raw as u16, Ordering::Relaxed); + raw + }, + val => val as *const u32, + }; + unsafe { + let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + } + + $(#[$outer])* + pub extern "C" fn $name( $($argname: $ty),* ) -> $ret { + $name::ptr()($($argname),*) + } + }; + + ( + $(#[$outer:meta])* + unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty + $lookup:block + ) => { + #[doc = r"Additional access for the `"] + #[doc = stringify!($name)] + #[doc = r"` ROM function."] + pub mod $name { + /// Retrieve a function pointer. + #[cfg(not(feature = "rom-func-cache"))] + pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { + let p: *const u32 = $lookup; + unsafe { + let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + + /// Retrieve a function pointer. + #[cfg(feature = "rom-func-cache")] + pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{AtomicU16, Ordering}; + + // All pointers in the ROM fit in 16 bits, so we don't need a + // full width word to store the cached value. + static CACHED_PTR: AtomicU16 = AtomicU16::new(0); + // This is safe because the lookup will always resolve + // to the same value. So even if an interrupt or another + // core starts at the same time, it just repeats some + // work and eventually writes back the correct value. + let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) { + 0 => { + let raw: *const u32 = $lookup; + CACHED_PTR.store(raw as u16, Ordering::Relaxed); + raw + }, + val => val as *const u32, + }; + unsafe { + let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); + func + } + } + } + + $(#[$outer])* + pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret { + $name::ptr()($($argname),*) + } + }; +} + +macro_rules! rom_functions { + () => {}; + + ( + $(#[$outer:meta])* + $c:literal fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty; + + $($rest:tt)* + ) => { + declare_rom_function! { + $(#[$outer])* + fn $name( $($argname: $ty),* ) -> $ret { + $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) + } + } + + rom_functions!($($rest)*); + }; + + ( + $(#[$outer:meta])* + $c:literal unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty; + + $($rest:tt)* + ) => { + declare_rom_function! { + $(#[$outer])* + unsafe fn $name( $($argname: $ty),* ) -> $ret { + $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) + } + } + + rom_functions!($($rest)*); + }; +} + +rom_functions! { + /// Return a count of the number of 1 bits in value. + b"P3" fn popcount32(value: u32) -> u32; + + /// Return the bits of value in the reverse order. + b"R3" fn reverse32(value: u32) -> u32; + + /// Return the number of consecutive high order 0 bits of value. If value is zero, returns 32. + b"L3" fn clz32(value: u32) -> u32; + + /// Return the number of consecutive low order 0 bits of value. If value is zero, returns 32. + b"T3" fn ctz32(value: u32) -> u32; + + /// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode: + /// * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED + /// for the USB Mass Storage Device: + /// * 0 No pins are used as per cold boot. + /// * Otherwise a single bit set indicating which GPIO pin should be set to output and + /// raised whenever there is mass storage activity from the host. + /// * disable_interface_mask may be used to control the exposed USB interfaces: + /// * 0 To enable both interfaces (as per cold boot). + /// * 1 To disable the USB Mass Storage Interface. + /// * 2 to Disable the USB PICOBOOT Interface. + b"UB" fn reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> (); + + /// Sets n bytes start at ptr to the value c and returns ptr + b"MS" unsafe fn memset(ptr: *mut u8, c: u8, n: u32) -> *mut u8; + + /// Sets n bytes start at ptr to the value c and returns ptr. + /// + /// Note this is a slightly more efficient variant of _memset that may only + /// be used if ptr is word aligned. + // Note the datasheet does not match the actual ROM for the code here, see + // https://github.com/raspberrypi/pico-feedback/issues/217 + b"S4" unsafe fn memset4(ptr: *mut u32, c: u8, n: u32) -> *mut u32; + + /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the + /// regions overlap. + b"MC" unsafe fn memcpy(dest: *mut u8, src: *const u8, n: u32) -> *mut u8; + + /// Copies n bytes starting at src to dest and returns dest. The results are undefined if the + /// regions overlap. + /// + /// Note this is a slightly more efficient variant of _memcpy that may only be + /// used if dest and src are word aligned. + b"C4" unsafe fn memcpy44(dest: *mut u32, src: *const u32, n: u32) -> *mut u8; + + /// Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads. + b"IF" unsafe fn connect_internal_flash() -> (); + + /// First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence. + /// + /// Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be + /// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This + /// function configures the SSI with a fixed SCK clock divisor of /6. + b"EX" unsafe fn flash_exit_xip() -> (); + + /// Erase a count bytes, starting at addr (offset from start of flash). Optionally, pass a + /// block erase command e.g. D8h block erase, and the size of the block erased by this + /// command — this function will use the larger block erase where possible, for much higher + /// erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of + /// 4096 bytes. + b"RE" unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> (); + + /// Program data to a range of flash addresses starting at `addr` (and + /// offset from the start of flash) and `count` bytes in size. The value + /// `addr` must be aligned to a 256-byte boundary, and `count` must be a + /// multiple of 256. + b"RP" unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> (); + + /// Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can + /// drive the flashchip select as normal. + b"FC" unsafe fn flash_flush_cache() -> (); + + /// Configure the SSI to generate a standard 03h serial read command, with 24 address bits, + /// upon each XIP access. This is a very slow XIP configuration, but is very widely supported. + /// The debugger calls this function after performing a flash erase/programming operation, so + /// that the freshly-programmed code and data is visible to the debug host, without having to + /// know exactly what kind of flash device is connected. + b"CX" unsafe fn flash_enter_cmd_xip() -> (); + + /// This is the method that is entered by core 1 on reset to wait to be launched by core 0. + /// There are few cases where you should call this method (resetting core 1 is much better). + /// This method does not return and should only ever be called on core 1. + b"WV" unsafe fn wait_for_vector() -> !; +} + +// Various C intrinsics in the ROM +intrinsics! { + #[alias = __popcountdi2] + extern "C" fn __popcountsi2(x: u32) -> u32 { + popcount32(x) + } + + #[alias = __clzdi2] + extern "C" fn __clzsi2(x: u32) -> u32 { + clz32(x) + } + + #[alias = __ctzdi2] + extern "C" fn __ctzsi2(x: u32) -> u32 { + ctz32(x) + } + + // __rbit is only unofficial, but it show up in the ARM documentation, + // so may as well hook it up. + #[alias = __rbitl] + extern "C" fn __rbit(x: u32) -> u32 { + reverse32(x) + } + + unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) -> () { + // Different argument order + memset(dest, c as u8, n as u32); + } + + #[alias = __aeabi_memset8] + unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) -> () { + // Different argument order + memset4(dest as *mut u32, c as u8, n as u32); + } + + unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) -> () { + memset(dest, 0, n as u32); + } + + #[alias = __aeabi_memclr8] + unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) -> () { + memset4(dest as *mut u32, 0, n as u32); + } + + unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) -> () { + memcpy(dest, src, n as u32); + } + + #[alias = __aeabi_memcpy8] + unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) -> () { + memcpy44(dest as *mut u32, src as *const u32, n as u32); + } +} + +unsafe fn convert_str(s: *const u8) -> &'static str { + let mut end = s; + while *end != 0 { + end = end.add(1); + } + let s = core::slice::from_raw_parts(s, end.offset_from(s) as usize); + core::str::from_utf8_unchecked(s) +} + +/// The version number of the rom. +pub fn rom_version_number() -> u8 { + unsafe { *VERSION_NUMBER } +} + +/// The Raspberry Pi Trading Ltd copyright string. +pub fn copyright_string() -> &'static str { + let s: *const u8 = rom_table_lookup(DATA_TABLE, *b"CR"); + unsafe { convert_str(s) } +} + +/// The 8 most significant hex digits of the Bootrom git revision. +pub fn git_revision() -> u32 { + let s: *const u32 = rom_table_lookup(DATA_TABLE, *b"GR"); + unsafe { *s } +} + +/// The start address of the floating point library code and data. +/// +/// This and fplib_end along with the individual function pointers in +/// soft_float_table can be used to copy the floating point implementation into +/// RAM if desired. +pub fn fplib_start() -> *const u8 { + rom_table_lookup(DATA_TABLE, *b"FS") +} + +/// See Table 180 in the RP2040 datasheet for the contents of this table. +pub fn soft_float_table() -> *const usize { + rom_table_lookup(DATA_TABLE, *b"SF") +} + +/// The end address of the floating point library code and data. +pub fn fplib_end() -> *const u8 { + rom_table_lookup(DATA_TABLE, *b"FE") +} + +/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table. +pub fn soft_double_table() -> *const usize { + if rom_version_number() < 2 { + panic!( + "Double precision operations require V2 bootrom (found: V{})", + rom_version_number() + ); + } + rom_table_lookup(DATA_TABLE, *b"SD") +} + +/// ROM functions using single-precision arithmetic (i.e. 'f32' in Rust terms) +pub mod float_funcs { + + macro_rules! make_functions { + ( + $( + $(#[$outer:meta])* + $offset:literal $name:ident ( + $( $aname:ident : $aty:ty ),* + ) -> $ret:ty; + )* + ) => { + $( + declare_rom_function! { + $(#[$outer])* + fn $name( $( $aname : $aty ),* ) -> $ret { + let table: *const usize = $crate::rom_data::soft_float_table(); + unsafe { + // This is the entry in the table. Our offset is given as a + // byte offset, but we want the table index (each pointer in + // the table is 4 bytes long) + let entry: *const usize = table.offset($offset / 4); + // Read the pointer from the table + core::ptr::read(entry) as *const u32 + } + } + } + )* + } + } + + make_functions! { + /// Calculates `a + b` + 0x00 fadd(a: f32, b: f32) -> f32; + /// Calculates `a - b` + 0x04 fsub(a: f32, b: f32) -> f32; + /// Calculates `a * b` + 0x08 fmul(a: f32, b: f32) -> f32; + /// Calculates `a / b` + 0x0c fdiv(a: f32, b: f32) -> f32; + + // 0x10 and 0x14 are deprecated + + /// Calculates `sqrt(v)` (or return -Infinity if v is negative) + 0x18 fsqrt(v: f32) -> f32; + /// Converts an f32 to a signed integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `-0x80000000` to `0x7FFFFFFF` + 0x1c float_to_int(v: f32) -> i32; + /// Converts an f32 to an signed fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x20 float_to_fix(v: f32, n: i32) -> i32; + /// Converts an f32 to an unsigned integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `0x00000000` to `0xFFFFFFFF` + 0x24 float_to_uint(v: f32) -> u32; + /// Converts an f32 to an unsigned fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x28 float_to_ufix(v: f32, n: i32) -> u32; + /// Converts a signed integer to the nearest + /// f32 value, rounding to even on tie + 0x2c int_to_float(v: i32) -> f32; + /// Converts a signed fixed point integer + /// representation to the nearest f32 value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x30 fix_to_float(v: i32, n: i32) -> f32; + /// Converts an unsigned integer to the nearest + /// f32 value, rounding to even on tie + 0x34 uint_to_float(v: u32) -> f32; + /// Converts an unsigned fixed point integer + /// representation to the nearest f32 value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x38 ufix_to_float(v: u32, n: i32) -> f32; + /// Calculates the cosine of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x3c fcos(angle: f32) -> f32; + /// Calculates the sine of `angle`. The value of + /// `angle` is in radians, and must be in the range `-1024` to `1024` + 0x40 fsin(angle: f32) -> f32; + /// Calculates the tangent of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x44 ftan(angle: f32) -> f32; + + // 0x48 is deprecated + + /// Calculates the exponential value of `v`, + /// i.e. `e ** v` + 0x4c fexp(v: f32) -> f32; + /// Calculates the natural logarithm of `v`. If `v <= 0` return -Infinity + 0x50 fln(v: f32) -> f32; + } + + macro_rules! make_functions_v2 { + ( + $( + $(#[$outer:meta])* + $offset:literal $name:ident ( + $( $aname:ident : $aty:ty ),* + ) -> $ret:ty; + )* + ) => { + $( + declare_rom_function! { + $(#[$outer])* + fn $name( $( $aname : $aty ),* ) -> $ret { + if $crate::rom_data::rom_version_number() < 2 { + panic!( + "Floating point function requires V2 bootrom (found: V{})", + $crate::rom_data::rom_version_number() + ); + } + let table: *const usize = $crate::rom_data::soft_float_table(); + unsafe { + // This is the entry in the table. Our offset is given as a + // byte offset, but we want the table index (each pointer in + // the table is 4 bytes long) + let entry: *const usize = table.offset($offset / 4); + // Read the pointer from the table + core::ptr::read(entry) as *const u32 + } + } + } + )* + } + } + + // These are only on BootROM v2 or higher + make_functions_v2! { + /// Compares two floating point numbers, returning: + /// • 0 if a == b + /// • -1 if a < b + /// • 1 if a > b + 0x54 fcmp(a: f32, b: f32) -> i32; + /// Computes the arc tangent of `y/x` using the + /// signs of arguments to determine the correct quadrant + 0x58 fatan2(y: f32, x: f32) -> f32; + /// Converts a signed 64-bit integer to the + /// nearest f32 value, rounding to even on tie + 0x5c int64_to_float(v: i64) -> f32; + /// Converts a signed fixed point 64-bit integer + /// representation to the nearest f32 value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x60 fix64_to_float(v: i64, n: i32) -> f32; + /// Converts an unsigned 64-bit integer to the + /// nearest f32 value, rounding to even on tie + 0x64 uint64_to_float(v: u64) -> f32; + /// Converts an unsigned fixed point 64-bit + /// integer representation to the nearest f32 value, rounding to even on + /// tie. `n` specifies the position of the binary point in fixed point, so + /// `f = nearest(v/(2^n))` + 0x68 ufix64_to_float(v: u64, n: i32) -> f32; + /// Convert an f32 to a signed 64-bit integer, rounding towards -Infinity, + /// and clamping the result to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x6c float_to_int64(v: f32) -> i64; + /// Converts an f32 to a signed fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation - e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x70 float_to_fix64(v: f32, n: i32) -> f32; + /// Converts an f32 to an unsigned 64-bit + /// integer, rounding towards -Infinity, and clamping the result to lie + /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF` + 0x74 float_to_uint64(v: f32) -> u64; + /// Converts an f32 to an unsigned fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation, e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `0x0000000000000000` to + /// `0xFFFFFFFFFFFFFFFF` + 0x78 float_to_ufix64(v: f32, n: i32) -> u64; + /// Converts an f32 to an f64. + 0x7c float_to_double(v: f32) -> f64; + } +} + +/// Functions using double-precision arithmetic (i.e. 'f64' in Rust terms) +pub mod double_funcs { + + macro_rules! make_double_funcs { + ( + $( + $(#[$outer:meta])* + $offset:literal $name:ident ( + $( $aname:ident : $aty:ty ),* + ) -> $ret:ty; + )* + ) => { + $( + declare_rom_function! { + $(#[$outer])* + fn $name( $( $aname : $aty ),* ) -> $ret { + let table: *const usize = $crate::rom_data::soft_double_table(); + unsafe { + // This is the entry in the table. Our offset is given as a + // byte offset, but we want the table index (each pointer in + // the table is 4 bytes long) + let entry: *const usize = table.offset($offset / 4); + // Read the pointer from the table + core::ptr::read(entry) as *const u32 + } + } + } + )* + } + } + + make_double_funcs! { + /// Calculates `a + b` + 0x00 dadd(a: f64, b: f64) -> f64; + /// Calculates `a - b` + 0x04 dsub(a: f64, b: f64) -> f64; + /// Calculates `a * b` + 0x08 dmul(a: f64, b: f64) -> f64; + /// Calculates `a / b` + 0x0c ddiv(a: f64, b: f64) -> f64; + + // 0x10 and 0x14 are deprecated + + /// Calculates `sqrt(v)` (or return -Infinity if v is negative) + 0x18 dsqrt(v: f64) -> f64; + /// Converts an f64 to a signed integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `-0x80000000` to `0x7FFFFFFF` + 0x1c double_to_int(v: f64) -> i32; + /// Converts an f64 to an signed fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x20 double_to_fix(v: f64, n: i32) -> i32; + /// Converts an f64 to an unsigned integer, + /// rounding towards -Infinity, and clamping the result to lie within the + /// range `0x00000000` to `0xFFFFFFFF` + 0x24 double_to_uint(v: f64) -> u32; + /// Converts an f64 to an unsigned fixed point + /// integer representation where n specifies the position of the binary + /// point in the resulting fixed point representation, e.g. + /// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity, + /// and clamps the resulting integer to lie within the range `0x00000000` to + /// `0xFFFFFFFF` + 0x28 double_to_ufix(v: f64, n: i32) -> u32; + /// Converts a signed integer to the nearest + /// double value, rounding to even on tie + 0x2c int_to_double(v: i32) -> f64; + /// Converts a signed fixed point integer + /// representation to the nearest double value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x30 fix_to_double(v: i32, n: i32) -> f64; + /// Converts an unsigned integer to the nearest + /// double value, rounding to even on tie + 0x34 uint_to_double(v: u32) -> f64; + /// Converts an unsigned fixed point integer + /// representation to the nearest double value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so f = + /// nearest(v/(2^n)) + 0x38 ufix_to_double(v: u32, n: i32) -> f64; + /// Calculates the cosine of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x3c dcos(angle: f64) -> f64; + /// Calculates the sine of `angle`. The value of + /// `angle` is in radians, and must be in the range `-1024` to `1024` + 0x40 dsin(angle: f64) -> f64; + /// Calculates the tangent of `angle`. The value + /// of `angle` is in radians, and must be in the range `-1024` to `1024` + 0x44 dtan(angle: f64) -> f64; + + // 0x48 is deprecated + + /// Calculates the exponential value of `v`, + /// i.e. `e ** v` + 0x4c dexp(v: f64) -> f64; + /// Calculates the natural logarithm of v. If v <= 0 return -Infinity + 0x50 dln(v: f64) -> f64; + + // These are only on BootROM v2 or higher + + /// Compares two floating point numbers, returning: + /// • 0 if a == b + /// • -1 if a < b + /// • 1 if a > b + 0x54 dcmp(a: f64, b: f64) -> i32; + /// Computes the arc tangent of `y/x` using the + /// signs of arguments to determine the correct quadrant + 0x58 datan2(y: f64, x: f64) -> f64; + /// Converts a signed 64-bit integer to the + /// nearest double value, rounding to even on tie + 0x5c int64_to_double(v: i64) -> f64; + /// Converts a signed fixed point 64-bit integer + /// representation to the nearest double value, rounding to even on tie. `n` + /// specifies the position of the binary point in fixed point, so `f = + /// nearest(v/(2^n))` + 0x60 fix64_to_doubl(v: i64, n: i32) -> f64; + /// Converts an unsigned 64-bit integer to the + /// nearest double value, rounding to even on tie + 0x64 uint64_to_double(v: u64) -> f64; + /// Converts an unsigned fixed point 64-bit + /// integer representation to the nearest double value, rounding to even on + /// tie. `n` specifies the position of the binary point in fixed point, so + /// `f = nearest(v/(2^n))` + 0x68 ufix64_to_double(v: u64, n: i32) -> f64; + /// Convert an f64 to a signed 64-bit integer, rounding towards -Infinity, + /// and clamping the result to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x6c double_to_int64(v: f64) -> i64; + /// Converts an f64 to a signed fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation - e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `-0x8000000000000000` to + /// `0x7FFFFFFFFFFFFFFF` + 0x70 double_to_fix64(v: f64, n: i32) -> i64; + /// Converts an f64 to an unsigned 64-bit + /// integer, rounding towards -Infinity, and clamping the result to lie + /// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF` + 0x74 double_to_uint64(v: f64) -> u64; + /// Converts an f64 to an unsigned fixed point + /// 64-bit integer representation where n specifies the position of the + /// binary point in the resulting fixed point representation, e.g. `f(0.5f, + /// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the + /// resulting integer to lie within the range `0x0000000000000000` to + /// `0xFFFFFFFFFFFFFFFF` + 0x78 double_to_ufix64(v: f64, n: i32) -> u64; + /// Converts an f64 to an f32 + 0x7c double_to_float(v: f64) -> f32; + } +} From b4f2c2a05ebe736de1faaf5541f1913ac3c4eff6 Mon Sep 17 00:00:00 2001 From: Zoey Riordan Date: Fri, 23 Sep 2022 12:34:02 +0200 Subject: [PATCH 22/42] Re-add timer.stop() --- embassy-nrf/src/buffered_uarte.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index eb0b1b0c..47f32fac 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -429,14 +429,15 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> { fn drop(&mut self) { let r = U::regs(); + self.timer.stop(); + r.inten.reset(); r.events_rxto.reset(); r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); - r.events_txstopped.reset(); r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); - while r.events_txstopped.read().bits() == 0 {} + while r.events_txstopped.read().bits() == 0 {} while r.events_rxto.read().bits() == 0 {} r.enable.write(|w| w.enable().disabled()); From a45fb2d7183738bdcc020a9b1916fdfab469ec0c Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sat, 24 Sep 2022 09:42:06 -0700 Subject: [PATCH 23/42] usb: fix compile errors with the log feature --- embassy-usb-serial/src/lib.rs | 6 +++--- embassy-usb/src/lib.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb-serial/src/lib.rs index 27b536a6..15c2bb0a 100644 --- a/embassy-usb-serial/src/lib.rs +++ b/embassy-usb-serial/src/lib.rs @@ -268,7 +268,7 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { } /// Number of stop bits for LineCoding -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum StopBits { /// 1 stop bit @@ -292,7 +292,7 @@ impl From for StopBits { } /// Parity for LineCoding -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ParityType { None = 0, @@ -316,7 +316,7 @@ impl From for ParityType { /// /// This is provided by the host for specifying the standard UART parameters such as baud rate. Can /// be ignored if you don't plan to interface with a physical UART. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct LineCoding { stop_bits: StopBits, diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index ca7dde62..6f58c953 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -247,7 +247,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { async fn handle_control(&mut self, req: [u8; 8]) { let req = Request::parse(&req); - trace!("control request: {:02x}", req); + trace!("control request: {:?}", req); match req.direction { UsbDirection::In => self.handle_control_in(req).await, From ca92302d038e3a8f1446085040228bac7a1d00e6 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Thu, 22 Sep 2022 09:05:40 +0300 Subject: [PATCH 24/42] Parameterize Signal with RawMutex --- embassy-sync/src/signal.rs | 69 ++++++++++++++---------- examples/nrf/src/bin/usb_hid_keyboard.rs | 2 +- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index b4d99513..7c38637c 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -1,9 +1,12 @@ //! A synchronization primitive for passing the latest value to a task. -use core::cell::UnsafeCell; +use core::cell::Cell; use core::future::{poll_fn, Future}; use core::mem; use core::task::{Context, Poll, Waker}; +use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; +use crate::blocking_mutex::Mutex; + /// Single-slot signaling primitive. /// /// This is similar to a [`Channel`](crate::channel::Channel) with a buffer size of 1, except @@ -28,8 +31,11 @@ use core::task::{Context, Poll, Waker}; /// /// static SOME_SIGNAL: Signal = Signal::new(); /// ``` -pub struct Signal { - state: UnsafeCell>, +pub struct Signal +where + R: RawMutex, +{ + state: Mutex>>, } enum State { @@ -38,24 +44,27 @@ enum State { Signaled(T), } -unsafe impl Send for Signal {} -unsafe impl Sync for Signal {} - -impl Signal { +impl Signal +where + R: RawMutex, +{ /// Create a new `Signal`. pub const fn new() -> Self { Self { - state: UnsafeCell::new(State::None), + state: Mutex::new(Cell::new(State::None)), } } } -impl Signal { +impl Signal +where + R: RawMutex, +{ /// Mark this Signal as signaled. pub fn signal(&self, val: T) { - critical_section::with(|_| unsafe { - let state = &mut *self.state.get(); - if let State::Waiting(waker) = mem::replace(state, State::Signaled(val)) { + self.state.lock(|cell| { + let state = cell.replace(State::Signaled(val)); + if let State::Waiting(waker) = state { waker.wake(); } }) @@ -63,31 +72,27 @@ impl Signal { /// Remove the queued value in this `Signal`, if any. pub fn reset(&self) { - critical_section::with(|_| unsafe { - let state = &mut *self.state.get(); - *state = State::None - }) + self.state.lock(|cell| cell.set(State::None)); } - /// Manually poll the Signal future. - pub fn poll_wait(&self, cx: &mut Context<'_>) -> Poll { - critical_section::with(|_| unsafe { - let state = &mut *self.state.get(); + fn poll_wait(&self, cx: &mut Context<'_>) -> Poll { + self.state.lock(|cell| { + let state = cell.replace(State::None); match state { State::None => { - *state = State::Waiting(cx.waker().clone()); + cell.set(State::Waiting(cx.waker().clone())); + Poll::Pending + } + State::Waiting(w) if w.will_wake(cx.waker()) => { + cell.set(State::Waiting(w)); Poll::Pending } - State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending, State::Waiting(w) => { - let w = mem::replace(w, cx.waker().clone()); + cell.set(State::Waiting(cx.waker().clone())); w.wake(); Poll::Pending } - State::Signaled(_) => match mem::replace(state, State::None) { - State::Signaled(res) => Poll::Ready(res), - _ => unreachable!(), - }, + State::Signaled(res) => Poll::Ready(res), } }) } @@ -99,6 +104,14 @@ impl Signal { /// non-blocking method to check whether this signal has been signaled. pub fn signaled(&self) -> bool { - critical_section::with(|_| matches!(unsafe { &*self.state.get() }, State::Signaled(_))) + self.state.lock(|cell| { + let state = cell.replace(State::None); + + let res = matches!(state, State::Signaled(_)); + + cell.set(state); + + res + }) } } diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 70318b78..980bc103 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -77,7 +77,7 @@ async fn main(_spawner: Spawner) { // Build the builder. let mut usb = builder.build(); - let remote_wakeup = Signal::new(); + let remote_wakeup: Signal<_> = Signal::new(); // Run the USB device. let usb_fut = async { From 85366661489c09fa8dec1375e9b9beee522e5e9f Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 24 Sep 2022 12:08:46 +0300 Subject: [PATCH 25/42] Remove default, reorder generic params --- embassy-sync/src/signal.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index 7c38637c..8cb832a2 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -1,7 +1,6 @@ //! A synchronization primitive for passing the latest value to a task. use core::cell::Cell; use core::future::{poll_fn, Future}; -use core::mem; use core::task::{Context, Poll, Waker}; use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; From c5ce02b30e488aade19f9f859425aa127d085b92 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 24 Sep 2022 12:08:46 +0300 Subject: [PATCH 26/42] Remove default, reorder generic params --- embassy-sync/src/signal.rs | 19 ++++++++++--------- examples/nrf/src/bin/usb_hid_keyboard.rs | 3 ++- examples/stm32h7/src/bin/signal.rs | 3 ++- examples/stm32wl/src/bin/subghz.rs | 3 ++- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index 8cb832a2..c3c10a8a 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -3,7 +3,7 @@ use core::cell::Cell; use core::future::{poll_fn, Future}; use core::task::{Context, Poll, Waker}; -use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; +use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; /// Single-slot signaling primitive. @@ -22,19 +22,20 @@ use crate::blocking_mutex::Mutex; /// /// ``` /// use embassy_sync::signal::Signal; +/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; /// /// enum SomeCommand { /// On, /// Off, /// } /// -/// static SOME_SIGNAL: Signal = Signal::new(); +/// static SOME_SIGNAL: Signal = Signal::new(); /// ``` -pub struct Signal +pub struct Signal where - R: RawMutex, + M: RawMutex, { - state: Mutex>>, + state: Mutex>>, } enum State { @@ -43,9 +44,9 @@ enum State { Signaled(T), } -impl Signal +impl Signal where - R: RawMutex, + M: RawMutex, { /// Create a new `Signal`. pub const fn new() -> Self { @@ -55,9 +56,9 @@ where } } -impl Signal +impl Signal where - R: RawMutex, + M: RawMutex, { /// Mark this Signal as signaled. pub fn signal(&self, val: T) { diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 980bc103..4eb7d37c 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -12,6 +12,7 @@ use embassy_futures::select::{select, Either}; use embassy_nrf::gpio::{Input, Pin, Pull}; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, DeviceStateHandler}; @@ -77,7 +78,7 @@ async fn main(_spawner: Spawner) { // Build the builder. let mut usb = builder.build(); - let remote_wakeup: Signal<_> = Signal::new(); + let remote_wakeup: Signal = Signal::new(); // Run the USB device. let usb_fut = async { diff --git a/examples/stm32h7/src/bin/signal.rs b/examples/stm32h7/src/bin/signal.rs index cc3e4e3c..6d7c168d 100644 --- a/examples/stm32h7/src/bin/signal.rs +++ b/examples/stm32h7/src/bin/signal.rs @@ -4,11 +4,12 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; -static SIGNAL: Signal = Signal::new(); +static SIGNAL: Signal = Signal::new(); #[embassy_executor::task] async fn my_sending_task() { diff --git a/examples/stm32wl/src/bin/subghz.rs b/examples/stm32wl/src/bin/subghz.rs index 3c60a8de..32c8b551 100644 --- a/examples/stm32wl/src/bin/subghz.rs +++ b/examples/stm32wl/src/bin/subghz.rs @@ -12,6 +12,7 @@ use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::interrupt; use embassy_stm32::interrupt::{Interrupt, InterruptExt}; use embassy_stm32::subghz::*; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; use {defmt_rtt as _, panic_probe as _}; @@ -64,7 +65,7 @@ async fn main(_spawner: Spawner) { let button = Input::new(p.PA0, Pull::Up); let mut pin = ExtiInput::new(button, p.EXTI0); - static IRQ_SIGNAL: Signal<()> = Signal::new(); + static IRQ_SIGNAL: Signal = Signal::new(); let radio_irq = interrupt::take!(SUBGHZ_RADIO); radio_irq.set_handler(|_| { IRQ_SIGNAL.signal(()); From 7f16b1cd23f53a429bf074e76254bcf592c0b9cf Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 06:01:18 +0200 Subject: [PATCH 27/42] Add blocking API to FirmwareUpdater, and allow for a split prepare/write api --- embassy-boot/boot/src/lib.rs | 186 +++++++++++++++++++++++++++++++++-- 1 file changed, 179 insertions(+), 7 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 96878ace..1c4d2d47 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -660,12 +660,6 @@ impl FirmwareUpdater { ) -> Result<(), F::Error> { assert!(data.len() >= F::ERASE_SIZE); - trace!( - "Writing firmware at offset 0x{:x} len {}", - self.dfu.from + offset, - data.len() - ); - flash .erase( (self.dfu.from + offset) as u32, @@ -679,7 +673,141 @@ impl FirmwareUpdater { self.dfu.from + offset + data.len() ); - let mut write_offset = self.dfu.from + offset; + FirmwareWriter(self) + .write_firmware(offset, data, flash, block_size) + .await?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning a `FirmwareWriter`. + /// + /// Using this instead of `write_firmware` allows for an optimized API in + /// exchange for added complexity. + pub fn prepare_update(&mut self, flash: &mut F) -> Result { + flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; + + trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + + Ok(FirmwareWriter(self)) + } + + // + // Blocking API + // + + /// Mark to trigger firmware swap on next boot. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub fn mark_updated_blocking(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic_blocking(aligned, SWAP_MAGIC, flash) + } + + /// Mark firmware boot successful and stop rollback on reset. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to. + pub fn mark_booted_blocking(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result<(), F::Error> { + assert_eq!(aligned.len(), F::WRITE_SIZE); + self.set_magic_blocking(aligned, BOOT_MAGIC, flash) + } + + fn set_magic_blocking( + &mut self, + aligned: &mut [u8], + magic: u8, + flash: &mut F, + ) -> Result<(), F::Error> { + flash.read(self.state.from as u32, aligned)?; + + if aligned.iter().any(|&b| b != magic) { + aligned.fill(0); + + flash.write(self.state.from as u32, aligned)?; + flash.erase(self.state.from as u32, self.state.to as u32)?; + + aligned.fill(magic); + flash.write(self.state.from as u32, aligned)?; + } + Ok(()) + } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub fn write_firmware_blocking( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), F::Error> { + assert!(data.len() >= F::ERASE_SIZE); + + flash.erase( + (self.dfu.from + offset) as u32, + (self.dfu.from + offset + data.len()) as u32, + )?; + + trace!( + "Erased from {} to {}", + self.dfu.from + offset, + self.dfu.from + offset + data.len() + ); + + FirmwareWriter(self).write_firmware_blocking(offset, data, flash, block_size)?; + + Ok(()) + } + + /// Prepare for an incoming DFU update by erasing the entire DFU area and + /// returning a `FirmwareWriter`. + /// + /// Using this instead of `write_firmware_blocking` allows for an optimized + /// API in exchange for added complexity. + pub fn prepare_update_blocking(&mut self, flash: &mut F) -> Result { + flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; + + trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); + + Ok(FirmwareWriter(self)) + } +} + +/// FirmwareWriter allows writing blocks to an already erased flash. +pub struct FirmwareWriter<'a>(&'a mut FirmwareUpdater); + +impl<'a> FirmwareWriter<'a> { + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub async fn write_firmware( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), F::Error> { + trace!( + "Writing firmware at offset 0x{:x} len {}", + self.0.dfu.from + offset, + data.len() + ); + + let mut write_offset = self.0.dfu.from + offset; for chunk in data.chunks(block_size) { trace!("Wrote chunk at {}: {:?}", write_offset, chunk); flash.write(write_offset as u32, chunk).await?; @@ -702,6 +830,50 @@ impl FirmwareUpdater { Ok(()) } + + /// Write data to a flash page. + /// + /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. + /// + /// # Safety + /// + /// Failing to meet alignment and size requirements may result in a panic. + pub fn write_firmware_blocking( + &mut self, + offset: usize, + data: &[u8], + flash: &mut F, + block_size: usize, + ) -> Result<(), F::Error> { + trace!( + "Writing firmware at offset 0x{:x} len {}", + self.0.dfu.from + offset, + data.len() + ); + + let mut write_offset = self.0.dfu.from + offset; + for chunk in data.chunks(block_size) { + trace!("Wrote chunk at {}: {:?}", write_offset, chunk); + flash.write(write_offset as u32, chunk)?; + write_offset += chunk.len(); + } + /* + trace!("Wrote data, reading back for verification"); + + let mut buf: [u8; 4096] = [0; 4096]; + let mut data_offset = 0; + let mut read_offset = self.dfu.from + offset; + for chunk in buf.chunks_mut(block_size) { + flash.read(read_offset as u32, chunk).await?; + trace!("Read chunk at {}: {:?}", read_offset, chunk); + assert_eq!(&data[data_offset..data_offset + block_size], chunk); + read_offset += chunk.len(); + data_offset += chunk.len(); + } + */ + + Ok(()) + } } #[cfg(test)] From b2a327a85884f822d011964bcd44b463b301467f Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 06:53:40 +0200 Subject: [PATCH 28/42] Add get_state helpers to allow self-testing before calling mark_booted --- embassy-boot/boot/src/lib.rs | 56 +++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 1c4d2d47..6f22d08e 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -604,6 +604,21 @@ impl FirmwareUpdater { self.dfu.len() } + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub async fn get_state(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result { + flash.read(self.state.from as u32, aligned).await?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + /// Mark to trigger firmware swap on next boot. /// /// # Safety @@ -673,8 +688,8 @@ impl FirmwareUpdater { self.dfu.from + offset + data.len() ); - FirmwareWriter(self) - .write_firmware(offset, data, flash, block_size) + FirmwareWriter(self.dfu) + .write_block(offset, data, flash, block_size) .await?; Ok(()) @@ -690,13 +705,28 @@ impl FirmwareUpdater { trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); - Ok(FirmwareWriter(self)) + Ok(FirmwareWriter(self.dfu)) } // // Blocking API // + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub fn get_state_blocking(&mut self, flash: &mut F, aligned: &mut [u8]) -> Result { + flash.read(self.state.from as u32, aligned)?; + + if !aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + /// Mark to trigger firmware swap on next boot. /// /// # Safety @@ -764,7 +794,7 @@ impl FirmwareUpdater { self.dfu.from + offset + data.len() ); - FirmwareWriter(self).write_firmware_blocking(offset, data, flash, block_size)?; + FirmwareWriter(self.dfu).write_block_blocking(offset, data, flash, block_size)?; Ok(()) } @@ -779,14 +809,14 @@ impl FirmwareUpdater { trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); - Ok(FirmwareWriter(self)) + Ok(FirmwareWriter(self.dfu)) } } /// FirmwareWriter allows writing blocks to an already erased flash. -pub struct FirmwareWriter<'a>(&'a mut FirmwareUpdater); +pub struct FirmwareWriter(Partition); -impl<'a> FirmwareWriter<'a> { +impl FirmwareWriter { /// Write data to a flash page. /// /// The buffer must follow alignment requirements of the target flash and a multiple of page size big. @@ -794,7 +824,7 @@ impl<'a> FirmwareWriter<'a> { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware( + pub async fn write_block( &mut self, offset: usize, data: &[u8], @@ -803,11 +833,11 @@ impl<'a> FirmwareWriter<'a> { ) -> Result<(), F::Error> { trace!( "Writing firmware at offset 0x{:x} len {}", - self.0.dfu.from + offset, + self.0.from + offset, data.len() ); - let mut write_offset = self.0.dfu.from + offset; + let mut write_offset = self.0.from + offset; for chunk in data.chunks(block_size) { trace!("Wrote chunk at {}: {:?}", write_offset, chunk); flash.write(write_offset as u32, chunk).await?; @@ -838,7 +868,7 @@ impl<'a> FirmwareWriter<'a> { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_firmware_blocking( + pub fn write_block_blocking( &mut self, offset: usize, data: &[u8], @@ -847,11 +877,11 @@ impl<'a> FirmwareWriter<'a> { ) -> Result<(), F::Error> { trace!( "Writing firmware at offset 0x{:x} len {}", - self.0.dfu.from + offset, + self.0.from + offset, data.len() ); - let mut write_offset = self.0.dfu.from + offset; + let mut write_offset = self.0.from + offset; for chunk in data.chunks(block_size) { trace!("Wrote chunk at {}: {:?}", write_offset, chunk); flash.write(write_offset as u32, chunk)?; From 6fa74b0c022c41c9ac6dd0b937ef402846cbdfae Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 10:36:21 +0200 Subject: [PATCH 29/42] make prepare_update async --- embassy-boot/boot/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 6f22d08e..8286601e 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -700,8 +700,8 @@ impl FirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - pub fn prepare_update(&mut self, flash: &mut F) -> Result { - flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32)?; + pub async fn prepare_update(&mut self, flash: &mut F) -> Result { + flash.erase((self.dfu.from) as u32, (self.dfu.to) as u32).await?; trace!("Erased from {} to {}", self.dfu.from, self.dfu.to); From fa7781c48d9e6687914e674aba63291225ee8d17 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 11:57:26 +0200 Subject: [PATCH 30/42] Add credits to rp-hal --- embassy-rp/src/intrinsics.rs | 3 +++ embassy-rp/src/rom_data.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index 9e6624cf..ac1f5480 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -1,5 +1,8 @@ #![macro_use] +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/intrinsics.rs + /// Generate a series of aliases for an intrinsic function. macro_rules! intrinsics_aliases { ( diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs index 93a3632a..757a2711 100644 --- a/embassy-rp/src/rom_data.rs +++ b/embassy-rp/src/rom_data.rs @@ -8,6 +8,9 @@ //! > functionality that would otherwise have to take up space in most user //! > binaries. +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/rom_data.rs + /// A bootrom function table code. pub type RomFnTableCode = [u8; 2]; From 7f7c14b7bce5b84eb27c8122535a96a6f0e5dd77 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 12:29:27 +0200 Subject: [PATCH 31/42] usb: split driver trait to separate crate. --- embassy-nrf/src/usb.rs | 17 +-- embassy-rp/src/usb.rs | 27 ++--- embassy-stm32/src/usb/usb.rs | 35 +++--- embassy-usb-driver/Cargo.toml | 16 +++ .../src/lib.rs | 107 +++++++++++++++++- embassy-usb/Cargo.toml | 4 + embassy-usb/src/builder.rs | 11 +- embassy-usb/src/control.rs | 5 +- embassy-usb/src/descriptor.rs | 7 +- embassy-usb/src/descriptor_reader.rs | 2 +- embassy-usb/src/lib.rs | 18 +-- embassy-usb/src/types.rs | 105 ----------------- 12 files changed, 189 insertions(+), 165 deletions(-) create mode 100644 embassy-usb-driver/Cargo.toml rename embassy-usb/src/driver.rs => embassy-usb-driver/src/lib.rs (72%) diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 0685d419..20510eb4 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs @@ -10,8 +10,9 @@ use cortex_m::peripheral::NVIC; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; pub use embassy_usb; -use embassy_usb::driver::{self, EndpointError, Event, Unsupported}; -use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; +use embassy_usb::driver::{ + self, Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, +}; use pac::usbd::RegisterBlock; use crate::interrupt::{Interrupt, InterruptExt}; @@ -243,7 +244,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> interval: u8, ) -> Result { let index = self.alloc_in.allocate(ep_type)?; - let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In); + let ep_addr = EndpointAddress::from_parts(index, Direction::In); Ok(Endpoint::new(EndpointInfo { addr: ep_addr, ep_type, @@ -259,7 +260,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> interval: u8, ) -> Result { let index = self.alloc_out.allocate(ep_type)?; - let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out); + let ep_addr = EndpointAddress::from_parts(index, Direction::Out); Ok(Endpoint::new(EndpointInfo { addr: ep_addr, ep_type, @@ -428,8 +429,8 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { let regs = T::regs(); let i = ep_addr.index(); match ep_addr.direction() { - UsbDirection::Out => regs.halted.epout[i].read().getstatus().is_halted(), - UsbDirection::In => regs.halted.epin[i].read().getstatus().is_halted(), + Direction::Out => regs.halted.epout[i].read().getstatus().is_halted(), + Direction::In => regs.halted.epin[i].read().getstatus().is_halted(), } } @@ -442,7 +443,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { debug!("endpoint_set_enabled {:?} {}", ep_addr, enabled); match ep_addr.direction() { - UsbDirection::In => { + Direction::In => { let mut was_enabled = false; regs.epinen.modify(|r, w| { let mut bits = r.bits(); @@ -466,7 +467,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> { In::waker(i).wake(); } - UsbDirection::Out => { + Direction::Out => { regs.epouten.modify(|r, w| { let mut bits = r.bits(); if enabled { diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index a7ec5fb7..ce473b21 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -7,8 +7,9 @@ use core::task::Poll; use atomic_polyfill::compiler_fence; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; -use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; -use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; +use embassy_usb::driver::{ + self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, +}; use crate::interrupt::{Interrupt, InterruptExt}; use crate::{pac, peripherals, Peripheral, RegExt}; @@ -204,8 +205,8 @@ impl<'d, T: Instance> Driver<'d, T> { ); let alloc = match D::dir() { - UsbDirection::Out => &mut self.ep_out, - UsbDirection::In => &mut self.ep_in, + Direction::Out => &mut self.ep_out, + Direction::In => &mut self.ep_in, }; let index = alloc.iter_mut().enumerate().find(|(i, ep)| { @@ -254,7 +255,7 @@ impl<'d, T: Instance> Driver<'d, T> { }; match D::dir() { - UsbDirection::Out => unsafe { + Direction::Out => unsafe { T::dpram().ep_out_control(index - 1).write(|w| { w.set_enable(false); w.set_buffer_address(addr); @@ -262,7 +263,7 @@ impl<'d, T: Instance> Driver<'d, T> { w.set_endpoint_type(ep_type_reg); }) }, - UsbDirection::In => unsafe { + Direction::In => unsafe { T::dpram().ep_in_control(index - 1).write(|w| { w.set_enable(false); w.set_buffer_address(addr); @@ -429,14 +430,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { let n = ep_addr.index(); match ep_addr.direction() { - UsbDirection::In => unsafe { + Direction::In => unsafe { T::dpram().ep_in_control(n - 1).modify(|w| w.set_enable(enabled)); T::dpram().ep_in_buffer_control(ep_addr.index()).write(|w| { w.set_pid(0, true); // first packet is DATA0, but PID is flipped before }); EP_IN_WAKERS[n].wake(); }, - UsbDirection::Out => unsafe { + Direction::Out => unsafe { T::dpram().ep_out_control(n - 1).modify(|w| w.set_enable(enabled)); T::dpram().ep_out_buffer_control(ep_addr.index()).write(|w| { @@ -474,14 +475,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } trait Dir { - fn dir() -> UsbDirection; + fn dir() -> Direction; fn waker(i: usize) -> &'static AtomicWaker; } pub enum In {} impl Dir for In { - fn dir() -> UsbDirection { - UsbDirection::In + fn dir() -> Direction { + Direction::In } #[inline] @@ -492,8 +493,8 @@ impl Dir for In { pub enum Out {} impl Dir for Out { - fn dir() -> UsbDirection { - UsbDirection::Out + fn dir() -> Direction { + Direction::Out } #[inline] diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index e5ee1181..39809a3e 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -9,8 +9,9 @@ use atomic_polyfill::{AtomicBool, AtomicU8}; use embassy_hal_common::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{block_for, Duration}; -use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; -use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; +use embassy_usb::driver::{ + self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, +}; use pac::common::{Reg, RW}; use pac::usb::vals::{EpType, Stat}; @@ -279,8 +280,8 @@ impl<'d, T: Instance> Driver<'d, T> { } let used = ep.used_out || ep.used_in; let used_dir = match D::dir() { - UsbDirection::Out => ep.used_out, - UsbDirection::In => ep.used_in, + Direction::Out => ep.used_out, + Direction::In => ep.used_in, }; !used || (ep.ep_type == ep_type && !used_dir) }); @@ -293,7 +294,7 @@ impl<'d, T: Instance> Driver<'d, T> { ep.ep_type = ep_type; let buf = match D::dir() { - UsbDirection::Out => { + Direction::Out => { assert!(!ep.used_out); ep.used_out = true; @@ -312,7 +313,7 @@ impl<'d, T: Instance> Driver<'d, T> { _phantom: PhantomData, } } - UsbDirection::In => { + Direction::In => { assert!(!ep.used_in); ep.used_in = true; @@ -504,7 +505,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { // This can race, so do a retry loop. let reg = T::regs().epr(ep_addr.index() as _); match ep_addr.direction() { - UsbDirection::In => { + Direction::In => { loop { let r = unsafe { reg.read() }; match r.stat_tx() { @@ -523,7 +524,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } EP_IN_WAKERS[ep_addr.index()].wake(); } - UsbDirection::Out => { + Direction::Out => { loop { let r = unsafe { reg.read() }; match r.stat_rx() { @@ -549,8 +550,8 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { let regs = T::regs(); let epr = unsafe { regs.epr(ep_addr.index() as _).read() }; match ep_addr.direction() { - UsbDirection::In => epr.stat_tx() == Stat::STALL, - UsbDirection::Out => epr.stat_rx() == Stat::STALL, + Direction::In => epr.stat_tx() == Stat::STALL, + Direction::Out => epr.stat_rx() == Stat::STALL, } } @@ -560,7 +561,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { let reg = T::regs().epr(ep_addr.index() as _); trace!("EPR before: {:04x}", unsafe { reg.read() }.0); match ep_addr.direction() { - UsbDirection::In => { + Direction::In => { loop { let want_stat = match enabled { false => Stat::DISABLED, @@ -576,7 +577,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } EP_IN_WAKERS[ep_addr.index()].wake(); } - UsbDirection::Out => { + Direction::Out => { loop { let want_stat = match enabled { false => Stat::DISABLED, @@ -616,14 +617,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { } trait Dir { - fn dir() -> UsbDirection; + fn dir() -> Direction; fn waker(i: usize) -> &'static AtomicWaker; } pub enum In {} impl Dir for In { - fn dir() -> UsbDirection { - UsbDirection::In + fn dir() -> Direction { + Direction::In } #[inline] @@ -634,8 +635,8 @@ impl Dir for In { pub enum Out {} impl Dir for Out { - fn dir() -> UsbDirection { - UsbDirection::Out + fn dir() -> Direction { + Direction::Out } #[inline] diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml new file mode 100644 index 00000000..b525df33 --- /dev/null +++ b/embassy-usb-driver/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "embassy-usb-driver" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-driver-v$VERSION/embassy-usb/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-driver/src/" +features = ["defmt"] +target = "thumbv7em-none-eabi" + +[dependencies] +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } \ No newline at end of file diff --git a/embassy-usb/src/driver.rs b/embassy-usb-driver/src/lib.rs similarity index 72% rename from embassy-usb/src/driver.rs rename to embassy-usb-driver/src/lib.rs index 7888f163..051190a4 100644 --- a/embassy-usb/src/driver.rs +++ b/embassy-usb-driver/src/lib.rs @@ -1,6 +1,111 @@ +#![no_std] + use core::future::Future; -use super::types::*; +/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from +/// the perspective of the host, which is backward for devices, but the standard directions are used +/// for consistency. +/// +/// The values of the enum also match the direction bit used in endpoint addresses and control +/// request types. +#[repr(u8)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Direction { + /// Host to device (OUT) + Out = 0x00, + /// Device to host (IN) + In = 0x80, +} + +impl From for Direction { + fn from(value: u8) -> Self { + unsafe { core::mem::transmute(value & 0x80) } + } +} + +/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the +/// transfer bmAttributes transfer type bits. +#[repr(u8)] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum EndpointType { + /// Control endpoint. Used for device management. Only the host can initiate requests. Usually + /// used only endpoint 0. + Control = 0b00, + /// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet. + Isochronous = 0b01, + /// Bulk endpoint. Used for large amounts of best-effort reliable data. + Bulk = 0b10, + /// Interrupt endpoint. Used for small amounts of time-critical reliable data. + Interrupt = 0b11, +} + +/// Type-safe endpoint address. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct EndpointAddress(u8); + +impl From for EndpointAddress { + #[inline] + fn from(addr: u8) -> EndpointAddress { + EndpointAddress(addr) + } +} + +impl From for u8 { + #[inline] + fn from(addr: EndpointAddress) -> u8 { + addr.0 + } +} + +impl EndpointAddress { + const INBITS: u8 = Direction::In as u8; + + /// Constructs a new EndpointAddress with the given index and direction. + #[inline] + pub fn from_parts(index: usize, dir: Direction) -> Self { + EndpointAddress(index as u8 | dir as u8) + } + + /// Gets the direction part of the address. + #[inline] + pub fn direction(&self) -> Direction { + if (self.0 & Self::INBITS) != 0 { + Direction::In + } else { + Direction::Out + } + } + + /// Returns true if the direction is IN, otherwise false. + #[inline] + pub fn is_in(&self) -> bool { + (self.0 & Self::INBITS) != 0 + } + + /// Returns true if the direction is OUT, otherwise false. + #[inline] + pub fn is_out(&self) -> bool { + (self.0 & Self::INBITS) == 0 + } + + /// Gets the index part of the endpoint address. + #[inline] + pub fn index(&self) -> usize { + (self.0 & !Self::INBITS) as usize + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct EndpointInfo { + pub addr: EndpointAddress, + pub ep_type: EndpointType, + pub max_packet_size: u16, + pub interval: u8, +} /// Driver for a specific USB peripheral. Implement this to add support for a new hardware /// platform. diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 8cad4d31..660ecc8c 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -9,8 +9,12 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb/s features = ["defmt"] target = "thumbv7em-none-eabi" +[features] +defmt = ["dep:defmt", "embassy-usb-driver/defmt"] + [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } +embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 6be88bc7..87a8333b 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -1,11 +1,10 @@ use heapless::Vec; -use super::control::ControlHandler; -use super::descriptor::{BosWriter, DescriptorWriter}; -use super::driver::{Driver, Endpoint}; -use super::types::*; -use super::{DeviceStateHandler, UsbDevice, MAX_INTERFACE_COUNT}; -use crate::{Interface, STRING_INDEX_CUSTOM_START}; +use crate::control::ControlHandler; +use crate::descriptor::{BosWriter, DescriptorWriter}; +use crate::driver::{Driver, Endpoint, EndpointType}; +use crate::types::*; +use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs index 3e5749a0..9e0dee88 100644 --- a/embassy-usb/src/control.rs +++ b/embassy-usb/src/control.rs @@ -1,7 +1,8 @@ //! USB control data types. use core::mem; -use super::types::*; +use crate::driver::Direction; +use crate::types::StringIndex; /// Control request type. #[repr(u8)] @@ -42,7 +43,7 @@ pub enum Recipient { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Request { /// Direction of the request. - pub direction: UsbDirection, + pub direction: Direction, /// Type of the request. pub request_type: RequestType, /// Recipient of the request. diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index b94a4b16..497f0319 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs @@ -1,6 +1,7 @@ -use super::builder::Config; -use super::types::*; -use super::CONFIGURATION_VALUE; +use crate::builder::Config; +use crate::driver::EndpointInfo; +use crate::types::*; +use crate::CONFIGURATION_VALUE; /// Standard descriptor types #[allow(missing_docs)] diff --git a/embassy-usb/src/descriptor_reader.rs b/embassy-usb/src/descriptor_reader.rs index 0a12b566..d64bcb73 100644 --- a/embassy-usb/src/descriptor_reader.rs +++ b/embassy-usb/src/descriptor_reader.rs @@ -1,5 +1,5 @@ use crate::descriptor::descriptor_type; -use crate::types::EndpointAddress; +use crate::driver::EndpointAddress; #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 6f58c953..e1a99cfa 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -4,23 +4,23 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +pub use embassy_usb_driver as driver; + mod builder; pub mod control; pub mod descriptor; mod descriptor_reader; -pub mod driver; pub mod types; use embassy_futures::select::{select, Either}; use heapless::Vec; -pub use self::builder::{Builder, Config}; -use self::control::*; -use self::descriptor::*; -use self::driver::{Bus, Driver, Event}; -use self::types::*; +pub use crate::builder::{Builder, Config}; +use crate::control::*; +use crate::descriptor::*; use crate::descriptor_reader::foreach_endpoint; -use crate::driver::ControlPipe; +use crate::driver::{Bus, ControlPipe, Direction, Driver, EndpointAddress, Event}; +use crate::types::*; /// The global state of the USB device. /// @@ -250,8 +250,8 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { trace!("control request: {:?}", req); match req.direction { - UsbDirection::In => self.handle_control_in(req).await, - UsbDirection::Out => self.handle_control_out(req).await, + Direction::In => self.handle_control_in(req).await, + Direction::Out => self.handle_control_out(req).await, } if self.inner.set_address_pending { diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs index b8717ffa..aeab063d 100644 --- a/embassy-usb/src/types.rs +++ b/embassy-usb/src/types.rs @@ -1,108 +1,3 @@ -/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from -/// the perspective of the host, which is backward for devices, but the standard directions are used -/// for consistency. -/// -/// The values of the enum also match the direction bit used in endpoint addresses and control -/// request types. -#[repr(u8)] -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum UsbDirection { - /// Host to device (OUT) - Out = 0x00, - /// Device to host (IN) - In = 0x80, -} - -impl From for UsbDirection { - fn from(value: u8) -> Self { - unsafe { core::mem::transmute(value & 0x80) } - } -} - -/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the -/// transfer bmAttributes transfer type bits. -#[repr(u8)] -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum EndpointType { - /// Control endpoint. Used for device management. Only the host can initiate requests. Usually - /// used only endpoint 0. - Control = 0b00, - /// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet. - Isochronous = 0b01, - /// Bulk endpoint. Used for large amounts of best-effort reliable data. - Bulk = 0b10, - /// Interrupt endpoint. Used for small amounts of time-critical reliable data. - Interrupt = 0b11, -} - -/// Type-safe endpoint address. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct EndpointAddress(u8); - -impl From for EndpointAddress { - #[inline] - fn from(addr: u8) -> EndpointAddress { - EndpointAddress(addr) - } -} - -impl From for u8 { - #[inline] - fn from(addr: EndpointAddress) -> u8 { - addr.0 - } -} - -impl EndpointAddress { - const INBITS: u8 = UsbDirection::In as u8; - - /// Constructs a new EndpointAddress with the given index and direction. - #[inline] - pub fn from_parts(index: usize, dir: UsbDirection) -> Self { - EndpointAddress(index as u8 | dir as u8) - } - - /// Gets the direction part of the address. - #[inline] - pub fn direction(&self) -> UsbDirection { - if (self.0 & Self::INBITS) != 0 { - UsbDirection::In - } else { - UsbDirection::Out - } - } - - /// Returns true if the direction is IN, otherwise false. - #[inline] - pub fn is_in(&self) -> bool { - (self.0 & Self::INBITS) != 0 - } - - /// Returns true if the direction is OUT, otherwise false. - #[inline] - pub fn is_out(&self) -> bool { - (self.0 & Self::INBITS) == 0 - } - - /// Gets the index part of the endpoint address. - #[inline] - pub fn index(&self) -> usize { - (self.0 & !Self::INBITS) as usize - } -} - -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct EndpointInfo { - pub addr: EndpointAddress, - pub ep_type: EndpointType, - pub max_packet_size: u16, - pub interval: u8, -} - /// A handle for a USB interface that contains its number. #[derive(Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] From f4f58249722bc656a13865e06535d208440c3e4a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 12:35:33 +0200 Subject: [PATCH 32/42] usb: do not allow converting Directon to/from u8 --- embassy-usb-driver/src/lib.rs | 11 ++--------- embassy-usb/src/control.rs | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 051190a4..fc29786f 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -8,20 +8,13 @@ use core::future::Future; /// /// The values of the enum also match the direction bit used in endpoint addresses and control /// request types. -#[repr(u8)] #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Direction { /// Host to device (OUT) - Out = 0x00, + Out, /// Device to host (IN) - In = 0x80, -} - -impl From for Direction { - fn from(value: u8) -> Self { - unsafe { core::mem::transmute(value & 0x80) } - } + In, } /// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs index 9e0dee88..d6d0c656 100644 --- a/embassy-usb/src/control.rs +++ b/embassy-usb/src/control.rs @@ -106,7 +106,7 @@ impl Request { let recipient = rt & 0b11111; Request { - direction: rt.into(), + direction: if rt & 0x80 == 0 { Direction::Out } else { Direction::In }, request_type: unsafe { mem::transmute((rt >> 5) & 0b11) }, recipient: if recipient <= 3 { unsafe { mem::transmute(recipient) } From f27a47a37b59bf3b9079f4d4d5f43caf7b7872f8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 13:00:21 +0200 Subject: [PATCH 33/42] usb: move classes into the `embassy-usb` crate. --- embassy-usb-hid/Cargo.toml | 24 -- embassy-usb-hid/src/fmt.rs | 225 ------------------ embassy-usb-ncm/Cargo.toml | 17 -- embassy-usb-ncm/src/fmt.rs | 225 ------------------ embassy-usb-serial/Cargo.toml | 17 -- embassy-usb-serial/src/fmt.rs | 225 ------------------ embassy-usb/Cargo.toml | 9 +- .../src/class/cdc_acm.rs | 15 +- .../src/class/cdc_ncm.rs | 13 +- .../lib.rs => embassy-usb/src/class/hid.rs | 23 +- embassy-usb/src/class/mod.rs | 3 + embassy-usb/src/lib.rs | 1 + examples/nrf/Cargo.toml | 5 +- examples/nrf/src/bin/usb_ethernet.rs | 2 +- examples/nrf/src/bin/usb_hid_keyboard.rs | 4 +- examples/nrf/src/bin/usb_hid_mouse.rs | 4 +- examples/nrf/src/bin/usb_serial.rs | 2 +- examples/nrf/src/bin/usb_serial_multitask.rs | 2 +- examples/rp/Cargo.toml | 2 - examples/rp/src/bin/usb_ethernet.rs | 2 +- examples/rp/src/bin/usb_serial.rs | 2 +- examples/stm32f1/Cargo.toml | 1 - examples/stm32f1/src/bin/usb_serial.rs | 2 +- examples/stm32f3/Cargo.toml | 2 - examples/stm32f3/src/bin/usb_serial.rs | 2 +- examples/stm32l5/Cargo.toml | 3 - examples/stm32l5/src/bin/usb_ethernet.rs | 2 +- examples/stm32l5/src/bin/usb_hid_mouse.rs | 4 +- examples/stm32l5/src/bin/usb_serial.rs | 2 +- 29 files changed, 45 insertions(+), 795 deletions(-) delete mode 100644 embassy-usb-hid/Cargo.toml delete mode 100644 embassy-usb-hid/src/fmt.rs delete mode 100644 embassy-usb-ncm/Cargo.toml delete mode 100644 embassy-usb-ncm/src/fmt.rs delete mode 100644 embassy-usb-serial/Cargo.toml delete mode 100644 embassy-usb-serial/src/fmt.rs rename embassy-usb-serial/src/lib.rs => embassy-usb/src/class/cdc_acm.rs (96%) rename embassy-usb-ncm/src/lib.rs => embassy-usb/src/class/cdc_ncm.rs (97%) rename embassy-usb-hid/src/lib.rs => embassy-usb/src/class/hid.rs (96%) create mode 100644 embassy-usb/src/class/mod.rs diff --git a/embassy-usb-hid/Cargo.toml b/embassy-usb-hid/Cargo.toml deleted file mode 100644 index 2f7733dc..00000000 --- a/embassy-usb-hid/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "embassy-usb-hid" -version = "0.1.0" -edition = "2021" - -[package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-hid-v$VERSION/embassy-usb-hid/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-hid/src/" -features = ["defmt"] -target = "thumbv7em-none-eabi" - -[features] -default = ["usbd-hid"] -usbd-hid = ["dep:usbd-hid", "ssmarshal"] - -[dependencies] -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embassy-usb = { version = "0.1.0", path = "../embassy-usb" } - -defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } -usbd-hid = { version = "0.6.0", optional = true } -ssmarshal = { version = "1.0", default-features = false, optional = true } -futures-util = { version = "0.3.21", default-features = false } diff --git a/embassy-usb-hid/src/fmt.rs b/embassy-usb-hid/src/fmt.rs deleted file mode 100644 index 06697081..00000000 --- a/embassy-usb-hid/src/fmt.rs +++ /dev/null @@ -1,225 +0,0 @@ -#![macro_use] -#![allow(unused_macros)] - -#[cfg(all(feature = "defmt", feature = "log"))] -compile_error!("You may not enable both `defmt` and `log` features."); - -macro_rules! assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert!($($x)*); - } - }; -} - -macro_rules! assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_eq!($($x)*); - } - }; -} - -macro_rules! assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_ne!($($x)*); - } - }; -} - -macro_rules! debug_assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert!($($x)*); - } - }; -} - -macro_rules! debug_assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_eq!($($x)*); - } - }; -} - -macro_rules! debug_assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_ne!($($x)*); - } - }; -} - -macro_rules! todo { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::todo!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::todo!($($x)*); - } - }; -} - -macro_rules! unreachable { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } - }; -} - -macro_rules! panic { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::panic!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::panic!($($x)*); - } - }; -} - -macro_rules! trace { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::trace!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::trace!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! debug { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::debug!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::debug!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! info { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::info!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::info!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! warn { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::warn!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::warn!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! error { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::error!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::error!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -#[cfg(feature = "defmt")] -macro_rules! unwrap { - ($($x:tt)*) => { - ::defmt::unwrap!($($x)*) - }; -} - -#[cfg(not(feature = "defmt"))] -macro_rules! unwrap { - ($arg:expr) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); - } - } - }; - ($arg:expr, $($msg:expr),+ $(,)? ) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); - } - } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct NoneError; - -pub trait Try { - type Ok; - type Error; - fn into_result(self) -> Result; -} - -impl Try for Option { - type Ok = T; - type Error = NoneError; - - #[inline] - fn into_result(self) -> Result { - self.ok_or(NoneError) - } -} - -impl Try for Result { - type Ok = T; - type Error = E; - - #[inline] - fn into_result(self) -> Self { - self - } -} diff --git a/embassy-usb-ncm/Cargo.toml b/embassy-usb-ncm/Cargo.toml deleted file mode 100644 index 15d3db96..00000000 --- a/embassy-usb-ncm/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "embassy-usb-ncm" -version = "0.1.0" -edition = "2021" - -[package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-ncm-v$VERSION/embassy-usb-ncm/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-ncm/src/" -features = ["defmt"] -target = "thumbv7em-none-eabi" - -[dependencies] -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embassy-usb = { version = "0.1.0", path = "../embassy-usb" } - -defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } diff --git a/embassy-usb-ncm/src/fmt.rs b/embassy-usb-ncm/src/fmt.rs deleted file mode 100644 index 06697081..00000000 --- a/embassy-usb-ncm/src/fmt.rs +++ /dev/null @@ -1,225 +0,0 @@ -#![macro_use] -#![allow(unused_macros)] - -#[cfg(all(feature = "defmt", feature = "log"))] -compile_error!("You may not enable both `defmt` and `log` features."); - -macro_rules! assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert!($($x)*); - } - }; -} - -macro_rules! assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_eq!($($x)*); - } - }; -} - -macro_rules! assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_ne!($($x)*); - } - }; -} - -macro_rules! debug_assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert!($($x)*); - } - }; -} - -macro_rules! debug_assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_eq!($($x)*); - } - }; -} - -macro_rules! debug_assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_ne!($($x)*); - } - }; -} - -macro_rules! todo { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::todo!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::todo!($($x)*); - } - }; -} - -macro_rules! unreachable { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } - }; -} - -macro_rules! panic { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::panic!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::panic!($($x)*); - } - }; -} - -macro_rules! trace { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::trace!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::trace!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! debug { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::debug!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::debug!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! info { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::info!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::info!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! warn { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::warn!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::warn!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! error { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::error!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::error!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -#[cfg(feature = "defmt")] -macro_rules! unwrap { - ($($x:tt)*) => { - ::defmt::unwrap!($($x)*) - }; -} - -#[cfg(not(feature = "defmt"))] -macro_rules! unwrap { - ($arg:expr) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); - } - } - }; - ($arg:expr, $($msg:expr),+ $(,)? ) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); - } - } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct NoneError; - -pub trait Try { - type Ok; - type Error; - fn into_result(self) -> Result; -} - -impl Try for Option { - type Ok = T; - type Error = NoneError; - - #[inline] - fn into_result(self) -> Result { - self.ok_or(NoneError) - } -} - -impl Try for Result { - type Ok = T; - type Error = E; - - #[inline] - fn into_result(self) -> Self { - self - } -} diff --git a/embassy-usb-serial/Cargo.toml b/embassy-usb-serial/Cargo.toml deleted file mode 100644 index 9788588e..00000000 --- a/embassy-usb-serial/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "embassy-usb-serial" -version = "0.1.0" -edition = "2021" - -[package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-serial-v$VERSION/embassy-usb-serial/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-serial/src/" -features = ["defmt"] -target = "thumbv7em-none-eabi" - -[dependencies] -embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embassy-usb = { version = "0.1.0", path = "../embassy-usb" } - -defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } diff --git a/embassy-usb-serial/src/fmt.rs b/embassy-usb-serial/src/fmt.rs deleted file mode 100644 index 06697081..00000000 --- a/embassy-usb-serial/src/fmt.rs +++ /dev/null @@ -1,225 +0,0 @@ -#![macro_use] -#![allow(unused_macros)] - -#[cfg(all(feature = "defmt", feature = "log"))] -compile_error!("You may not enable both `defmt` and `log` features."); - -macro_rules! assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert!($($x)*); - } - }; -} - -macro_rules! assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_eq!($($x)*); - } - }; -} - -macro_rules! assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::assert_ne!($($x)*); - } - }; -} - -macro_rules! debug_assert { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert!($($x)*); - } - }; -} - -macro_rules! debug_assert_eq { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_eq!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_eq!($($x)*); - } - }; -} - -macro_rules! debug_assert_ne { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::debug_assert_ne!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::debug_assert_ne!($($x)*); - } - }; -} - -macro_rules! todo { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::todo!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::todo!($($x)*); - } - }; -} - -macro_rules! unreachable { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } - }; -} - -macro_rules! panic { - ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::panic!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::panic!($($x)*); - } - }; -} - -macro_rules! trace { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::trace!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::trace!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! debug { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::debug!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::debug!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! info { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::info!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::info!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! warn { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::warn!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::warn!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -macro_rules! error { - ($s:literal $(, $x:expr)* $(,)?) => { - { - #[cfg(feature = "log")] - ::log::error!($s $(, $x)*); - #[cfg(feature = "defmt")] - ::defmt::error!($s $(, $x)*); - #[cfg(not(any(feature = "log", feature="defmt")))] - let _ = ($( & $x ),*); - } - }; -} - -#[cfg(feature = "defmt")] -macro_rules! unwrap { - ($($x:tt)*) => { - ::defmt::unwrap!($($x)*) - }; -} - -#[cfg(not(feature = "defmt"))] -macro_rules! unwrap { - ($arg:expr) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); - } - } - }; - ($arg:expr, $($msg:expr),+ $(,)? ) => { - match $crate::fmt::Try::into_result($arg) { - ::core::result::Result::Ok(t) => t, - ::core::result::Result::Err(e) => { - ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); - } - } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct NoneError; - -pub trait Try { - type Ok; - type Error; - fn into_result(self) -> Result; -} - -impl Try for Option { - type Ok = T; - type Error = NoneError; - - #[inline] - fn into_result(self) -> Result { - self.ok_or(NoneError) - } -} - -impl Try for Result { - type Ok = T; - type Error = E; - - #[inline] - fn into_result(self) -> Self { - self - } -} diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 660ecc8c..aad54dba 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -11,11 +11,18 @@ target = "thumbv7em-none-eabi" [features] defmt = ["dep:defmt", "embassy-usb-driver/defmt"] +usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] +default = ["usbd-hid"] [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } +embassy-sync = { version = "0.1.0", path = "../embassy-sync" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -heapless = "0.7.10" \ No newline at end of file +heapless = "0.7.10" + +# for HID +usbd-hid = { version = "0.6.0", optional = true } +ssmarshal = { version = "1.0", default-features = false, optional = true } diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb/src/class/cdc_acm.rs similarity index 96% rename from embassy-usb-serial/src/lib.rs rename to embassy-usb/src/class/cdc_acm.rs index 15c2bb0a..09bb1cc8 100644 --- a/embassy-usb-serial/src/lib.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -1,18 +1,13 @@ -#![no_std] -#![feature(type_alias_impl_trait)] - -// This mod MUST go first, so that the others see its macros. -pub(crate) mod fmt; - use core::cell::Cell; use core::mem::{self, MaybeUninit}; use core::sync::atomic::{AtomicBool, Ordering}; use embassy_sync::blocking_mutex::CriticalSectionMutex; -use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request}; -use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; -use embassy_usb::types::*; -use embassy_usb::Builder; + +use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; +use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; +use crate::types::*; +use crate::Builder; /// This should be used as `device_class` when building the `UsbDevice`. pub const USB_CLASS_CDC: u8 = 0x02; diff --git a/embassy-usb-ncm/src/lib.rs b/embassy-usb/src/class/cdc_ncm.rs similarity index 97% rename from embassy-usb-ncm/src/lib.rs rename to embassy-usb/src/class/cdc_ncm.rs index e796af28..a39b87e9 100644 --- a/embassy-usb-ncm/src/lib.rs +++ b/embassy-usb/src/class/cdc_ncm.rs @@ -1,15 +1,10 @@ -#![no_std] - -// This mod MUST go first, so that the others see its macros. -pub(crate) mod fmt; - use core::intrinsics::copy_nonoverlapping; use core::mem::{size_of, MaybeUninit}; -use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request}; -use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; -use embassy_usb::types::*; -use embassy_usb::Builder; +use crate::control::{self, ControlHandler, InResponse, OutResponse, Request}; +use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; +use crate::types::*; +use crate::Builder; /// This should be used as `device_class` when building the `UsbDevice`. pub const USB_CLASS_CDC: u8 = 0x02; diff --git a/embassy-usb-hid/src/lib.rs b/embassy-usb/src/class/hid.rs similarity index 96% rename from embassy-usb-hid/src/lib.rs rename to embassy-usb/src/class/hid.rs index 8b181aec..4d1fa995 100644 --- a/embassy-usb-hid/src/lib.rs +++ b/embassy-usb/src/class/hid.rs @@ -1,23 +1,16 @@ -#![no_std] -#![feature(type_alias_impl_trait)] - -//! Implements HID functionality for a usb-device device. - -// This mod MUST go first, so that the others see its macros. -pub(crate) mod fmt; - use core::mem::MaybeUninit; use core::ops::Range; use core::sync::atomic::{AtomicUsize, Ordering}; -use embassy_usb::control::{ControlHandler, InResponse, OutResponse, Request, RequestType}; -use embassy_usb::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; -use embassy_usb::Builder; #[cfg(feature = "usbd-hid")] use ssmarshal::serialize; #[cfg(feature = "usbd-hid")] use usbd_hid::descriptor::AsInputReport; +use crate::control::{ControlHandler, InResponse, OutResponse, Request, RequestType}; +use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; +use crate::Builder; + const USB_CLASS_HID: u8 = 0x03; const USB_SUBCLASS_NONE: u8 = 0x00; const USB_PROTOCOL_NONE: u8 = 0x00; @@ -204,9 +197,9 @@ pub enum ReadError { Sync(Range), } -impl From for ReadError { - fn from(val: embassy_usb::driver::EndpointError) -> Self { - use embassy_usb::driver::EndpointError::*; +impl From for ReadError { + fn from(val: EndpointError) -> Self { + use EndpointError::*; match val { BufferOverflow => ReadError::BufferOverflow, Disabled => ReadError::Disabled, @@ -437,7 +430,7 @@ impl<'d> ControlHandler for Control<'d> { } } - fn control_out(&mut self, req: embassy_usb::control::Request, data: &[u8]) -> OutResponse { + fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse { trace!("HID control_out {:?} {=[u8]:x}", req, data); if let RequestType::Class = req.request_type { match req.request { diff --git a/embassy-usb/src/class/mod.rs b/embassy-usb/src/class/mod.rs new file mode 100644 index 00000000..af27577a --- /dev/null +++ b/embassy-usb/src/class/mod.rs @@ -0,0 +1,3 @@ +pub mod cdc_acm; +pub mod cdc_ncm; +pub mod hid; diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index e1a99cfa..661b8411 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -7,6 +7,7 @@ pub(crate) mod fmt; pub use embassy_usb_driver as driver; mod builder; +pub mod class; pub mod control; pub mod descriptor; mod descriptor_reader; diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index dbc659cd..a5d340c6 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" [features] default = ["nightly"] -nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net"] +nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net"] [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } @@ -15,9 +15,6 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } -embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } -embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"], optional = true } embedded-io = "0.3.0" defmt = "0.3" diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index 33ca380f..de93a2b4 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs @@ -15,8 +15,8 @@ use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, peripherals}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; use embassy_usb::{Builder, Config, UsbDevice}; -use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs index 4eb7d37c..76e19871 100644 --- a/examples/nrf/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs @@ -14,9 +14,9 @@ use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; +use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, DeviceStateHandler}; -use embassy_usb_hid::{HidReaderWriter, ReportId, RequestHandler, State}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; @@ -67,7 +67,7 @@ async fn main(_spawner: Spawner) { ); // Create classes on the builder. - let config = embassy_usb_hid::Config { + let config = embassy_usb::class::hid::Config { report_descriptor: KeyboardReport::desc(), request_handler: Some(&request_handler), poll_ms: 60, diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs index 65fbda1c..4916a38d 100644 --- a/examples/nrf/src/bin/usb_hid_mouse.rs +++ b/examples/nrf/src/bin/usb_hid_mouse.rs @@ -10,9 +10,9 @@ use embassy_futures::join::join; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac}; use embassy_time::{Duration, Timer}; +use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config}; -use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; @@ -59,7 +59,7 @@ async fn main(_spawner: Spawner) { ); // Create classes on the builder. - let config = embassy_usb_hid::Config { + let config = embassy_usb::class::hid::Config { report_descriptor: MouseReport::desc(), request_handler: Some(&request_handler), poll_ms: 60, diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs index a740b4e0..7c9c4184 100644 --- a/examples/nrf/src/bin/usb_serial.rs +++ b/examples/nrf/src/bin/usb_serial.rs @@ -9,9 +9,9 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; use embassy_nrf::{interrupt, pac}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf/src/bin/usb_serial_multitask.rs index c646c0bb..93efc2fe 100644 --- a/examples/nrf/src/bin/usb_serial_multitask.rs +++ b/examples/nrf/src/bin/usb_serial_multitask.rs @@ -8,9 +8,9 @@ use defmt::{info, panic, unwrap}; use embassy_executor::Spawner; use embassy_nrf::usb::{Driver, PowerUsb}; use embassy_nrf::{interrupt, pac, peripherals}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config, UsbDevice}; -use embassy_usb_serial::{CdcAcmClass, State}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 24c3cdd6..3c8f923e 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -10,9 +10,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } -embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 166ffe17..1057fe7f 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -13,8 +13,8 @@ use embassy_rp::usb::Driver; use embassy_rp::{interrupt, peripherals}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; use embassy_usb::{Builder, Config, UsbDevice}; -use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; use embedded_io::asynch::Write; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index bf92a163..b7d6493b 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -7,9 +7,9 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_rp::interrupt; use embassy_rp::usb::{Driver, Instance}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 895e043d..e6553789 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -9,7 +9,6 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index a14e728b..ad92cdeb 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -10,9 +10,9 @@ use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; use embassy_time::{Duration, Timer}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 27f5c260..f5b0b880 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -9,8 +9,6 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } -embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index b9fd20e2..f6d27c86 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -10,9 +10,9 @@ use embassy_stm32::time::mhz; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; use embassy_time::{Duration, Timer}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 05945f6b..9ebab647 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -11,9 +11,6 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } -embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } -embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.6.0" diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index c96a83ea..4f36d3f5 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -15,8 +15,8 @@ use embassy_stm32::usb::Driver; use embassy_stm32::{interrupt, Config}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; +use embassy_usb::class::cdc_ncm::{CdcNcmClass, Receiver, Sender, State}; use embassy_usb::{Builder, UsbDevice}; -use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; use embedded_io::asynch::Write; use rand_core::RngCore; use static_cell::StaticCell; diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index fa92ceae..d38ed749 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -9,9 +9,9 @@ use embassy_stm32::rcc::*; use embassy_stm32::usb::Driver; use embassy_stm32::{interrupt, Config}; use embassy_time::{Duration, Timer}; +use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; -use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; @@ -55,7 +55,7 @@ async fn main(_spawner: Spawner) { ); // Create classes on the builder. - let config = embassy_usb_hid::Config { + let config = embassy_usb::class::hid::Config { report_descriptor: MouseReport::desc(), request_handler: Some(&request_handler), poll_ms: 60, diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index 7484dc83..7562a4e9 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -8,9 +8,9 @@ use embassy_futures::join::join; use embassy_stm32::rcc::*; use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::{interrupt, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; use embassy_usb::Builder; -use embassy_usb_serial::{CdcAcmClass, State}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] From 1e95c4fcfff3edf87a6c4cdacb228cb08e6d4e50 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 19:52:55 +0200 Subject: [PATCH 34/42] rp: Disable intrinsics by default. --- ci.sh | 1 + embassy-rp/Cargo.toml | 2 +- embassy-rp/src/intrinsics.rs | 18 +++++++++--------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ci.sh b/ci.sh index ae1b4428..69440ec3 100755 --- a/ci.sh +++ b/ci.sh @@ -58,6 +58,7 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,log \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \ + --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits \ diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index df0af8df..c43fd7e7 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -23,7 +23,7 @@ unstable-pac = [] time-driver = [] rom-func-cache = [] -disable-intrinsics = [] +intrinsics = [] rom-v2-intrinsics = [] # Enable nightly-only features diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index ac1f5480..3e75fb7f 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -17,7 +17,7 @@ macro_rules! intrinsics_aliases { $alias:ident $($rest:ident)* ) => { - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] intrinsics! { extern $abi fn $alias( $($argname: $ty),* ) -> $ret { $name($($argname),*) @@ -35,7 +35,7 @@ macro_rules! intrinsics_aliases { $alias:ident $($rest:ident)* ) => { - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] intrinsics! { unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret { $name($($argname),*) @@ -55,7 +55,7 @@ macro_rules! intrinsics_aliases { /// is to abstract anything special that needs to be done to override an /// intrinsic function. Intrinsic generation is disabled for non-ARM targets /// so things like CI and docs generation do not have problems. Additionally -/// they can be disabled with the crate feature `disable-intrinsics` for +/// they can be disabled by disabling the crate feature `intrinsics` for /// testing or comparing performance. /// /// Like the compiler-builtins macro, it accepts a series of functions that @@ -214,13 +214,13 @@ macro_rules! intrinsics { $($rest:tt)* ) => { - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] $(#[$($attr)*])* extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] mod $name { #[no_mangle] $(#[$($attr)*])* @@ -231,7 +231,7 @@ macro_rules! intrinsics { // Not exported, but defined so the actual implementation is // considered used - #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[cfg(not(all(target_arch = "arm", feature = "intrinsics")))] #[allow(dead_code)] fn $name( $($argname: $ty),* ) -> $ret { $($body)* @@ -248,13 +248,13 @@ macro_rules! intrinsics { $($rest:tt)* ) => { - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] $(#[$($attr)*])* unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { $($body)* } - #[cfg(all(target_arch = "arm", not(feature = "disable-intrinsics")))] + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] mod $name { #[no_mangle] $(#[$($attr)*])* @@ -265,7 +265,7 @@ macro_rules! intrinsics { // Not exported, but defined so the actual implementation is // considered used - #[cfg(not(all(target_arch = "arm", not(feature = "disable-intrinsics"))))] + #[cfg(not(all(target_arch = "arm", feature = "intrinsics")))] #[allow(dead_code)] unsafe fn $name( $($argname: $ty),* ) -> $ret { $($body)* From 75e93cc142c1c017e94dd28f6f04d3a4cb1ac67b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 20:33:20 +0200 Subject: [PATCH 35/42] rp: enable time-driver in Cargo.toml instead of ci.sh --- ci.sh | 2 +- tests/rp/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci.sh b/ci.sh index 69440ec3..cd1c0786 100755 --- a/ci.sh +++ b/ci.sh @@ -121,7 +121,7 @@ cargo batch \ --- 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 stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ --- 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/rp/Cargo.toml --target thumbv6m-none-eabi --features embassy-rp/time-driver --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_EXTRA diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 11ecb916..7e2717dd 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -7,7 +7,7 @@ version = "0.1.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" From ee76831f93e792757bf43136be712c343c4d5336 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 26 Aug 2022 09:05:12 +0200 Subject: [PATCH 36/42] Add BufferedUart implementation, and feature-guard time-driver initialization, to free up TIMER peripheral if not used with embassy executor --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/uart/buffered.rs | 286 ++++++++++++++++++++++++ embassy-rp/src/{uart.rs => uart/mod.rs} | 84 ++++++- 3 files changed, 369 insertions(+), 2 deletions(-) create mode 100644 embassy-rp/src/uart/buffered.rs rename embassy-rp/src/{uart.rs => uart/mod.rs} (87%) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index c43fd7e7..211b6a40 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -52,6 +52,7 @@ cortex-m = "0.7.6" critical-section = "1.1" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } chrono = { version = "0.4", default-features = false, optional = true } +embedded-io = { version = "0.3.0", features = ["async"], optional = true } rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs new file mode 100644 index 00000000..c31af801 --- /dev/null +++ b/embassy-rp/src/uart/buffered.rs @@ -0,0 +1,286 @@ +use core::future::Future; +use core::task::Poll; + +use atomic_polyfill::{compiler_fence, Ordering}; +use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; +use embassy_hal_common::ring_buffer::RingBuffer; +use embassy_sync::waitqueue::WakerRegistration; +use futures::future::poll_fn; + +use super::*; + +pub struct State<'d, T: Instance>(StateStorage>); +impl<'d, T: Instance> State<'d, T> { + pub fn new() -> Self { + Self(StateStorage::new()) + } +} + +struct StateInner<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + + rx_waker: WakerRegistration, + rx: RingBuffer<'d>, + + tx_waker: WakerRegistration, + tx: RingBuffer<'d>, +} + +unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {} + +pub struct BufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, StateInner<'d, T>>, +} + +impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} + +impl<'d, T: Instance> BufferedUart<'d, T> { + pub fn new( + state: &'d mut State<'d, T>, + _uart: Uart<'d, T, M>, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + ) -> BufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner { + phantom: PhantomData, + tx: RingBuffer::new(tx_buffer), + tx_waker: WakerRegistration::new(), + + rx: RingBuffer::new(rx_buffer), + rx_waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> StateInner<'d, T> +where + Self: 'd, +{ + fn on_rx(&mut self) { + let r = T::regs(); + unsafe { + let ris = r.uartris().read(); + // Clear interrupt flags + r.uarticr().write(|w| { + w.set_rxic(true); + w.set_rtic(true); + }); + + if ris.rxris() { + if ris.peris() { + warn!("Parity error"); + } + if ris.feris() { + warn!("Framing error"); + } + if ris.beris() { + warn!("Break error"); + } + if ris.oeris() { + warn!("Overrun error"); + } + + let buf = self.rx.push_buf(); + if !buf.is_empty() { + buf[0] = r.uartdr().read().data(); + self.rx.push(1); + } else { + warn!("RX buffer full, discard received byte"); + } + + if self.rx.is_full() { + self.rx_waker.wake(); + } + } + + if ris.rtris() { + self.rx_waker.wake(); + }; + } + } + + fn on_tx(&mut self) { + let r = T::regs(); + unsafe { + let ris = r.uartris().read(); + // Clear interrupt flags + r.uarticr().write(|w| { + w.set_rtic(true); + }); + + if ris.txris() { + let buf = self.tx.pop_buf(); + if !buf.is_empty() { + r.uartimsc().modify(|w| { + w.set_txim(true); + }); + r.uartdr().write(|w| w.set_data(buf[0].into())); + self.tx.pop(1); + self.tx_waker.wake(); + } else { + // Disable interrupt until we have something to transmit again + r.uartimsc().modify(|w| { + w.set_txim(false); + }); + } + } + } + } +} + +impl<'d, T: Instance> PeripheralState for StateInner<'d, T> +where + Self: 'd, +{ + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { + self.on_rx(); + self.on_tx(); + } +} + +impl embedded_io::Error for Error { + fn kind(&self) -> embedded_io::ErrorKind { + embedded_io::ErrorKind::Other + } +} + +impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { + type Error = Error; +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { + type ReadFuture<'a> = impl Future> + where + Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + poll_fn(move |cx| { + let mut do_pend = false; + let res = self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let data = state.rx.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if state.rx.is_full() { + do_pend = true; + } + state.rx.pop(len); + + return Poll::Ready(Ok(len)); + } + + state.rx_waker.register(cx.waker()); + Poll::Pending + }); + + if do_pend { + self.inner.pend(); + } + + res + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { + type FillBufFuture<'a> = impl Future> + where + Self: 'a; + + fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let buf = state.rx.pop_buf(); + if !buf.is_empty() { + let buf: &[u8] = buf; + // Safety: buffer lives as long as uart + let buf: &[u8] = unsafe { core::mem::transmute(buf) }; + return Poll::Ready(Ok(buf)); + } + + state.rx_waker.register(cx.waker()); + Poll::>::Pending + }) + }) + } + + fn consume(&mut self, amt: usize) { + let signal = self.inner.with(|state| { + let full = state.rx.is_full(); + state.rx.pop(amt); + full + }); + if signal { + self.inner.pend(); + } + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { + type WriteFuture<'a> = impl Future> + where + Self: 'a; + + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + poll_fn(move |cx| { + let (poll, empty) = self.inner.with(|state| { + let empty = state.tx.is_empty(); + let tx_buf = state.tx.push_buf(); + if tx_buf.is_empty() { + state.tx_waker.register(cx.waker()); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.tx.push(n); + + (Poll::Ready(Ok(n)), empty) + }); + if empty { + self.inner.pend(); + } + poll + }) + } + + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + if !state.tx.is_empty() { + state.tx_waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + } +} diff --git a/embassy-rp/src/uart.rs b/embassy-rp/src/uart/mod.rs similarity index 87% rename from embassy-rp/src/uart.rs rename to embassy-rp/src/uart/mod.rs index 987b716b..3b71d87b 100644 --- a/embassy-rp/src/uart.rs +++ b/embassy-rp/src/uart/mod.rs @@ -475,6 +475,76 @@ mod eh1 { impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartRx<'d, T, M> { type Error = Error; } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for UartRx<'d, T, M> { + fn read(&mut self) -> nb::Result { + let r = T::regs(); + unsafe { + let dr = r.uartdr().read(); + + if dr.oe() { + Err(nb::Error::Other(Error::Overrun)) + } else if dr.be() { + Err(nb::Error::Other(Error::Break)) + } else if dr.pe() { + Err(nb::Error::Other(Error::Parity)) + } else if dr.fe() { + Err(nb::Error::Other(Error::Framing)) + } else if dr.fe() { + Ok(dr.data()) + } else { + Err(nb::Error::WouldBlock) + } + } + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for UartTx<'d, T, M> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for UartTx<'d, T, M> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for Uart<'d, T, M> { + fn read(&mut self) -> Result> { + embedded_hal_02::serial::Read::read(&mut self.rx) + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for Uart<'d, T, M> { + fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(buffer) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } + } + + impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for Uart<'d, T, M> { + fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { + self.blocking_write(&[char]).map_err(nb::Error::Other) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.blocking_flush().map_err(nb::Error::Other) + } + } + } #[cfg(all( @@ -532,6 +602,12 @@ mod eha { } } +#[cfg(feature = "nightly")] +mod buffered; +#[cfg(feature = "nightly")] +pub use buffered::*; + + mod sealed { use super::*; @@ -541,6 +617,8 @@ mod sealed { const TX_DREQ: u8; const RX_DREQ: u8; + type Interrupt: crate::interrupt::Interrupt; + fn regs() -> pac::uart::Uart; } pub trait TxPin {} @@ -571,6 +649,8 @@ macro_rules! impl_instance { impl sealed::Instance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; + + type Interrupt = crate::interrupt::$irq; fn regs() -> pac::uart::Uart { pac::$inst @@ -580,8 +660,8 @@ macro_rules! impl_instance { }; } -impl_instance!(UART0, UART0, 20, 21); -impl_instance!(UART1, UART1, 22, 23); +impl_instance!(UART0, UART0_IRQ, 20, 21); +impl_instance!(UART1, UART1_IRQ, 22, 23); pub trait TxPin: sealed::TxPin + crate::gpio::Pin {} pub trait RxPin: sealed::RxPin + crate::gpio::Pin {} From f2239d34cc26bb147d136d312b8b6af1020d4e0f Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 10:36:27 +0200 Subject: [PATCH 37/42] Add bufferedUart, including a split version for only Rx or Tx --- embassy-rp/src/uart/buffered.rs | 379 ++++++++++++++++++++++++++------ embassy-rp/src/uart/mod.rs | 2 +- 2 files changed, 315 insertions(+), 66 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index c31af801..3eb96e3d 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -9,31 +9,70 @@ use futures::future::poll_fn; use super::*; -pub struct State<'d, T: Instance>(StateStorage>); +pub struct State<'d, T: Instance>(StateStorage>); impl<'d, T: Instance> State<'d, T> { - pub fn new() -> Self { + pub const fn new() -> Self { Self(StateStorage::new()) } } -struct StateInner<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, - - rx_waker: WakerRegistration, - rx: RingBuffer<'d>, - - tx_waker: WakerRegistration, - tx: RingBuffer<'d>, +pub struct RxState<'d, T: Instance>(StateStorage>); +impl<'d, T: Instance> RxState<'d, T> { + pub const fn new() -> Self { + Self(StateStorage::new()) + } } -unsafe impl<'d, T: Instance> Send for StateInner<'d, T> {} -unsafe impl<'d, T: Instance> Sync for StateInner<'d, T> {} +pub struct TxState<'d, T: Instance>(StateStorage>); +impl<'d, T: Instance> TxState<'d, T> { + pub const fn new() -> Self { + Self(StateStorage::new()) + } +} + +struct RxStateInner<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + + waker: WakerRegistration, + buf: RingBuffer<'d>, +} + +struct TxStateInner<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + + waker: WakerRegistration, + buf: RingBuffer<'d>, +} + +struct FullStateInner<'d, T: Instance> { + rx: RxStateInner<'d, T>, + tx: TxStateInner<'d, T>, +} + +unsafe impl<'d, T: Instance> Send for RxStateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for RxStateInner<'d, T> {} + +unsafe impl<'d, T: Instance> Send for TxStateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for TxStateInner<'d, T> {} + +unsafe impl<'d, T: Instance> Send for FullStateInner<'d, T> {} +unsafe impl<'d, T: Instance> Sync for FullStateInner<'d, T> {} pub struct BufferedUart<'d, T: Instance> { - inner: PeripheralMutex<'d, StateInner<'d, T>>, + inner: PeripheralMutex<'d, FullStateInner<'d, T>>, +} + +pub struct RxBufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, RxStateInner<'d, T>>, +} + +pub struct TxBufferedUart<'d, T: Instance> { + inner: PeripheralMutex<'d, TxStateInner<'d, T>>, } impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {} +impl<'d, T: Instance> Unpin for RxBufferedUart<'d, T> {} +impl<'d, T: Instance> Unpin for TxBufferedUart<'d, T> {} impl<'d, T: Instance> BufferedUart<'d, T> { pub fn new( @@ -55,66 +94,158 @@ impl<'d, T: Instance> BufferedUart<'d, T> { } Self { - inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner { - phantom: PhantomData, - tx: RingBuffer::new(tx_buffer), - tx_waker: WakerRegistration::new(), - - rx: RingBuffer::new(rx_buffer), - rx_waker: WakerRegistration::new(), + inner: PeripheralMutex::new(irq, &mut state.0, move || FullStateInner { + tx: TxStateInner { + phantom: PhantomData, + waker: WakerRegistration::new(), + buf: RingBuffer::new(tx_buffer), + }, + rx: RxStateInner { + phantom: PhantomData, + waker: WakerRegistration::new(), + buf: RingBuffer::new(rx_buffer), + }, }), } } } -impl<'d, T: Instance> StateInner<'d, T> +impl<'d, T: Instance> RxBufferedUart<'d, T> { + pub fn new( + state: &'d mut RxState<'d, T>, + _uart: UartRx<'d, T, M>, + irq: impl Peripheral

+ 'd, + rx_buffer: &'d mut [u8], + ) -> RxBufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || RxStateInner { + phantom: PhantomData, + + buf: RingBuffer::new(rx_buffer), + waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> TxBufferedUart<'d, T> { + pub fn new( + state: &'d mut TxState<'d, T>, + _uart: UartTx<'d, T, M>, + irq: impl Peripheral

+ 'd, + tx_buffer: &'d mut [u8], + ) -> TxBufferedUart<'d, T> { + into_ref!(irq); + + let r = T::regs(); + unsafe { + r.uartimsc().modify(|w| { + // TODO: Should and more or fewer interrupts be enabled? + w.set_rxim(true); + w.set_rtim(true); + }); + } + + Self { + inner: PeripheralMutex::new(irq, &mut state.0, move || TxStateInner { + phantom: PhantomData, + + buf: RingBuffer::new(tx_buffer), + waker: WakerRegistration::new(), + }), + } + } +} + +impl<'d, T: Instance> PeripheralState for FullStateInner<'d, T> where Self: 'd, { - fn on_rx(&mut self) { + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { + self.rx.on_interrupt(); + self.tx.on_interrupt(); + } +} + +impl<'d, T: Instance> PeripheralState for RxStateInner<'d, T> +where + Self: 'd, +{ + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { let r = T::regs(); unsafe { - let ris = r.uartris().read(); + let ris = r.uartmis().read(); // Clear interrupt flags - r.uarticr().write(|w| { + r.uarticr().modify(|w| { w.set_rxic(true); w.set_rtic(true); }); - if ris.rxris() { - if ris.peris() { + if ris.rxmis() { + if ris.pemis() { warn!("Parity error"); + r.uarticr().modify(|w| { + w.set_peic(true); + }); } - if ris.feris() { + if ris.femis() { warn!("Framing error"); + r.uarticr().modify(|w| { + w.set_feic(true); + }); } - if ris.beris() { + if ris.bemis() { warn!("Break error"); + r.uarticr().modify(|w| { + w.set_beic(true); + }); } - if ris.oeris() { + if ris.oemis() { warn!("Overrun error"); + r.uarticr().modify(|w| { + w.set_oeic(true); + }); } - let buf = self.rx.push_buf(); + let buf = self.buf.push_buf(); if !buf.is_empty() { buf[0] = r.uartdr().read().data(); - self.rx.push(1); + self.buf.push(1); } else { warn!("RX buffer full, discard received byte"); } - if self.rx.is_full() { - self.rx_waker.wake(); + if self.buf.is_full() { + self.waker.wake(); } } - if ris.rtris() { - self.rx_waker.wake(); + if ris.rtmis() { + self.waker.wake(); }; } } +} - fn on_tx(&mut self) { +impl<'d, T: Instance> PeripheralState for TxStateInner<'d, T> +where + Self: 'd, +{ + type Interrupt = T::Interrupt; + fn on_interrupt(&mut self) { let r = T::regs(); unsafe { let ris = r.uartris().read(); @@ -124,14 +255,14 @@ where }); if ris.txris() { - let buf = self.tx.pop_buf(); + let buf = self.buf.pop_buf(); if !buf.is_empty() { r.uartimsc().modify(|w| { w.set_txim(true); }); r.uartdr().write(|w| w.set_data(buf[0].into())); - self.tx.pop(1); - self.tx_waker.wake(); + self.buf.pop(1); + self.waker.wake(); } else { // Disable interrupt until we have something to transmit again r.uartimsc().modify(|w| { @@ -143,17 +274,6 @@ where } } -impl<'d, T: Instance> PeripheralState for StateInner<'d, T> -where - Self: 'd, -{ - type Interrupt = T::Interrupt; - fn on_interrupt(&mut self) { - self.on_rx(); - self.on_tx(); - } -} - impl embedded_io::Error for Error { fn kind(&self) -> embedded_io::ErrorKind { embedded_io::ErrorKind::Other @@ -164,8 +284,16 @@ impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { type Error = Error; } +impl<'d, T: Instance> embedded_io::Io for RxBufferedUart<'d, T> { + type Error = Error; +} + +impl<'d, T: Instance> embedded_io::Io for TxBufferedUart<'d, T> { + type Error = Error; +} + impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + type ReadFuture<'a> = impl Future> where Self: 'a; @@ -176,20 +304,58 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { compiler_fence(Ordering::SeqCst); // We have data ready in buffer? Return it. - let data = state.rx.pop_buf(); + let data = state.rx.buf.pop_buf(); if !data.is_empty() { let len = data.len().min(buf.len()); buf[..len].copy_from_slice(&data[..len]); - if state.rx.is_full() { + if state.rx.buf.is_full() { do_pend = true; } - state.rx.pop(len); + state.rx.buf.pop(len); return Poll::Ready(Ok(len)); } - state.rx_waker.register(cx.waker()); + state.rx.waker.register(cx.waker()); + Poll::Pending + }); + + if do_pend { + self.inner.pend(); + } + + res + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Read for RxBufferedUart<'d, T> { + type ReadFuture<'a> = impl Future> + where + Self: 'a; + + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { + poll_fn(move |cx| { + let mut do_pend = false; + let res = self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let data = state.buf.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if state.buf.is_full() { + do_pend = true; + } + state.buf.pop(len); + + return Poll::Ready(Ok(len)); + } + + state.waker.register(cx.waker()); Poll::Pending }); @@ -213,7 +379,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> compiler_fence(Ordering::SeqCst); // We have data ready in buffer? Return it. - let buf = state.rx.pop_buf(); + let buf = state.rx.buf.pop_buf(); if !buf.is_empty() { let buf: &[u8] = buf; // Safety: buffer lives as long as uart @@ -221,7 +387,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> return Poll::Ready(Ok(buf)); } - state.rx_waker.register(cx.waker()); + state.rx.waker.register(cx.waker()); Poll::>::Pending }) }) @@ -229,8 +395,45 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> fn consume(&mut self, amt: usize) { let signal = self.inner.with(|state| { - let full = state.rx.is_full(); - state.rx.pop(amt); + let full = state.rx.buf.is_full(); + state.rx.buf.pop(amt); + full + }); + if signal { + self.inner.pend(); + } + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for RxBufferedUart<'d, T> { + type FillBufFuture<'a> = impl Future> + where + Self: 'a; + + fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + compiler_fence(Ordering::SeqCst); + + // We have data ready in buffer? Return it. + let buf = state.buf.pop_buf(); + if !buf.is_empty() { + let buf: &[u8] = buf; + // Safety: buffer lives as long as uart + let buf: &[u8] = unsafe { core::mem::transmute(buf) }; + return Poll::Ready(Ok(buf)); + } + + state.waker.register(cx.waker()); + Poll::>::Pending + }) + }) + } + + fn consume(&mut self, amt: usize) { + let signal = self.inner.with(|state| { + let full = state.buf.is_full(); + state.buf.pop(amt); full }); if signal { @@ -247,16 +450,16 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { let (poll, empty) = self.inner.with(|state| { - let empty = state.tx.is_empty(); - let tx_buf = state.tx.push_buf(); + let empty = state.tx.buf.is_empty(); + let tx_buf = state.tx.buf.push_buf(); if tx_buf.is_empty() { - state.tx_waker.register(cx.waker()); + state.tx.waker.register(cx.waker()); return (Poll::Pending, empty); } let n = core::cmp::min(tx_buf.len(), buf.len()); tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.push(n); + state.tx.buf.push(n); (Poll::Ready(Ok(n)), empty) }); @@ -274,8 +477,54 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { poll_fn(move |cx| { self.inner.with(|state| { - if !state.tx.is_empty() { - state.tx_waker.register(cx.waker()); + if !state.tx.buf.is_empty() { + state.tx.waker.register(cx.waker()); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + }) + }) + } +} + +impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> { + type WriteFuture<'a> = impl Future> + where + Self: 'a; + + fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { + poll_fn(move |cx| { + let (poll, empty) = self.inner.with(|state| { + let empty = state.buf.is_empty(); + let tx_buf = state.buf.push_buf(); + if tx_buf.is_empty() { + state.waker.register(cx.waker()); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + state.buf.push(n); + + (Poll::Ready(Ok(n)), empty) + }); + if empty { + self.inner.pend(); + } + poll + }) + } + + type FlushFuture<'a> = impl Future> + where + Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + poll_fn(move |cx| { + self.inner.with(|state| { + if !state.buf.is_empty() { + state.waker.register(cx.waker()); return Poll::Pending; } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 3b71d87b..67e24b60 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -343,7 +343,7 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> { w.set_stp2(config.stop_bits == StopBits::STOP2); w.set_pen(pen); w.set_eps(eps); - w.set_fen(true); + w.set_fen(false); }); r.uartcr().write(|w| { From d6af0f62860f79f790d6d1e042f36c8623328ab1 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 10:49:47 +0200 Subject: [PATCH 38/42] Formatting --- embassy-rp/src/uart/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 67e24b60..76ecdf7a 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -544,7 +544,6 @@ mod eh1 { self.blocking_flush().map_err(nb::Error::Other) } } - } #[cfg(all( @@ -607,7 +606,6 @@ mod buffered; #[cfg(feature = "nightly")] pub use buffered::*; - mod sealed { use super::*; @@ -649,7 +647,7 @@ macro_rules! impl_instance { impl sealed::Instance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; - + type Interrupt = crate::interrupt::$irq; fn regs() -> pac::uart::Uart { From 1db9e464ff13a05d1267bb33fde490bcce35af5a Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 9 Sep 2022 12:28:35 +0200 Subject: [PATCH 39/42] Enable embedded-io on nightly --- embassy-rp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 211b6a40..d0cf8025 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -27,7 +27,7 @@ intrinsics = [] rom-v2-intrinsics = [] # Enable nightly-only features -nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"] +nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb", "dep:embedded-io"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. From b3dfd06dd6da3369813cf469a7fcd87c22047e87 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 21 Sep 2022 06:00:35 +0200 Subject: [PATCH 40/42] Remove code-duplication in async bufferedUart implementations --- embassy-rp/src/uart/buffered.rs | 215 +++++++++++++------------------- 1 file changed, 89 insertions(+), 126 deletions(-) diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 3eb96e3d..6d395b6f 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,5 +1,5 @@ use core::future::Future; -use core::task::Poll; +use core::task::{Poll, Waker}; use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; @@ -87,9 +87,9 @@ impl<'d, T: Instance> BufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? w.set_rxim(true); w.set_rtim(true); + w.set_txim(true); }); } @@ -122,7 +122,6 @@ impl<'d, T: Instance> RxBufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? w.set_rxim(true); w.set_rtim(true); }); @@ -151,9 +150,7 @@ impl<'d, T: Instance> TxBufferedUart<'d, T> { let r = T::regs(); unsafe { r.uartimsc().modify(|w| { - // TODO: Should and more or fewer interrupts be enabled? - w.set_rxim(true); - w.set_rtim(true); + w.set_txim(true); }); } @@ -179,6 +176,51 @@ where } } +impl<'d, T: Instance> RxStateInner<'d, T> +where + Self: 'd, +{ + fn read(&mut self, buf: &mut [u8], waker: &Waker) -> (Poll>, bool) { + // We have data ready in buffer? Return it. + let mut do_pend = false; + let data = self.buf.pop_buf(); + if !data.is_empty() { + let len = data.len().min(buf.len()); + buf[..len].copy_from_slice(&data[..len]); + + if self.buf.is_full() { + do_pend = true; + } + self.buf.pop(len); + + return (Poll::Ready(Ok(len)), do_pend); + } + + self.waker.register(waker); + (Poll::Pending, do_pend) + } + + fn fill_buf<'a>(&mut self, waker: &Waker) -> Poll> { + // We have data ready in buffer? Return it. + let buf = self.buf.pop_buf(); + if !buf.is_empty() { + let buf: &[u8] = buf; + // Safety: buffer lives as long as uart + let buf: &[u8] = unsafe { core::mem::transmute(buf) }; + return Poll::Ready(Ok(buf)); + } + + self.waker.register(waker); + Poll::Pending + } + + fn consume(&mut self, amt: usize) -> bool { + let full = self.buf.is_full(); + self.buf.pop(amt); + full + } +} + impl<'d, T: Instance> PeripheralState for RxStateInner<'d, T> where Self: 'd, @@ -240,6 +282,35 @@ where } } +impl<'d, T: Instance> TxStateInner<'d, T> +where + Self: 'd, +{ + fn write(&mut self, buf: &[u8], waker: &Waker) -> (Poll>, bool) { + let empty = self.buf.is_empty(); + let tx_buf = self.buf.push_buf(); + if tx_buf.is_empty() { + self.waker.register(waker); + return (Poll::Pending, empty); + } + + let n = core::cmp::min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + self.buf.push(n); + + (Poll::Ready(Ok(n)), empty) + } + + fn flush(&mut self, waker: &Waker) -> Poll> { + if !self.buf.is_empty() { + self.waker.register(waker); + return Poll::Pending; + } + + Poll::Ready(Ok(())) + } +} + impl<'d, T: Instance> PeripheralState for TxStateInner<'d, T> where Self: 'd, @@ -299,26 +370,9 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.with(|state| { + let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.rx.buf.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.rx.buf.is_full() { - do_pend = true; - } - state.rx.buf.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.rx.waker.register(cx.waker()); - Poll::Pending + state.rx.read(buf, cx.waker()) }); if do_pend { @@ -337,26 +391,9 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Read for RxBufferedUart<'d, T> { fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { poll_fn(move |cx| { - let mut do_pend = false; - let res = self.inner.with(|state| { + let (res, do_pend) = self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let data = state.buf.pop_buf(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); - - if state.buf.is_full() { - do_pend = true; - } - state.buf.pop(len); - - return Poll::Ready(Ok(len)); - } - - state.waker.register(cx.waker()); - Poll::Pending + state.read(buf, cx.waker()) }); if do_pend { @@ -377,28 +414,13 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.rx.buf.pop_buf(); - if !buf.is_empty() { - let buf: &[u8] = buf; - // Safety: buffer lives as long as uart - let buf: &[u8] = unsafe { core::mem::transmute(buf) }; - return Poll::Ready(Ok(buf)); - } - - state.rx.waker.register(cx.waker()); - Poll::>::Pending + state.rx.fill_buf(cx.waker()) }) }) } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| { - let full = state.rx.buf.is_full(); - state.rx.buf.pop(amt); - full - }); + let signal = self.inner.with(|state| state.rx.consume(amt)); if signal { self.inner.pend(); } @@ -414,28 +436,13 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for RxBufferedUart<'d, T poll_fn(move |cx| { self.inner.with(|state| { compiler_fence(Ordering::SeqCst); - - // We have data ready in buffer? Return it. - let buf = state.buf.pop_buf(); - if !buf.is_empty() { - let buf: &[u8] = buf; - // Safety: buffer lives as long as uart - let buf: &[u8] = unsafe { core::mem::transmute(buf) }; - return Poll::Ready(Ok(buf)); - } - - state.waker.register(cx.waker()); - Poll::>::Pending + state.fill_buf(cx.waker()) }) }) } fn consume(&mut self, amt: usize) { - let signal = self.inner.with(|state| { - let full = state.buf.is_full(); - state.buf.pop(amt); - full - }); + let signal = self.inner.with(|state| state.consume(amt)); if signal { self.inner.pend(); } @@ -449,20 +456,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| { - let empty = state.tx.buf.is_empty(); - let tx_buf = state.tx.buf.push_buf(); - if tx_buf.is_empty() { - state.tx.waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.tx.buf.push(n); - - (Poll::Ready(Ok(n)), empty) - }); + let (poll, empty) = self.inner.with(|state| state.tx.write(buf, cx.waker())); if empty { self.inner.pend(); } @@ -475,16 +469,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - if !state.tx.buf.is_empty() { - state.tx.waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker()))) } } @@ -495,20 +480,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { poll_fn(move |cx| { - let (poll, empty) = self.inner.with(|state| { - let empty = state.buf.is_empty(); - let tx_buf = state.buf.push_buf(); - if tx_buf.is_empty() { - state.waker.register(cx.waker()); - return (Poll::Pending, empty); - } - - let n = core::cmp::min(tx_buf.len(), buf.len()); - tx_buf[..n].copy_from_slice(&buf[..n]); - state.buf.push(n); - - (Poll::Ready(Ok(n)), empty) - }); + let (poll, empty) = self.inner.with(|state| state.write(buf, cx.waker())); if empty { self.inner.pend(); } @@ -521,15 +493,6 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for TxBufferedUart<'d, T> Self: 'a; fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - poll_fn(move |cx| { - self.inner.with(|state| { - if !state.buf.is_empty() { - state.waker.register(cx.waker()); - return Poll::Pending; - } - - Poll::Ready(Ok(())) - }) - }) + poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker()))) } } From f76444bdc43f0a000b878dc52cb46ac06661a8b3 Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 26 Sep 2022 05:32:45 +0200 Subject: [PATCH 41/42] Add HIL test for bufferedUart --- embassy-rp/src/uart/buffered.rs | 3 +-- tests/rp/Cargo.toml | 1 + tests/rp/src/bin/uart_buffered.rs | 37 +++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tests/rp/src/bin/uart_buffered.rs diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 6d395b6f..9c4fbfeb 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,11 +1,10 @@ -use core::future::Future; +use core::future::{poll_fn, Future}; use core::task::{Poll, Waker}; use atomic_polyfill::{compiler_fence, Ordering}; use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage}; use embassy_hal_common::ring_buffer::RingBuffer; use embassy_sync::waitqueue::WakerRegistration; -use futures::future::poll_fn; use super::*; diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 7e2717dd..50337375 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -20,6 +20,7 @@ embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } embedded-hal-async = { version = "0.1.0-alpha.1" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +embedded-io = { version = "0.3.0", features = ["async"] } [profile.dev] debug = 2 diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs new file mode 100644 index 00000000..a0a3df8d --- /dev/null +++ b/tests/rp/src/bin/uart_buffered.rs @@ -0,0 +1,37 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_rp::interrupt; +use embassy_rp::uart::{BufferedUart, Config, State, Uart}; +use embedded_io::asynch::{Read, Write}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let (tx, rx, uart) = (p.PIN_0, p.PIN_1, p.UART0); + + let config = Config::default(); + let uart = Uart::new_blocking(uart, tx, rx, config); + + let irq = interrupt::take!(UART0_IRQ); + let tx_buf = &mut [0u8; 32]; + let rx_buf = &mut [0u8; 32]; + let mut state = State::new(); + let mut uart = BufferedUart::new(&mut state, uart, irq, tx_buf, rx_buf); + + let data = [0xC0, 0xDE]; + uart.write(&data).await.unwrap(); + + let mut buf = [0; 2]; + uart.read(&mut buf).await.unwrap(); + assert_eq!(buf, data); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From c863acd24f6430950a7fdb5c527b33b42c305fec Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 26 Sep 2022 20:36:06 +0200 Subject: [PATCH 42/42] rp: set correct teleprobe target for rpi-pico tests. --- tests/rp/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml index 0330025e..9611db3a 100644 --- a/tests/rp/.cargo/config.toml +++ b/tests/rp/.cargo/config.toml @@ -3,7 +3,7 @@ build-std = ["core"] build-std-features = ["panic_immediate_abort"] [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -#runner = "teleprobe client run --target bluepill-stm32f103c8 --elf" +#runner = "teleprobe client run --target rpi-pico --elf" runner = "teleprobe local run --chip RP2040 --elf" rustflags = [