From a83560c6b166e3f354471a2e1cf1c52a6d2be59b Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 29 Sep 2022 07:49:32 +0200 Subject: [PATCH 01/46] Implement RTC peripheral for all stm32 families with rtc --- embassy-stm32/Cargo.toml | 1 + embassy-stm32/src/lib.rs | 2 + embassy-stm32/src/rtc/datetime_chrono.rs | 85 ++++++++ embassy-stm32/src/rtc/datetime_no_deps.rs | 146 +++++++++++++ embassy-stm32/src/rtc/mod.rs | 238 ++++++++++++++++++++++ embassy-stm32/src/rtc/v2/mod.rs | 171 ++++++++++++++++ embassy-stm32/src/rtc/v2/v2f0.rs | 41 ++++ embassy-stm32/src/rtc/v2/v2f2.rs | 31 +++ embassy-stm32/src/rtc/v2/v2f3.rs | 31 +++ embassy-stm32/src/rtc/v2/v2f4.rs | 31 +++ embassy-stm32/src/rtc/v2/v2f7.rs | 41 ++++ embassy-stm32/src/rtc/v2/v2h7.rs | 33 +++ embassy-stm32/src/rtc/v2/v2l0.rs | 40 ++++ embassy-stm32/src/rtc/v2/v2l1.rs | 40 ++++ embassy-stm32/src/rtc/v2/v2l4.rs | 41 ++++ embassy-stm32/src/rtc/v2/v2wb.rs | 39 ++++ embassy-stm32/src/rtc/v3.rs | 212 +++++++++++++++++++ 17 files changed, 1223 insertions(+) create mode 100644 embassy-stm32/src/rtc/datetime_chrono.rs create mode 100644 embassy-stm32/src/rtc/datetime_no_deps.rs create mode 100644 embassy-stm32/src/rtc/mod.rs create mode 100644 embassy-stm32/src/rtc/v2/mod.rs create mode 100644 embassy-stm32/src/rtc/v2/v2f0.rs create mode 100644 embassy-stm32/src/rtc/v2/v2f2.rs create mode 100644 embassy-stm32/src/rtc/v2/v2f3.rs create mode 100644 embassy-stm32/src/rtc/v2/v2f4.rs create mode 100644 embassy-stm32/src/rtc/v2/v2f7.rs create mode 100644 embassy-stm32/src/rtc/v2/v2h7.rs create mode 100644 embassy-stm32/src/rtc/v2/v2l0.rs create mode 100644 embassy-stm32/src/rtc/v2/v2l1.rs create mode 100644 embassy-stm32/src/rtc/v2/v2l4.rs create mode 100644 embassy-stm32/src/rtc/v2/v2wb.rs create mode 100644 embassy-stm32/src/rtc/v3.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index a4a232f51..a3ce9d996 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -66,6 +66,7 @@ stm32-fmc = "0.2.4" seq-macro = "0.3.0" cfg-if = "1.0.0" embedded-io = { version = "0.3.0", features = ["async"], optional = true } +chrono = { version = "^0.4", default-features = false, optional = true} [build-dependencies] proc-macro2 = "1.0.36" diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 0392e8086..906980ac4 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -46,6 +46,8 @@ pub mod flash; pub mod pwm; #[cfg(rng)] pub mod rng; +#[cfg(all(rtc, not(rtc_v1)))] +pub mod rtc; #[cfg(sdmmc)] pub mod sdmmc; #[cfg(spi)] diff --git a/embassy-stm32/src/rtc/datetime_chrono.rs b/embassy-stm32/src/rtc/datetime_chrono.rs new file mode 100644 index 000000000..b46316cc9 --- /dev/null +++ b/embassy-stm32/src/rtc/datetime_chrono.rs @@ -0,0 +1,85 @@ +use chrono::{Datelike, Timelike}; + +use super::byte_to_bcd2; +use crate::pac::rtc::Rtc; + +/// 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_monday() 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_date_time(rtc: &Rtc, t: DateTime) { + let (ht, hu) = byte_to_bcd2(t.hour() as u8); + let (mnt, mnu) = byte_to_bcd2(t.minute() as u8); + let (st, su) = byte_to_bcd2(t.second() as u8); + + let (dt, du) = byte_to_bcd2(t.day() as u8); + let (mt, mu) = byte_to_bcd2(t.month() as u8); + let yr = t.year() as u16; + let yr_offset = (yr - 1970_u16) as u8; + let (yt, yu) = byte_to_bcd2(yr_offset); + + unsafe { + rtc.tr().write(|w| { + w.set_ht(ht); + w.set_hu(hu); + w.set_mnt(mnt); + w.set_mnu(mnu); + w.set_st(st); + w.set_su(su); + w.set_pm(stm32_metapac::rtc::vals::Ampm::AM); + }); + + rtc.dr().write(|w| { + w.set_dt(dt); + w.set_du(du); + w.set_mt(mt > 0); + w.set_mu(mu); + w.set_yt(yt); + w.set_yu(yu); + w.set_wdu(day_of_week_to_u8(t.weekday())); + }); + } +} + +pub(super) fn datetime( + year: u16, + month: u8, + day: u8, + _day_of_week: u8, + hour: u8, + minute: u8, + second: u8, +) -> Result { + let date = chrono::NaiveDate::from_ymd_opt(year.into(), month.try_into().unwrap(), day.into()) + .ok_or(Error::InvalidDate)?; + let time = chrono::NaiveTime::from_hms_opt(hour.into(), minute.into(), second.into()).ok_or(Error::InvalidTime)?; + Ok(DateTime::new(date, time)) +} diff --git a/embassy-stm32/src/rtc/datetime_no_deps.rs b/embassy-stm32/src/rtc/datetime_no_deps.rs new file mode 100644 index 000000000..173f38377 --- /dev/null +++ b/embassy-stm32/src/rtc/datetime_no_deps.rs @@ -0,0 +1,146 @@ +use super::byte_to_bcd2; +use crate::pac::rtc::Rtc; + +/// Errors regarding the [`DateTime`] struct. +#[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 { + Monday = 0, + Tuesday = 1, + Wednesday = 2, + Thursday = 3, + Friday = 4, + Saturday = 5, + Sunday = 6, +} + +fn day_of_week_from_u8(v: u8) -> Result { + Ok(match v { + 0 => DayOfWeek::Monday, + 1 => DayOfWeek::Tuesday, + 2 => DayOfWeek::Wednesday, + 3 => DayOfWeek::Thursday, + 4 => DayOfWeek::Friday, + 5 => DayOfWeek::Saturday, + 6 => DayOfWeek::Sunday, + 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_date_time(rtc: &Rtc, t: DateTime) { + let (ht, hu) = byte_to_bcd2(t.hour as u8); + let (mnt, mnu) = byte_to_bcd2(t.minute as u8); + let (st, su) = byte_to_bcd2(t.second as u8); + + let (dt, du) = byte_to_bcd2(t.day as u8); + let (mt, mu) = byte_to_bcd2(t.month as u8); + let yr = t.year as u16; + let yr_offset = (yr - 1970_u16) as u8; + let (yt, yu) = byte_to_bcd2(yr_offset); + + unsafe { + rtc.tr().write(|w| { + w.set_ht(ht); + w.set_hu(hu); + w.set_mnt(mnt); + w.set_mnu(mnu); + w.set_st(st); + w.set_su(su); + w.set_pm(stm32_metapac::rtc::vals::Ampm::AM); + }); + + rtc.dr().write(|w| { + w.set_dt(dt); + w.set_du(du); + w.set_mt(mt > 0); + w.set_mu(mu); + w.set_yt(yt); + w.set_yu(yu); + w.set_wdu(day_of_week_to_u8(t.day_of_week)); + }); + } +} + +pub(super) fn datetime( + year: u16, + month: u8, + day: u8, + day_of_week: u8, + hour: u8, + minute: u8, + second: u8, +) -> Result { + 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-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs new file mode 100644 index 000000000..ee3349b27 --- /dev/null +++ b/embassy-stm32/src/rtc/mod.rs @@ -0,0 +1,238 @@ +//! RTC peripheral abstraction +use core::marker::PhantomData; + +#[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}; + +/// refer to AN4759 to compare features of RTC2 and RTC3 +#[cfg_attr(any(rtc_v1), path = "v1.rs")] +#[cfg_attr( + any( + rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb + ), + path = "v2/mod.rs" +)] +#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")] +mod versions; +use embassy_hal_common::Peripheral; +pub use versions::*; + +/// 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, +} + +/// RTC Abstraction +pub struct Rtc<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, + rtc_config: RtcConfig, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +#[repr(u8)] +pub enum RtcClockSource { + /// 00: No clock + NoClock = 0b00, + /// 01: LSE oscillator clock used as RTC clock + LSE = 0b01, + /// 10: LSI oscillator clock used as RTC clock + LSI = 0b10, + /// 11: HSE oscillator clock divided by 32 used as RTC clock + HSE = 0b11, +} + +#[derive(Copy, Clone, PartialEq)] +pub struct RtcConfig { + /// RTC clock source + clock_config: RtcClockSource, + /// Asynchronous prescaler factor + /// This is the asynchronous division factor: + /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1) + /// ck_apre drives the subsecond register + async_prescaler: u8, + /// Synchronous prescaler factor + /// This is the synchronous division factor: + /// ck_spre frequency = ck_apre frequency/(PREDIV_S+1) + /// ck_spre must be 1Hz + sync_prescaler: u16, +} + +impl Default for RtcConfig { + /// LSI with prescalers assuming 32.768 kHz. + /// Raw sub-seconds in 1/256. + fn default() -> Self { + RtcConfig { + clock_config: RtcClockSource::LSI, + async_prescaler: 127, + sync_prescaler: 255, + } + } +} + +impl RtcConfig { + /// Sets the clock source of RTC config + pub fn clock_config(mut self, cfg: RtcClockSource) -> Self { + self.clock_config = cfg; + self + } + + /// Set the asynchronous prescaler of RTC config + pub fn async_prescaler(mut self, prescaler: u8) -> Self { + self.async_prescaler = prescaler; + self + } + + /// Set the synchronous prescaler of RTC config + pub fn sync_prescaler(mut self, prescaler: u16) -> Self { + self.sync_prescaler = prescaler; + self + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +#[repr(u8)] +pub enum RtcCalibrationCyclePeriod { + /// 8-second calibration period + Seconds8, + /// 16-second calibration period + Seconds16, + /// 32-second calibration period + Seconds32, +} + +impl Default for RtcCalibrationCyclePeriod { + fn default() -> Self { + RtcCalibrationCyclePeriod::Seconds32 + } +} + +impl<'d, T: Instance> Rtc<'d, T> { + pub fn new(_rtc: impl Peripheral

+ 'd, rtc_config: RtcConfig) -> Self { + unsafe { enable_peripheral_clk() }; + + let mut rtc_struct = Self { + phantom: PhantomData, + rtc_config, + }; + + rtc_struct.apply_config(rtc_config); + + rtc_struct + } + + /// 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)?; + self.write(true, |rtc| self::datetime::write_date_time(rtc, t)); + + 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 { + let r = T::regs(); + unsafe { + let tr = r.tr().read(); + let second = bcd2_to_byte((tr.st(), tr.su())); + let minute = bcd2_to_byte((tr.mnt(), tr.mnu())); + let hour = bcd2_to_byte((tr.ht(), tr.hu())); + // Reading either RTC_SSR or RTC_TR locks the values in the higher-order + // calendar shadow registers until RTC_DR is read. + let dr = r.dr().read(); + + let weekday = dr.wdu(); + let day = bcd2_to_byte((dr.dt(), dr.du())); + let month = bcd2_to_byte((dr.mt() as u8, dr.mu())); + let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16; + + self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime) + } + } + + /// Check if daylight savings time is active. + pub fn get_daylight_savings(&self) -> bool { + let cr = unsafe { T::regs().cr().read() }; + cr.bkp() + } + + /// Enable/disable daylight savings time. + pub fn set_daylight_savings(&mut self, daylight_savings: bool) { + self.write(true, |rtc| { + unsafe { rtc.cr().modify(|w| w.set_bkp(daylight_savings)) }; + }) + } + + pub fn get_config(&self) -> RtcConfig { + self.rtc_config + } + + pub const BACKUP_REGISTER_COUNT: usize = BACKUP_REGISTER_COUNT; + + /// Read content of the backup register. + /// + /// The registers retain their values during wakes from standby mode or system resets. They also + /// retain their value when Vdd is switched off as long as V_BAT is powered. + pub fn read_backup_register(&self, register: usize) -> Option { + read_backup_register(&T::regs(), register) + } + + /// Set content of the backup register. + /// + /// The registers retain their values during wakes from standby mode or system resets. They also + /// retain their value when Vdd is switched off as long as V_BAT is powered. + pub fn write_backup_register(&self, register: usize, value: u32) { + write_backup_register(&T::regs(), register, value) + } +} + +pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) { + let mut bcd_high: u8 = 0; + let mut value = byte; + + while value >= 10 { + bcd_high += 1; + value -= 10; + } + + (bcd_high, ((bcd_high << 4) | value) as u8) +} + +pub(crate) fn bcd2_to_byte(bcd: (u8, u8)) -> u8 { + let value = bcd.1 | bcd.0 << 4; + + let tmp = ((value & 0xF0) >> 0x4) * 10; + + tmp + (value & 0x0F) +} + +pub(crate) mod sealed { + pub trait Instance { + fn regs() -> crate::pac::rtc::Rtc; + } +} + +pub trait Instance: sealed::Instance + 'static {} + +impl sealed::Instance for crate::peripherals::RTC { + fn regs() -> crate::pac::rtc::Rtc { + crate::pac::RTC + } +} + +impl Instance for crate::peripherals::RTC {} diff --git a/embassy-stm32/src/rtc/v2/mod.rs b/embassy-stm32/src/rtc/v2/mod.rs new file mode 100644 index 000000000..296adae89 --- /dev/null +++ b/embassy-stm32/src/rtc/v2/mod.rs @@ -0,0 +1,171 @@ +use stm32_metapac::rtc::vals::{Init, Osel, Pol}; + +use super::{Instance, RtcConfig}; +use crate::pac::rtc::Rtc; + +#[cfg_attr(rtc_v2f0, path = "v2f0.rs")] +#[cfg_attr(rtc_v2f2, path = "v2f2.rs")] +#[cfg_attr(rtc_v2f3, path = "v2f3.rs")] +#[cfg_attr(rtc_v2f4, path = "v2f4.rs")] +#[cfg_attr(rtc_v2f7, path = "v2f7.rs")] +#[cfg_attr(rtc_v2h7, path = "v2h7.rs")] +#[cfg_attr(rtc_v2l0, path = "v2l0.rs")] +#[cfg_attr(rtc_v2l1, path = "v2l1.rs")] +#[cfg_attr(rtc_v2l4, path = "v2l4.rs")] +#[cfg_attr(rtc_v2wb, path = "v2wb.rs")] +mod family; + +pub use family::*; + +impl<'d, T: Instance> super::Rtc<'d, T> { + /// Applies the RTC config + /// It this changes the RTC clock source the time will be reset + pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { + // Unlock the backup domain + unsafe { + unlock_backup_domain(rtc_config.clock_config as u8); + } + + self.write(true, |rtc| unsafe { + rtc.cr().modify(|w| { + #[cfg(rtc_v2f2)] + w.set_fmt(false); + #[cfg(not(rtc_v2f2))] + w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR); + w.set_osel(Osel::DISABLED); + w.set_pol(Pol::HIGH); + }); + + rtc.prer().modify(|w| { + w.set_prediv_s(rtc_config.sync_prescaler); + w.set_prediv_a(rtc_config.async_prescaler); + }); + }); + + self.rtc_config = rtc_config; + } + + /// Calibrate the clock drift. + /// + /// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range. + /// + /// ### Note + /// + /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler` + /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12). + #[cfg(not(rtc_v2f2))] + pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) { + const RTC_CALR_MIN_PPM: f32 = -487.1; + const RTC_CALR_MAX_PPM: f32 = 488.5; + const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537; + + if clock_drift < RTC_CALR_MIN_PPM { + clock_drift = RTC_CALR_MIN_PPM; + } else if clock_drift > RTC_CALR_MAX_PPM { + clock_drift = RTC_CALR_MAX_PPM; + } + + clock_drift = clock_drift / RTC_CALR_RESOLUTION_PPM; + + self.write(false, |rtc| { + unsafe { + rtc.calr().write(|w| { + match period { + super::RtcCalibrationCyclePeriod::Seconds8 => { + w.set_calw8(stm32_metapac::rtc::vals::Calw8::EIGHT_SECOND); + } + super::RtcCalibrationCyclePeriod::Seconds16 => { + w.set_calw16(stm32_metapac::rtc::vals::Calw16::SIXTEEN_SECOND); + } + super::RtcCalibrationCyclePeriod::Seconds32 => { + // Set neither `calw8` nor `calw16` to use 32 seconds + } + } + + // Extra pulses during calibration cycle period: CALP * 512 - CALM + // + // CALP sets whether pulses are added or omitted. + // + // CALM contains how many pulses (out of 512) are masked in a + // given calibration cycle period. + if clock_drift > 0.0 { + // Maximum (about 512.2) rounds to 512. + clock_drift += 0.5; + + // When the offset is positive (0 to 512), the opposite of + // the offset (512 - offset) is masked, i.e. for the + // maximum offset (512), 0 pulses are masked. + w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASEFREQ); + w.set_calm(512 - clock_drift as u16); + } else { + // Minimum (about -510.7) rounds to -511. + clock_drift -= 0.5; + + // When the offset is negative or zero (-511 to 0), + // the absolute offset is masked, i.e. for the minimum + // offset (-511), 511 pulses are masked. + w.set_calp(stm32_metapac::rtc::vals::Calp::NOCHANGE); + w.set_calm((clock_drift * -1.0) as u16); + } + }); + } + }) + } + + pub(super) fn write(&mut self, init_mode: bool, f: F) -> R + where + F: FnOnce(&crate::pac::rtc::Rtc) -> R, + { + let r = T::regs(); + // Disable write protection. + // This is safe, as we're only writin the correct and expected values. + unsafe { + r.wpr().write(|w| w.set_key(0xca)); + r.wpr().write(|w| w.set_key(0x53)); + + // true if initf bit indicates RTC peripheral is in init mode + if init_mode && !r.isr().read().initf() { + // to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode + r.isr().modify(|w| w.set_init(Init::INITMODE)); + // wait till init state entered + // ~2 RTCCLK cycles + while !r.isr().read().initf() {} + } + } + + let result = f(&r); + + unsafe { + if init_mode { + r.isr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode + } + + // Re-enable write protection. + // This is safe, as the field accepts the full range of 8-bit values. + r.wpr().write(|w| w.set_key(0xff)); + } + result + } +} + +/// Read content of the backup register. +/// +/// The registers retain their values during wakes from standby mode or system resets. They also +/// retain their value when Vdd is switched off as long as V_BAT is powered. +pub fn read_backup_register(rtc: &Rtc, register: usize) -> Option { + if register < BACKUP_REGISTER_COUNT { + Some(unsafe { rtc.bkpr(register).read().bkp() }) + } else { + None + } +} + +/// Set content of the backup register. +/// +/// The registers retain their values during wakes from standby mode or system resets. They also +/// retain their value when Vdd is switched off as long as V_BAT is powered. +pub fn write_backup_register(rtc: &Rtc, register: usize, value: u32) { + if register < BACKUP_REGISTER_COUNT { + unsafe { rtc.bkpr(register).write(|w| w.set_bkp(value)) } + } +} diff --git a/embassy-stm32/src/rtc/v2/v2f0.rs b/embassy-stm32/src/rtc/v2/v2f0.rs new file mode 100644 index 000000000..d6871d91e --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2f0.rs @@ -0,0 +1,41 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + // Restore bcdr + w.set_lscosel(reg.lscosel()); + w.set_lscoen(reg.lscoen()); + + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // enable peripheral clock for communication + crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // read to allow the pwr clock to enable + crate::pac::PWR.cr1().read(); +} diff --git a/embassy-stm32/src/rtc/v2/v2f2.rs b/embassy-stm32/src/rtc/v2/v2f2.rs new file mode 100644 index 000000000..e041f3f4e --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2f2.rs @@ -0,0 +1,31 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + w.set_lseon(reg.lseon()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // Nothing to do +} diff --git a/embassy-stm32/src/rtc/v2/v2f3.rs b/embassy-stm32/src/rtc/v2/v2f3.rs new file mode 100644 index 000000000..e041f3f4e --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2f3.rs @@ -0,0 +1,31 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + w.set_lseon(reg.lseon()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // Nothing to do +} diff --git a/embassy-stm32/src/rtc/v2/v2f4.rs b/embassy-stm32/src/rtc/v2/v2f4.rs new file mode 100644 index 000000000..4dd21cae4 --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2f4.rs @@ -0,0 +1,31 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + w.set_lseon(reg.lseon()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // Nothing to do +} diff --git a/embassy-stm32/src/rtc/v2/v2f7.rs b/embassy-stm32/src/rtc/v2/v2f7.rs new file mode 100644 index 000000000..d6871d91e --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2f7.rs @@ -0,0 +1,41 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + // Restore bcdr + w.set_lscosel(reg.lscosel()); + w.set_lscoen(reg.lscoen()); + + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // enable peripheral clock for communication + crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // read to allow the pwr clock to enable + crate::pac::PWR.cr1().read(); +} diff --git a/embassy-stm32/src/rtc/v2/v2h7.rs b/embassy-stm32/src/rtc/v2/v2h7.rs new file mode 100644 index 000000000..f3b180683 --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2h7.rs @@ -0,0 +1,33 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // Nothing to do +} diff --git a/embassy-stm32/src/rtc/v2/v2l0.rs b/embassy-stm32/src/rtc/v2/v2l0.rs new file mode 100644 index 000000000..8d8005887 --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2l0.rs @@ -0,0 +1,40 @@ +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(_clock_config: u8) { + // FIXME: + // crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + // while !crate::pac::PWR.cr1().read().dbp() {} + + // let reg = crate::pac::RCC.bdcr().read(); + // assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + // if !reg.rtcen() || reg.rtcsel().0 != clock_config { + // crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + // crate::pac::RCC.bdcr().modify(|w| { + // // Reset + // w.set_bdrst(false); + + // // Select RTC source + // w.set_rtcsel(Rtcsel(clock_config)); + // w.set_rtcen(true); + + // // Restore bcdr + // w.set_lscosel(reg.lscosel()); + // w.set_lscoen(reg.lscoen()); + + // w.set_lseon(reg.lseon()); + // w.set_lsedrv(reg.lsedrv()); + // w.set_lsebyp(reg.lsebyp()); + // }); + // } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // // enable peripheral clock for communication + // crate::pac::rcc.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // // read to allow the pwr clock to enable + // crate::pac::PWR.cr1().read(); +} diff --git a/embassy-stm32/src/rtc/v2/v2l1.rs b/embassy-stm32/src/rtc/v2/v2l1.rs new file mode 100644 index 000000000..8d8005887 --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2l1.rs @@ -0,0 +1,40 @@ +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(_clock_config: u8) { + // FIXME: + // crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + // while !crate::pac::PWR.cr1().read().dbp() {} + + // let reg = crate::pac::RCC.bdcr().read(); + // assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + // if !reg.rtcen() || reg.rtcsel().0 != clock_config { + // crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + // crate::pac::RCC.bdcr().modify(|w| { + // // Reset + // w.set_bdrst(false); + + // // Select RTC source + // w.set_rtcsel(Rtcsel(clock_config)); + // w.set_rtcen(true); + + // // Restore bcdr + // w.set_lscosel(reg.lscosel()); + // w.set_lscoen(reg.lscoen()); + + // w.set_lseon(reg.lseon()); + // w.set_lsedrv(reg.lsedrv()); + // w.set_lsebyp(reg.lsebyp()); + // }); + // } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // // enable peripheral clock for communication + // crate::pac::rcc.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // // read to allow the pwr clock to enable + // crate::pac::PWR.cr1().read(); +} diff --git a/embassy-stm32/src/rtc/v2/v2l4.rs b/embassy-stm32/src/rtc/v2/v2l4.rs new file mode 100644 index 000000000..d6871d91e --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2l4.rs @@ -0,0 +1,41 @@ +use stm32_metapac::rcc::vals::Rtcsel; + +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(Rtcsel(clock_config)); + w.set_rtcen(true); + + // Restore bcdr + w.set_lscosel(reg.lscosel()); + w.set_lscoen(reg.lscoen()); + + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // enable peripheral clock for communication + crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // read to allow the pwr clock to enable + crate::pac::PWR.cr1().read(); +} diff --git a/embassy-stm32/src/rtc/v2/v2wb.rs b/embassy-stm32/src/rtc/v2/v2wb.rs new file mode 100644 index 000000000..98761fa60 --- /dev/null +++ b/embassy-stm32/src/rtc/v2/v2wb.rs @@ -0,0 +1,39 @@ +pub const BACKUP_REGISTER_COUNT: usize = 20; + +/// Unlock the backup domain +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + + let reg = crate::pac::RCC.bdcr().read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + if !reg.rtcen() || reg.rtcsel() != clock_config { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(clock_config); + w.set_rtcen(true); + + // Restore bcdr + w.set_lscosel(reg.lscosel()); + w.set_lscoen(reg.lscoen()); + + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } +} + +pub(crate) unsafe fn enable_peripheral_clk() { + // enable peripheral clock for communication + crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // read to allow the pwr clock to enable + crate::pac::PWR.cr1().read(); +} diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs new file mode 100644 index 000000000..7255e97eb --- /dev/null +++ b/embassy-stm32/src/rtc/v3.rs @@ -0,0 +1,212 @@ +use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType}; + +use super::{Instance, RtcCalibrationCyclePeriod, RtcConfig}; +use crate::pac::rtc::Rtc; + +impl<'d, T: Instance> super::Rtc<'d, T> { + /// Applies the RTC config + /// It this changes the RTC clock source the time will be reset + pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { + // Unlock the backup domain + unsafe { + #[cfg(feature = "stm32g0c1ve")] + { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + } + + #[cfg(not(feature = "stm32g0c1ve"))] + { + crate::pac::PWR + .cr1() + .modify(|w| w.set_dbp(stm32_metapac::pwr::vals::Dbp::ENABLED)); + while crate::pac::PWR.cr1().read().dbp() != stm32_metapac::pwr::vals::Dbp::DISABLED {} + } + + let reg = crate::pac::RCC.bdcr().read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + let config_rtcsel = rtc_config.clock_config as u8; + #[cfg(rtc_v3)] + #[cfg(not(any(feature = "stm32wl54jc-cm0p", feature = "stm32wle5ub", feature = "stm32g0c1ve")))] + let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel); + #[cfg(feature = "stm32g0c1ve")] + let config_rtcsel = stm32_metapac::rcc::vals::Rtcsel(config_rtcsel); + + if !reg.rtcen() || reg.rtcsel() != config_rtcsel { + crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + + crate::pac::RCC.bdcr().modify(|w| { + // Reset + w.set_bdrst(false); + + // Select RTC source + w.set_rtcsel(config_rtcsel); + + w.set_rtcen(true); + + // Restore bcdr + w.set_lscosel(reg.lscosel()); + w.set_lscoen(reg.lscoen()); + + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } + } + + self.write(true, |rtc| { + unsafe { + rtc.cr().modify(|w| { + w.set_fmt(Fmt::TWENTYFOURHOUR); + w.set_osel(Osel::DISABLED); + w.set_pol(Pol::HIGH); + }); + + rtc.prer().modify(|w| { + w.set_prediv_s(rtc_config.sync_prescaler); + w.set_prediv_a(rtc_config.async_prescaler); + }); + + // TODO: configuration for output pins + rtc.cr().modify(|w| { + w.set_out2en(false); + w.set_tampalrm_type(TampalrmType::PUSHPULL); + w.set_tampalrm_pu(TampalrmPu::NOPULLUP); + }); + } + }); + + self.rtc_config = rtc_config; + } + + const RTC_CALR_MIN_PPM: f32 = -487.1; + const RTC_CALR_MAX_PPM: f32 = 488.5; + const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537; + + /// Calibrate the clock drift. + /// + /// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range. + /// + /// ### Note + /// + /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler` + /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12). + pub fn calibrate(&mut self, mut clock_drift: f32, period: RtcCalibrationCyclePeriod) { + if clock_drift < Self::RTC_CALR_MIN_PPM { + clock_drift = Self::RTC_CALR_MIN_PPM; + } else if clock_drift > Self::RTC_CALR_MAX_PPM { + clock_drift = Self::RTC_CALR_MAX_PPM; + } + + clock_drift = clock_drift / Self::RTC_CALR_RESOLUTION_PPM; + + self.write(false, |rtc| { + unsafe { + rtc.calr().write(|w| { + match period { + RtcCalibrationCyclePeriod::Seconds8 => { + w.set_calw8(Calw8::EIGHTSECONDS); + } + RtcCalibrationCyclePeriod::Seconds16 => { + w.set_calw16(Calw16::SIXTEENSECONDS); + } + RtcCalibrationCyclePeriod::Seconds32 => { + // Set neither `calw8` nor `calw16` to use 32 seconds + } + } + + // Extra pulses during calibration cycle period: CALP * 512 - CALM + // + // CALP sets whether pulses are added or omitted. + // + // CALM contains how many pulses (out of 512) are masked in a + // given calibration cycle period. + if clock_drift > 0.0 { + // Maximum (about 512.2) rounds to 512. + clock_drift += 0.5; + + // When the offset is positive (0 to 512), the opposite of + // the offset (512 - offset) is masked, i.e. for the + // maximum offset (512), 0 pulses are masked. + w.set_calp(Calp::INCREASEFREQ); + w.set_calm(512 - clock_drift as u16); + } else { + // Minimum (about -510.7) rounds to -511. + clock_drift -= 0.5; + + // When the offset is negative or zero (-511 to 0), + // the absolute offset is masked, i.e. for the minimum + // offset (-511), 511 pulses are masked. + w.set_calp(Calp::NOCHANGE); + w.set_calm((clock_drift * -1.0) as u16); + } + }); + } + }) + } + + pub(super) fn write(&mut self, init_mode: bool, f: F) -> R + where + F: FnOnce(&crate::pac::rtc::Rtc) -> R, + { + let r = T::regs(); + // Disable write protection. + // This is safe, as we're only writin the correct and expected values. + unsafe { + r.wpr().write(|w| w.set_key(Key::DEACTIVATE1)); + r.wpr().write(|w| w.set_key(Key::DEACTIVATE2)); + + if init_mode && !r.icsr().read().initf() { + r.icsr().modify(|w| w.set_init(Init::INITMODE)); + // wait till init state entered + // ~2 RTCCLK cycles + while !r.icsr().read().initf() {} + } + } + + let result = f(&r); + + unsafe { + if init_mode { + r.icsr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode + } + + // Re-enable write protection. + // This is safe, as the field accepts the full range of 8-bit values. + r.wpr().write(|w| w.set_key(Key::ACTIVATE)); + } + result + } +} + +pub(super) unsafe fn enable_peripheral_clk() { + // Nothing to do +} + +pub const BACKUP_REGISTER_COUNT: usize = 32; + +/// Read content of the backup register. +/// +/// The registers retain their values during wakes from standby mode or system resets. They also +/// retain their value when Vdd is switched off as long as V_BAT is powered. +pub fn read_backup_register(_rtc: &Rtc, register: usize) -> Option { + if register < BACKUP_REGISTER_COUNT { + //Some(rtc.bkpr()[register].read().bits()) + None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC + } else { + None + } +} + +/// Set content of the backup register. +/// +/// The registers retain their values during wakes from standby mode or system resets. They also +/// retain their value when Vdd is switched off as long as V_BAT is powered. +pub fn write_backup_register(_rtc: &Rtc, register: usize, _value: u32) { + if register < BACKUP_REGISTER_COUNT { + // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC + //unsafe { self.rtc.bkpr()[register].write(|w| w.bits(value)) } + } +} From 79cee7415110817b529a68848247a86708bcd6af Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 11 Oct 2022 09:19:47 +0200 Subject: [PATCH 02/46] Fix stm32wl55jc-cm4 RTC --- embassy-stm32/src/rtc/v3.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 7255e97eb..dfe27ed6e 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -28,7 +28,12 @@ impl<'d, T: Instance> super::Rtc<'d, T> { let config_rtcsel = rtc_config.clock_config as u8; #[cfg(rtc_v3)] - #[cfg(not(any(feature = "stm32wl54jc-cm0p", feature = "stm32wle5ub", feature = "stm32g0c1ve")))] + #[cfg(not(any( + feature = "stm32wl54jc-cm0p", + feature = "stm32wle5ub", + feature = "stm32g0c1ve", + feature = "stm32wl55jc-cm4" + )))] let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel); #[cfg(feature = "stm32g0c1ve")] let config_rtcsel = stm32_metapac::rcc::vals::Rtcsel(config_rtcsel); From 9223b67306fcdf41b03d2a9e5d833e5b71b44954 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 11 Oct 2022 10:28:28 +0200 Subject: [PATCH 03/46] Fix RTC for v2l0 & v2l1 --- embassy-stm32/src/rtc/v2/v2l0.rs | 46 +++++++++++--------------------- embassy-stm32/src/rtc/v2/v2l1.rs | 44 ++++++++++-------------------- embassy-stm32/src/rtc/v3.rs | 3 ++- 3 files changed, 32 insertions(+), 61 deletions(-) diff --git a/embassy-stm32/src/rtc/v2/v2l0.rs b/embassy-stm32/src/rtc/v2/v2l0.rs index 8d8005887..dbd3b0882 100644 --- a/embassy-stm32/src/rtc/v2/v2l0.rs +++ b/embassy-stm32/src/rtc/v2/v2l0.rs @@ -1,40 +1,26 @@ pub const BACKUP_REGISTER_COUNT: usize = 20; /// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(_clock_config: u8) { - // FIXME: - // crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - // while !crate::pac::PWR.cr1().read().dbp() {} +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + // TODO: Missing from PAC? + // crate::pac::PWR.cr().modify(|w| w.set_dbp(true)); + // while !crate::pac::PWR.cr().read().dbp() {} - // let reg = crate::pac::RCC.bdcr().read(); - // assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + let reg = crate::pac::RCC.csr().read(); - // if !reg.rtcen() || reg.rtcsel().0 != clock_config { - // crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.csr().modify(|w| { + // Select RTC source + w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config)); + w.set_rtcen(true); - // crate::pac::RCC.bdcr().modify(|w| { - // // Reset - // w.set_bdrst(false); - - // // Select RTC source - // w.set_rtcsel(Rtcsel(clock_config)); - // w.set_rtcen(true); - - // // Restore bcdr - // w.set_lscosel(reg.lscosel()); - // w.set_lscoen(reg.lscoen()); - - // w.set_lseon(reg.lseon()); - // w.set_lsedrv(reg.lsedrv()); - // w.set_lsebyp(reg.lsebyp()); - // }); - // } + w.set_lseon(reg.lseon()); + w.set_lsedrv(reg.lsedrv()); + w.set_lsebyp(reg.lsebyp()); + }); + } } pub(crate) unsafe fn enable_peripheral_clk() { - // // enable peripheral clock for communication - // crate::pac::rcc.apb1enr1().modify(|w| w.set_rtcapben(true)); - - // // read to allow the pwr clock to enable - // crate::pac::PWR.cr1().read(); + // Nothing to do } diff --git a/embassy-stm32/src/rtc/v2/v2l1.rs b/embassy-stm32/src/rtc/v2/v2l1.rs index 8d8005887..1ac78b31a 100644 --- a/embassy-stm32/src/rtc/v2/v2l1.rs +++ b/embassy-stm32/src/rtc/v2/v2l1.rs @@ -1,40 +1,24 @@ pub const BACKUP_REGISTER_COUNT: usize = 20; /// Unlock the backup domain -pub(super) unsafe fn unlock_backup_domain(_clock_config: u8) { - // FIXME: - // crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - // while !crate::pac::PWR.cr1().read().dbp() {} +pub(super) unsafe fn unlock_backup_domain(clock_config: u8) { + crate::pac::PWR.cr().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr().read().dbp() {} - // let reg = crate::pac::RCC.bdcr().read(); - // assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + let reg = crate::pac::RCC.csr().read(); - // if !reg.rtcen() || reg.rtcsel().0 != clock_config { - // crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + if !reg.rtcen() || reg.rtcsel().0 != clock_config { + crate::pac::RCC.csr().modify(|w| { + // Select RTC source + w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config)); + w.set_rtcen(true); - // crate::pac::RCC.bdcr().modify(|w| { - // // Reset - // w.set_bdrst(false); - - // // Select RTC source - // w.set_rtcsel(Rtcsel(clock_config)); - // w.set_rtcen(true); - - // // Restore bcdr - // w.set_lscosel(reg.lscosel()); - // w.set_lscoen(reg.lscoen()); - - // w.set_lseon(reg.lseon()); - // w.set_lsedrv(reg.lsedrv()); - // w.set_lsebyp(reg.lsebyp()); - // }); - // } + w.set_lseon(reg.lseon()); + w.set_lsebyp(reg.lsebyp()); + }); + } } pub(crate) unsafe fn enable_peripheral_clk() { - // // enable peripheral clock for communication - // crate::pac::rcc.apb1enr1().modify(|w| w.set_rtcapben(true)); - - // // read to allow the pwr clock to enable - // crate::pac::PWR.cr1().read(); + // Nothing to do } diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index dfe27ed6e..c9a794c3a 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -32,7 +32,8 @@ impl<'d, T: Instance> super::Rtc<'d, T> { feature = "stm32wl54jc-cm0p", feature = "stm32wle5ub", feature = "stm32g0c1ve", - feature = "stm32wl55jc-cm4" + feature = "stm32wl55jc-cm4", + feature = "stm32wl55uc-cm4" )))] let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel); #[cfg(feature = "stm32g0c1ve")] From 86113e199f37fe0888979608a08bfdaf21bff19a Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 11 Oct 2022 10:35:43 +0200 Subject: [PATCH 04/46] Remove unused feature gate --- embassy-stm32/src/rtc/v3.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index c9a794c3a..eba67c28f 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -27,7 +27,6 @@ impl<'d, T: Instance> super::Rtc<'d, T> { assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); let config_rtcsel = rtc_config.clock_config as u8; - #[cfg(rtc_v3)] #[cfg(not(any( feature = "stm32wl54jc-cm0p", feature = "stm32wle5ub", From 5e74926907b48b7f778de2ff4dfd6f1fd6e24dcb Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 13 Feb 2023 15:12:33 +0100 Subject: [PATCH 05/46] feature-gate variants without vals defined --- embassy-stm32/src/rtc/v3.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index eba67c28f..6998c48c2 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -15,7 +15,12 @@ impl<'d, T: Instance> super::Rtc<'d, T> { while !crate::pac::PWR.cr1().read().dbp() {} } - #[cfg(not(feature = "stm32g0c1ve"))] + #[cfg(not(any( + feature = "stm32g0c1ve", + feature = "stm32g491re", + feature = "stm32u585zi", + feature = "stm32g473cc" + )))] { crate::pac::PWR .cr1() @@ -32,7 +37,11 @@ impl<'d, T: Instance> super::Rtc<'d, T> { feature = "stm32wle5ub", feature = "stm32g0c1ve", feature = "stm32wl55jc-cm4", - feature = "stm32wl55uc-cm4" + feature = "stm32wl55uc-cm4", + feature = "stm32g491re", + feature = "stm32g473cc", + feature = "stm32u585zi", + feature = "stm32wle5jb" )))] let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel); #[cfg(feature = "stm32g0c1ve")] From 0289630fe429e0a8b84073b740ae839eb8ef5909 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 12 Apr 2023 18:04:44 -0500 Subject: [PATCH 06/46] stm32/rcc: add i2s pll on some f4 micros --- embassy-stm32/src/rcc/f4.rs | 67 ++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index 2a17eb9b0..3e2345c6b 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -28,11 +28,65 @@ pub struct Config { pub sys_ck: Option, pub pclk1: Option, pub pclk2: Option, + pub plli2s: Option, pub pll48: bool, } -unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48clk: bool) -> PllResults { +#[cfg(stm32f410)] +unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { + None +} + +// Not currently implemented, but will be in the future +#[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))] +unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { + None +} + +#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] +unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { + let min_div = 2; + let max_div = 7; + let target = match plli2s { + Some(target) => target, + None => return None, + }; + + // We loop through the possible divider values to find the best configuration. Looping + // through all possible "N" values would result in more iterations. + let (n, outdiv, output, _error) = (min_div..=max_div) + .filter_map(|outdiv| { + let target_vco_out = match target.checked_mul(outdiv) { + Some(x) => x, + None => return None, + }; + let n = (target_vco_out + (vco_in >> 1)) / vco_in; + let vco_out = vco_in * n; + if !(100_000_000..=432_000_000).contains(&vco_out) { + return None; + } + let output = vco_out / outdiv; + let error = (output as i32 - target as i32).unsigned_abs(); + Some((n, outdiv, output, error)) + }) + .min_by_key(|(_, _, _, error)| *error)?; + + RCC.plli2scfgr().modify(|w| { + w.set_plli2sn(n as u16); + w.set_plli2sr(outdiv as u8); + }); + + Some(output) +} + +unsafe fn setup_pll( + pllsrcclk: u32, + use_hse: bool, + pllsysclk: Option, + plli2s: Option, + pll48clk: bool, +) -> PllResults { use crate::pac::rcc::vals::{Pllp, Pllsrc}; let sysclk = pllsysclk.unwrap_or(pllsrcclk); @@ -43,6 +97,7 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48 use_pll: false, pllsysclk: None, pll48clk: None, + plli2sclk: None, }; } // Input divisor from PLL source clock, must result to frequency in @@ -101,6 +156,7 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option, pll48 use_pll: true, pllsysclk: Some(real_pllsysclk), pll48clk: if pll48clk { Some(real_pll48clk) } else { None }, + plli2sclk: setup_i2s_pll(vco_in, plli2s), } } @@ -286,6 +342,7 @@ pub(crate) unsafe fn init(config: Config) { pllsrcclk, config.hse.is_some(), if sysclk_on_pll { Some(sysclk) } else { None }, + config.plli2s.map(|i2s| i2s.0), config.pll48, ); @@ -376,6 +433,13 @@ pub(crate) unsafe fn init(config: Config) { while !RCC.cr().read().pllrdy() {} } + #[cfg(not(stm32f410))] + if plls.plli2sclk.is_some() { + RCC.cr().modify(|w| w.set_plli2son(true)); + + while !RCC.cr().read().plli2srdy() {} + } + RCC.cfgr().modify(|w| { w.set_ppre2(Ppre(ppre2_bits)); w.set_ppre1(Ppre(ppre1_bits)); @@ -416,6 +480,7 @@ struct PllResults { use_pll: bool, pllsysclk: Option, pll48clk: Option, + plli2sclk: Option, } mod max { From c1d5f868714accd6780913e652d8a884368c60d3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 12 Apr 2023 18:11:55 -0500 Subject: [PATCH 07/46] stm32/rcc: fix warnings --- embassy-stm32/src/rcc/f4.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index 3e2345c6b..e8dfba011 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -34,13 +34,13 @@ pub struct Config { } #[cfg(stm32f410)] -unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { +unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option) -> Option { None } // Not currently implemented, but will be in the future #[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))] -unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option) -> Option { +unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option) -> Option { None } @@ -480,6 +480,7 @@ struct PllResults { use_pll: bool, pllsysclk: Option, pll48clk: Option, + #[allow(dead_code)] plli2sclk: Option, } From 6a6c673c5fd1baa1d3ca3eebb55ba430a86b1438 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Thu, 13 Apr 2023 14:21:41 -0500 Subject: [PATCH 08/46] Executor: Replace unnecessary atomics in runqueue --- embassy-executor/src/raw/run_queue.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index a88174a0c..f1ec19ac1 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -4,15 +4,16 @@ use core::ptr::NonNull; use atomic_polyfill::{AtomicPtr, Ordering}; use super::{TaskHeader, TaskRef}; +use crate::raw::util::SyncUnsafeCell; pub(crate) struct RunQueueItem { - next: AtomicPtr, + next: SyncUnsafeCell>, } impl RunQueueItem { pub const fn new() -> Self { Self { - next: AtomicPtr::new(ptr::null_mut()), + next: SyncUnsafeCell::new(None), } } } @@ -51,7 +52,12 @@ impl RunQueue { self.head .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| { was_empty = prev.is_null(); - task.header().run_queue_item.next.store(prev, Ordering::Relaxed); + unsafe { + // safety: the pointer is either null or valid + let prev = NonNull::new(prev).map(|ptr| TaskRef::from_ptr(ptr.as_ptr())); + // safety: there are no concurrent accesses to `next` + task.header().run_queue_item.next.set(prev); + } Some(task.as_ptr() as *mut _) }) .ok(); @@ -64,18 +70,19 @@ impl RunQueue { /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { // Atomically empty the queue. - let mut ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel); + let ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel); + + // safety: the pointer is either null or valid + let mut next = unsafe { NonNull::new(ptr).map(|ptr| TaskRef::from_ptr(ptr.as_ptr())) }; // Iterate the linked list of tasks that were previously in the queue. - while let Some(task) = NonNull::new(ptr) { - let task = unsafe { TaskRef::from_ptr(task.as_ptr()) }; + while let Some(task) = next { // If the task re-enqueues itself, the `next` pointer will get overwritten. // Therefore, first read the next pointer, and only then process the task. - let next = task.header().run_queue_item.next.load(Ordering::Relaxed); + // safety: there are no concurrent accesses to `next` + next = unsafe { task.header().run_queue_item.next.get() }; on_task(task); - - ptr = next } } } From b9fc2a6b33d8af88d95d750965f3fb8b1e9032d4 Mon Sep 17 00:00:00 2001 From: Jacob Davis-Hansson Date: Fri, 14 Apr 2023 21:05:03 +0200 Subject: [PATCH 09/46] Add ability to invert UART pins This is useful in some cases where the surrounding circuit for some reason inverts the UART signal, for instance if you're talking to a device via an optocoupler. --- embassy-rp/src/uart/mod.rs | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index a945f2295..dfa56a842 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -6,6 +6,7 @@ use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; +use crate::pac::io::vals::Inover; #[cfg(feature = "nightly")] mod buffered; @@ -53,6 +54,14 @@ pub struct Config { pub data_bits: DataBits, pub stop_bits: StopBits, pub parity: Parity, + /// Invert the tx pin output + pub invert_tx: bool, + /// Invert the rx pin input + pub invert_rx: bool, + // Invert the rts pin + pub invert_rts: bool, + // Invert the cts pin + pub invert_cts: bool, } impl Default for Config { @@ -62,6 +71,10 @@ impl Default for Config { data_bits: DataBits::DataBits8, stop_bits: StopBits::STOP1, parity: Parity::ParityNone, + invert_rx: false, + invert_tx: false, + invert_rts: false, + invert_cts: false, } } } @@ -381,19 +394,31 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { let r = T::regs(); unsafe { if let Some(pin) = &tx { - pin.io().ctrl().write(|w| w.set_funcsel(2)); + pin.io().ctrl().write(|w| { + w.set_funcsel(2); + w.set_inover(if config.invert_tx { Inover::INVERT } else { Inover::NORMAL }); + }); pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rx { - pin.io().ctrl().write(|w| w.set_funcsel(2)); + pin.io().ctrl().write(|w| { + w.set_funcsel(2); + w.set_inover(if config.invert_rx { Inover::INVERT } else { Inover::NORMAL }); + }); pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &cts { - pin.io().ctrl().write(|w| w.set_funcsel(2)); + pin.io().ctrl().write(|w| { + w.set_funcsel(2); + w.set_inover(if config.invert_cts { Inover::INVERT } else { Inover::NORMAL }); + }); pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rts { - pin.io().ctrl().write(|w| w.set_funcsel(2)); + pin.io().ctrl().write(|w| { + w.set_funcsel(2); + w.set_inover(if config.invert_rts { Inover::INVERT } else { Inover::NORMAL }); + }); pin.pad_ctrl().write(|w| w.set_ie(true)); } From 650589ab3f030ed63c129245c89e3056bc5f31e5 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 14 Apr 2023 16:30:36 -0500 Subject: [PATCH 10/46] stm32/rcc: add plli2s to Clocks and cfg directives --- embassy-stm32/src/rcc/f4.rs | 8 ++++++++ embassy-stm32/src/rcc/mod.rs | 3 +++ 2 files changed, 11 insertions(+) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index e8dfba011..5427d2fb4 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -28,6 +28,8 @@ pub struct Config { pub sys_ck: Option, pub pclk1: Option, pub pclk2: Option, + + #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] pub plli2s: Option, pub pll48: bool, @@ -342,7 +344,10 @@ pub(crate) unsafe fn init(config: Config) { pllsrcclk, config.hse.is_some(), if sysclk_on_pll { Some(sysclk) } else { None }, + #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] config.plli2s.map(|i2s| i2s.0), + #[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))] + None, config.pll48, ); @@ -473,6 +478,9 @@ pub(crate) unsafe fn init(config: Config) { ahb3: Hertz(hclk), pll48: plls.pll48clk.map(Hertz), + + #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] + plli2s: plls.plli2sclk.map(Hertz), }); } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index d6a31f17b..7c1e416fe 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -60,6 +60,9 @@ pub struct Clocks { #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))] pub pll48: Option, + #[cfg(all(stm32f4, not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))))] + pub plli2s: Option, + #[cfg(stm32f1)] pub adc: Hertz, From f681b9d4e5fc142ee0bb847e0e00260c932a1401 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 14 Apr 2023 22:39:53 +0200 Subject: [PATCH 11/46] Remove the _todo_embedded_hal_serial impls. EH will probably not have these serial traits. --- embassy-embedded-hal/src/adapter.rs | 42 ---------------- embassy-nrf/src/uarte.rs | 77 ----------------------------- embassy-rp/src/uart/buffered.rs | 55 --------------------- embassy-rp/src/uart/mod.rs | 55 --------------------- embassy-stm32/src/usart/mod.rs | 67 ------------------------- 5 files changed, 296 deletions(-) diff --git a/embassy-embedded-hal/src/adapter.rs b/embassy-embedded-hal/src/adapter.rs index ee919bd84..171ff6c9f 100644 --- a/embassy-embedded-hal/src/adapter.rs +++ b/embassy-embedded-hal/src/adapter.rs @@ -131,48 +131,6 @@ where type Error = E; } -#[cfg(feature = "_todo_embedded_hal_serial")] -impl embedded_hal_async::serial::Read for BlockingAsync -where - T: serial::Read, - E: embedded_hal_1::serial::Error + 'static, -{ - type ReadFuture<'a> = impl Future> + 'a where T: 'a; - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - async move { - let mut pos = 0; - while pos < buf.len() { - match self.wrapped.read() { - Err(nb::Error::WouldBlock) => {} - Err(nb::Error::Other(e)) => return Err(e), - Ok(b) => { - buf[pos] = b; - pos += 1; - } - } - } - Ok(()) - } - } -} - -#[cfg(feature = "_todo_embedded_hal_serial")] -impl embedded_hal_async::serial::Write for BlockingAsync -where - T: blocking::serial::Write + serial::Read, - E: embedded_hal_1::serial::Error + 'static, -{ - type WriteFuture<'a> = impl Future> + 'a where T: 'a; - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - async move { self.wrapped.bwrite_all(buf) } - } - - type FlushFuture<'a> = impl Future> + 'a where T: 'a; - fn flush(&mut self) -> Result<(), Self::Error> { - async move { self.wrapped.bflush() } - } -} - /// NOR flash wrapper use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index e59b2332a..586c88b2d 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -992,80 +992,3 @@ mod eh1 { type Error = Error; } } - -#[cfg(all( - feature = "unstable-traits", - feature = "nightly", - feature = "_todo_embedded_hal_serial" -))] -mod eha { - use core::future::Future; - - use super::*; - - impl<'d, T: Instance> embedded_hal_async::serial::Read for Uarte<'d, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buffer) - } - } - - impl<'d, T: Instance> embedded_hal_async::serial::Write for Uarte<'d, T> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buffer) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush(&mut self) -> Result<(), Self::Error> { - async move { Ok(()) } - } - } - - impl<'d, T: Instance> embedded_hal_async::serial::Write for UarteTx<'d, T> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buffer) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush(&mut self) -> Result<(), Self::Error> { - async move { Ok(()) } - } - } - - impl<'d, T: Instance> embedded_hal_async::serial::Read for UarteRx<'d, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buffer) - } - } - - impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read for UarteWithIdle<'d, U, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buffer) - } - } - - impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Write for UarteWithIdle<'d, U, T> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buffer) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush(&mut self) -> Result<(), Self::Error> { - async move { Ok(()) } - } - } -} diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index c620ed08c..cb0461930 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -726,58 +726,3 @@ mod eh1 { } } } - -#[cfg(all( - feature = "unstable-traits", - feature = "nightly", - feature = "_todo_embedded_hal_serial" -))] -mod eha { - use core::future::Future; - - use super::*; - - impl<'d, T: Instance> embedded_hal_async::serial::Write for BufferedUartTx<'d, T> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - Self::write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - Self::flush() - } - } - - impl<'d, T: Instance> embedded_hal_async::serial::Read for BufferedUartRx<'d, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - Self::read(buf) - } - } - - impl<'d, T: Instance> embedded_hal_async::serial::Write for BufferedUart<'d, T> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - BufferedUartTx::<'d, T>::write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - BufferedUartTx::<'d, T>::flush() - } - } - - impl<'d, T: Instance> embedded_hal_async::serial::Read for BufferedUart<'d, T> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - BufferedUartRx::<'d, T>::read(buf) - } - } -} diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index a945f2295..7122930f5 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -651,61 +651,6 @@ mod eh1 { } } -#[cfg(all( - feature = "unstable-traits", - feature = "nightly", - feature = "_todo_embedded_hal_serial" -))] -mod eha { - use core::future::Future; - - use super::*; - - impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for UartTx<'d, T, M> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } - } - - impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for UartRx<'d, T, M> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buf) - } - } - - impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for Uart<'d, T, M> { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } - } - - impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for Uart<'d, T, M> { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buf) - } - } -} - mod sealed { use super::*; diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index a42eede18..8bbba305b 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -973,73 +973,6 @@ mod eio { } } -#[cfg(all( - feature = "unstable-traits", - feature = "nightly", - feature = "_todo_embedded_hal_serial" -))] -mod eha { - use core::future::Future; - - use super::*; - - impl<'d, T: BasicInstance, TxDma> embedded_hal_async::serial::Write for UartTx<'d, T, TxDma> - where - TxDma: crate::usart::TxDma, - { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } - } - - impl<'d, T: BasicInstance, RxDma> embedded_hal_async::serial::Read for UartRx<'d, T, RxDma> - where - RxDma: crate::usart::RxDma, - { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buf) - } - } - - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Write for Uart<'d, T, TxDma, RxDma> - where - TxDma: crate::usart::TxDma, - { - type WriteFuture<'a> = impl Future> + 'a where Self: 'a; - - fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { - self.write(buf) - } - - type FlushFuture<'a> = impl Future> + 'a where Self: 'a; - - fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { - async move { Ok(()) } - } - } - - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_async::serial::Read for Uart<'d, T, TxDma, RxDma> - where - RxDma: crate::usart::RxDma, - { - type ReadFuture<'a> = impl Future> + 'a where Self: 'a; - - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { - self.read(buf) - } - } -} - #[cfg(feature = "nightly")] pub use buffered::*; #[cfg(feature = "nightly")] From 224eaaf79792a04a25bf7d5e768da41b2a030f7a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 14 Apr 2023 22:49:56 +0200 Subject: [PATCH 12/46] stm32/sdmmc: switch to AFIT. --- embassy-stm32/Cargo.toml | 2 +- embassy-stm32/src/sdmmc/mod.rs | 56 +++++++++++++--------------------- examples/stm32f4/Cargo.toml | 2 +- 3 files changed, 23 insertions(+), 37 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 969dc3b70..18b1d4d0e 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -55,7 +55,7 @@ cortex-m = "0.7.6" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } rand_core = "0.6.3" sdio-host = "0.5.0" -embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "46d1b1c2ff13e31e282ec1e352421721694f126a", optional = true } +embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" stm32-metapac = "6" diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 03d24dcb1..ac00b5176 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1651,8 +1651,6 @@ foreach_peripheral!( #[cfg(feature = "embedded-sdmmc")] mod sdmmc_rs { - use core::future::Future; - use embedded_sdmmc::{Block, BlockCount, BlockDevice, BlockIdx}; use super::*; @@ -1660,49 +1658,37 @@ mod sdmmc_rs { impl<'d, T: Instance, Dma: SdmmcDma> BlockDevice for Sdmmc<'d, T, Dma> { type Error = Error; - type ReadFuture<'a> = impl Future> + 'a - where - Self: 'a; - - type WriteFuture<'a> = impl Future> + 'a - where - Self: 'a; - - fn read<'a>( - &'a mut self, - blocks: &'a mut [Block], + async fn read( + &mut self, + blocks: &mut [Block], start_block_idx: BlockIdx, _reason: &str, - ) -> Self::ReadFuture<'a> { - async move { - let mut address = start_block_idx.0; + ) -> Result<(), Self::Error> { + let mut address = start_block_idx.0; - for block in blocks.iter_mut() { - let block: &mut [u8; 512] = &mut block.contents; + for block in blocks.iter_mut() { + let block: &mut [u8; 512] = &mut block.contents; - // NOTE(unsafe) Block uses align(4) - let block = unsafe { &mut *(block as *mut _ as *mut DataBlock) }; - self.read_block(address, block).await?; - address += 1; - } - Ok(()) + // NOTE(unsafe) Block uses align(4) + let block = unsafe { &mut *(block as *mut _ as *mut DataBlock) }; + self.read_block(address, block).await?; + address += 1; } + Ok(()) } - fn write<'a>(&'a mut self, blocks: &'a [Block], start_block_idx: BlockIdx) -> Self::WriteFuture<'a> { - async move { - let mut address = start_block_idx.0; + async fn write(&mut self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> { + let mut address = start_block_idx.0; - for block in blocks.iter() { - let block: &[u8; 512] = &block.contents; + for block in blocks.iter() { + let block: &[u8; 512] = &block.contents; - // NOTE(unsafe) DataBlock uses align 4 - let block = unsafe { &*(block as *const _ as *const DataBlock) }; - self.write_block(address, block).await?; - address += 1; - } - Ok(()) + // NOTE(unsafe) DataBlock uses align 4 + let block = unsafe { &*(block as *const _ as *const DataBlock) }; + self.write_block(address, block).await?; + address += 1; } + Ok(()) } fn num_blocks(&self) -> Result { diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 4b2f3d21c..1736769ef 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } From 63941432e3f68a0a3f2f4e97a807bfe16f4aff2b Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 14 Apr 2023 23:01:13 +0200 Subject: [PATCH 13/46] Update to rust-lorawan with afit support --- embassy-lora/Cargo.toml | 4 +- embassy-lora/src/lib.rs | 13 ++- embassy-lora/src/stm32wl/mod.rs | 12 +-- embassy-lora/src/sx126x/mod.rs | 132 +++++++++++------------- embassy-lora/src/sx127x/mod.rs | 151 ++++++++++++---------------- examples/nrf52840/Cargo.toml | 4 +- examples/stm32l0/Cargo.toml | 4 +- examples/stm32l0/src/bin/lorawan.rs | 2 +- examples/stm32wl/Cargo.toml | 4 +- examples/stm32wl/src/bin/lorawan.rs | 2 +- 10 files changed, 142 insertions(+), 186 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 79d5660f4..13b3acab2 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -38,5 +38,5 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw embedded-hal = { version = "0.2", features = ["unproven"] } bit_field = { version = "0.10" } -lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } -lorawan = { version = "0.7.1", default-features = false } +lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"] } +lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false } diff --git a/embassy-lora/src/lib.rs b/embassy-lora/src/lib.rs index 3e4748430..5c919cbb6 100644 --- a/embassy-lora/src/lib.rs +++ b/embassy-lora/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] -#![feature(type_alias_impl_trait)] +#![feature(async_fn_in_trait, impl_trait_projections)] +#![allow(incomplete_features)] //! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device //! crate's async LoRaWAN MAC implementation. @@ -34,13 +35,11 @@ impl lorawan_device::async_device::radio::Timer for LoraTimer { self.start = Instant::now(); } - type AtFuture<'m> = impl core::future::Future + 'm; - fn at<'m>(&'m mut self, millis: u64) -> Self::AtFuture<'m> { - Timer::at(self.start + Duration::from_millis(millis)) + async fn at(&mut self, millis: u64) { + Timer::at(self.start + Duration::from_millis(millis)).await } - type DelayFuture<'m> = impl core::future::Future + 'm; - fn delay_ms<'m>(&'m mut self, millis: u64) -> Self::DelayFuture<'m> { - Timer::after(Duration::from_millis(millis)) + async fn delay_ms(&mut self, millis: u64) { + Timer::after(Duration::from_millis(millis)).await } } diff --git a/embassy-lora/src/stm32wl/mod.rs b/embassy-lora/src/stm32wl/mod.rs index 3d52c1cc7..d76e8c43b 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::{poll_fn, Future}; +use core::future::poll_fn; use core::task::Poll; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; @@ -241,14 +241,12 @@ fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConf impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> { type PhyError = RadioError; - type TxFuture<'m> = impl Future> + 'm where Self: 'm; - fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { - async move { self.do_tx(config, buf).await } + async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { + self.do_tx(config, buf).await } - type RxFuture<'m> = impl Future> + 'm where Self: 'm; - fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { - async move { self.do_rx(config, buf).await } + async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> { + self.do_rx(config, buf).await } } diff --git a/embassy-lora/src/sx126x/mod.rs b/embassy-lora/src/sx126x/mod.rs index 8559574cb..2f0b8c8e3 100644 --- a/embassy-lora/src/sx126x/mod.rs +++ b/embassy-lora/src/sx126x/mod.rs @@ -1,5 +1,3 @@ -use core::future::Future; - use defmt::Format; use embedded_hal::digital::v2::OutputPin; use embedded_hal_async::digital::Wait; @@ -71,83 +69,69 @@ where { type PhyError = RadioError; - type TxFuture<'m> = impl Future> + 'm - where - SPI: 'm, - CTRL: 'm, - WAIT: 'm, - BUS: 'm; - - fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> { + async fn tx(&mut self, config: TxConfig, buffer: &[u8]) -> Result { trace!("TX START"); - async move { - self.lora - .set_tx_config( - config.pw, - config.rf.spreading_factor.into(), - config.rf.bandwidth.into(), - config.rf.coding_rate.into(), - 8, - false, - true, - false, - 0, - false, - ) - .await?; - self.lora.set_max_payload_length(buffer.len() as u8).await?; - self.lora.set_channel(config.rf.frequency).await?; - self.lora.send(buffer, 0xffffff).await?; - self.lora.process_irq(None, None, None).await?; - trace!("TX DONE"); - return Ok(0); - } + self.lora + .set_tx_config( + config.pw, + config.rf.spreading_factor.into(), + config.rf.bandwidth.into(), + config.rf.coding_rate.into(), + 8, + false, + true, + false, + 0, + false, + ) + .await?; + self.lora.set_max_payload_length(buffer.len() as u8).await?; + self.lora.set_channel(config.rf.frequency).await?; + self.lora.send(buffer, 0xffffff).await?; + self.lora.process_irq(None, None, None).await?; + trace!("TX DONE"); + return Ok(0); } - type RxFuture<'m> = impl Future> + 'm - where - SPI: 'm, - CTRL: 'm, - WAIT: 'm, - BUS: 'm; - - fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> { + async fn rx( + &mut self, + config: RfConfig, + receiving_buffer: &mut [u8], + ) -> Result<(usize, RxQuality), Self::PhyError> { trace!("RX START"); - async move { - self.lora - .set_rx_config( - config.spreading_factor.into(), - config.bandwidth.into(), - config.coding_rate.into(), - 8, - 4, - false, - 0u8, - true, - false, - 0, - true, - true, - ) - .await?; - self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?; - self.lora.set_channel(config.frequency).await?; - self.lora.rx(90 * 1000).await?; - let mut received_len = 0u8; - self.lora - .process_irq(Some(receiving_buffer), Some(&mut received_len), None) - .await?; - trace!("RX DONE"); + self.lora + .set_rx_config( + config.spreading_factor.into(), + config.bandwidth.into(), + config.coding_rate.into(), + 8, + 4, + false, + 0u8, + true, + false, + 0, + true, + true, + ) + .await?; + self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?; + self.lora.set_channel(config.frequency).await?; + self.lora.rx(90 * 1000).await?; + let mut received_len = 0u8; + self.lora + .process_irq(Some(receiving_buffer), Some(&mut received_len), None) + .await?; + trace!("RX DONE"); - let packet_status = self.lora.get_latest_packet_status(); - let mut rssi = 0i16; - let mut snr = 0i8; - if packet_status.is_some() { - rssi = packet_status.unwrap().rssi as i16; - snr = packet_status.unwrap().snr; - } - - Ok((received_len as usize, RxQuality::new(rssi, snr))) + let packet_status = self.lora.get_latest_packet_status(); + let mut rssi = 0i16; + let mut snr = 0i8; + if packet_status.is_some() { + rssi = packet_status.unwrap().rssi as i16; + snr = packet_status.unwrap().snr; } + + Ok((received_len as usize, RxQuality::new(rssi, snr))) } } diff --git a/embassy-lora/src/sx127x/mod.rs b/embassy-lora/src/sx127x/mod.rs index 8904c9a13..4e8dc2232 100644 --- a/embassy-lora/src/sx127x/mod.rs +++ b/embassy-lora/src/sx127x/mod.rs @@ -1,5 +1,3 @@ -use core::future::Future; - use embedded_hal::digital::v2::OutputPin; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::*; @@ -88,101 +86,78 @@ where { type PhyError = Sx127xError; - type TxFuture<'m> = impl Future> + 'm - where - SPI: 'm, - CS: 'm, - RESET: 'm, - E: 'm, - I: 'm, - RFS: 'm; - - fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> { + async fn tx(&mut self, config: TxConfig, buf: &[u8]) -> Result { trace!("TX START"); - async move { + self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); + self.rfs.set_tx(); + self.radio.set_tx_power(14, 0).await?; + self.radio.set_frequency(config.rf.frequency).await?; + // TODO: Modify radio to support other coding rates + self.radio.set_coding_rate_4(5).await?; + self.radio + .set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth)) + .await?; + self.radio + .set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor)) + .await?; + + self.radio.set_preamble_length(8).await?; + self.radio.set_lora_pa_ramp().await?; + self.radio.set_lora_sync_word().await?; + self.radio.set_invert_iq(false).await?; + self.radio.set_crc(true).await?; + + self.radio.set_dio0_tx_done().await?; + + self.radio.transmit_start(buf).await?; + + loop { + self.irq.wait_for_rising_edge().await.unwrap(); self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); - self.rfs.set_tx(); - self.radio.set_tx_power(14, 0).await?; - self.radio.set_frequency(config.rf.frequency).await?; - // TODO: Modify radio to support other coding rates - self.radio.set_coding_rate_4(5).await?; - self.radio - .set_signal_bandwidth(bandwidth_to_i64(config.rf.bandwidth)) - .await?; - self.radio - .set_spreading_factor(spreading_factor_to_u8(config.rf.spreading_factor)) - .await?; - - self.radio.set_preamble_length(8).await?; - self.radio.set_lora_pa_ramp().await?; - self.radio.set_lora_sync_word().await?; - self.radio.set_invert_iq(false).await?; - self.radio.set_crc(true).await?; - - self.radio.set_dio0_tx_done().await?; - - self.radio.transmit_start(buf).await?; - - loop { - self.irq.wait_for_rising_edge().await.unwrap(); - self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); - let irq = self.radio.clear_irq().await.ok().unwrap(); - if (irq & IRQ::IrqTxDoneMask.addr()) != 0 { - trace!("TX DONE"); - return Ok(0); - } + let irq = self.radio.clear_irq().await.ok().unwrap(); + if (irq & IRQ::IrqTxDoneMask.addr()) != 0 { + trace!("TX DONE"); + return Ok(0); } } } - type RxFuture<'m> = impl Future> + 'm - where - SPI: 'm, - CS: 'm, - RESET: 'm, - E: 'm, - I: 'm, - RFS: 'm; + async fn rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), Self::PhyError> { + self.rfs.set_rx(); + self.radio.reset_payload_length().await?; + self.radio.set_frequency(config.frequency).await?; + // TODO: Modify radio to support other coding rates + self.radio.set_coding_rate_4(5).await?; + self.radio + .set_signal_bandwidth(bandwidth_to_i64(config.bandwidth)) + .await?; + self.radio + .set_spreading_factor(spreading_factor_to_u8(config.spreading_factor)) + .await?; - fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> { - trace!("RX START"); - async move { - self.rfs.set_rx(); - self.radio.reset_payload_length().await?; - self.radio.set_frequency(config.frequency).await?; - // TODO: Modify radio to support other coding rates - self.radio.set_coding_rate_4(5).await?; - self.radio - .set_signal_bandwidth(bandwidth_to_i64(config.bandwidth)) - .await?; - self.radio - .set_spreading_factor(spreading_factor_to_u8(config.spreading_factor)) - .await?; + self.radio.set_preamble_length(8).await?; + self.radio.set_lora_sync_word().await?; + self.radio.set_invert_iq(true).await?; + self.radio.set_crc(true).await?; - self.radio.set_preamble_length(8).await?; - self.radio.set_lora_sync_word().await?; - self.radio.set_invert_iq(true).await?; - self.radio.set_crc(true).await?; + self.radio.set_dio0_rx_done().await?; + self.radio.set_mode(RadioMode::RxContinuous).await?; - self.radio.set_dio0_rx_done().await?; - self.radio.set_mode(RadioMode::RxContinuous).await?; - - loop { - self.irq.wait_for_rising_edge().await.unwrap(); - self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); - let irq = self.radio.clear_irq().await.ok().unwrap(); - if (irq & IRQ::IrqRxDoneMask.addr()) != 0 { - let rssi = self.radio.get_packet_rssi().await.unwrap_or(0) as i16; - let snr = self.radio.get_packet_snr().await.unwrap_or(0.0) as i8; - let response = if let Ok(size) = self.radio.read_packet_size().await { - self.radio.read_packet(buf).await?; - Ok((size, RxQuality::new(rssi, snr))) - } else { - Ok((0, RxQuality::new(rssi, snr))) - }; - trace!("RX DONE"); - return response; - } + loop { + self.irq.wait_for_rising_edge().await.unwrap(); + self.radio.set_mode(RadioMode::Stdby).await.ok().unwrap(); + let irq = self.radio.clear_irq().await.ok().unwrap(); + if (irq & IRQ::IrqRxDoneMask.addr()) != 0 { + let rssi = self.radio.get_packet_rssi().await.unwrap_or(0) as i16; + let snr = self.radio.get_packet_snr().await.unwrap_or(0.0) as i8; + let response = if let Ok(size) = self.radio.read_packet_size().await { + self.radio.read_packet(buf).await?; + Ok((size, RxQuality::new(rssi, snr))) + } else { + Ok((0, RxQuality::new(rssi, snr))) + }; + trace!("RX DONE"); + return response; } } } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index fc614cb80..9ef7944c4 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -20,8 +20,8 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } -lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } -lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } +lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"], optional = true } +lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index d446d41b2..9f63fee1c 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -15,8 +15,8 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} -lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true } -lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true } +lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"], optional = true } +lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32l0/src/bin/lorawan.rs b/examples/stm32l0/src/bin/lorawan.rs index 27d7c29c2..ea01f610c 100644 --- a/examples/stm32l0/src/bin/lorawan.rs +++ b/examples/stm32l0/src/bin/lorawan.rs @@ -45,7 +45,7 @@ async fn main(_spawner: Spawner) { let radio = Sx127xRadio::new(spi, cs, reset, ready_pin, DummySwitch).await.unwrap(); - let region = region::EU868::default().into(); + let region = region::Configuration::new(region::Region::EU868); let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); defmt::info!("Joining LoRaWAN network"); diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index df295ca49..19a7cfcea 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -11,8 +11,8 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } -lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] } -lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"] } +lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"] } +lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["default-crypto"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32wl/src/bin/lorawan.rs b/examples/stm32wl/src/bin/lorawan.rs index 7f34dd306..32f29cc5d 100644 --- a/examples/stm32wl/src/bin/lorawan.rs +++ b/examples/stm32wl/src/bin/lorawan.rs @@ -63,7 +63,7 @@ async fn main(_spawner: Spawner) { radio_config.calibrate_image = CalibrateImage::ISM_863_870; let radio = SubGhzRadio::new(radio, rfs, irq, radio_config).unwrap(); - let mut region: region::Configuration = region::EU868::default().into(); + let mut region = region::Configuration::new(region::Region::EU868); // NOTE: This is specific for TTN, as they have a special RX1 delay region.set_receive_delay1(5000); From f395ec44e8442a3133b080df96da54da18127b97 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 14 Apr 2023 21:28:27 -0500 Subject: [PATCH 14/46] stm32/rcc: add pllsai clock --- embassy-stm32/src/rcc/f4.rs | 5 ++++- embassy-stm32/src/rcc/mod.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index 5427d2fb4..e0929ca49 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -479,8 +479,11 @@ pub(crate) unsafe fn init(config: Config) { pll48: plls.pll48clk.map(Hertz), - #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))] + #[cfg(not(stm32f410))] plli2s: plls.plli2sclk.map(Hertz), + + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] + pllsai: None, }); } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 7c1e416fe..d6816d6a8 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -60,9 +60,12 @@ pub struct Clocks { #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))] pub pll48: Option, - #[cfg(all(stm32f4, not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))))] + #[cfg(all(rcc_f4, not(stm32f410)))] pub plli2s: Option, + #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] + pub pllsai: Option, + #[cfg(stm32f1)] pub adc: Hertz, From 81f10e136a95c33f98f5fa06ac3996bee97e66d3 Mon Sep 17 00:00:00 2001 From: Jacob Davis-Hansson Date: Sat, 15 Apr 2023 15:13:44 +0200 Subject: [PATCH 15/46] outover instead of inover --- embassy-rp/src/uart/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index dfa56a842..f7a4c5a60 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -6,7 +6,7 @@ use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; use crate::{pac, peripherals, Peripheral}; -use crate::pac::io::vals::Inover; +use crate::pac::io::vals::{Inover, Outover}; #[cfg(feature = "nightly")] mod buffered; @@ -396,7 +396,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { if let Some(pin) = &tx { pin.io().ctrl().write(|w| { w.set_funcsel(2); - w.set_inover(if config.invert_tx { Inover::INVERT } else { Inover::NORMAL }); + w.set_outover(if config.invert_tx { Outover::INVERT } else { Outover::NORMAL }); }); pin.pad_ctrl().write(|w| w.set_ie(true)); } @@ -417,7 +417,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { if let Some(pin) = &rts { pin.io().ctrl().write(|w| { w.set_funcsel(2); - w.set_inover(if config.invert_rts { Inover::INVERT } else { Inover::NORMAL }); + w.set_outover(if config.invert_rts { Outover::INVERT } else { Outover::NORMAL }); }); pin.pad_ctrl().write(|w| w.set_ie(true)); } From 8da9c07a656f375b62f76cdb601b6eba75b6d9d0 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 11:06:05 -0500 Subject: [PATCH 16/46] stm32/rtc: disable nonworking versions --- embassy-stm32/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 70e6aa2bf..675fd1ec1 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -49,7 +49,7 @@ pub mod pwm; pub mod qspi; #[cfg(rng)] pub mod rng; -#[cfg(all(rtc, not(rtc_v1)))] +#[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3))))] pub mod rtc; #[cfg(sdmmc)] pub mod sdmmc; From bc550cbfda773483dd1a90e87c2300f627695cea Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 11:06:24 -0500 Subject: [PATCH 17/46] adjust .vscode file --- .vscode/settings.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 700804dca..9ef7fe1ce 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,10 +16,11 @@ // "embassy-executor/Cargo.toml", // "embassy-sync/Cargo.toml", "examples/nrf52840/Cargo.toml", - //"examples/nrf5340/Cargo.toml", + // "examples/nrf5340/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", + // "examples/stm32c0/Cargo.toml", // "examples/stm32f0/Cargo.toml", // "examples/stm32f1/Cargo.toml", // "examples/stm32f2/Cargo.toml", @@ -28,6 +29,7 @@ // "examples/stm32f7/Cargo.toml", // "examples/stm32g0/Cargo.toml", // "examples/stm32g4/Cargo.toml", + // "examples/stm32h5/Cargo.toml", // "examples/stm32h7/Cargo.toml", // "examples/stm32l0/Cargo.toml", // "examples/stm32l1/Cargo.toml", @@ -35,9 +37,7 @@ // "examples/stm32l5/Cargo.toml", // "examples/stm32u5/Cargo.toml", // "examples/stm32wb/Cargo.toml", - // "examples/stm32wb55/Cargo.toml", // "examples/stm32wl/Cargo.toml", - // "examples/stm32wl55/Cargo.toml", // "examples/wasm/Cargo.toml", ], } \ No newline at end of file From e9ede443bca345f8651a7d8229b319432650dfce Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 11:14:17 -0500 Subject: [PATCH 18/46] stm32/rtc: disable nonworking versions --- embassy-stm32/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 675fd1ec1..0dbc9e5c8 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -49,7 +49,7 @@ pub mod pwm; pub mod qspi; #[cfg(rng)] pub mod rng; -#[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3))))] +#[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3, rtc_v3u5))))] pub mod rtc; #[cfg(sdmmc)] pub mod sdmmc; From 7a682ec02af50026d31296ce5cac6383580f5e55 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 16 Apr 2023 19:39:32 +0200 Subject: [PATCH 19/46] rp: add division intrinsics rp2040-hal adds division intrinsics using the hardware divider unit in the SIO, as does the pico-sdk itself. using the hardware is faster than the compiler_rt implementations, and more compact too. --- embassy-rp/src/intrinsics.rs | 198 +++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index 3e75fb7fc..3b63846d4 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -274,3 +274,201 @@ macro_rules! intrinsics { intrinsics!($($rest)*); }; } + +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/sio.rs + +// This takes advantage of how AAPCS defines a 64-bit return on 32-bit registers +// by packing it into r0[0:31] and r1[32:63]. So all we need to do is put +// the remainder in the high order 32 bits of a 64 bit result. We can also +// alias the division operators to these for a similar reason r0 is the +// result either way and r1 a scratch register, so the caller can't assume it +// retains the argument value. +#[cfg(target_arch = "arm")] +core::arch::global_asm!( + ".macro hwdivider_head", + "ldr r2, =(0xd0000000)", // SIO_BASE + // Check the DIRTY state of the divider by shifting it into the C + // status bit. + "ldr r3, [r2, #0x078]", // DIV_CSR + "lsrs r3, #2", // DIRTY = 1, so shift 2 down + // We only need to save the state when DIRTY, otherwise we can just do the + // division directly. + "bcs 2f", + "1:", + // Do the actual division now, we're either not DIRTY, or we've saved the + // state and branched back here so it's safe now. + ".endm", + ".macro hwdivider_tail", + // 8 cycle delay to wait for the result. Each branch takes two cycles + // and fits into a 2-byte Thumb instruction, so this is smaller than + // 8 NOPs. + "b 3f", + "3: b 3f", + "3: b 3f", + "3: b 3f", + "3:", + // Read the quotient last, since that's what clears the dirty flag. + "ldr r1, [r2, #0x074]", // DIV_REMAINDER + "ldr r0, [r2, #0x070]", // DIV_QUOTIENT + // Either return to the caller or back to the state restore. + "bx lr", + "2:", + // Since we can't save the signed-ness of the calculation, we have to make + // sure that there's at least an 8 cycle delay before we read the result. + // The push takes 5 cycles, and we've already spent at least 7 checking + // the DIRTY state to get here. + "push {{r4-r6, lr}}", + // Read the quotient last, since that's what clears the dirty flag. + "ldr r3, [r2, #0x060]", // DIV_UDIVIDEND + "ldr r4, [r2, #0x064]", // DIV_UDIVISOR + "ldr r5, [r2, #0x074]", // DIV_REMAINDER + "ldr r6, [r2, #0x070]", // DIV_QUOTIENT + // If we get interrupted here (before a write sets the DIRTY flag) it's + // fine, since we have the full state, so the interruptor doesn't have to + // restore it. Once the write happens and the DIRTY flag is set, the + // interruptor becomes responsible for restoring our state. + "bl 1b", + // If we are interrupted here, then the interruptor will start an incorrect + // calculation using a wrong divisor, but we'll restore the divisor and + // result ourselves correctly. This sets DIRTY, so any interruptor will + // save the state. + "str r3, [r2, #0x060]", // DIV_UDIVIDEND + // If we are interrupted here, the the interruptor may start the + // calculation using incorrectly signed inputs, but we'll restore the + // result ourselves. This sets DIRTY, so any interruptor will save the + // state. + "str r4, [r2, #0x064]", // DIV_UDIVISOR + // If we are interrupted here, the interruptor will have restored + // everything but the quotient may be wrongly signed. If the calculation + // started by the above writes is still ongoing it is stopped, so it won't + // replace the result we're restoring. DIRTY and READY set, but only + // DIRTY matters to make the interruptor save the state. + "str r5, [r2, #0x074]", // DIV_REMAINDER + // State fully restored after the quotient write. This sets both DIRTY + // and READY, so whatever we may have interrupted can read the result. + "str r6, [r2, #0x070]", // DIV_QUOTIENT + "pop {{r4-r6, pc}}", + ".endm", +); + +macro_rules! division_function { + ( + $name:ident $($intrinsic:ident)* ( $argty:ty ) { + $($begin:literal),+ + } + ) => { + #[cfg(all(target_arch = "arm", feature = "intrinsics"))] + core::arch::global_asm!( + // Mangle the name slightly, since this is a global symbol. + concat!(".global _rphal_", stringify!($name)), + concat!(".type _rphal_", stringify!($name), ", %function"), + ".align 2", + concat!("_rphal_", stringify!($name), ":"), + $( + concat!(".global ", stringify!($intrinsic)), + concat!(".type ", stringify!($intrinsic), ", %function"), + concat!(stringify!($intrinsic), ":"), + )* + + "hwdivider_head", + $($begin),+ , + "hwdivider_tail", + ); + + #[cfg(all(target_arch = "arm", not(feature = "intrinsics")))] + core::arch::global_asm!( + // Mangle the name slightly, since this is a global symbol. + concat!(".global _rphal_", stringify!($name)), + concat!(".type _rphal_", stringify!($name), ", %function"), + ".align 2", + concat!("_rphal_", stringify!($name), ":"), + + "hwdivider_head", + $($begin),+ , + "hwdivider_tail", + ); + + #[cfg(target_arch = "arm")] + extern "aapcs" { + // Connect a local name to global symbol above through FFI. + #[link_name = concat!("_rphal_", stringify!($name)) ] + fn $name(n: $argty, d: $argty) -> u64; + } + + #[cfg(not(target_arch = "arm"))] + #[allow(unused_variables)] + unsafe fn $name(n: $argty, d: $argty) -> u64 { 0 } + }; +} + +division_function! { + unsigned_divmod __aeabi_uidivmod __aeabi_uidiv ( u32 ) { + "str r0, [r2, #0x060]", // DIV_UDIVIDEND + "str r1, [r2, #0x064]" // DIV_UDIVISOR + } +} + +division_function! { + signed_divmod __aeabi_idivmod __aeabi_idiv ( i32 ) { + "str r0, [r2, #0x068]", // DIV_SDIVIDEND + "str r1, [r2, #0x06c]" // DIV_SDIVISOR + } +} + +fn divider_unsigned(n: u32, d: u32) -> DivResult { + let packed = unsafe { unsigned_divmod(n, d) }; + DivResult { + quotient: packed as u32, + remainder: (packed >> 32) as u32, + } +} + +fn divider_signed(n: i32, d: i32) -> DivResult { + let packed = unsafe { signed_divmod(n, d) }; + // Double casts to avoid sign extension + DivResult { + quotient: packed as u32 as i32, + remainder: (packed >> 32) as u32 as i32, + } +} + +/// Result of divide/modulo operation +struct DivResult { + /// The quotient of divide/modulo operation + pub quotient: T, + /// The remainder of divide/modulo operation + pub remainder: T, +} + +intrinsics! { + extern "C" fn __udivsi3(n: u32, d: u32) -> u32 { + divider_unsigned(n, d).quotient + } + + extern "C" fn __umodsi3(n: u32, d: u32) -> u32 { + divider_unsigned(n, d).remainder + } + + extern "C" fn __udivmodsi4(n: u32, d: u32, rem: Option<&mut u32>) -> u32 { + let quo_rem = divider_unsigned(n, d); + if let Some(rem) = rem { + *rem = quo_rem.remainder; + } + quo_rem.quotient + } + + extern "C" fn __divsi3(n: i32, d: i32) -> i32 { + divider_signed(n, d).quotient + } + + extern "C" fn __modsi3(n: i32, d: i32) -> i32 { + divider_signed(n, d).remainder + } + + extern "C" fn __divmodsi4(n: i32, d: i32, rem: &mut i32) -> i32 { + let quo_rem = divider_signed(n, d); + *rem = quo_rem.remainder; + quo_rem.quotient + } +} From a258e15c239305ba654e5d60a5f649b87855759f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 16 Apr 2023 23:59:26 +0200 Subject: [PATCH 20/46] rp: switch to released rp-pac v1.0 --- embassy-rp/Cargo.toml | 3 +-- embassy-rp/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 2ef2c8f07..b7ed6ccbb 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -61,8 +61,7 @@ embedded-io = { version = "0.4.0", features = ["async"], optional = true } embedded-storage = { version = "0.3" } rand_core = "0.6.4" -rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] } -#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] } +rp-pac = { version = "1", features = ["rt"] } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 9e99b2fbb..1d63f6c2e 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -44,9 +44,9 @@ pub use embassy_cortex_m::executor; pub use embassy_cortex_m::interrupt::_export::interrupt; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] -pub use rp2040_pac2 as pac; +pub use rp_pac as pac; #[cfg(not(feature = "unstable-pac"))] -pub(crate) use rp2040_pac2 as pac; +pub(crate) use rp_pac as pac; embassy_hal_common::peripherals! { PIN_0, From 776e001b5befc50ff409b5cd8252bbb61fbb5acc Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 17:47:25 -0500 Subject: [PATCH 21/46] stm32: remove TIMX singleton when used on timer driver fixes #1316. --- embassy-hal-common/src/macros.rs | 25 ++++++- embassy-stm32/build.rs | 114 ++++++++++++++++++------------- 2 files changed, 92 insertions(+), 47 deletions(-) diff --git a/embassy-hal-common/src/macros.rs b/embassy-hal-common/src/macros.rs index 5e62e048a..f06b46002 100644 --- a/embassy-hal-common/src/macros.rs +++ b/embassy-hal-common/src/macros.rs @@ -1,5 +1,5 @@ #[macro_export] -macro_rules! peripherals { +macro_rules! peripherals_definition { ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { /// Types for the peripheral singletons. pub mod peripherals { @@ -26,7 +26,12 @@ macro_rules! peripherals { $crate::impl_peripheral!($name); )* } + }; +} +#[macro_export] +macro_rules! peripherals_struct { + ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { /// Struct containing all the peripheral singletons. /// /// To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`]. @@ -76,6 +81,24 @@ macro_rules! peripherals { }; } +#[macro_export] +macro_rules! peripherals { + ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { + $crate::peripherals_definition!( + $( + $(#[$cfg])? + $name, + )* + ); + $crate::peripherals_struct!( + $( + $(#[$cfg])? + $name, + )* + ); + }; +} + #[macro_export] macro_rules! into_ref { ($($name:ident),*) => { diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 73bd29fcf..bb6c99963 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -81,11 +81,74 @@ fn main() { singletons.push(c.name.to_string()); } + // ======== + // Handle time-driver-XXXX features. + + let time_driver = match env::vars() + .map(|(a, _)| a) + .filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_")) + .get_one() + { + Ok(x) => Some( + x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_") + .unwrap() + .to_ascii_lowercase(), + ), + Err(GetOneError::None) => None, + Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"), + }; + + let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) { + None => "", + Some("tim2") => "TIM2", + Some("tim3") => "TIM3", + Some("tim4") => "TIM4", + Some("tim5") => "TIM5", + Some("tim12") => "TIM12", + Some("tim15") => "TIM15", + Some("any") => { + if singletons.contains(&"TIM2".to_string()) { + "TIM2" + } else if singletons.contains(&"TIM3".to_string()) { + "TIM3" + } else if singletons.contains(&"TIM4".to_string()) { + "TIM4" + } else if singletons.contains(&"TIM5".to_string()) { + "TIM5" + } else if singletons.contains(&"TIM12".to_string()) { + "TIM12" + } else if singletons.contains(&"TIM15".to_string()) { + "TIM15" + } else { + panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM12 or TIM15.") + } + } + _ => panic!("unknown time_driver {:?}", time_driver), + }; + + if time_driver_singleton != "" { + println!("cargo:rustc-cfg=time_driver_{}", time_driver_singleton.to_lowercase()); + } + + // ======== + // Write singletons + let mut g = TokenStream::new(); let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect(); + g.extend(quote! { - embassy_hal_common::peripherals!(#(#singleton_tokens),*); + embassy_hal_common::peripherals_definition!(#(#singleton_tokens),*); + }); + + let singleton_tokens: Vec<_> = singletons + .iter() + .filter(|s| *s != &time_driver_singleton.to_string()) + .map(|s| format_ident!("{}", s)) + .collect(); + + g.extend(quote! { + embassy_hal_common::peripherals_struct!(#(#singleton_tokens),*); }); // ======== @@ -536,6 +599,10 @@ fn main() { let pin_name = format_ident!("{}", pin.pin); let af = pin.af.unwrap_or(0); + if peri == time_driver_singleton { + continue; + } + // MCO is special if pin.signal.starts_with("MCO_") { // Supported in H7 only for now @@ -838,51 +905,6 @@ fn main() { println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9 - // ======== - // Handle time-driver-XXXX features. - - let time_driver = match env::vars() - .map(|(a, _)| a) - .filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_")) - .get_one() - { - Ok(x) => Some( - x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_") - .unwrap() - .to_ascii_lowercase(), - ), - Err(GetOneError::None) => None, - Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"), - }; - - match time_driver.as_ref().map(|x| x.as_ref()) { - None => {} - Some("tim2") => println!("cargo:rustc-cfg=time_driver_tim2"), - Some("tim3") => println!("cargo:rustc-cfg=time_driver_tim3"), - Some("tim4") => println!("cargo:rustc-cfg=time_driver_tim4"), - Some("tim5") => println!("cargo:rustc-cfg=time_driver_tim5"), - Some("tim12") => println!("cargo:rustc-cfg=time_driver_tim12"), - Some("tim15") => println!("cargo:rustc-cfg=time_driver_tim15"), - Some("any") => { - if singletons.contains(&"TIM2".to_string()) { - println!("cargo:rustc-cfg=time_driver_tim2"); - } else if singletons.contains(&"TIM3".to_string()) { - println!("cargo:rustc-cfg=time_driver_tim3"); - } else if singletons.contains(&"TIM4".to_string()) { - println!("cargo:rustc-cfg=time_driver_tim4"); - } else if singletons.contains(&"TIM5".to_string()) { - println!("cargo:rustc-cfg=time_driver_tim5"); - } else if singletons.contains(&"TIM12".to_string()) { - println!("cargo:rustc-cfg=time_driver_tim12"); - } else if singletons.contains(&"TIM15".to_string()) { - println!("cargo:rustc-cfg=time_driver_tim15"); - } else { - panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM12 or TIM15.") - } - } - _ => panic!("unknown time_driver {:?}", time_driver), - } - // Handle time-driver-XXXX features. if env::var("CARGO_FEATURE_TIME_DRIVER_ANY").is_ok() {} println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]); From 9e1ddeac8604f5df85df56159b97004de890aeb3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 18:32:55 -0500 Subject: [PATCH 22/46] stm32: fix defective example --- embassy-stm32/build.rs | 4 ---- examples/stm32g4/src/bin/pwm.rs | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index bb6c99963..c7d12e13a 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -599,10 +599,6 @@ fn main() { let pin_name = format_ident!("{}", pin.pin); let af = pin.af.unwrap_or(0); - if peri == time_driver_singleton { - continue; - } - // MCO is special if pin.signal.starts_with("MCO_") { // Supported in H7 only for now diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index 017e89e41..8f7842ed7 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -15,8 +15,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PA5); - let mut pwm = SimplePwm::new(p.TIM2, Some(ch1), None, None, None, khz(10)); + let ch1 = PwmPin::new_ch1(p.PC0); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10)); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); From 90c1422381f4a798c83146a2125529bb2f761598 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 19:30:42 -0500 Subject: [PATCH 23/46] stm32/rtc: remove chrono datetime and add converters --- .../rtc/{datetime_no_deps.rs => datetime.rs} | 56 +++++++++++++++++++ embassy-stm32/src/rtc/mod.rs | 3 - examples/stm32f4/Cargo.toml | 2 +- 3 files changed, 57 insertions(+), 4 deletions(-) rename embassy-stm32/src/rtc/{datetime_no_deps.rs => datetime.rs} (69%) diff --git a/embassy-stm32/src/rtc/datetime_no_deps.rs b/embassy-stm32/src/rtc/datetime.rs similarity index 69% rename from embassy-stm32/src/rtc/datetime_no_deps.rs rename to embassy-stm32/src/rtc/datetime.rs index 173f38377..5be68b89b 100644 --- a/embassy-stm32/src/rtc/datetime_no_deps.rs +++ b/embassy-stm32/src/rtc/datetime.rs @@ -1,3 +1,8 @@ +use core::convert::From; + +#[cfg(feature = "chrono")] +use chrono::{self, Datelike, NaiveDate, Timelike, Weekday}; + use super::byte_to_bcd2; use crate::pac::rtc::Rtc; @@ -41,6 +46,35 @@ pub struct DateTime { pub second: u8, } +#[cfg(feature = "chrono")] +impl From for DateTime { + fn from(date_time: chrono::NaiveDateTime) -> Self { + Self { + year: (date_time.year() - 1970) as u16, + month: date_time.month() as u8, + day: date_time.day() as u8, + day_of_week: date_time.weekday().into(), + hour: date_time.hour() as u8, + minute: date_time.minute() as u8, + second: date_time.second() as u8, + } + } +} + +#[cfg(feature = "chrono")] +impl From for chrono::NaiveDateTime { + fn from(date_time: DateTime) -> Self { + NaiveDate::from_ymd_opt( + (date_time.year + 1970) as i32, + date_time.month as u32, + date_time.day as u32, + ) + .unwrap() + .and_hms_opt(date_time.hour as u32, date_time.minute as u32, date_time.second as u32) + .unwrap() + } +} + /// A day of the week #[repr(u8)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] @@ -55,6 +89,28 @@ pub enum DayOfWeek { Sunday = 6, } +#[cfg(feature = "chrono")] +impl From for DayOfWeek { + fn from(weekday: Weekday) -> Self { + day_of_week_from_u8(weekday.number_from_monday() as u8).unwrap() + } +} + +#[cfg(feature = "chrono")] +impl From for chrono::Weekday { + fn from(weekday: DayOfWeek) -> Self { + match weekday { + DayOfWeek::Monday => Weekday::Mon, + DayOfWeek::Tuesday => Weekday::Tue, + DayOfWeek::Wednesday => Weekday::Wed, + DayOfWeek::Thursday => Weekday::Thu, + DayOfWeek::Friday => Weekday::Fri, + DayOfWeek::Saturday => Weekday::Sat, + DayOfWeek::Sunday => Weekday::Sun, + } + } +} + fn day_of_week_from_u8(v: u8) -> Result { Ok(match v { 0 => DayOfWeek::Monday, diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index ee3349b27..170783b2d 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -1,8 +1,5 @@ //! RTC peripheral abstraction use core::marker::PhantomData; - -#[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}; diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 1736769ef..69dcab64c 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } From 27ec29e2c53e4bc618ec97f0071befe52c8520b9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Apr 2023 19:32:15 -0500 Subject: [PATCH 24/46] stm32/rtc: remove unused import --- embassy-stm32/src/rtc/datetime.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs index 5be68b89b..6274c1e05 100644 --- a/embassy-stm32/src/rtc/datetime.rs +++ b/embassy-stm32/src/rtc/datetime.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "chrono")] use core::convert::From; #[cfg(feature = "chrono")] From 4044d728a64d457d8b0fade83dcc40abbf1867c6 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 17 Apr 2023 15:35:02 +0200 Subject: [PATCH 25/46] update to released versions --- embassy-lora/Cargo.toml | 4 ++-- examples/nrf52840/Cargo.toml | 4 ++-- examples/stm32l0/Cargo.toml | 4 ++-- examples/stm32wl/Cargo.toml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 13b3acab2..50449dd46 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -38,5 +38,5 @@ futures = { version = "0.3.17", default-features = false, features = [ "async-aw embedded-hal = { version = "0.2", features = ["unproven"] } bit_field = { version = "0.10" } -lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"] } -lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false } +lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] } +lorawan = { version = "0.7.2", default-features = false } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 9ef7944c4..af19413cc 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -20,8 +20,8 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } -lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"], optional = true } -lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["default-crypto"], optional = true } +lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true } +lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 9f63fee1c..b04faf535 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -15,8 +15,8 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx127x", "time", "defmt"], optional = true} -lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"], optional = true } -lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["default-crypto"], optional = true } +lorawan-device = { version = "0.9.0", default-features = false, features = ["async"], optional = true } +lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"], optional = true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 19a7cfcea..cb3526fa4 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -11,8 +11,8 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } -lorawan-device = { version = "0.8.0", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["async"] } -lorawan = { version = "0.7.1", git = "https://github.com/ivajloip/rust-lorawan.git", rev = "7d3eb40bc2412536c846cea40caff25198b6b068", default-features = false, features = ["default-crypto"] } +lorawan-device = { version = "0.9.0", default-features = false, features = ["async"] } +lorawan = { version = "0.7.2", default-features = false, features = ["default-crypto"] } defmt = "0.3" defmt-rtt = "0.4" From df7ef1d98f48a18027f5f0dcd0bc39cef0bfe8b9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 00:13:25 +0200 Subject: [PATCH 26/46] stm32/sdmmc: remove cfg_if. --- embassy-stm32/src/sdmmc/mod.rs | 178 ++++++++++++++++----------------- 1 file changed, 89 insertions(+), 89 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index ac00b5176..92c54d815 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -135,57 +135,53 @@ enum Response { Long = 3, } -cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to - /// `sdmmc_ck` in Hertz. - /// - /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), - /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. - fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { - // sdmmc_v1 maximum clock is 50 MHz - if sdmmc_ck > 50_000_000 { - return Err(Error::BadClock); - } +/// Calculate clock divisor. Returns a SDMMC_CK less than or equal to +/// `sdmmc_ck` in Hertz. +/// +/// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), +/// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. +#[cfg(sdmmc_v1)] +fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u8, Hertz), Error> { + // sdmmc_v1 maximum clock is 50 MHz + if sdmmc_ck > 50_000_000 { + return Err(Error::BadClock); + } - // bypass divisor - if ker_ck.0 <= sdmmc_ck { - return Ok((true, 0, ker_ck)); - } + // bypass divisor + if ker_ck.0 <= sdmmc_ck { + return Ok((true, 0, ker_ck)); + } - // `ker_ck / sdmmc_ck` rounded up - let clk_div = match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { - 0 | 1 => Ok(0), - x @ 2..=258 => { - Ok((x - 2) as u8) - } - _ => Err(Error::BadClock), - }?; + // `ker_ck / sdmmc_ck` rounded up + let clk_div = match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { + 0 | 1 => Ok(0), + x @ 2..=258 => Ok((x - 2) as u8), + _ => Err(Error::BadClock), + }?; - // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2] - let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2)); - Ok((false, clk_div, clk_f)) - } - } else if #[cfg(sdmmc_v2)] { - /// Calculate clock divisor. Returns a SDMMC_CK less than or equal to - /// `sdmmc_ck` in Hertz. - /// - /// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), - /// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. - fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { - // `ker_ck / sdmmc_ck` rounded up - match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { - 0 | 1 => Ok((false, 0, ker_ck)), - x @ 2..=2046 => { - // SDMMC_CK frequency = SDMMCCLK / [CLKDIV + 2] - let clk_div = ((x + 1) / 2) as u16; - let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); - - Ok((false, clk_div, clk)) - } - _ => Err(Error::BadClock), - } + // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2] + let clk_f = Hertz(ker_ck.0 / (clk_div as u32 + 2)); + Ok((false, clk_div, clk_f)) +} + +/// Calculate clock divisor. Returns a SDMMC_CK less than or equal to +/// `sdmmc_ck` in Hertz. +/// +/// Returns `(bypass, clk_div, clk_f)`, where `bypass` enables clock divisor bypass (only sdmmc_v1), +/// `clk_div` is the divisor register value and `clk_f` is the resulting new clock frequency. +#[cfg(sdmmc_v2)] +fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { + // `ker_ck / sdmmc_ck` rounded up + match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { + 0 | 1 => Ok((false, 0, ker_ck)), + x @ 2..=2046 => { + // SDMMC_CK frequency = SDMMCCLK / [CLKDIV + 2] + let clk_div = ((x + 1) / 2) as u16; + let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); + + Ok((false, clk_div, clk)) } + _ => Err(Error::BadClock), } } @@ -904,13 +900,10 @@ impl SdmmcInner { // NOTE(unsafe) Atomic read with no side-effects unsafe { let status = regs.star().read(); - cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - status.rxact() || status.txact() - } else if #[cfg(sdmmc_v2)] { - status.dpsmact() - } - } + #[cfg(sdmmc_v1)] + return status.rxact() || status.txact(); + #[cfg(sdmmc_v2)] + return status.dpsmact(); } } @@ -922,13 +915,10 @@ impl SdmmcInner { // NOTE(unsafe) Atomic read with no side-effects unsafe { let status = regs.star().read(); - cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - status.cmdact() - } else if #[cfg(sdmmc_v2)] { - status.cpsmact() - } - } + #[cfg(sdmmc_v1)] + return status.cmdact(); + #[cfg(sdmmc_v2)] + return status.cpsmact(); } } @@ -961,20 +951,26 @@ impl SdmmcInner { regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); regs.dlenr().write(|w| w.set_datalength(length_bytes)); - cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - let request = dma.request(); - dma.start_read(request, regs.fifor().ptr() as *const u32, buffer, crate::dma::TransferOptions { + #[cfg(sdmmc_v1)] + { + let request = dma.request(); + dma.start_read( + request, + regs.fifor().ptr() as *const u32, + buffer, + crate::dma::TransferOptions { pburst: crate::dma::Burst::Incr4, mburst: crate::dma::Burst::Incr4, flow_ctrl: crate::dma::FlowControl::Peripheral, fifo_threshold: Some(crate::dma::FifoThreshold::Full), ..Default::default() - }); - } else if #[cfg(sdmmc_v2)] { - regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } + }, + ); + } + #[cfg(sdmmc_v2)] + { + regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); } regs.dctrl().modify(|w| { @@ -1011,20 +1007,27 @@ impl SdmmcInner { regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); regs.dlenr().write(|w| w.set_datalength(length_bytes)); - cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - let request = dma.request(); - dma.start_write(request, buffer, regs.fifor().ptr() as *mut u32, crate::dma::TransferOptions { + #[cfg(sdmmc_v1)] + { + let request = dma.request(); + dma.start_write( + request, + buffer, + regs.fifor().ptr() as *mut u32, + crate::dma::TransferOptions { pburst: crate::dma::Burst::Incr4, mburst: crate::dma::Burst::Incr4, flow_ctrl: crate::dma::FlowControl::Peripheral, fifo_threshold: Some(crate::dma::FifoThreshold::Full), ..Default::default() - }); - } else if #[cfg(sdmmc_v2)] { - regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *const u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } + }, + ); + } + #[cfg(sdmmc_v2)] + { + regs.idmabase0r() + .write(|w| w.set_idmabase0(buffer as *const u32 as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); } regs.dctrl().modify(|w| { @@ -1043,16 +1046,13 @@ impl SdmmcInner { let regs = self.0; unsafe { - cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - regs.dctrl().modify(|w| { - w.set_dmaen(false); - w.set_dten(false); - }); - } else if #[cfg(sdmmc_v2)] { - regs.idmactrlr().modify(|w| w.set_idmaen(false)); - } - } + #[cfg(sdmmc_v1)] + regs.dctrl().modify(|w| { + w.set_dmaen(false); + w.set_dten(false); + }); + #[cfg(sdmmc_v2)] + regs.idmactrlr().modify(|w| w.set_idmaen(false)); } } From e14fa11fc35c1b163d86404935d180b9ef88e461 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 00:13:34 +0200 Subject: [PATCH 27/46] stm32/sdmmc: remove unneeded pointer casts. --- embassy-stm32/src/sdmmc/mod.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 92c54d815..22b578dcf 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -793,7 +793,7 @@ impl SdmmcInner { let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); unsafe { - self.prepare_datapath_read(buffer as *mut [u32; 128], 512, 9, data_transfer_timeout, dma); + self.prepare_datapath_read(buffer, 512, 9, data_transfer_timeout, dma); self.data_interrupts(true); } self.cmd(Cmd::read_single_block(address), true)?; @@ -1121,7 +1121,7 @@ impl SdmmcInner { let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); unsafe { - self.prepare_datapath_read(&mut status as *mut [u32; 16], 64, 6, data_transfer_timeout, dma); + self.prepare_datapath_read(&mut status, 64, 6, data_transfer_timeout, dma); self.data_interrupts(true); } self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 @@ -1202,7 +1202,7 @@ impl SdmmcInner { let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); unsafe { - self.prepare_datapath_read(&mut status as *mut [u32; 16], 64, 6, data_transfer_timeout, dma); + self.prepare_datapath_read(&mut status, 64, 6, data_transfer_timeout, dma); self.data_interrupts(true); } self.cmd(Cmd::card_status(0), true)?; @@ -1320,7 +1320,7 @@ impl SdmmcInner { let on_drop = OnDrop::new(move || unsafe { self.on_drop() }); unsafe { - self.prepare_datapath_read(&mut scr as *mut [u32], 8, 3, data_transfer_timeout, dma); + self.prepare_datapath_read(&mut scr[..], 8, 3, data_transfer_timeout, dma); self.data_interrupts(true); } self.cmd(Cmd::cmd51(), true)?; @@ -1560,15 +1560,14 @@ pin_trait!(D5Pin, Instance); pin_trait!(D6Pin, Instance); pin_trait!(D7Pin, Instance); -cfg_if::cfg_if! { - if #[cfg(sdmmc_v1)] { - dma_trait!(SdmmcDma, Instance); - } else if #[cfg(sdmmc_v2)] { - // SDMMCv2 uses internal DMA - pub trait SdmmcDma {} - impl SdmmcDma for NoDma {} - } -} +#[cfg(sdmmc_v1)] +dma_trait!(SdmmcDma, Instance); + +// SDMMCv2 uses internal DMA +#[cfg(sdmmc_v2)] +pub trait SdmmcDma {} +#[cfg(sdmmc_v2)] +impl SdmmcDma for NoDma {} cfg_if::cfg_if! { // TODO, these could not be implemented, because required clocks are not exposed in RCC: From 0dfa19299224f28ab686062d472c0ccea72ffd5a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 00:54:22 +0200 Subject: [PATCH 28/46] stm32/sdmmc: remove "inner" layer. --- embassy-stm32/src/sdmmc/mod.rs | 1444 ++++++++++++++------------------ 1 file changed, 642 insertions(+), 802 deletions(-) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 22b578dcf..23ece3a2a 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -41,7 +41,7 @@ impl Default for Signalling { } #[repr(align(4))] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataBlock(pub [u8; 512]); @@ -61,7 +61,7 @@ impl DerefMut for DataBlock { /// Errors #[non_exhaustive] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { Timeout, @@ -175,7 +175,7 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { match (ker_ck.0 + sdmmc_ck - 1) / sdmmc_ck { 0 | 1 => Ok((false, 0, ker_ck)), x @ 2..=2046 => { - // SDMMC_CK frequency = SDMMCCLK / [CLKDIV + 2] + // SDMMC_CK frequency = SDMMCCLK / [CLKDIV * 2] let clk_div = ((x + 1) / 2) as u16; let clk = Hertz(ker_ck.0 / (clk_div as u32 * 2)); @@ -204,9 +204,10 @@ impl Default for Config { } /// Sdmmc device -pub struct Sdmmc<'d, T: Instance, Dma = NoDma> { +pub struct Sdmmc<'d, T: Instance, Dma: SdmmcDma = NoDma> { _peri: PeripheralRef<'d, T>, irq: PeripheralRef<'d, T::Interrupt>, + #[allow(unused)] dma: PeripheralRef<'d, Dma>, clk: PeripheralRef<'d, AnyPin>, @@ -305,49 +306,6 @@ impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { config, ) } - - fn new_inner( - sdmmc: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, - dma: impl Peripheral

+ 'd, - clk: PeripheralRef<'d, AnyPin>, - cmd: PeripheralRef<'d, AnyPin>, - d0: PeripheralRef<'d, AnyPin>, - d1: Option>, - d2: Option>, - d3: Option>, - config: Config, - ) -> Self { - into_ref!(sdmmc, irq, dma); - - T::enable(); - T::reset(); - - let inner = T::inner(); - unsafe { inner.new_inner() }; - - irq.set_handler(Self::on_interrupt); - irq.unpend(); - irq.enable(); - - Self { - _peri: sdmmc, - irq, - dma, - - clk, - cmd, - d0, - d1, - d2, - d3, - - config, - clock: SD_INIT_FREQ, - signalling: Default::default(), - card: None, - } - } } #[cfg(sdmmc_v2)] @@ -375,6 +333,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { Self::new_inner( sdmmc, irq, + NoDma.into_ref(), clk.map_into(), cmd.map_into(), d0.map_into(), @@ -417,6 +376,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { Self::new_inner( sdmmc, irq, + NoDma.into_ref(), clk.map_into(), cmd.map_into(), d0.map_into(), @@ -426,10 +386,13 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { config, ) } +} +impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { fn new_inner( sdmmc: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, + dma: impl Peripheral

+ 'd, clk: PeripheralRef<'d, AnyPin>, cmd: PeripheralRef<'d, AnyPin>, d0: PeripheralRef<'d, AnyPin>, @@ -438,22 +401,41 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { d3: Option>, config: Config, ) -> Self { - into_ref!(sdmmc, irq); + into_ref!(sdmmc, irq, dma); T::enable(); T::reset(); - let inner = T::inner(); - unsafe { inner.new_inner() }; - irq.set_handler(Self::on_interrupt); irq.unpend(); irq.enable(); + let regs = T::regs(); + unsafe { + regs.clkcr().write(|w| { + w.set_pwrsav(false); + w.set_negedge(false); + + // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors. + // See chip erratas for more details. + #[cfg(sdmmc_v1)] + w.set_hwfc_en(false); + #[cfg(sdmmc_v2)] + w.set_hwfc_en(true); + + #[cfg(sdmmc_v1)] + w.set_clken(true); + }); + + // Power off, writen 00: Clock to the card is stopped; + // D[7:0], CMD, and CK are driven high. + regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); + } + Self { _peri: sdmmc, irq, - dma: NoDma.into_ref(), + dma, clk, cmd, @@ -468,177 +450,552 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> { card: None, } } -} -impl<'d, T: Instance, Dma: SdmmcDma> Sdmmc<'d, T, Dma> { + /// Data transfer is in progress #[inline(always)] + fn data_active() -> bool { + let regs = T::regs(); + + // NOTE(unsafe) Atomic read with no side-effects + unsafe { + let status = regs.star().read(); + #[cfg(sdmmc_v1)] + return status.rxact() || status.txact(); + #[cfg(sdmmc_v2)] + return status.dpsmact(); + } + } + + /// Coammand transfer is in progress + #[inline(always)] + fn cmd_active() -> bool { + let regs = T::regs(); + + // NOTE(unsafe) Atomic read with no side-effects + unsafe { + let status = regs.star().read(); + #[cfg(sdmmc_v1)] + return status.cmdact(); + #[cfg(sdmmc_v2)] + return status.cpsmact(); + } + } + + /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) + #[inline(always)] + fn wait_idle() { + while Self::data_active() || Self::cmd_active() {} + } + + /// # Safety + /// + /// `buffer` must be valid for the whole transfer and word aligned + unsafe fn prepare_datapath_read(&mut self, buffer: *mut [u32], length_bytes: u32, block_size: u8) { + assert!(block_size <= 14, "Block size up to 2^14 bytes"); + let regs = T::regs(); + + // Command AND Data state machines must be idle + Self::wait_idle(); + Self::clear_interrupt_flags(); + + // NOTE(unsafe) We have exclusive access to the regisers + + regs.dtimer() + .write(|w| w.set_datatime(self.config.data_transfer_timeout)); + regs.dlenr().write(|w| w.set_datalength(length_bytes)); + + #[cfg(sdmmc_v1)] + { + let request = self.dma.request(); + self.dma.start_read( + request, + regs.fifor().ptr() as *const u32, + buffer, + crate::dma::TransferOptions { + pburst: crate::dma::Burst::Incr4, + mburst: crate::dma::Burst::Incr4, + flow_ctrl: crate::dma::FlowControl::Peripheral, + fifo_threshold: Some(crate::dma::FifoThreshold::Full), + ..Default::default() + }, + ); + } + #[cfg(sdmmc_v2)] + { + regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); + } + + regs.dctrl().modify(|w| { + w.set_dblocksize(block_size); + w.set_dtdir(true); + #[cfg(sdmmc_v1)] + { + w.set_dmaen(true); + w.set_dten(true); + } + }); + } + + /// # Safety + /// + /// `buffer` must be valid for the whole transfer and word aligned + unsafe fn prepare_datapath_write(&mut self, buffer: *const [u32], length_bytes: u32, block_size: u8) { + assert!(block_size <= 14, "Block size up to 2^14 bytes"); + let regs = T::regs(); + + // Command AND Data state machines must be idle + Self::wait_idle(); + Self::clear_interrupt_flags(); + + // NOTE(unsafe) We have exclusive access to the regisers + + regs.dtimer() + .write(|w| w.set_datatime(self.config.data_transfer_timeout)); + regs.dlenr().write(|w| w.set_datalength(length_bytes)); + + #[cfg(sdmmc_v1)] + { + let request = self.dma.request(); + self.dma.start_write( + request, + buffer, + regs.fifor().ptr() as *mut u32, + crate::dma::TransferOptions { + pburst: crate::dma::Burst::Incr4, + mburst: crate::dma::Burst::Incr4, + flow_ctrl: crate::dma::FlowControl::Peripheral, + fifo_threshold: Some(crate::dma::FifoThreshold::Full), + ..Default::default() + }, + ); + } + #[cfg(sdmmc_v2)] + { + regs.idmabase0r() + .write(|w| w.set_idmabase0(buffer as *const u32 as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); + } + + regs.dctrl().modify(|w| { + w.set_dblocksize(block_size); + w.set_dtdir(false); + #[cfg(sdmmc_v1)] + { + w.set_dmaen(true); + w.set_dten(true); + } + }); + } + + /// Stops the DMA datapath + fn stop_datapath() { + let regs = T::regs(); + + unsafe { + #[cfg(sdmmc_v1)] + regs.dctrl().modify(|w| { + w.set_dmaen(false); + w.set_dten(false); + }); + #[cfg(sdmmc_v2)] + regs.idmactrlr().modify(|w| w.set_idmaen(false)); + } + } + + /// Sets the CLKDIV field in CLKCR. Updates clock field in self + fn clkcr_set_clkdiv(&mut self, freq: u32, width: BusWidth) -> Result<(), Error> { + let regs = T::regs(); + + let width_u32 = match width { + BusWidth::One => 1u32, + BusWidth::Four => 4u32, + BusWidth::Eight => 8u32, + _ => panic!("Invalid Bus Width"), + }; + + let ker_ck = T::kernel_clk(); + let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; + + // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 + // Section 55.5.8 + let sdmmc_bus_bandwidth = new_clock.0 * width_u32; + assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); + self.clock = new_clock; + + // NOTE(unsafe) We have exclusive access to the regblock + unsafe { + // CPSMACT and DPSMACT must be 0 to set CLKDIV + Self::wait_idle(); + regs.clkcr().modify(|w| { + w.set_clkdiv(clkdiv); + #[cfg(sdmmc_v1)] + w.set_bypass(_bypass); + }); + } + + Ok(()) + } + + /// Switch mode using CMD6. + /// + /// Attempt to set a new signalling mode. The selected + /// signalling mode is returned. Expects the current clock + /// frequency to be > 12.5MHz. + async fn switch_signalling_mode(&mut self, signalling: Signalling) -> Result { + // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not + // necessary" + + let set_function = 0x8000_0000 + | match signalling { + // See PLSS v7_10 Table 4-11 + Signalling::DDR50 => 0xFF_FF04, + Signalling::SDR104 => 0xFF_1F03, + Signalling::SDR50 => 0xFF_1F02, + Signalling::SDR25 => 0xFF_FF01, + Signalling::SDR12 => 0xFF_FF00, + }; + + let mut status = [0u32; 16]; + + // Arm `OnDrop` after the buffer, so it will be dropped first + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); + + unsafe { + self.prepare_datapath_read(&mut status, 64, 6); + Self::data_interrupts(true); + } + self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 + + let res = poll_fn(|cx| { + T::state().register(cx.waker()); + let status = unsafe { regs.star().read() }; + + if status.dcrcfail() { + return Poll::Ready(Err(Error::Crc)); + } else if status.dtimeout() { + return Poll::Ready(Err(Error::Timeout)); + } else if status.dataend() { + return Poll::Ready(Ok(())); + } + Poll::Pending + }) + .await; + Self::clear_interrupt_flags(); + + // Host is allowed to use the new functions at least 8 + // clocks after the end of the switch command + // transaction. We know the current clock period is < 80ns, + // so a total delay of 640ns is required here + for _ in 0..300 { + cortex_m::asm::nop(); + } + + match res { + Ok(_) => { + on_drop.defuse(); + Self::stop_datapath(); + + // Function Selection of Function Group 1 + let selection = (u32::from_be(status[4]) >> 24) & 0xF; + + match selection { + 0 => Ok(Signalling::SDR12), + 1 => Ok(Signalling::SDR25), + 2 => Ok(Signalling::SDR50), + 3 => Ok(Signalling::SDR104), + 4 => Ok(Signalling::DDR50), + _ => Err(Error::UnsupportedCardType), + } + } + Err(e) => Err(e), + } + } + + /// Query the card status (CMD13, returns R1) + fn read_status(&self, card: &Card) -> Result { + let regs = T::regs(); + let rca = card.rca; + + self.cmd(Cmd::card_status(rca << 16), false)?; // CMD13 + + // NOTE(unsafe) Atomic read with no side-effects + let r1 = unsafe { regs.respr(0).read().cardstatus() }; + Ok(r1.into()) + } + + /// Reads the SD Status (ACMD13) + async fn read_sd_status(&mut self) -> Result<(), Error> { + let card = self.card.as_mut().ok_or(Error::NoCard)?; + let rca = card.rca; + + self.cmd(Cmd::set_block_length(64), false)?; // CMD16 + self.cmd(Cmd::app_cmd(rca << 16), false)?; // APP + + let mut status = [0u32; 16]; + + // Arm `OnDrop` after the buffer, so it will be dropped first + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); + + unsafe { + self.prepare_datapath_read(&mut status, 64, 6); + Self::data_interrupts(true); + } + self.cmd(Cmd::card_status(0), true)?; + + let res = poll_fn(|cx| { + T::state().register(cx.waker()); + let status = unsafe { regs.star().read() }; + + if status.dcrcfail() { + return Poll::Ready(Err(Error::Crc)); + } else if status.dtimeout() { + return Poll::Ready(Err(Error::Timeout)); + } else if status.dataend() { + return Poll::Ready(Ok(())); + } + Poll::Pending + }) + .await; + Self::clear_interrupt_flags(); + + if res.is_ok() { + on_drop.defuse(); + Self::stop_datapath(); + + for byte in status.iter_mut() { + *byte = u32::from_be(*byte); + } + self.card.as_mut().unwrap().status = status.into(); + } + res + } + + /// Select one card and place it into the _Tranfer State_ + /// + /// If `None` is specifed for `card`, all cards are put back into + /// _Stand-by State_ + fn select_card(&self, card: Option<&Card>) -> Result<(), Error> { + // Determine Relative Card Address (RCA) of given card + let rca = card.map(|c| c.rca << 16).unwrap_or(0); + + let r = self.cmd(Cmd::sel_desel_card(rca), false); + match (r, rca) { + (Err(Error::Timeout), 0) => Ok(()), + _ => r, + } + } + + /// Clear flags in interrupt clear register + #[inline(always)] + fn clear_interrupt_flags() { + let regs = T::regs(); + // NOTE(unsafe) Atomic write + unsafe { + regs.icr().write(|w| { + w.set_ccrcfailc(true); + w.set_dcrcfailc(true); + w.set_ctimeoutc(true); + w.set_dtimeoutc(true); + w.set_txunderrc(true); + w.set_rxoverrc(true); + w.set_cmdrendc(true); + w.set_cmdsentc(true); + w.set_dataendc(true); + w.set_dbckendc(true); + w.set_sdioitc(true); + + #[cfg(sdmmc_v2)] + { + w.set_dholdc(true); + w.set_dabortc(true); + w.set_busyd0endc(true); + w.set_ackfailc(true); + w.set_acktimeoutc(true); + w.set_vswendc(true); + w.set_ckstopc(true); + w.set_idmatec(true); + w.set_idmabtcc(true); + } + }); + } + } + + /// Enables the interrupts for data transfer + #[inline(always)] + fn data_interrupts(enable: bool) { + let regs = T::regs(); + // NOTE(unsafe) Atomic write + unsafe { + regs.maskr().write(|w| { + w.set_dcrcfailie(enable); + w.set_dtimeoutie(enable); + w.set_dataendie(enable); + + #[cfg(sdmmc_v2)] + w.set_dabortie(enable); + }); + } + } + + async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { + // Read the the 64-bit SCR register + self.cmd(Cmd::set_block_length(8), false)?; // CMD16 + self.cmd(Cmd::app_cmd(card.rca << 16), false)?; + + let mut scr = [0u32; 2]; + + // Arm `OnDrop` after the buffer, so it will be dropped first + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); + + unsafe { + self.prepare_datapath_read(&mut scr[..], 8, 3); + Self::data_interrupts(true); + } + self.cmd(Cmd::cmd51(), true)?; + + let res = poll_fn(|cx| { + T::state().register(cx.waker()); + let status = unsafe { regs.star().read() }; + + if status.dcrcfail() { + return Poll::Ready(Err(Error::Crc)); + } else if status.dtimeout() { + return Poll::Ready(Err(Error::Timeout)); + } else if status.dataend() { + return Poll::Ready(Ok(())); + } + Poll::Pending + }) + .await; + Self::clear_interrupt_flags(); + + if res.is_ok() { + on_drop.defuse(); + Self::stop_datapath(); + + unsafe { + let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]); + card.scr = SCR(u64::from_be_bytes(*scr_bytes)); + } + } + res + } + + /// Send command to card + #[allow(unused_variables)] + fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { + let regs = T::regs(); + + Self::clear_interrupt_flags(); + // NOTE(safety) Atomic operations + unsafe { + // CP state machine must be idle + while Self::cmd_active() {} + + // Command arg + regs.argr().write(|w| w.set_cmdarg(cmd.arg)); + + // Command index and start CP State Machine + regs.cmdr().write(|w| { + w.set_waitint(false); + w.set_waitresp(cmd.resp as u8); + w.set_cmdindex(cmd.cmd); + w.set_cpsmen(true); + + #[cfg(sdmmc_v2)] + { + // Special mode in CP State Machine + // CMD12: Stop Transmission + let cpsm_stop_transmission = cmd.cmd == 12; + w.set_cmdstop(cpsm_stop_transmission); + w.set_cmdtrans(data); + } + }); + + let mut status; + if cmd.resp == Response::None { + // Wait for CMDSENT or a timeout + while { + status = regs.star().read(); + !(status.ctimeout() || status.cmdsent()) + } {} + } else { + // Wait for CMDREND or CCRCFAIL or a timeout + while { + status = regs.star().read(); + !(status.ctimeout() || status.cmdrend() || status.ccrcfail()) + } {} + } + + if status.ctimeout() { + return Err(Error::Timeout); + } else if status.ccrcfail() { + return Err(Error::Crc); + } + Ok(()) + } + } + + /// # Safety + /// + /// Ensure that `regs` has exclusive access to the regblocks + unsafe fn on_drop() { + let regs = T::regs(); + if Self::data_active() { + Self::clear_interrupt_flags(); + // Send abort + // CP state machine must be idle + while Self::cmd_active() {} + + // Command arg + regs.argr().write(|w| w.set_cmdarg(0)); + + // Command index and start CP State Machine + regs.cmdr().write(|w| { + w.set_waitint(false); + w.set_waitresp(Response::Short as u8); + w.set_cmdindex(12); + w.set_cpsmen(true); + + #[cfg(sdmmc_v2)] + { + w.set_cmdstop(true); + w.set_cmdtrans(false); + } + }); + + // Wait for the abort + while Self::data_active() {} + } + Self::data_interrupts(false); + Self::clear_interrupt_flags(); + Self::stop_datapath(); + } + + /// Initializes card (if present) and sets the bus at the + /// specified frequency. pub async fn init_card(&mut self, freq: Hertz) -> Result<(), Error> { - let inner = T::inner(); - let freq = freq.into(); + let regs = T::regs(); + let ker_ck = T::kernel_clk(); let bus_width = match self.d3.is_some() { true => BusWidth::Four, false => BusWidth::One, }; - inner - .init_card( - freq, - bus_width, - &mut self.card, - &mut self.signalling, - T::kernel_clk(), - &mut self.clock, - T::state(), - self.config.data_transfer_timeout, - &mut *self.dma, - ) - .await - } - - #[inline(always)] - pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { - let card_capacity = self.card()?.card_type; - let inner = T::inner(); - let state = T::state(); - - // NOTE(unsafe) DataBlock uses align 4 - let buf = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; - inner - .read_block( - block_idx, - buf, - card_capacity, - state, - self.config.data_transfer_timeout, - &mut *self.dma, - ) - .await - } - - pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { - let card = self.card.as_mut().ok_or(Error::NoCard)?; - let inner = T::inner(); - let state = T::state(); - - // NOTE(unsafe) DataBlock uses align 4 - let buf = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; - inner - .write_block( - block_idx, - buf, - card, - state, - self.config.data_transfer_timeout, - &mut *self.dma, - ) - .await - } - - /// Get a reference to the initialized card - /// - /// # Errors - /// - /// Returns Error::NoCard if [`init_card`](#method.init_card) - /// has not previously succeeded - #[inline(always)] - pub fn card(&self) -> Result<&Card, Error> { - self.card.as_ref().ok_or(Error::NoCard) - } - - /// Get the current SDMMC bus clock - pub fn clock(&self) -> Hertz { - self.clock - } - - #[inline(always)] - fn on_interrupt(_: *mut ()) { - let regs = T::inner(); - let state = T::state(); - - regs.data_interrupts(false); - state.wake(); - } -} - -impl<'d, T: Instance, Dma> Drop for Sdmmc<'d, T, Dma> { - fn drop(&mut self) { - self.irq.disable(); - let inner = T::inner(); - unsafe { inner.on_drop() }; - - critical_section::with(|_| unsafe { - self.clk.set_as_disconnected(); - self.cmd.set_as_disconnected(); - self.d0.set_as_disconnected(); - if let Some(x) = &mut self.d1 { - x.set_as_disconnected(); - } - if let Some(x) = &mut self.d2 { - x.set_as_disconnected(); - } - if let Some(x) = &mut self.d3 { - x.set_as_disconnected(); - } - }); - } -} - -pub struct SdmmcInner(pub(crate) RegBlock); - -impl SdmmcInner { - /// # Safety - /// - /// Access to `regs` registers should be exclusive - unsafe fn new_inner(&self) { - let regs = self.0; - - regs.clkcr().write(|w| { - w.set_pwrsav(false); - w.set_negedge(false); - - // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors. - // See chip erratas for more details. - #[cfg(sdmmc_v1)] - w.set_hwfc_en(false); - #[cfg(sdmmc_v2)] - w.set_hwfc_en(true); - - #[cfg(sdmmc_v1)] - w.set_clken(true); - }); - - // Power off, writen 00: Clock to the card is stopped; - // D[7:0], CMD, and CK are driven high. - regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8)); - } - - /// Initializes card (if present) and sets the bus at the - /// specified frequency. - #[allow(clippy::too_many_arguments)] - async fn init_card>( - &self, - freq: Hertz, - bus_width: BusWidth, - old_card: &mut Option, - signalling: &mut Signalling, - ker_ck: Hertz, - clock: &mut Hertz, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { - let regs = self.0; - // NOTE(unsafe) We have exclusive access to the peripheral unsafe { // While the SD/SDIO card or eMMC is in identification mode, // the SDMMC_CK frequency must be no more than 400 kHz. let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0)); - *clock = init_clock; + self.clock = init_clock; // CPSMACT and DPSMACT must be 0 to set WIDBUS - self.wait_idle(); + Self::wait_idle(); regs.clkcr().modify(|w| { w.set_widbus(0); @@ -712,7 +1069,7 @@ impl SdmmcInner { self.select_card(Some(&card))?; - self.get_scr(&mut card, waker_reg, data_transfer_timeout, dma).await?; + self.get_scr(&mut card).await?; // Set bus width let (width, acmd_arg) = match bus_width { @@ -724,7 +1081,7 @@ impl SdmmcInner { self.cmd(Cmd::cmd6(acmd_arg), false)?; // CPSMACT and DPSMACT must be 0 to set WIDBUS - self.wait_idle(); + Self::wait_idle(); regs.clkcr().modify(|w| { w.set_widbus(match width { @@ -738,25 +1095,24 @@ impl SdmmcInner { // Set Clock if freq.0 <= 25_000_000 { // Final clock frequency - self.clkcr_set_clkdiv(freq.0, width, ker_ck, clock)?; + self.clkcr_set_clkdiv(freq.0, width)?; } else { // Switch to max clock for SDR12 - self.clkcr_set_clkdiv(25_000_000, width, ker_ck, clock)?; + self.clkcr_set_clkdiv(25_000_000, width)?; } + self.card = Some(card); + // Read status - self.read_sd_status(&mut card, waker_reg, data_transfer_timeout, dma) - .await?; + self.read_sd_status().await?; if freq.0 > 25_000_000 { // Switch to SDR25 - *signalling = self - .switch_signalling_mode(Signalling::SDR25, waker_reg, data_transfer_timeout, dma) - .await?; + self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?; - if *signalling == Signalling::SDR25 { + if self.signalling == Signalling::SDR25 { // Set final clock frequency - self.clkcr_set_clkdiv(freq.0, width, ker_ck, clock)?; + self.clkcr_set_clkdiv(freq.0, width)?; if self.read_status(&card)?.state() != CurrentState::Transfer { return Err(Error::SignalingSwitchFailed); @@ -764,42 +1120,38 @@ impl SdmmcInner { } } // Read status after signalling change - self.read_sd_status(&mut card, waker_reg, data_transfer_timeout, dma) - .await?; - old_card.replace(card); + self.read_sd_status().await?; } Ok(()) } - async fn read_block>( - &self, - block_idx: u32, - buffer: &mut [u32; 128], - capacity: CardCapacity, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { + #[inline(always)] + pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { + let card_capacity = self.card()?.card_type; + + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &mut *((&mut buffer.0) as *mut [u8; 512] as *mut [u32; 128]) }; + // Always read 1 block of 512 bytes // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes - let address = match capacity { + let address = match card_capacity { CardCapacity::SDSC => block_idx * 512, _ => block_idx, }; self.cmd(Cmd::set_block_length(512), false)?; // CMD16 - let regs = self.0; - let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); unsafe { - self.prepare_datapath_read(buffer, 512, 9, data_transfer_timeout, dma); - self.data_interrupts(true); + self.prepare_datapath_read(buffer, 512, 9); + Self::data_interrupts(true); } self.cmd(Cmd::read_single_block(address), true)?; let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); + T::state().register(cx.waker()); let status = unsafe { regs.star().read() }; if status.dcrcfail() { @@ -812,24 +1164,21 @@ impl SdmmcInner { Poll::Pending }) .await; - self.clear_interrupt_flags(); + Self::clear_interrupt_flags(); if res.is_ok() { on_drop.defuse(); - self.stop_datapath(); + Self::stop_datapath(); } res } - async fn write_block>( - &self, - block_idx: u32, - buffer: &[u32; 128], - card: &mut Card, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { + pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { + let card = self.card.as_mut().ok_or(Error::NoCard)?; + + // NOTE(unsafe) DataBlock uses align 4 + let buffer = unsafe { &*((&buffer.0) as *const [u8; 512] as *const [u32; 128]) }; + // Always read 1 block of 512 bytes // SDSC cards are byte addressed hence the blockaddress is in multiples of 512 bytes let address = match card.card_type { @@ -838,23 +1187,23 @@ impl SdmmcInner { }; self.cmd(Cmd::set_block_length(512), false)?; // CMD16 - let regs = self.0; - let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); + let regs = T::regs(); + let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); // sdmmc_v1 uses different cmd/dma order than v2, but only for writes #[cfg(sdmmc_v1)] self.cmd(Cmd::write_single_block(address), true)?; unsafe { - self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9, data_transfer_timeout, dma); - self.data_interrupts(true); + self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9); + Self::data_interrupts(true); } #[cfg(sdmmc_v2)] self.cmd(Cmd::write_single_block(address), true)?; let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); + T::state().register(cx.waker()); let status = unsafe { regs.star().read() }; if status.dcrcfail() { @@ -867,19 +1216,19 @@ impl SdmmcInner { Poll::Pending }) .await; - self.clear_interrupt_flags(); + Self::clear_interrupt_flags(); match res { Ok(_) => { on_drop.defuse(); - self.stop_datapath(); + Self::stop_datapath(); // TODO: Make this configurable let mut timeout: u32 = 0x00FF_FFFF; // Try to read card status (ACMD13) while timeout > 0 { - match self.read_sd_status(card, waker_reg, data_transfer_timeout, dma).await { + match self.read_sd_status().await { Ok(_) => return Ok(()), Err(Error::Timeout) => (), // Try again Err(e) => return Err(e), @@ -892,557 +1241,49 @@ impl SdmmcInner { } } - /// Data transfer is in progress - #[inline(always)] - fn data_active(&self) -> bool { - let regs = self.0; - - // NOTE(unsafe) Atomic read with no side-effects - unsafe { - let status = regs.star().read(); - #[cfg(sdmmc_v1)] - return status.rxact() || status.txact(); - #[cfg(sdmmc_v2)] - return status.dpsmact(); - } - } - - /// Coammand transfer is in progress - #[inline(always)] - fn cmd_active(&self) -> bool { - let regs = self.0; - - // NOTE(unsafe) Atomic read with no side-effects - unsafe { - let status = regs.star().read(); - #[cfg(sdmmc_v1)] - return status.cmdact(); - #[cfg(sdmmc_v2)] - return status.cpsmact(); - } - } - - /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2) - #[inline(always)] - fn wait_idle(&self) { - while self.data_active() || self.cmd_active() {} - } - - /// # Safety + /// Get a reference to the initialized card /// - /// `buffer` must be valid for the whole transfer and word aligned - unsafe fn prepare_datapath_read>( - &self, - buffer: *mut [u32], - length_bytes: u32, - block_size: u8, - data_transfer_timeout: u32, - #[allow(unused_variables)] dma: &mut Dma, - ) { - assert!(block_size <= 14, "Block size up to 2^14 bytes"); - let regs = self.0; + /// # Errors + /// + /// Returns Error::NoCard if [`init_card`](#method.init_card) + /// has not previously succeeded + #[inline(always)] + pub fn card(&self) -> Result<&Card, Error> { + self.card.as_ref().ok_or(Error::NoCard) + } - // Command AND Data state machines must be idle - self.wait_idle(); - self.clear_interrupt_flags(); + /// Get the current SDMMC bus clock + pub fn clock(&self) -> Hertz { + self.clock + } - // NOTE(unsafe) We have exclusive access to the regisers + #[inline(always)] + fn on_interrupt(_: *mut ()) { + Self::data_interrupts(false); + T::state().wake(); + } +} - regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); +impl<'d, T: Instance, Dma: SdmmcDma + 'd> Drop for Sdmmc<'d, T, Dma> { + fn drop(&mut self) { + self.irq.disable(); + unsafe { Self::on_drop() }; - #[cfg(sdmmc_v1)] - { - let request = dma.request(); - dma.start_read( - request, - regs.fifor().ptr() as *const u32, - buffer, - crate::dma::TransferOptions { - pburst: crate::dma::Burst::Incr4, - mburst: crate::dma::Burst::Incr4, - flow_ctrl: crate::dma::FlowControl::Peripheral, - fifo_threshold: Some(crate::dma::FifoThreshold::Full), - ..Default::default() - }, - ); - } - #[cfg(sdmmc_v2)] - { - regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } - - regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); - w.set_dtdir(true); - #[cfg(sdmmc_v1)] - { - w.set_dmaen(true); - w.set_dten(true); + critical_section::with(|_| unsafe { + self.clk.set_as_disconnected(); + self.cmd.set_as_disconnected(); + self.d0.set_as_disconnected(); + if let Some(x) = &mut self.d1 { + x.set_as_disconnected(); + } + if let Some(x) = &mut self.d2 { + x.set_as_disconnected(); + } + if let Some(x) = &mut self.d3 { + x.set_as_disconnected(); } }); } - - /// # Safety - /// - /// `buffer` must be valid for the whole transfer and word aligned - unsafe fn prepare_datapath_write>( - &self, - buffer: *const [u32], - length_bytes: u32, - block_size: u8, - data_transfer_timeout: u32, - #[allow(unused_variables)] dma: &mut Dma, - ) { - assert!(block_size <= 14, "Block size up to 2^14 bytes"); - let regs = self.0; - - // Command AND Data state machines must be idle - self.wait_idle(); - self.clear_interrupt_flags(); - - // NOTE(unsafe) We have exclusive access to the regisers - - regs.dtimer().write(|w| w.set_datatime(data_transfer_timeout)); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); - - #[cfg(sdmmc_v1)] - { - let request = dma.request(); - dma.start_write( - request, - buffer, - regs.fifor().ptr() as *mut u32, - crate::dma::TransferOptions { - pburst: crate::dma::Burst::Incr4, - mburst: crate::dma::Burst::Incr4, - flow_ctrl: crate::dma::FlowControl::Peripheral, - fifo_threshold: Some(crate::dma::FifoThreshold::Full), - ..Default::default() - }, - ); - } - #[cfg(sdmmc_v2)] - { - regs.idmabase0r() - .write(|w| w.set_idmabase0(buffer as *const u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } - - regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); - w.set_dtdir(false); - #[cfg(sdmmc_v1)] - { - w.set_dmaen(true); - w.set_dten(true); - } - }); - } - - /// Stops the DMA datapath - fn stop_datapath(&self) { - let regs = self.0; - - unsafe { - #[cfg(sdmmc_v1)] - regs.dctrl().modify(|w| { - w.set_dmaen(false); - w.set_dten(false); - }); - #[cfg(sdmmc_v2)] - regs.idmactrlr().modify(|w| w.set_idmaen(false)); - } - } - - /// Sets the CLKDIV field in CLKCR. Updates clock field in self - fn clkcr_set_clkdiv(&self, freq: u32, width: BusWidth, ker_ck: Hertz, clock: &mut Hertz) -> Result<(), Error> { - let regs = self.0; - - let width_u32 = match width { - BusWidth::One => 1u32, - BusWidth::Four => 4u32, - BusWidth::Eight => 8u32, - _ => panic!("Invalid Bus Width"), - }; - - let (_bypass, clkdiv, new_clock) = clk_div(ker_ck, freq)?; - - // Enforce AHB and SDMMC_CK clock relation. See RM0433 Rev 7 - // Section 55.5.8 - let sdmmc_bus_bandwidth = new_clock.0 * width_u32; - assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32); - *clock = new_clock; - - // NOTE(unsafe) We have exclusive access to the regblock - unsafe { - // CPSMACT and DPSMACT must be 0 to set CLKDIV - self.wait_idle(); - regs.clkcr().modify(|w| { - w.set_clkdiv(clkdiv); - #[cfg(sdmmc_v1)] - w.set_bypass(_bypass); - }); - } - - Ok(()) - } - - /// Switch mode using CMD6. - /// - /// Attempt to set a new signalling mode. The selected - /// signalling mode is returned. Expects the current clock - /// frequency to be > 12.5MHz. - async fn switch_signalling_mode>( - &self, - signalling: Signalling, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result { - // NB PLSS v7_10 4.3.10.4: "the use of SET_BLK_LEN command is not - // necessary" - - let set_function = 0x8000_0000 - | match signalling { - // See PLSS v7_10 Table 4-11 - Signalling::DDR50 => 0xFF_FF04, - Signalling::SDR104 => 0xFF_1F03, - Signalling::SDR50 => 0xFF_1F02, - Signalling::SDR25 => 0xFF_FF01, - Signalling::SDR12 => 0xFF_FF00, - }; - - let mut status = [0u32; 16]; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = self.0; - let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); - - unsafe { - self.prepare_datapath_read(&mut status, 64, 6, data_transfer_timeout, dma); - self.data_interrupts(true); - } - self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 - - let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); - let status = unsafe { regs.star().read() }; - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } else if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } else if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - self.clear_interrupt_flags(); - - // Host is allowed to use the new functions at least 8 - // clocks after the end of the switch command - // transaction. We know the current clock period is < 80ns, - // so a total delay of 640ns is required here - for _ in 0..300 { - cortex_m::asm::nop(); - } - - match res { - Ok(_) => { - on_drop.defuse(); - self.stop_datapath(); - - // Function Selection of Function Group 1 - let selection = (u32::from_be(status[4]) >> 24) & 0xF; - - match selection { - 0 => Ok(Signalling::SDR12), - 1 => Ok(Signalling::SDR25), - 2 => Ok(Signalling::SDR50), - 3 => Ok(Signalling::SDR104), - 4 => Ok(Signalling::DDR50), - _ => Err(Error::UnsupportedCardType), - } - } - Err(e) => Err(e), - } - } - - /// Query the card status (CMD13, returns R1) - fn read_status(&self, card: &Card) -> Result { - let regs = self.0; - let rca = card.rca; - - self.cmd(Cmd::card_status(rca << 16), false)?; // CMD13 - - // NOTE(unsafe) Atomic read with no side-effects - let r1 = unsafe { regs.respr(0).read().cardstatus() }; - Ok(r1.into()) - } - - /// Reads the SD Status (ACMD13) - async fn read_sd_status>( - &self, - card: &mut Card, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { - let rca = card.rca; - self.cmd(Cmd::set_block_length(64), false)?; // CMD16 - self.cmd(Cmd::app_cmd(rca << 16), false)?; // APP - - let mut status = [0u32; 16]; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = self.0; - let on_drop = OnDrop::new(|| unsafe { self.on_drop() }); - - unsafe { - self.prepare_datapath_read(&mut status, 64, 6, data_transfer_timeout, dma); - self.data_interrupts(true); - } - self.cmd(Cmd::card_status(0), true)?; - - let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); - let status = unsafe { regs.star().read() }; - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } else if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } else if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - self.clear_interrupt_flags(); - - if res.is_ok() { - on_drop.defuse(); - self.stop_datapath(); - - for byte in status.iter_mut() { - *byte = u32::from_be(*byte); - } - card.status = status.into(); - } - res - } - - /// Select one card and place it into the _Tranfer State_ - /// - /// If `None` is specifed for `card`, all cards are put back into - /// _Stand-by State_ - fn select_card(&self, card: Option<&Card>) -> Result<(), Error> { - // Determine Relative Card Address (RCA) of given card - let rca = card.map(|c| c.rca << 16).unwrap_or(0); - - let r = self.cmd(Cmd::sel_desel_card(rca), false); - match (r, rca) { - (Err(Error::Timeout), 0) => Ok(()), - _ => r, - } - } - - /// Clear flags in interrupt clear register - #[inline(always)] - fn clear_interrupt_flags(&self) { - let regs = self.0; - // NOTE(unsafe) Atomic write - unsafe { - regs.icr().write(|w| { - w.set_ccrcfailc(true); - w.set_dcrcfailc(true); - w.set_ctimeoutc(true); - w.set_dtimeoutc(true); - w.set_txunderrc(true); - w.set_rxoverrc(true); - w.set_cmdrendc(true); - w.set_cmdsentc(true); - w.set_dataendc(true); - w.set_dbckendc(true); - w.set_sdioitc(true); - - #[cfg(sdmmc_v2)] - { - w.set_dholdc(true); - w.set_dabortc(true); - w.set_busyd0endc(true); - w.set_ackfailc(true); - w.set_acktimeoutc(true); - w.set_vswendc(true); - w.set_ckstopc(true); - w.set_idmatec(true); - w.set_idmabtcc(true); - } - }); - } - } - - /// Enables the interrupts for data transfer - #[inline(always)] - fn data_interrupts(&self, enable: bool) { - let regs = self.0; - // NOTE(unsafe) Atomic write - unsafe { - regs.maskr().write(|w| { - w.set_dcrcfailie(enable); - w.set_dtimeoutie(enable); - w.set_dataendie(enable); - - #[cfg(sdmmc_v2)] - w.set_dabortie(enable); - }); - } - } - - async fn get_scr>( - &self, - card: &mut Card, - waker_reg: &AtomicWaker, - data_transfer_timeout: u32, - dma: &mut Dma, - ) -> Result<(), Error> { - // Read the the 64-bit SCR register - self.cmd(Cmd::set_block_length(8), false)?; // CMD16 - self.cmd(Cmd::app_cmd(card.rca << 16), false)?; - - let mut scr = [0u32; 2]; - - // Arm `OnDrop` after the buffer, so it will be dropped first - let regs = self.0; - let on_drop = OnDrop::new(move || unsafe { self.on_drop() }); - - unsafe { - self.prepare_datapath_read(&mut scr[..], 8, 3, data_transfer_timeout, dma); - self.data_interrupts(true); - } - self.cmd(Cmd::cmd51(), true)?; - - let res = poll_fn(|cx| { - waker_reg.register(cx.waker()); - let status = unsafe { regs.star().read() }; - - if status.dcrcfail() { - return Poll::Ready(Err(Error::Crc)); - } else if status.dtimeout() { - return Poll::Ready(Err(Error::Timeout)); - } else if status.dataend() { - return Poll::Ready(Ok(())); - } - Poll::Pending - }) - .await; - self.clear_interrupt_flags(); - - if res.is_ok() { - on_drop.defuse(); - self.stop_datapath(); - - unsafe { - let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]); - card.scr = SCR(u64::from_be_bytes(*scr_bytes)); - } - } - res - } - - /// Send command to card - #[allow(unused_variables)] - fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { - let regs = self.0; - - self.clear_interrupt_flags(); - // NOTE(safety) Atomic operations - unsafe { - // CP state machine must be idle - while self.cmd_active() {} - - // Command arg - regs.argr().write(|w| w.set_cmdarg(cmd.arg)); - - // Command index and start CP State Machine - regs.cmdr().write(|w| { - w.set_waitint(false); - w.set_waitresp(cmd.resp as u8); - w.set_cmdindex(cmd.cmd); - w.set_cpsmen(true); - - #[cfg(sdmmc_v2)] - { - // Special mode in CP State Machine - // CMD12: Stop Transmission - let cpsm_stop_transmission = cmd.cmd == 12; - w.set_cmdstop(cpsm_stop_transmission); - w.set_cmdtrans(data); - } - }); - - let mut status; - if cmd.resp == Response::None { - // Wait for CMDSENT or a timeout - while { - status = regs.star().read(); - !(status.ctimeout() || status.cmdsent()) - } {} - } else { - // Wait for CMDREND or CCRCFAIL or a timeout - while { - status = regs.star().read(); - !(status.ctimeout() || status.cmdrend() || status.ccrcfail()) - } {} - } - - if status.ctimeout() { - return Err(Error::Timeout); - } else if status.ccrcfail() { - return Err(Error::Crc); - } - Ok(()) - } - } - - /// # Safety - /// - /// Ensure that `regs` has exclusive access to the regblocks - unsafe fn on_drop(&self) { - let regs = self.0; - if self.data_active() { - self.clear_interrupt_flags(); - // Send abort - // CP state machine must be idle - while self.cmd_active() {} - - // Command arg - regs.argr().write(|w| w.set_cmdarg(0)); - - // Command index and start CP State Machine - regs.cmdr().write(|w| { - w.set_waitint(false); - w.set_waitresp(Response::Short as u8); - w.set_cmdindex(12); - w.set_cpsmen(true); - - #[cfg(sdmmc_v2)] - { - w.set_cmdstop(true); - w.set_cmdtrans(false); - } - }); - - // Wait for the abort - while self.data_active() {} - } - self.data_interrupts(false); - self.clear_interrupt_flags(); - self.stop_datapath(); - } } /// SD card Commands @@ -1540,7 +1381,7 @@ pub(crate) mod sealed { pub trait Instance { type Interrupt: Interrupt; - fn inner() -> SdmmcInner; + fn regs() -> RegBlock; fn state() -> &'static AtomicWaker; fn kernel_clk() -> Hertz; } @@ -1629,9 +1470,8 @@ foreach_peripheral!( impl sealed::Instance for peripherals::$inst { type Interrupt = crate::interrupt::$inst; - fn inner() -> SdmmcInner { - const INNER: SdmmcInner = SdmmcInner(crate::pac::$inst); - INNER + fn regs() -> RegBlock { + crate::pac::$inst } fn state() -> &'static ::embassy_sync::waitqueue::AtomicWaker { From 82dd7a5f8c08fe9b03b9d885ad992fcab1a9b00f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 21:48:47 +0200 Subject: [PATCH 29/46] stm32/sdmmc: add init_card retry. --- examples/stm32f4/src/bin/sdmmc.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index ebdfdb22d..eeecbd321 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs @@ -39,7 +39,18 @@ async fn main(_spawner: Spawner) { // Should print 400kHz for initialization info!("Configured clock: {}", sdmmc.clock().0); - unwrap!(sdmmc.init_card(mhz(48)).await); + let mut err = None; + loop { + match sdmmc.init_card(mhz(24)).await { + Ok(_) => break, + Err(e) => { + if err != Some(e) { + info!("waiting for card error, retrying: {:?}", e); + err = Some(e); + } + } + } + } let card = unwrap!(sdmmc.card()); From e63a34ba2167cd7637601ff2e7770dca00b1e3e1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 21:49:34 +0200 Subject: [PATCH 30/46] stm32/sdmmc: add hil test for f4. --- tests/stm32/Cargo.toml | 43 +++++++++- tests/stm32/gen_test.py | 44 +++++++++++ tests/stm32/src/bin/sdmmc.rs | 148 +++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 tests/stm32/gen_test.py create mode 100644 tests/stm32/src/bin/sdmmc.rs diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 8b70a1015..3047c34ce 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [features] stm32f103c8 = ["embassy-stm32/stm32f103c8"] # Blue Pill -stm32f429zi = ["embassy-stm32/stm32f429zi"] # Nucleo +stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc"] # Nucleo stm32g071rb = ["embassy-stm32/stm32g071rb"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re"] # Nucleo @@ -15,6 +15,8 @@ stm32wb55rg = ["embassy-stm32/stm32wb55rg"] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board +sdmmc = [] + [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } @@ -31,6 +33,45 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } embedded-hal-async = { version = "=0.2.0-alpha.1" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } +# BEGIN TESTS +# Generated by gen_test.py. DO NOT EDIT. +[[bin]] +name = "gpio" +path = "src/bin/gpio.rs" +required-features = [] + +[[bin]] +name = "sdmmc" +path = "src/bin/sdmmc.rs" +required-features = [ "sdmmc",] + +[[bin]] +name = "spi" +path = "src/bin/spi.rs" +required-features = [] + +[[bin]] +name = "spi_dma" +path = "src/bin/spi_dma.rs" +required-features = [] + +[[bin]] +name = "timer" +path = "src/bin/timer.rs" +required-features = [] + +[[bin]] +name = "usart" +path = "src/bin/usart.rs" +required-features = [] + +[[bin]] +name = "usart_dma" +path = "src/bin/usart_dma.rs" +required-features = [] + +# END TESTS + [profile.dev] debug = 2 debug-assertions = true diff --git a/tests/stm32/gen_test.py b/tests/stm32/gen_test.py new file mode 100644 index 000000000..8ff156c0e --- /dev/null +++ b/tests/stm32/gen_test.py @@ -0,0 +1,44 @@ +import os +import toml +from glob import glob + +abspath = os.path.abspath(__file__) +dname = os.path.dirname(abspath) +os.chdir(dname) + +# ======= load test list +tests = {} +for f in sorted(glob('./src/bin/*.rs')): + name = os.path.splitext(os.path.basename(f))[0] + features = [] + with open(f, 'r') as f: + for line in f: + if line.startswith('// required-features:'): + features = line.split(':', 2)[1].strip().split(',') + + tests[name] = features + +# ========= Update Cargo.toml + +things = { + 'bin': [ + { + 'name': f'{name}', + 'path': f'src/bin/{name}.rs', + 'required-features': features, + } + for name, features in tests.items() + ] +} + +SEPARATOR_START = '# BEGIN TESTS\n' +SEPARATOR_END = '# END TESTS\n' +HELP = '# Generated by gen_test.py. DO NOT EDIT.\n' +with open('Cargo.toml', 'r') as f: + data = f.read() +before, data = data.split(SEPARATOR_START, maxsplit=1) +_, after = data.split(SEPARATOR_END, maxsplit=1) +data = before + SEPARATOR_START + HELP + \ + toml.dumps(things) + SEPARATOR_END + after +with open('Cargo.toml', 'w') as f: + f.write(data) diff --git a/tests/stm32/src/bin/sdmmc.rs b/tests/stm32/src/bin/sdmmc.rs new file mode 100644 index 000000000..c4e50cb4a --- /dev/null +++ b/tests/stm32/src/bin/sdmmc.rs @@ -0,0 +1,148 @@ +// required-features: sdmmc +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; +use embassy_stm32::time::mhz; +use embassy_stm32::{interrupt, Config}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + config.rcc.sys_ck = Some(mhz(48)); + config.rcc.pll48 = true; + let p = embassy_stm32::init(config); + + #[cfg(feature = "stm32f429zi")] + let (mut sdmmc, mut irq, mut dma, mut clk, mut cmd, mut d0, mut d1, mut d2, mut d3) = ( + p.SDIO, + interrupt::take!(SDIO), + p.DMA2_CH3, + p.PC12, + p.PD2, + p.PC8, + p.PC9, + p.PC10, + p.PC11, + ); + + // Arbitrary block index + let block_idx = 16; + + let mut pattern1 = DataBlock([0u8; 512]); + let mut pattern2 = DataBlock([0u8; 512]); + for i in 0..512 { + pattern1[i] = i as u8; + pattern2[i] = !i as u8; + } + + let mut block = DataBlock([0u8; 512]); + + // ======== Try 4bit. ============== + info!("initializing in 4-bit mode..."); + let mut s = Sdmmc::new_4bit( + &mut sdmmc, + &mut irq, + &mut dma, + &mut clk, + &mut cmd, + &mut d0, + &mut d1, + &mut d2, + &mut d3, + Default::default(), + ); + + let mut err = None; + loop { + match s.init_card(mhz(24)).await { + Ok(_) => break, + Err(e) => { + if err != Some(e) { + info!("waiting for card: {:?}", e); + err = Some(e); + } + } + } + } + + let card = unwrap!(s.card()); + + info!("Card: {:#?}", Debug2Format(card)); + info!("Clock: {}", s.clock()); + + info!("writing pattern1..."); + s.write_block(block_idx, &pattern1).await.unwrap(); + + info!("reading..."); + s.read_block(block_idx, &mut block).await.unwrap(); + assert_eq!(block, pattern1); + + info!("writing pattern2..."); + s.write_block(block_idx, &pattern2).await.unwrap(); + + info!("reading..."); + s.read_block(block_idx, &mut block).await.unwrap(); + assert_eq!(block, pattern2); + + drop(s); + + // ======== Try 1bit. ============== + info!("initializing in 1-bit mode..."); + let mut s = Sdmmc::new_1bit( + &mut sdmmc, + &mut irq, + &mut dma, + &mut clk, + &mut cmd, + &mut d0, + Default::default(), + ); + + let mut err = None; + loop { + match s.init_card(mhz(24)).await { + Ok(_) => break, + Err(e) => { + if err != Some(e) { + info!("waiting for card: {:?}", e); + err = Some(e); + } + } + } + } + + let card = unwrap!(s.card()); + + info!("Card: {:#?}", Debug2Format(card)); + info!("Clock: {}", s.clock()); + + info!("reading pattern2 written in 4bit mode..."); + s.read_block(block_idx, &mut block).await.unwrap(); + assert_eq!(block, pattern2); + + info!("writing pattern1..."); + s.write_block(block_idx, &pattern1).await.unwrap(); + + info!("reading..."); + s.read_block(block_idx, &mut block).await.unwrap(); + assert_eq!(block, pattern1); + + info!("writing pattern2..."); + s.write_block(block_idx, &pattern2).await.unwrap(); + + info!("reading..."); + s.read_block(block_idx, &mut block).await.unwrap(); + assert_eq!(block, pattern2); + + drop(s); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From f5216624bb228b5b70f97f0e7ebc40ea4d7e1a27 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 17 Apr 2023 15:24:24 -0500 Subject: [PATCH 31/46] stm32/i2c: fix races when using dma. fixes #1341. --- embassy-stm32/src/i2c/v2.rs | 103 ++++++++++++++---------------------- 1 file changed, 41 insertions(+), 62 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 7218f7706..44237b890 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1,6 +1,5 @@ use core::cmp; use core::future::poll_fn; -use core::sync::atomic::{AtomicUsize, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; @@ -35,14 +34,12 @@ impl Default for Config { pub struct State { waker: AtomicWaker, - chunks_transferred: AtomicUsize, } impl State { pub(crate) const fn new() -> Self { Self { waker: AtomicWaker::new(), - chunks_transferred: AtomicUsize::new(0), } } } @@ -130,10 +127,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let isr = regs.isr().read(); if isr.tcr() || isr.tc() { - let state = T::state(); - let transferred = state.chunks_transferred.load(Ordering::Relaxed); - state.chunks_transferred.store(transferred + 1, Ordering::Relaxed); - state.waker.wake(); + T::state().waker.wake(); } // The flag can only be cleared by writting to nbytes, we won't do that here, so disable // the interrupt @@ -457,12 +451,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { TXDMA: crate::i2c::TxDma, { let total_len = write.len(); - let completed_chunks = total_len / 255; - let total_chunks = if completed_chunks * 255 == total_len { - completed_chunks - } else { - completed_chunks + 1 - }; let dma_transfer = unsafe { let regs = T::regs(); @@ -480,7 +468,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }; let state = T::state(); - state.chunks_transferred.store(0, Ordering::Relaxed); let mut remaining_len = total_len; let on_drop = OnDrop::new(|| { @@ -495,33 +482,31 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } }); - // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers - if first_slice { - unsafe { - Self::master_write( - address, - total_len.min(255), - Stop::Software, - (total_chunks != 1) || !last_slice, - &check_timeout, - )?; - } - } else { - unsafe { - Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice, &check_timeout)?; - T::regs().cr1().modify(|w| w.set_tcie(true)); - } - } - poll_fn(|cx| { state.waker.register(cx.waker()); - let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed); - if chunks_transferred == total_chunks { + if remaining_len == total_len { + // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers + if first_slice { + unsafe { + Self::master_write( + address, + total_len.min(255), + Stop::Software, + (total_len > 255) || !last_slice, + &check_timeout, + )?; + } + } else { + unsafe { + Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, &check_timeout)?; + T::regs().cr1().modify(|w| w.set_tcie(true)); + } + } + } else if remaining_len == 0 { return Poll::Ready(Ok(())); - } else if chunks_transferred != 0 { - remaining_len = remaining_len.saturating_sub(255); - let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice; + } else { + let last_piece = (remaining_len <= 255) && last_slice; // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers unsafe { @@ -531,6 +516,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { T::regs().cr1().modify(|w| w.set_tcie(true)); } } + + remaining_len = remaining_len.saturating_sub(255); Poll::Pending }) .await?; @@ -559,12 +546,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { RXDMA: crate::i2c::RxDma, { let total_len = buffer.len(); - let completed_chunks = total_len / 255; - let total_chunks = if completed_chunks * 255 == total_len { - completed_chunks - } else { - completed_chunks + 1 - }; let dma_transfer = unsafe { let regs = T::regs(); @@ -580,7 +561,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }; let state = T::state(); - state.chunks_transferred.store(0, Ordering::Relaxed); let mut remaining_len = total_len; let on_drop = OnDrop::new(|| { @@ -593,27 +573,24 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } }); - // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers - unsafe { - Self::master_read( - address, - total_len.min(255), - Stop::Software, - total_chunks != 1, - restart, - &check_timeout, - )?; - } - poll_fn(|cx| { state.waker.register(cx.waker()); - let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed); - - if chunks_transferred == total_chunks { + if remaining_len == total_len { + // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers + unsafe { + Self::master_read( + address, + total_len.min(255), + Stop::Software, + total_len > 255, + restart, + &check_timeout, + )?; + } + } else if remaining_len == 0 { return Poll::Ready(Ok(())); - } else if chunks_transferred != 0 { - remaining_len = remaining_len.saturating_sub(255); - let last_piece = chunks_transferred + 1 == total_chunks; + } else { + let last_piece = remaining_len <= 255; // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { @@ -623,6 +600,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { T::regs().cr1().modify(|w| w.set_tcie(true)); } } + + remaining_len = remaining_len.saturating_sub(255); Poll::Pending }) .await?; From 1c68c62ebdbfdad0ad0a9cc8d7718baf6a3c94e8 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 18 Apr 2023 13:48:37 +0200 Subject: [PATCH 32/46] Implement embedded-storage traits for full flash struct --- embassy-stm32/src/flash/common.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 1189e447e..95fe26126 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -162,6 +162,35 @@ impl FlashRegion { } } +impl embedded_storage::nor_flash::ErrorType for Flash<'_> { + type Error = Error; +} + +impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_> { + const READ_SIZE: usize = 1; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(offset, bytes) + } + + fn capacity(&self) -> usize { + FLASH_SIZE + } +} + +impl embedded_storage::nor_flash::NorFlash for Flash<'_> { + const WRITE_SIZE: usize = 8; + const ERASE_SIZE: usize = 2048; + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(offset, bytes) + } + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.blocking_erase(from, to) + } +} + foreach_flash_region! { ($type_name:ident, $write_size:literal, $erase_size:literal) => { impl crate::_generated::flash_regions::$type_name<'_> { From 095f5ef279b58da0e3b63559b4f1a039538673c9 Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 18 Apr 2023 15:49:33 +0200 Subject: [PATCH 33/46] Add MAX_ERASE_SIZE const in build script, and use it in flash-wide implementation of embedded-storage traits --- embassy-stm32/build.rs | 8 ++++++++ embassy-stm32/src/flash/common.rs | 6 +++--- embassy-stm32/src/flash/mod.rs | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index c7d12e13a..7959cca03 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -255,6 +255,14 @@ fn main() { ]; }); + let max_erase_size = flash_memory_regions + .iter() + .map(|region| region.settings.as_ref().unwrap().erase_size) + .max() + .unwrap(); + + g.extend(quote! { pub const MAX_ERASE_SIZE: u32 = #max_erase_size }); + g.extend(quote! { pub mod flash_regions { #flash_regions } }); // ======== diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 95fe26126..6d7f55974 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -2,7 +2,7 @@ use atomic_polyfill::{fence, Ordering}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; +use super::{family, Error, FlashLayout, FlashRegion, FlashSector, FLASH_BASE, FLASH_SIZE, MAX_ERASE_SIZE, WRITE_SIZE}; use crate::flash::FlashBank; use crate::Peripheral; @@ -179,8 +179,8 @@ impl embedded_storage::nor_flash::ReadNorFlash for Flash<'_> { } impl embedded_storage::nor_flash::NorFlash for Flash<'_> { - const WRITE_SIZE: usize = 8; - const ERASE_SIZE: usize = 2048; + const WRITE_SIZE: usize = WRITE_SIZE; + const ERASE_SIZE: usize = MAX_ERASE_SIZE; fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { self.blocking_write(offset, bytes) diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 231ff1f9e..7d5596b1f 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -7,6 +7,7 @@ mod common; pub use common::*; pub use crate::_generated::flash_regions::*; +pub use crate::_generated::MAX_ERASE_SIZE; pub use crate::pac::{FLASH_BASE, FLASH_SIZE, WRITE_SIZE}; #[derive(Debug)] From bba8b0ded52383e9a958e11322ac5c75d01e70de Mon Sep 17 00:00:00 2001 From: Mathias Date: Tue, 18 Apr 2023 15:54:13 +0200 Subject: [PATCH 34/46] Missing semi-colon --- embassy-stm32/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 7959cca03..a85d3db6e 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -261,7 +261,7 @@ fn main() { .max() .unwrap(); - g.extend(quote! { pub const MAX_ERASE_SIZE: u32 = #max_erase_size }); + g.extend(quote! { pub const MAX_ERASE_SIZE: usize = #max_erase_size as usize; }); g.extend(quote! { pub mod flash_regions { #flash_regions } }); From 173c65b5430e57548cc747f0387dd001e30b1ac1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 17 Apr 2023 00:04:54 +0200 Subject: [PATCH 35/46] stm32/dma: refactor. --- embassy-stm32/build.rs | 12 +- embassy-stm32/src/dcmi.rs | 13 +- embassy-stm32/src/dma/bdma.rs | 415 +++++++++++++---------- embassy-stm32/src/dma/dma.rs | 580 ++++++++++++++++---------------- embassy-stm32/src/dma/dmamux.rs | 22 +- embassy-stm32/src/dma/gpdma.rs | 414 +++++++++++++---------- embassy-stm32/src/dma/mod.rs | 317 +++-------------- embassy-stm32/src/i2c/v2.rs | 6 +- embassy-stm32/src/lib.rs | 7 +- embassy-stm32/src/qspi/mod.rs | 32 +- embassy-stm32/src/sdmmc/mod.rs | 247 +++++++------- embassy-stm32/src/spi/mod.rs | 23 +- embassy-stm32/src/traits.rs | 2 +- embassy-stm32/src/usart/mod.rs | 27 +- 14 files changed, 1025 insertions(+), 1092 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index c7d12e13a..9f84caefb 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -260,7 +260,7 @@ fn main() { // ======== // Generate DMA IRQs. - let mut dma_irqs: HashMap<&str, Vec<(&str, &str)>> = HashMap::new(); + let mut dma_irqs: HashMap<&str, Vec<(&str, &str, &str)>> = HashMap::new(); for p in METADATA.peripherals { if let Some(r) = &p.registers { @@ -270,7 +270,10 @@ fn main() { continue; } for irq in p.interrupts { - dma_irqs.entry(irq.interrupt).or_default().push((p.name, irq.signal)); + dma_irqs + .entry(irq.interrupt) + .or_default() + .push((r.kind, p.name, irq.signal)); } } } @@ -279,13 +282,14 @@ fn main() { for (irq, channels) in dma_irqs { let irq = format_ident!("{}", irq); - let channels = channels.iter().map(|(dma, ch)| format_ident!("{}_{}", dma, ch)); + let xdma = format_ident!("{}", channels[0].0); + let channels = channels.iter().map(|(_, dma, ch)| format_ident!("{}_{}", dma, ch)); g.extend(quote! { #[crate::interrupt] unsafe fn #irq () { #( - ::on_irq(); + ::on_irq(); )* } }); diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index 20e1a4070..0b34553cf 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -4,6 +4,7 @@ use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; +use crate::dma::Transfer; use crate::gpio::sealed::AFType; use crate::gpio::Speed; use crate::interrupt::{Interrupt, InterruptExt}; @@ -385,14 +386,11 @@ where return self.capture_giant(buffer).await; } } - async fn capture_small(&mut self, buffer: &mut [u32]) -> Result<(), Error> { - let channel = &mut self.dma; - let request = channel.request(); - let r = self.inner.regs(); let src = r.dr().ptr() as *mut u32; - let dma_read = crate::dma::read(channel, request, src, buffer); + let request = self.dma.request(); + let dma_read = unsafe { Transfer::new_read(&mut self.dma, request, src, buffer, Default::default()) }; Self::clear_interrupt_flags(); Self::enable_irqs(); @@ -436,7 +434,9 @@ where result } - async fn capture_giant(&mut self, buffer: &mut [u32]) -> Result<(), Error> { + async fn capture_giant(&mut self, _buffer: &mut [u32]) -> Result<(), Error> { + todo!() + /* use crate::dma::TransferOptions; let data_len = buffer.len(); @@ -542,6 +542,7 @@ where unsafe { Self::toggle(false) }; result + */ } } diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 5a7a408bb..cf1222c46 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -1,18 +1,31 @@ #![macro_use] +use core::future::Future; +use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; -use core::task::Waker; +use core::task::{Context, Poll}; use embassy_cortex_m::interrupt::Priority; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::{TransferOptions, Word, WordSize}; +use super::{Dir, Word, WordSize}; use crate::_generated::BDMA_CHANNEL_COUNT; -use crate::dma::Request; use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac; use crate::pac::bdma::vals; +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct TransferOptions {} + +impl Default for TransferOptions { + fn default() -> Self { + Self {} + } +} + impl From for vals::Size { fn from(raw: WordSize) -> Self { match raw { @@ -23,6 +36,15 @@ impl From for vals::Size { } } +impl From

for vals::Dir { + fn from(raw: Dir) -> Self { + match raw { + Dir::MemoryToPeripheral => Self::FROMMEMORY, + Dir::PeripheralToMemory => Self::FROMPERIPHERAL, + } + } +} + struct State { ch_wakers: [AtomicWaker; BDMA_CHANNEL_COUNT], } @@ -55,228 +77,267 @@ foreach_dma_channel! { // BDMA1 in H7 doesn't use DMAMUX, which breaks }; ($channel_peri:ident, $dma_peri:ident, bdma, $channel_num:expr, $index:expr, $dmamux:tt) => { - impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { - - unsafe fn start_write(&mut self, _request: Request, buf: *const[W], reg_addr: *mut W, options: TransferOptions) { - let (ptr, len) = super::slice_ptr_parts(buf); - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - #[cfg(any(bdma_v2, dmamux))] - _request, - vals::Dir::FROMMEMORY, - reg_addr as *const u32, - ptr as *mut u32, - len, - true, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ); + impl sealed::Channel for crate::peripherals::$channel_peri { + fn regs(&self) -> pac::bdma::Dma { + pac::$dma_peri } - - unsafe fn start_write_repeated(&mut self, _request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - #[cfg(any(bdma_v2, dmamux))] - _request, - vals::Dir::FROMMEMORY, - reg_addr as *const u32, - repeated as *mut u32, - count, - false, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ) + fn num(&self) -> usize { + $channel_num } - - unsafe fn start_read(&mut self, _request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) { - let (ptr, len) = super::slice_ptr_parts_mut(buf); - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - #[cfg(any(bdma_v2, dmamux))] - _request, - vals::Dir::FROMPERIPHERAL, - reg_addr as *const u32, - ptr as *mut u32, - len, - true, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ); + fn index(&self) -> usize { + $index } - - unsafe fn start_double_buffered_read( - &mut self, - _request: Request, - _reg_addr: *const W, - _buffer0: *mut W, - _buffer1: *mut W, - _buffer_len: usize, - _options: TransferOptions, - ) { - panic!("Unsafe double buffered mode is unavailable on BDMA"); - } - - unsafe fn set_buffer0(&mut self, _buffer: *mut W) { - panic!("Unsafe double buffered mode is unavailable on BDMA"); - } - - unsafe fn set_buffer1(&mut self, _buffer: *mut W) { - panic!("Unsafe double buffered mode is unavailable on BDMA"); - } - - unsafe fn is_buffer0_accessible(&mut self) -> bool { - panic!("Unsafe double buffered mode is unavailable on BDMA"); - } - - fn request_stop(&mut self){ - unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);} - } - - fn is_running(&self) -> bool { - unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)} - } - fn remaining_transfers(&mut self) -> u16 { - unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)} - } - - fn set_waker(&mut self, waker: &Waker) { - unsafe { low_level_api::set_waker($index, waker) } - } - fn on_irq() { - unsafe { - low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index); - } + unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) } } } - impl crate::dma::Channel for crate::peripherals::$channel_peri {} + impl Channel for crate::peripherals::$channel_peri {} }; } -mod low_level_api { +/// Safety: Must be called with a matching set of parameters for a valid dma channel +pub(crate) unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: usize, index: usize) { + let isr = dma.isr().read(); + let cr = dma.ch(channel_num).cr(); + + if isr.teif(channel_num) { + panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num); + } + if isr.tcif(channel_num) && cr.read().tcie() { + cr.write(|_| ()); // Disable channel interrupts with the default value. + STATE.ch_wakers[index].wake(); + } +} + +#[cfg(any(bdma_v2, dmamux))] +pub type Request = u8; +#[cfg(not(any(bdma_v2, dmamux)))] +pub type Request = (); + +#[cfg(dmamux)] +pub trait Channel: sealed::Channel + Peripheral

+ 'static + super::dmamux::MuxChannel {} +#[cfg(not(dmamux))] +pub trait Channel: sealed::Channel + Peripheral

+ 'static {} + +pub(crate) mod sealed { use super::*; - pub unsafe fn start_transfer( - dma: pac::bdma::Dma, - channel_number: u8, - #[cfg(any(bdma_v2, dmamux))] request: Request, - dir: vals::Dir, + pub trait Channel { + fn regs(&self) -> pac::bdma::Dma; + fn num(&self) -> usize; + fn index(&self) -> usize; + fn on_irq(); + } +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Transfer<'a, C: Channel> { + channel: PeripheralRef<'a, C>, +} + +impl<'a, C: Channel> Transfer<'a, C> { + pub unsafe fn new_read( + channel: impl Peripheral

+ 'a, + request: Request, + peri_addr: *mut W, + buf: &'a mut [W], + options: TransferOptions, + ) -> Self { + Self::new_read_raw(channel, request, peri_addr, buf, options) + } + + pub unsafe fn new_read_raw( + channel: impl Peripheral

+ 'a, + request: Request, + peri_addr: *mut W, + buf: *mut [W], + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let (ptr, len) = super::slice_ptr_parts_mut(buf); + assert!(len > 0 && len <= 0xFFFF); + + Self::new_inner( + channel, + request, + Dir::PeripheralToMemory, + peri_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ) + } + + pub unsafe fn new_write( + channel: impl Peripheral

+ 'a, + request: Request, + buf: &'a [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + Self::new_write_raw(channel, request, buf, peri_addr, options) + } + + pub unsafe fn new_write_raw( + channel: impl Peripheral

+ 'a, + request: Request, + buf: *const [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let (ptr, len) = super::slice_ptr_parts(buf); + assert!(len > 0 && len <= 0xFFFF); + + Self::new_inner( + channel, + request, + Dir::MemoryToPeripheral, + peri_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ) + } + + pub unsafe fn new_write_repeated( + channel: impl Peripheral

+ 'a, + request: Request, + repeated: &'a W, + count: usize, + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + Self::new_inner( + channel, + request, + Dir::MemoryToPeripheral, + peri_addr as *const u32, + repeated as *const W as *mut u32, + count, + false, + W::bits(), + options, + ) + } + + unsafe fn new_inner( + channel: PeripheralRef<'a, C>, + _request: Request, + dir: Dir, peri_addr: *const u32, mem_addr: *mut u32, mem_len: usize, incr_mem: bool, - data_size: vals::Size, - options: TransferOptions, - #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, - #[cfg(dmamux)] dmamux_ch_num: u8, - ) { - assert!(options.mburst == crate::dma::Burst::Single, "Burst mode not supported"); - assert!(options.pburst == crate::dma::Burst::Single, "Burst mode not supported"); - assert!( - options.flow_ctrl == crate::dma::FlowControl::Dma, - "Peripheral flow control not supported" - ); - assert!(options.fifo_threshold.is_none(), "FIFO mode not supported"); - - let ch = dma.ch(channel_number as _); - - reset_status(dma, channel_number); - - #[cfg(dmamux)] - super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); - - #[cfg(bdma_v2)] - critical_section::with(|_| dma.cselr().modify(|w| w.set_cs(channel_number as _, request))); + data_size: WordSize, + _options: TransferOptions, + ) -> Self { + let ch = channel.regs().ch(channel.num()); // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::SeqCst); + #[cfg(bdma_v2)] + critical_section::with(|_| channel.regs().cselr().modify(|w| w.set_cs(channel.num(), _request))); + + let mut this = Self { channel }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); + ch.par().write_value(peri_addr as u32); ch.mar().write_value(mem_addr as u32); ch.ndtr().write(|w| w.set_ndt(mem_len as u16)); ch.cr().write(|w| { - w.set_psize(data_size); - w.set_msize(data_size); + w.set_psize(data_size.into()); + w.set_msize(data_size.into()); if incr_mem { w.set_minc(vals::Inc::ENABLED); } else { w.set_minc(vals::Inc::DISABLED); } - w.set_dir(dir); + w.set_dir(dir.into()); w.set_teie(true); w.set_tcie(true); w.set_en(true); }); + + this } - pub unsafe fn request_stop(dma: pac::bdma::Dma, channel_number: u8) { - reset_status(dma, channel_number); - - let ch = dma.ch(channel_number as _); - - // Disable the channel and interrupts with the default value. - ch.cr().write(|_| ()); - - // "Subsequent reads and writes cannot be moved ahead of preceding reads." - fence(Ordering::SeqCst); + fn clear_irqs(&mut self) { + unsafe { + self.channel.regs().ifcr().write(|w| { + w.set_tcif(self.channel.num(), true); + w.set_teif(self.channel.num(), true); + }) + } } - pub unsafe fn is_running(dma: pac::bdma::Dma, ch: u8) -> bool { - let ch = dma.ch(ch as _); - ch.cr().read().en() + pub fn request_stop(&mut self) { + let ch = self.channel.regs().ch(self.channel.num()); + + // Disable the channel. Keep the IEs enabled so the irqs still fire. + unsafe { + ch.cr().write(|w| { + w.set_teie(true); + w.set_tcie(true); + }) + } + } + + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().ch(self.channel.num()); + unsafe { ch.cr().read() }.en() } /// Gets the total remaining transfers for the channel /// Note: this will be zero for transfers that completed without cancellation. - pub unsafe fn get_remaining_transfers(dma: pac::bdma::Dma, ch: u8) -> u16 { - // get a handle on the channel itself - let ch = dma.ch(ch as _); - // read the remaining transfer count. If this is zero, the transfer completed fully. - ch.ndtr().read().ndt() as u16 + pub fn get_remaining_transfers(&self) -> u16 { + let ch = self.channel.regs().ch(self.channel.num()); + unsafe { ch.ndtr().read() }.ndt() } - /// Sets the waker for the specified DMA channel - pub unsafe fn set_waker(state_number: usize, waker: &Waker) { - STATE.ch_wakers[state_number].register(waker); + pub fn blocking_wait(mut self) { + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + + core::mem::forget(self); } +} - pub unsafe fn reset_status(dma: pac::bdma::Dma, channel_number: u8) { - dma.ifcr().write(|w| { - w.set_tcif(channel_number as _, true); - w.set_teif(channel_number as _, true); - }); +impl<'a, C: Channel> Drop for Transfer<'a, C> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); } +} - /// Safety: Must be called with a matching set of parameters for a valid dma channel - pub unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: u8, index: u8) { - let channel_num = channel_num as usize; - let index = index as usize; +impl<'a, C: Channel> Unpin for Transfer<'a, C> {} +impl<'a, C: Channel> Future for Transfer<'a, C> { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + STATE.ch_wakers[self.channel.index()].register(cx.waker()); - let isr = dma.isr().read(); - let cr = dma.ch(channel_num).cr(); - - if isr.teif(channel_num) { - panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num); - } - if isr.tcif(channel_num) && cr.read().tcie() { - cr.write(|_| ()); // Disable channel interrupts with the default value. - STATE.ch_wakers[index].wake(); + if self.is_running() { + Poll::Pending + } else { + Poll::Ready(()) } } } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 59937f4b0..9052aa110 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -1,15 +1,44 @@ +use core::future::Future; +use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; -use core::task::Waker; +use core::task::{Context, Poll}; use embassy_cortex_m::interrupt::Priority; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; +use pac::dma::regs; -use super::{Burst, FifoThreshold, FlowControl, Request, TransferOptions, Word, WordSize}; +use super::{Dir, Word, WordSize}; use crate::_generated::DMA_CHANNEL_COUNT; use crate::interrupt::{Interrupt, InterruptExt}; -use crate::pac::dma::{regs, vals}; +use crate::pac::dma::vals; use crate::{interrupt, pac}; +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct TransferOptions { + /// Peripheral burst transfer configuration + pub pburst: Burst, + /// Memory burst transfer configuration + pub mburst: Burst, + /// Flow control configuration + pub flow_ctrl: FlowControl, + /// FIFO threshold for DMA FIFO mode. If none, direct mode is used. + pub fifo_threshold: Option, +} + +impl Default for TransferOptions { + fn default() -> Self { + Self { + pburst: Burst::Single, + mburst: Burst::Single, + flow_ctrl: FlowControl::Dma, + fifo_threshold: None, + } + } +} + impl From for vals::Size { fn from(raw: WordSize) -> Self { match raw { @@ -20,6 +49,28 @@ impl From for vals::Size { } } +impl From

for vals::Dir { + fn from(raw: Dir) -> Self { + match raw { + Dir::MemoryToPeripheral => Self::MEMORYTOPERIPHERAL, + Dir::PeripheralToMemory => Self::PERIPHERALTOMEMORY, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Burst { + /// Single transfer + Single, + /// Incremental burst of 4 beats + Incr4, + /// Incremental burst of 8 beats + Incr8, + /// Incremental burst of 16 beats + Incr16, +} + impl From for vals::Burst { fn from(burst: Burst) -> Self { match burst { @@ -31,6 +82,15 @@ impl From for vals::Burst { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum FlowControl { + /// Flow control by DMA + Dma, + /// Flow control by peripheral + Peripheral, +} + impl From for vals::Pfctrl { fn from(flow: FlowControl) -> Self { match flow { @@ -40,6 +100,19 @@ impl From for vals::Pfctrl { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum FifoThreshold { + /// 1/4 full FIFO + Quarter, + /// 1/2 full FIFO + Half, + /// 3/4 full FIFO + ThreeQuarters, + /// Full FIFO + Full, +} + impl From for vals::Fth { fn from(value: FifoThreshold) -> Self { match value { @@ -51,27 +124,15 @@ impl From for vals::Fth { } } -struct ChannelState { - waker: AtomicWaker, -} - -impl ChannelState { - const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } - } -} - struct State { - channels: [ChannelState; DMA_CHANNEL_COUNT], + ch_wakers: [AtomicWaker; DMA_CHANNEL_COUNT], } impl State { const fn new() -> Self { - const CH: ChannelState = ChannelState::new(); + const AW: AtomicWaker = AtomicWaker::new(); Self { - channels: [CH; DMA_CHANNEL_COUNT], + ch_wakers: [AW; DMA_CHANNEL_COUNT], } } } @@ -92,158 +153,183 @@ pub(crate) unsafe fn init(irq_priority: Priority) { foreach_dma_channel! { ($channel_peri:ident, $dma_peri:ident, dma, $channel_num:expr, $index:expr, $dmamux:tt) => { - impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { - unsafe fn start_write(&mut self, request: Request, buf: *const [W], reg_addr: *mut W, options: TransferOptions) { - let (ptr, len) = super::slice_ptr_parts(buf); - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - request, - vals::Dir::MEMORYTOPERIPHERAL, - reg_addr as *const u32, - ptr as *mut u32, - len, - true, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ) + impl sealed::Channel for crate::peripherals::$channel_peri { + fn regs(&self) -> pac::dma::Dma { + pac::$dma_peri } - - unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - request, - vals::Dir::MEMORYTOPERIPHERAL, - reg_addr as *const u32, - repeated as *mut u32, - count, - false, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ) + fn num(&self) -> usize { + $channel_num } - - unsafe fn start_read(&mut self, request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) { - let (ptr, len) = super::slice_ptr_parts_mut(buf); - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - request, - vals::Dir::PERIPHERALTOMEMORY, - reg_addr as *const u32, - ptr as *mut u32, - len, - true, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ); + fn index(&self) -> usize { + $index } - - unsafe fn start_double_buffered_read( - &mut self, - request: Request, - reg_addr: *const W, - buffer0: *mut W, - buffer1: *mut W, - buffer_len: usize, - options: TransferOptions, - ) { - low_level_api::start_dbm_transfer( - pac::$dma_peri, - $channel_num, - request, - vals::Dir::PERIPHERALTOMEMORY, - reg_addr as *const u32, - buffer0 as *mut u32, - buffer1 as *mut u32, - buffer_len, - true, - vals::Size::from(W::bits()), - options, - #[cfg(dmamux)] - ::DMAMUX_REGS, - #[cfg(dmamux)] - ::DMAMUX_CH_NUM, - ); - } - - unsafe fn set_buffer0(&mut self, buffer: *mut W) { - low_level_api::set_dbm_buffer0(pac::$dma_peri, $channel_num, buffer as *mut u32); - } - - unsafe fn set_buffer1(&mut self, buffer: *mut W) { - low_level_api::set_dbm_buffer1(pac::$dma_peri, $channel_num, buffer as *mut u32); - } - - unsafe fn is_buffer0_accessible(&mut self) -> bool { - low_level_api::is_buffer0_accessible(pac::$dma_peri, $channel_num) - } - - fn request_stop(&mut self) { - unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);} - } - - fn is_running(&self) -> bool { - unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)} - } - - fn remaining_transfers(&mut self) -> u16 { - unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)} - } - - fn set_waker(&mut self, waker: &Waker) { - unsafe {low_level_api::set_waker($index, waker )} - } - fn on_irq() { - unsafe { - low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index); - } + unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) } } } - impl crate::dma::Channel for crate::peripherals::$channel_peri { } + + impl Channel for crate::peripherals::$channel_peri {} }; } -mod low_level_api { +/// Safety: Must be called with a matching set of parameters for a valid dma channel +pub(crate) unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: usize, index: usize) { + let cr = dma.st(channel_num).cr(); + let isr = dma.isr(channel_num / 4).read(); + + if isr.teif(channel_num % 4) { + panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num); + } + + if isr.tcif(channel_num % 4) && cr.read().tcie() { + /* acknowledge transfer complete interrupt */ + dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true)); + STATE.ch_wakers[index].wake(); + } +} + +#[cfg(any(dma_v2, dmamux))] +pub type Request = u8; +#[cfg(not(any(dma_v2, dmamux)))] +pub type Request = (); + +#[cfg(dmamux)] +pub trait Channel: sealed::Channel + Peripheral

+ 'static + super::dmamux::MuxChannel {} +#[cfg(not(dmamux))] +pub trait Channel: sealed::Channel + Peripheral

+ 'static {} + +pub(crate) mod sealed { use super::*; - pub unsafe fn start_transfer( - dma: pac::dma::Dma, - channel_number: u8, + pub trait Channel { + fn regs(&self) -> pac::dma::Dma; + fn num(&self) -> usize; + fn index(&self) -> usize; + fn on_irq(); + } +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Transfer<'a, C: Channel> { + channel: PeripheralRef<'a, C>, +} + +impl<'a, C: Channel> Transfer<'a, C> { + pub unsafe fn new_read( + channel: impl Peripheral

+ 'a, request: Request, - dir: vals::Dir, + peri_addr: *mut W, + buf: &'a mut [W], + options: TransferOptions, + ) -> Self { + Self::new_read_raw(channel, request, peri_addr, buf, options) + } + + pub unsafe fn new_read_raw( + channel: impl Peripheral

+ 'a, + request: Request, + peri_addr: *mut W, + buf: *mut [W], + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let (ptr, len) = super::slice_ptr_parts_mut(buf); + assert!(len > 0 && len <= 0xFFFF); + + Self::new_inner( + channel, + request, + Dir::PeripheralToMemory, + peri_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ) + } + + pub unsafe fn new_write( + channel: impl Peripheral

+ 'a, + request: Request, + buf: &'a [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + Self::new_write_raw(channel, request, buf, peri_addr, options) + } + + pub unsafe fn new_write_raw( + channel: impl Peripheral

+ 'a, + request: Request, + buf: *const [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let (ptr, len) = super::slice_ptr_parts(buf); + assert!(len > 0 && len <= 0xFFFF); + + Self::new_inner( + channel, + request, + Dir::MemoryToPeripheral, + peri_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ) + } + + pub unsafe fn new_write_repeated( + channel: impl Peripheral

+ 'a, + request: Request, + repeated: &'a W, + count: usize, + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + Self::new_inner( + channel, + request, + Dir::MemoryToPeripheral, + peri_addr as *const u32, + repeated as *const W as *mut u32, + count, + false, + W::bits(), + options, + ) + } + + unsafe fn new_inner( + channel: PeripheralRef<'a, C>, + _request: Request, + dir: Dir, peri_addr: *const u32, mem_addr: *mut u32, mem_len: usize, incr_mem: bool, - data_size: vals::Size, + data_size: WordSize, options: TransferOptions, - #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, - #[cfg(dmamux)] dmamux_ch_num: u8, - ) { - #[cfg(dmamux)] - super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); + ) -> Self { + let ch = channel.regs().st(channel.num()); // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::SeqCst); - reset_status(dma, channel_number); + let mut this = Self { channel }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); - let ch = dma.st(channel_number as _); ch.par().write_value(peri_addr as u32); ch.m0ar().write_value(mem_addr as u32); ch.ndtr().write_value(regs::Ndtr(mem_len as _)); @@ -258,15 +344,14 @@ mod low_level_api { } }); ch.cr().write(|w| { - w.set_dir(dir); - w.set_msize(data_size); - w.set_psize(data_size); + w.set_dir(dir.into()); + w.set_msize(data_size.into()); + w.set_psize(data_size.into()); w.set_pl(vals::Pl::VERYHIGH); - if incr_mem { - w.set_minc(vals::Inc::INCREMENTED); - } else { - w.set_minc(vals::Inc::FIXED); - } + w.set_minc(match incr_mem { + true => vals::Inc::INCREMENTED, + false => vals::Inc::FIXED, + }); w.set_pinc(vals::Inc::FIXED); w.set_teie(true); w.set_tcie(true); @@ -274,7 +359,7 @@ mod low_level_api { w.set_trbuff(true); #[cfg(dma_v2)] - w.set_chsel(request); + w.set_chsel(_request); w.set_pburst(options.pburst.into()); w.set_mburst(options.mburst.into()); @@ -282,159 +367,76 @@ mod low_level_api { w.set_en(true); }); + + this } - pub unsafe fn start_dbm_transfer( - dma: pac::dma::Dma, - channel_number: u8, - request: Request, - dir: vals::Dir, - peri_addr: *const u32, - mem0_addr: *mut u32, - mem1_addr: *mut u32, - mem_len: usize, - incr_mem: bool, - data_size: vals::Size, - options: TransferOptions, - #[cfg(dmamux)] dmamux_regs: pac::dmamux::Dmamux, - #[cfg(dmamux)] dmamux_ch_num: u8, - ) { - #[cfg(dmamux)] - super::super::dmamux::configure_dmamux(dmamux_regs, dmamux_ch_num, request); + fn clear_irqs(&mut self) { + let isrn = self.channel.num() / 4; + let isrbit = self.channel.num() % 4; - trace!( - "Starting DBM transfer with 0: 0x{:x}, 1: 0x{:x}, len: 0x{:x}", - mem0_addr as u32, - mem1_addr as u32, - mem_len - ); - - // "Preceding reads and writes cannot be moved past subsequent writes." - fence(Ordering::SeqCst); - - reset_status(dma, channel_number); - - let ch = dma.st(channel_number as _); - ch.par().write_value(peri_addr as u32); - ch.m0ar().write_value(mem0_addr as u32); - // configures the second buffer for DBM - ch.m1ar().write_value(mem1_addr as u32); - ch.ndtr().write_value(regs::Ndtr(mem_len as _)); - ch.cr().write(|w| { - w.set_dir(dir); - w.set_msize(data_size); - w.set_psize(data_size); - w.set_pl(vals::Pl::VERYHIGH); - if incr_mem { - w.set_minc(vals::Inc::INCREMENTED); - } else { - w.set_minc(vals::Inc::FIXED); - } - w.set_pinc(vals::Inc::FIXED); - w.set_teie(true); - w.set_tcie(true); - - #[cfg(dma_v1)] - w.set_trbuff(true); - - #[cfg(dma_v2)] - w.set_chsel(request); - - // enable double buffered mode - w.set_dbm(vals::Dbm::ENABLED); - - w.set_pburst(options.pburst.into()); - w.set_mburst(options.mburst.into()); - w.set_pfctrl(options.flow_ctrl.into()); - - w.set_en(true); - }); + unsafe { + self.channel.regs().ifcr(isrn).write(|w| { + w.set_tcif(isrbit, true); + w.set_teif(isrbit, true); + }) + } } - pub unsafe fn set_dbm_buffer0(dma: pac::dma::Dma, channel_number: u8, mem_addr: *mut u32) { - // get a handle on the channel itself - let ch = dma.st(channel_number as _); - // change M0AR to the new address - ch.m0ar().write_value(mem_addr as _); - } - - pub unsafe fn set_dbm_buffer1(dma: pac::dma::Dma, channel_number: u8, mem_addr: *mut u32) { - // get a handle on the channel itself - let ch = dma.st(channel_number as _); - // change M1AR to the new address - ch.m1ar().write_value(mem_addr as _); - } - - pub unsafe fn is_buffer0_accessible(dma: pac::dma::Dma, channel_number: u8) -> bool { - // get a handle on the channel itself - let ch = dma.st(channel_number as _); - // check the current target register value - ch.cr().read().ct() == vals::Ct::MEMORY1 - } - - /// Stops the DMA channel. - pub unsafe fn request_stop(dma: pac::dma::Dma, channel_number: u8) { - // get a handle on the channel itself - let ch = dma.st(channel_number as _); + pub fn request_stop(&mut self) { + let ch = self.channel.regs().st(self.channel.num()); // Disable the channel. Keep the IEs enabled so the irqs still fire. - ch.cr().write(|w| { - w.set_teie(true); - w.set_tcie(true); - }); - - // "Subsequent reads and writes cannot be moved ahead of preceding reads." - fence(Ordering::SeqCst); + unsafe { + ch.cr().write(|w| { + w.set_teie(true); + w.set_tcie(true); + }) + } } - /// Gets the running status of the channel - pub unsafe fn is_running(dma: pac::dma::Dma, ch: u8) -> bool { - // get a handle on the channel itself - let ch = dma.st(ch as _); - // Get whether it's enabled (running) - ch.cr().read().en() + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().st(self.channel.num()); + unsafe { ch.cr().read() }.en() } /// Gets the total remaining transfers for the channel /// Note: this will be zero for transfers that completed without cancellation. - pub unsafe fn get_remaining_transfers(dma: pac::dma::Dma, ch: u8) -> u16 { - // get a handle on the channel itself - let ch = dma.st(ch as _); - // read the remaining transfer count. If this is zero, the transfer completed fully. - ch.ndtr().read().ndt() + pub fn get_remaining_transfers(&self) -> u16 { + let ch = self.channel.regs().st(self.channel.num()); + unsafe { ch.ndtr().read() }.ndt() } - /// Sets the waker for the specified DMA channel - pub unsafe fn set_waker(state_number: usize, waker: &Waker) { - STATE.channels[state_number].waker.register(waker); + pub fn blocking_wait(mut self) { + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + + core::mem::forget(self); } +} - pub unsafe fn reset_status(dma: pac::dma::Dma, channel_number: u8) { - let isrn = channel_number as usize / 4; - let isrbit = channel_number as usize % 4; +impl<'a, C: Channel> Drop for Transfer<'a, C> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} - dma.ifcr(isrn).write(|w| { - w.set_tcif(isrbit, true); - w.set_teif(isrbit, true); - }); + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); } +} - /// Safety: Must be called with a matching set of parameters for a valid dma channel - pub unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: u8, state_index: u8) { - let channel_num = channel_num as usize; - let state_index = state_index as usize; +impl<'a, C: Channel> Unpin for Transfer<'a, C> {} +impl<'a, C: Channel> Future for Transfer<'a, C> { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + STATE.ch_wakers[self.channel.index()].register(cx.waker()); - let cr = dma.st(channel_num).cr(); - let isr = dma.isr(channel_num / 4).read(); - - if isr.teif(channel_num % 4) { - panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num); - } - - if isr.tcif(channel_num % 4) && cr.read().tcie() { - /* acknowledge transfer complete interrupt */ - dma.ifcr(channel_num / 4).write(|w| w.set_tcif(channel_num % 4, true)); - STATE.channels[state_index].waker.wake(); + if self.is_running() { + Poll::Pending + } else { + Poll::Ready(()) } } } diff --git a/embassy-stm32/src/dma/dmamux.rs b/embassy-stm32/src/dma/dmamux.rs index e9967e349..a8c4c5827 100644 --- a/embassy-stm32/src/dma/dmamux.rs +++ b/embassy-stm32/src/dma/dmamux.rs @@ -2,8 +2,8 @@ use crate::{pac, peripherals}; -pub(crate) unsafe fn configure_dmamux(dmamux_regs: pac::dmamux::Dmamux, dmamux_ch_num: u8, request: u8) { - let ch_mux_regs = dmamux_regs.ccr(dmamux_ch_num as _); +pub(crate) unsafe fn configure_dmamux(channel: &mut M, request: u8) { + let ch_mux_regs = channel.mux_regs().ccr(channel.mux_num()); ch_mux_regs.write(|reg| { reg.set_nbreq(0); reg.set_dmareq_id(request); @@ -14,11 +14,11 @@ pub(crate) unsafe fn configure_dmamux(dmamux_regs: pac::dmamux::Dmamux, dmamux_c }); } -pub(crate) mod sealed { +pub(crate) mod dmamux_sealed { use super::*; pub trait MuxChannel { - const DMAMUX_CH_NUM: u8; - const DMAMUX_REGS: pac::dmamux::Dmamux; + fn mux_regs(&self) -> pac::dmamux::Dmamux; + fn mux_num(&self) -> usize; } } @@ -26,15 +26,19 @@ pub struct DMAMUX1; #[cfg(stm32h7)] pub struct DMAMUX2; -pub trait MuxChannel: sealed::MuxChannel + super::Channel { +pub trait MuxChannel: dmamux_sealed::MuxChannel { type Mux; } foreach_dma_channel! { ($channel_peri:ident, $dma_peri:ident, $version:ident, $channel_num:expr, $index:expr, {dmamux: $dmamux:ident, dmamux_channel: $dmamux_channel:expr}) => { - impl sealed::MuxChannel for peripherals::$channel_peri { - const DMAMUX_CH_NUM: u8 = $dmamux_channel; - const DMAMUX_REGS: pac::dmamux::Dmamux = pac::$dmamux; + impl dmamux_sealed::MuxChannel for peripherals::$channel_peri { + fn mux_regs(&self) -> pac::dmamux::Dmamux { + pac::$dmamux + } + fn mux_num(&self) -> usize { + $dmamux_channel + } } impl MuxChannel for peripherals::$channel_peri { type Mux = $dmamux; diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 6f26fd194..5c6676a5f 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -1,13 +1,30 @@ -use core::sync::atomic::{fence, Ordering}; -use core::task::Waker; +#![macro_use] +use core::future::Future; +use core::pin::Pin; +use core::sync::atomic::{fence, Ordering}; +use core::task::{Context, Poll}; + +use embassy_cortex_m::interrupt::Priority; +use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::{Request, TransferOptions, Word, WordSize}; +use super::{Dir, Word, WordSize}; use crate::_generated::GPDMA_CHANNEL_COUNT; use crate::interrupt::{Interrupt, InterruptExt}; -use crate::pac::gpdma::{vals, Gpdma}; -use crate::{interrupt, pac}; +use crate::pac; +use crate::pac::gpdma::vals; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct TransferOptions {} + +impl Default for TransferOptions { + fn default() -> Self { + Self {} + } +} impl From for vals::ChTr1Dw { fn from(raw: WordSize) -> Self { @@ -19,27 +36,15 @@ impl From for vals::ChTr1Dw { } } -struct ChannelState { - waker: AtomicWaker, -} - -impl ChannelState { - const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } - } -} - struct State { - channels: [ChannelState; GPDMA_CHANNEL_COUNT], + ch_wakers: [AtomicWaker; GPDMA_CHANNEL_COUNT], } impl State { const fn new() -> Self { - const CH: ChannelState = ChannelState::new(); + const AW: AtomicWaker = AtomicWaker::new(); Self { - channels: [CH; GPDMA_CHANNEL_COUNT], + ch_wakers: [AW; GPDMA_CHANNEL_COUNT], } } } @@ -47,10 +52,12 @@ impl State { static STATE: State = State::new(); /// safety: must be called only once -pub(crate) unsafe fn init() { +pub(crate) unsafe fn init(irq_priority: Priority) { foreach_interrupt! { ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => { - interrupt::$irq::steal().enable(); + let irq = crate::interrupt::$irq::steal(); + irq.set_priority(irq_priority); + irq.enable(); }; } crate::_generated::init_gpdma(); @@ -58,117 +65,171 @@ pub(crate) unsafe fn init() { foreach_dma_channel! { ($channel_peri:ident, $dma_peri:ident, gpdma, $channel_num:expr, $index:expr, $dmamux:tt) => { - impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { - unsafe fn start_write(&mut self, request: Request, buf: *const [W], reg_addr: *mut W, options: TransferOptions) { - let (ptr, len) = super::slice_ptr_parts(buf); - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - request, - low_level_api::Dir::MemoryToPeripheral, - reg_addr as *const u32, - ptr as *mut u32, - len, - true, - W::bits(), - options, - ) + impl sealed::Channel for crate::peripherals::$channel_peri { + fn regs(&self) -> pac::gpdma::Gpdma { + pac::$dma_peri } - - unsafe fn start_write_repeated(&mut self, request: Request, repeated: *const W, count: usize, reg_addr: *mut W, options: TransferOptions) { - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - request, - low_level_api::Dir::MemoryToPeripheral, - reg_addr as *const u32, - repeated as *mut u32, - count, - false, - W::bits(), - options, - ) + fn num(&self) -> usize { + $channel_num } - - unsafe fn start_read(&mut self, request: Request, reg_addr: *const W, buf: *mut [W], options: TransferOptions) { - let (ptr, len) = super::slice_ptr_parts_mut(buf); - low_level_api::start_transfer( - pac::$dma_peri, - $channel_num, - request, - low_level_api::Dir::PeripheralToMemory, - reg_addr as *const u32, - ptr as *mut u32, - len, - true, - W::bits(), - options, - ); + fn index(&self) -> usize { + $index } - - unsafe fn start_double_buffered_read( - &mut self, - _request: Request, - _reg_addr: *const W, - _buffer0: *mut W, - _buffer1: *mut W, - _buffer_len: usize, - _options: TransferOptions, - ) { - panic!("Unsafe double buffered mode is unavailable on GPBDMA"); - } - - unsafe fn set_buffer0(&mut self, _buffer: *mut W) { - panic!("Unsafe double buffered mode is unavailable on GPBDMA"); - } - - unsafe fn set_buffer1(&mut self, _buffer: *mut W) { - panic!("Unsafe double buffered mode is unavailable on GPBDMA"); - } - - unsafe fn is_buffer0_accessible(&mut self) -> bool { - panic!("Unsafe double buffered mode is unavailable on GPBDMA"); - } - - fn request_stop(&mut self) { - unsafe {low_level_api::request_stop(pac::$dma_peri, $channel_num);} - } - - fn is_running(&self) -> bool { - unsafe {low_level_api::is_running(pac::$dma_peri, $channel_num)} - } - - fn remaining_transfers(&mut self) -> u16 { - unsafe {low_level_api::get_remaining_transfers(pac::$dma_peri, $channel_num)} - } - - fn set_waker(&mut self, waker: &Waker) { - unsafe {low_level_api::set_waker($index, waker )} - } - fn on_irq() { - unsafe { - low_level_api::on_irq_inner(pac::$dma_peri, $channel_num, $index); - } + unsafe { on_irq_inner(pac::$dma_peri, $channel_num, $index) } } } - impl crate::dma::Channel for crate::peripherals::$channel_peri { } + + impl Channel for crate::peripherals::$channel_peri {} }; } -mod low_level_api { - use super::*; +/// Safety: Must be called with a matching set of parameters for a valid dma channel +pub(crate) unsafe fn on_irq_inner(dma: pac::gpdma::Gpdma, channel_num: usize, index: usize) { + let ch = dma.ch(channel_num); + let sr = ch.sr().read(); - #[derive(Debug, Copy, Clone, PartialEq, Eq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum Dir { - MemoryToPeripheral, - PeripheralToMemory, + if sr.dtef() { + panic!( + "DMA: data transfer error on DMA@{:08x} channel {}", + dma.0 as u32, channel_num + ); + } + if sr.usef() { + panic!( + "DMA: user settings error on DMA@{:08x} channel {}", + dma.0 as u32, channel_num + ); } - pub unsafe fn start_transfer( - dma: Gpdma, - channel_number: u8, + if sr.suspf() || sr.tcf() { + // disable all xxIEs to prevent the irq from firing again. + ch.cr().write(|_| {}); + + // Wake the future. It'll look at tcf and see it's set. + STATE.ch_wakers[index].wake(); + } +} + +pub type Request = u8; + +#[cfg(dmamux)] +pub trait Channel: sealed::Channel + Peripheral

+ 'static + super::dmamux::MuxChannel {} +#[cfg(not(dmamux))] +pub trait Channel: sealed::Channel + Peripheral

+ 'static {} + +pub(crate) mod sealed { + use super::*; + + pub trait Channel { + fn regs(&self) -> pac::gpdma::Gpdma; + fn num(&self) -> usize; + fn index(&self) -> usize; + fn on_irq(); + } +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Transfer<'a, C: Channel> { + channel: PeripheralRef<'a, C>, +} + +impl<'a, C: Channel> Transfer<'a, C> { + pub unsafe fn new_read( + channel: impl Peripheral

+ 'a, + request: Request, + peri_addr: *mut W, + buf: &'a mut [W], + options: TransferOptions, + ) -> Self { + Self::new_read_raw(channel, request, peri_addr, buf, options) + } + + pub unsafe fn new_read_raw( + channel: impl Peripheral

+ 'a, + request: Request, + peri_addr: *mut W, + buf: *mut [W], + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let (ptr, len) = super::slice_ptr_parts_mut(buf); + assert!(len > 0 && len <= 0xFFFF); + + Self::new_inner( + channel, + request, + Dir::PeripheralToMemory, + peri_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ) + } + + pub unsafe fn new_write( + channel: impl Peripheral

+ 'a, + request: Request, + buf: &'a [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + Self::new_write_raw(channel, request, buf, peri_addr, options) + } + + pub unsafe fn new_write_raw( + channel: impl Peripheral

+ 'a, + request: Request, + buf: *const [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let (ptr, len) = super::slice_ptr_parts(buf); + assert!(len > 0 && len <= 0xFFFF); + + Self::new_inner( + channel, + request, + Dir::MemoryToPeripheral, + peri_addr as *const u32, + ptr as *mut u32, + len, + true, + W::bits(), + options, + ) + } + + pub unsafe fn new_write_repeated( + channel: impl Peripheral

+ 'a, + request: Request, + repeated: &'a W, + count: usize, + peri_addr: *mut W, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + Self::new_inner( + channel, + request, + Dir::MemoryToPeripheral, + peri_addr as *const u32, + repeated as *const W as *mut u32, + count, + false, + W::bits(), + options, + ) + } + + unsafe fn new_inner( + channel: PeripheralRef<'a, C>, request: Request, dir: Dir, peri_addr: *const u32, @@ -176,24 +237,19 @@ mod low_level_api { mem_len: usize, incr_mem: bool, data_size: WordSize, - options: TransferOptions, - ) { - assert!(options.mburst == crate::dma::Burst::Single, "Burst mode not supported"); - assert!(options.pburst == crate::dma::Burst::Single, "Burst mode not supported"); - assert!( - options.flow_ctrl == crate::dma::FlowControl::Dma, - "Peripheral flow control not supported" - ); - assert!(options.fifo_threshold.is_none(), "FIFO mode not supported"); + _options: TransferOptions, + ) -> Self { + let ch = channel.regs().ch(channel.num()); // "Preceding reads and writes cannot be moved past subsequent writes." fence(Ordering::SeqCst); - let ch = dma.ch(channel_number as _); + let this = Self { channel }; + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, request); - // Reset ch ch.cr().write(|w| w.set_reset(true)); - ch.llr().write(|_| {}); // no linked list ch.tr1().write(|w| { w.set_sdw(data_size.into()); @@ -234,72 +290,66 @@ mod low_level_api { // Start it w.set_en(true); }); + + this } - /// Stops the DMA channel. - pub unsafe fn request_stop(dma: Gpdma, channel_number: u8) { - // get a handle on the channel itself - let ch = dma.ch(channel_number as _); + pub fn request_stop(&mut self) { + let ch = self.channel.regs().ch(self.channel.num()); // Disable the channel. Keep the IEs enabled so the irqs still fire. - ch.cr().write(|w| { - w.set_tcie(true); - w.set_useie(true); - w.set_dteie(true); - w.set_suspie(true); - }); - - // "Subsequent reads and writes cannot be moved ahead of preceding reads." - fence(Ordering::SeqCst); + unsafe { + ch.cr().write(|w| { + w.set_tcie(true); + w.set_useie(true); + w.set_dteie(true); + w.set_suspie(true); + }) + } } - /// Gets the running status of the channel - pub unsafe fn is_running(dma: Gpdma, ch: u8) -> bool { - let ch = dma.ch(ch as _); - !ch.sr().read().tcf() + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().ch(self.channel.num()); + !unsafe { ch.sr().read() }.tcf() } /// Gets the total remaining transfers for the channel /// Note: this will be zero for transfers that completed without cancellation. - pub unsafe fn get_remaining_transfers(dma: Gpdma, ch: u8) -> u16 { - // get a handle on the channel itself - let ch = dma.ch(ch as _); - // read the remaining transfer count. If this is zero, the transfer completed fully. - ch.br1().read().bndt() + pub fn get_remaining_transfers(&self) -> u16 { + let ch = self.channel.regs().ch(self.channel.num()); + unsafe { ch.br1().read() }.bndt() } - /// Sets the waker for the specified DMA channel - pub unsafe fn set_waker(state_number: usize, waker: &Waker) { - STATE.channels[state_number].waker.register(waker); + pub fn blocking_wait(mut self) { + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + + core::mem::forget(self); } +} - /// Safety: Must be called with a matching set of parameters for a valid dma channel - pub unsafe fn on_irq_inner(dma: Gpdma, channel_num: u8, state_index: u8) { - let channel_num = channel_num as usize; - let state_index = state_index as usize; +impl<'a, C: Channel> Drop for Transfer<'a, C> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} - let ch = dma.ch(channel_num); - let sr = ch.sr().read(); + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } +} - if sr.dtef() { - panic!( - "DMA: data transfer error on DMA@{:08x} channel {}", - dma.0 as u32, channel_num - ); - } - if sr.usef() { - panic!( - "DMA: user settings error on DMA@{:08x} channel {}", - dma.0 as u32, channel_num - ); - } +impl<'a, C: Channel> Unpin for Transfer<'a, C> {} +impl<'a, C: Channel> Future for Transfer<'a, C> { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + STATE.ch_wakers[self.channel.index()].register(cx.waker()); - if sr.suspf() || sr.tcf() { - // disable all xxIEs to prevent the irq from firing again. - ch.cr().write(|_| {}); - - // Wake the future. It'll look at tcf and see it's set. - STATE.channels[state_index].waker.wake(); + if self.is_running() { + Poll::Pending + } else { + Poll::Ready(()) } } } diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 0030bd575..d29ef4a1f 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -1,124 +1,39 @@ -#[cfg(bdma)] -pub(crate) mod bdma; #[cfg(dma)] pub(crate) mod dma; +#[cfg(dma)] +pub use dma::*; + +// stm32h7 has both dma and bdma. In that case, we export dma as "main" dma, +// and bdma as "secondary", under `embassy_stm32::dma::bdma`. +#[cfg(all(bdma, dma))] +pub mod bdma; + +#[cfg(all(bdma, not(dma)))] +pub(crate) mod bdma; +#[cfg(all(bdma, not(dma)))] +pub use bdma::*; + +#[cfg(gpdma)] +pub(crate) mod gpdma; +#[cfg(gpdma)] +pub use gpdma::*; + #[cfg(dmamux)] mod dmamux; -#[cfg(gpdma)] -mod gpdma; -use core::future::Future; use core::mem; -use core::pin::Pin; -use core::task::{Context, Poll, Waker}; -#[cfg(any(dma, bdma))] use embassy_cortex_m::interrupt::Priority; -use embassy_hal_common::{impl_peripheral, into_ref}; +use embassy_hal_common::impl_peripheral; #[cfg(dmamux)] pub use self::dmamux::*; -use crate::Peripheral; -#[cfg(feature = "unstable-pac")] -pub mod low_level { - pub use super::transfers::*; -} - -pub(crate) use transfers::*; - -#[cfg(any(bdma_v2, dma_v2, dmamux, gpdma))] -pub type Request = u8; -#[cfg(not(any(bdma_v2, dma_v2, dmamux, gpdma)))] -pub type Request = (); - -pub(crate) mod sealed { - use super::*; - - pub trait Word {} - - pub trait Channel { - /// Starts this channel for writing a stream of words. - /// - /// Safety: - /// - `buf` must point to a valid buffer for DMA reading. - /// - `buf` must be alive for the entire duration of the DMA transfer. - /// - `reg_addr` must be a valid peripheral register address to write to. - unsafe fn start_write( - &mut self, - request: Request, - buf: *const [W], - reg_addr: *mut W, - options: TransferOptions, - ); - - /// Starts this channel for writing a word repeatedly. - /// - /// Safety: - /// - `reg_addr` must be a valid peripheral register address to write to. - unsafe fn start_write_repeated( - &mut self, - request: Request, - repeated: *const W, - count: usize, - reg_addr: *mut W, - options: TransferOptions, - ); - - /// Starts this channel for reading a stream of words. - /// - /// Safety: - /// - `buf` must point to a valid buffer for DMA writing. - /// - `buf` must be alive for the entire duration of the DMA transfer. - /// - `reg_addr` must be a valid peripheral register address to read from. - unsafe fn start_read( - &mut self, - request: Request, - reg_addr: *const W, - buf: *mut [W], - options: TransferOptions, - ); - - /// DMA double-buffered mode is unsafe as UB can happen when the hardware writes to a buffer currently owned by the software - /// more information can be found here: https://github.com/embassy-rs/embassy/issues/702 - /// This feature is now used solely for the purposes of implementing giant DMA transfers required for DCMI - unsafe fn start_double_buffered_read( - &mut self, - request: Request, - reg_addr: *const W, - buffer0: *mut W, - buffer1: *mut W, - buffer_len: usize, - options: TransferOptions, - ); - - unsafe fn set_buffer0(&mut self, buffer: *mut W); - - unsafe fn set_buffer1(&mut self, buffer: *mut W); - - unsafe fn is_buffer0_accessible(&mut self) -> bool; - - /// Requests the channel to stop. - /// NOTE: The channel does not immediately stop, you have to wait - /// for `is_running() = false`. - fn request_stop(&mut self); - - /// Returns whether this channel is running or stopped. - /// - /// The channel stops running when it either completes or is manually stopped. - fn is_running(&self) -> bool; - - /// Returns the total number of remaining transfers. - fn remaining_transfers(&mut self) -> u16; - - /// Sets the waker that is called when this channel stops (either completed or manually stopped) - fn set_waker(&mut self, waker: &Waker); - - /// This is called when this channel triggers an interrupt. - /// Note: Because some channels share an interrupt, this function might be - /// called for a channel that didn't trigger an interrupt. - fn on_irq(); - } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum Dir { + MemoryToPeripheral, + PeripheralToMemory, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -139,191 +54,39 @@ impl WordSize { } } -pub trait Word: sealed::Word { +mod word_sealed { + pub trait Word {} +} + +pub trait Word: word_sealed::Word { fn bits() -> WordSize; } -impl sealed::Word for u8 {} +impl word_sealed::Word for u8 {} impl Word for u8 { fn bits() -> WordSize { WordSize::OneByte } } -impl sealed::Word for u16 {} +impl word_sealed::Word for u16 {} impl Word for u16 { fn bits() -> WordSize { WordSize::TwoBytes } } -impl sealed::Word for u32 {} +impl word_sealed::Word for u32 {} impl Word for u32 { fn bits() -> WordSize { WordSize::FourBytes } } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Burst { - /// Single transfer - Single, - /// Incremental burst of 4 beats - Incr4, - /// Incremental burst of 8 beats - Incr8, - /// Incremental burst of 16 beats - Incr16, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FlowControl { - /// Flow control by DMA - Dma, - /// Flow control by peripheral - Peripheral, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum FifoThreshold { - /// 1/4 full FIFO - Quarter, - /// 1/2 full FIFO - Half, - /// 3/4 full FIFO - ThreeQuarters, - /// Full FIFO - Full, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TransferOptions { - /// Peripheral burst transfer configuration - pub pburst: Burst, - /// Memory burst transfer configuration - pub mburst: Burst, - /// Flow control configuration - pub flow_ctrl: FlowControl, - /// FIFO threshold for DMA FIFO mode. If none, direct mode is used. - pub fifo_threshold: Option, -} - -impl Default for TransferOptions { - fn default() -> Self { - Self { - pburst: Burst::Single, - mburst: Burst::Single, - flow_ctrl: FlowControl::Dma, - fifo_threshold: None, - } - } -} - -mod transfers { - use embassy_hal_common::PeripheralRef; - - use super::*; - - #[allow(unused)] - pub fn read<'a, W: Word>( - channel: impl Peripheral

+ 'a, - request: Request, - reg_addr: *mut W, - buf: &'a mut [W], - ) -> impl Future + 'a { - assert!(buf.len() > 0 && buf.len() <= 0xFFFF); - into_ref!(channel); - - unsafe { channel.start_read::(request, reg_addr, buf, Default::default()) }; - - Transfer::new(channel) - } - - #[allow(unused)] - pub fn write<'a, W: Word>( - channel: impl Peripheral

+ 'a, - request: Request, - buf: &'a [W], - reg_addr: *mut W, - ) -> impl Future + 'a { - assert!(buf.len() > 0 && buf.len() <= 0xFFFF); - into_ref!(channel); - - unsafe { channel.start_write::(request, buf, reg_addr, Default::default()) }; - - Transfer::new(channel) - } - - #[allow(unused)] - pub fn write_repeated<'a, W: Word>( - channel: impl Peripheral

+ 'a, - request: Request, - repeated: *const W, - count: usize, - reg_addr: *mut W, - ) -> impl Future + 'a { - into_ref!(channel); - - unsafe { channel.start_write_repeated::(request, repeated, count, reg_addr, Default::default()) }; - - Transfer::new(channel) - } - - #[must_use = "futures do nothing unless you `.await` or poll them"] - pub(crate) struct Transfer<'a, C: Channel> { - channel: PeripheralRef<'a, C>, - } - - impl<'a, C: Channel> Transfer<'a, C> { - pub(crate) fn new(channel: impl Peripheral

+ 'a) -> Self { - into_ref!(channel); - Self { channel } - } - } - - impl<'a, C: Channel> Drop for Transfer<'a, C> { - fn drop(&mut self) { - self.channel.request_stop(); - while self.channel.is_running() {} - } - } - - impl<'a, C: Channel> Unpin for Transfer<'a, C> {} - impl<'a, C: Channel> Future for Transfer<'a, C> { - type Output = (); - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.channel.set_waker(cx.waker()); - if self.channel.is_running() { - Poll::Pending - } else { - Poll::Ready(()) - } - } - } -} - -pub trait Channel: sealed::Channel + Peripheral

+ 'static {} - pub struct NoDma; impl_peripheral!(NoDma); -// safety: must be called only once at startup -pub(crate) unsafe fn init(#[cfg(bdma)] bdma_priority: Priority, #[cfg(dma)] dma_priority: Priority) { - #[cfg(bdma)] - bdma::init(bdma_priority); - #[cfg(dma)] - dma::init(dma_priority); - #[cfg(dmamux)] - dmamux::init(); - #[cfg(gpdma)] - gpdma::init(); -} - // TODO: replace transmutes with core::ptr::metadata once it's stable #[allow(unused)] pub(crate) fn slice_ptr_parts(slice: *const [T]) -> (usize, usize) { @@ -334,3 +97,19 @@ pub(crate) fn slice_ptr_parts(slice: *const [T]) -> (usize, usize) { pub(crate) fn slice_ptr_parts_mut(slice: *mut [T]) -> (usize, usize) { unsafe { mem::transmute(slice) } } + +// safety: must be called only once at startup +pub(crate) unsafe fn init( + #[cfg(bdma)] bdma_priority: Priority, + #[cfg(dma)] dma_priority: Priority, + #[cfg(gpdma)] gpdma_priority: Priority, +) { + #[cfg(bdma)] + bdma::init(bdma_priority); + #[cfg(dma)] + dma::init(dma_priority); + #[cfg(gpdma)] + gpdma::init(gpdma_priority); + #[cfg(dmamux)] + dmamux::init(); +} diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 7218f7706..39e6702e5 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -8,7 +8,7 @@ use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use crate::dma::NoDma; +use crate::dma::{NoDma, Transfer}; use crate::gpio::sealed::AFType; use crate::gpio::Pull; use crate::i2c::{Error, Instance, SclPin, SdaPin}; @@ -476,7 +476,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let ch = &mut self.tx_dma; let request = ch.request(); - crate::dma::write(ch, request, write, dst) + Transfer::new_write(ch, request, write, dst, Default::default()) }; let state = T::state(); @@ -576,7 +576,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let ch = &mut self.rx_dma; let request = ch.request(); - crate::dma::read(ch, request, src, buffer) + Transfer::new_read(ch, request, src, buffer, Default::default()) }; let state = T::state(); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 0dbc9e5c8..bbde2da57 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -78,7 +78,6 @@ pub(crate) mod _generated { // Reexports pub use _generated::{peripherals, Peripherals}; pub use embassy_cortex_m::executor; -#[cfg(any(dma, bdma))] use embassy_cortex_m::interrupt::Priority; pub use embassy_cortex_m::interrupt::_export::interrupt; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; @@ -96,6 +95,8 @@ pub struct Config { pub bdma_interrupt_priority: Priority, #[cfg(dma)] pub dma_interrupt_priority: Priority, + #[cfg(gpdma)] + pub gpdma_interrupt_priority: Priority, } impl Default for Config { @@ -108,6 +109,8 @@ impl Default for Config { bdma_interrupt_priority: Priority::P0, #[cfg(dma)] dma_interrupt_priority: Priority::P0, + #[cfg(gpdma)] + gpdma_interrupt_priority: Priority::P0, } } } @@ -151,6 +154,8 @@ pub fn init(config: Config) -> Peripherals { config.bdma_interrupt_priority, #[cfg(dma)] config.dma_interrupt_priority, + #[cfg(gpdma)] + config.gpdma_interrupt_priority, ); #[cfg(feature = "exti")] exti::init(); diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index f33319620..c3126b37f 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -5,7 +5,7 @@ pub mod enums; use embassy_hal_common::{into_ref, PeripheralRef}; use enums::*; -use crate::dma::TransferOptions; +use crate::dma::Transfer; use crate::gpio::sealed::AFType; use crate::gpio::AnyPin; use crate::pac::quadspi::Quadspi as Regs; @@ -230,9 +230,6 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { unsafe { self.setup_transaction(QspiMode::IndirectWrite, &transaction); - let request = self.dma.request(); - let options = TransferOptions::default(); - T::REGS.ccr().modify(|v| { v.set_fmode(QspiMode::IndirectRead.into()); }); @@ -241,12 +238,18 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { v.set_address(current_ar); }); - self.dma - .start_read(request, T::REGS.dr().ptr() as *mut u8, buf, options); + let request = self.dma.request(); + let transfer = Transfer::new_read( + &mut self.dma, + request, + T::REGS.dr().ptr() as *mut u8, + buf, + Default::default(), + ); T::REGS.cr().modify(|v| v.set_dmaen(true)); - while self.dma.is_running() {} + transfer.blocking_wait(); } } @@ -257,19 +260,22 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { unsafe { self.setup_transaction(QspiMode::IndirectWrite, &transaction); - let request = self.dma.request(); - let options = TransferOptions::default(); - T::REGS.ccr().modify(|v| { v.set_fmode(QspiMode::IndirectWrite.into()); }); - self.dma - .start_write(request, buf, T::REGS.dr().ptr() as *mut u8, options); + let request = self.dma.request(); + let transfer = Transfer::new_write( + &mut self.dma, + request, + buf, + T::REGS.dr().ptr() as *mut u8, + Default::default(), + ); T::REGS.cr().modify(|v| v.set_dmaen(true)); - while self.dma.is_running() {} + transfer.blocking_wait(); } } diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 23ece3a2a..433f73d79 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -185,6 +185,21 @@ fn clk_div(ker_ck: Hertz, sdmmc_ck: u32) -> Result<(bool, u16, Hertz), Error> { } } +#[cfg(sdmmc_v1)] +type Transfer<'a, C> = crate::dma::Transfer<'a, C>; +#[cfg(sdmmc_v2)] +type Transfer<'a, C> = core::marker::PhantomData<&'a mut C>; + +#[cfg(all(sdmmc_v1, dma))] +const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { + pburst: crate::dma::Burst::Incr4, + mburst: crate::dma::Burst::Incr4, + flow_ctrl: crate::dma::FlowControl::Peripheral, + fifo_threshold: Some(crate::dma::FifoThreshold::Full), +}; +#[cfg(all(sdmmc_v1, not(dma)))] +const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {}; + /// SDMMC configuration /// /// Default values: @@ -490,7 +505,12 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { /// # Safety /// /// `buffer` must be valid for the whole transfer and word aligned - unsafe fn prepare_datapath_read(&mut self, buffer: *mut [u32], length_bytes: u32, block_size: u8) { + fn prepare_datapath_read<'a>( + &'a mut self, + buffer: &'a mut [u32], + length_bytes: u32, + block_size: u8, + ) -> Transfer<'a, Dma> { assert!(block_size <= 14, "Block size up to 2^14 bytes"); let regs = T::regs(); @@ -499,48 +519,52 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Self::clear_interrupt_flags(); // NOTE(unsafe) We have exclusive access to the regisers + unsafe { + regs.dtimer() + .write(|w| w.set_datatime(self.config.data_transfer_timeout)); + regs.dlenr().write(|w| w.set_datalength(length_bytes)); - regs.dtimer() - .write(|w| w.set_datatime(self.config.data_transfer_timeout)); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); - - #[cfg(sdmmc_v1)] - { - let request = self.dma.request(); - self.dma.start_read( - request, - regs.fifor().ptr() as *const u32, - buffer, - crate::dma::TransferOptions { - pburst: crate::dma::Burst::Incr4, - mburst: crate::dma::Burst::Incr4, - flow_ctrl: crate::dma::FlowControl::Peripheral, - fifo_threshold: Some(crate::dma::FifoThreshold::Full), - ..Default::default() - }, - ); - } - #[cfg(sdmmc_v2)] - { - regs.idmabase0r().write(|w| w.set_idmabase0(buffer as *mut u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } - - regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); - w.set_dtdir(true); #[cfg(sdmmc_v1)] - { - w.set_dmaen(true); - w.set_dten(true); - } - }); + let transfer = { + let request = self.dma.request(); + Transfer::new_read( + &mut self.dma, + request, + regs.fifor().ptr() as *mut u32, + buffer, + DMA_TRANSFER_OPTIONS, + ) + }; + #[cfg(sdmmc_v2)] + let transfer = { + regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); + core::marker::PhantomData + }; + + regs.dctrl().modify(|w| { + w.set_dblocksize(block_size); + w.set_dtdir(true); + #[cfg(sdmmc_v1)] + { + w.set_dmaen(true); + w.set_dten(true); + } + }); + + transfer + } } /// # Safety /// /// `buffer` must be valid for the whole transfer and word aligned - unsafe fn prepare_datapath_write(&mut self, buffer: *const [u32], length_bytes: u32, block_size: u8) { + fn prepare_datapath_write<'a>( + &'a mut self, + buffer: &'a [u32], + length_bytes: u32, + block_size: u8, + ) -> Transfer<'a, Dma> { assert!(block_size <= 14, "Block size up to 2^14 bytes"); let regs = T::regs(); @@ -549,43 +573,41 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Self::clear_interrupt_flags(); // NOTE(unsafe) We have exclusive access to the regisers + unsafe { + regs.dtimer() + .write(|w| w.set_datatime(self.config.data_transfer_timeout)); + regs.dlenr().write(|w| w.set_datalength(length_bytes)); - regs.dtimer() - .write(|w| w.set_datatime(self.config.data_transfer_timeout)); - regs.dlenr().write(|w| w.set_datalength(length_bytes)); - - #[cfg(sdmmc_v1)] - { - let request = self.dma.request(); - self.dma.start_write( - request, - buffer, - regs.fifor().ptr() as *mut u32, - crate::dma::TransferOptions { - pburst: crate::dma::Burst::Incr4, - mburst: crate::dma::Burst::Incr4, - flow_ctrl: crate::dma::FlowControl::Peripheral, - fifo_threshold: Some(crate::dma::FifoThreshold::Full), - ..Default::default() - }, - ); - } - #[cfg(sdmmc_v2)] - { - regs.idmabase0r() - .write(|w| w.set_idmabase0(buffer as *const u32 as u32)); - regs.idmactrlr().modify(|w| w.set_idmaen(true)); - } - - regs.dctrl().modify(|w| { - w.set_dblocksize(block_size); - w.set_dtdir(false); #[cfg(sdmmc_v1)] - { - w.set_dmaen(true); - w.set_dten(true); - } - }); + let transfer = { + let request = self.dma.request(); + Transfer::new_write( + &mut self.dma, + request, + buffer, + regs.fifor().ptr() as *mut u32, + DMA_TRANSFER_OPTIONS, + ) + }; + #[cfg(sdmmc_v2)] + let transfer = { + regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_ptr() as u32)); + regs.idmactrlr().modify(|w| w.set_idmaen(true)); + core::marker::PhantomData + }; + + regs.dctrl().modify(|w| { + w.set_dblocksize(block_size); + w.set_dtdir(false); + #[cfg(sdmmc_v1)] + { + w.set_dmaen(true); + w.set_dten(true); + } + }); + + transfer + } } /// Stops the DMA datapath @@ -662,11 +684,9 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let regs = T::regs(); let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); - unsafe { - self.prepare_datapath_read(&mut status, 64, 6); - Self::data_interrupts(true); - } - self.cmd(Cmd::cmd6(set_function), true)?; // CMD6 + let transfer = self.prepare_datapath_read(&mut status, 64, 6); + Self::data_interrupts(true); + Self::cmd(Cmd::cmd6(set_function), true)?; // CMD6 let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -696,6 +716,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Ok(_) => { on_drop.defuse(); Self::stop_datapath(); + drop(transfer); // Function Selection of Function Group 1 let selection = (u32::from_be(status[4]) >> 24) & 0xF; @@ -718,7 +739,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let regs = T::regs(); let rca = card.rca; - self.cmd(Cmd::card_status(rca << 16), false)?; // CMD13 + Self::cmd(Cmd::card_status(rca << 16), false)?; // CMD13 // NOTE(unsafe) Atomic read with no side-effects let r1 = unsafe { regs.respr(0).read().cardstatus() }; @@ -730,8 +751,8 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let card = self.card.as_mut().ok_or(Error::NoCard)?; let rca = card.rca; - self.cmd(Cmd::set_block_length(64), false)?; // CMD16 - self.cmd(Cmd::app_cmd(rca << 16), false)?; // APP + Self::cmd(Cmd::set_block_length(64), false)?; // CMD16 + Self::cmd(Cmd::app_cmd(rca << 16), false)?; // APP let mut status = [0u32; 16]; @@ -739,11 +760,9 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let regs = T::regs(); let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); - unsafe { - self.prepare_datapath_read(&mut status, 64, 6); - Self::data_interrupts(true); - } - self.cmd(Cmd::card_status(0), true)?; + let transfer = self.prepare_datapath_read(&mut status, 64, 6); + Self::data_interrupts(true); + Self::cmd(Cmd::card_status(0), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -764,6 +783,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { if res.is_ok() { on_drop.defuse(); Self::stop_datapath(); + drop(transfer); for byte in status.iter_mut() { *byte = u32::from_be(*byte); @@ -781,7 +801,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { // Determine Relative Card Address (RCA) of given card let rca = card.map(|c| c.rca << 16).unwrap_or(0); - let r = self.cmd(Cmd::sel_desel_card(rca), false); + let r = Self::cmd(Cmd::sel_desel_card(rca), false); match (r, rca) { (Err(Error::Timeout), 0) => Ok(()), _ => r, @@ -842,8 +862,8 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> { // Read the the 64-bit SCR register - self.cmd(Cmd::set_block_length(8), false)?; // CMD16 - self.cmd(Cmd::app_cmd(card.rca << 16), false)?; + Self::cmd(Cmd::set_block_length(8), false)?; // CMD16 + Self::cmd(Cmd::app_cmd(card.rca << 16), false)?; let mut scr = [0u32; 2]; @@ -851,11 +871,9 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let regs = T::regs(); let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); - unsafe { - self.prepare_datapath_read(&mut scr[..], 8, 3); - Self::data_interrupts(true); - } - self.cmd(Cmd::cmd51(), true)?; + let transfer = self.prepare_datapath_read(&mut scr[..], 8, 3); + Self::data_interrupts(true); + Self::cmd(Cmd::cmd51(), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -876,6 +894,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { if res.is_ok() { on_drop.defuse(); Self::stop_datapath(); + drop(transfer); unsafe { let scr_bytes = &*(&scr as *const [u32; 2] as *const [u8; 8]); @@ -887,7 +906,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { /// Send command to card #[allow(unused_variables)] - fn cmd(&self, cmd: Cmd, data: bool) -> Result<(), Error> { + fn cmd(cmd: Cmd, data: bool) -> Result<(), Error> { let regs = T::regs(); Self::clear_interrupt_flags(); @@ -1005,10 +1024,10 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { }); regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8)); - self.cmd(Cmd::idle(), false)?; + Self::cmd(Cmd::idle(), false)?; // Check if cards supports CMD8 (with pattern) - self.cmd(Cmd::hs_send_ext_csd(0x1AA), false)?; + Self::cmd(Cmd::hs_send_ext_csd(0x1AA), false)?; let r1 = regs.respr(0).read().cardstatus(); let mut card = if r1 == 0x1AA { @@ -1020,14 +1039,14 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let ocr = loop { // Signal that next command is a app command - self.cmd(Cmd::app_cmd(0), false)?; // CMD55 + Self::cmd(Cmd::app_cmd(0), false)?; // CMD55 let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32 | CmdAppOper::HIGH_CAPACITY as u32 | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32; // Initialize card - match self.cmd(Cmd::app_op_cmd(arg), false) { + match Self::cmd(Cmd::app_op_cmd(arg), false) { // ACMD41 Ok(_) => (), Err(Error::Crc) => (), @@ -1048,7 +1067,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { } card.ocr = ocr; - self.cmd(Cmd::all_send_cid(), false)?; // CMD2 + Self::cmd(Cmd::all_send_cid(), false)?; // CMD2 let cid0 = regs.respr(0).read().cardstatus() as u128; let cid1 = regs.respr(1).read().cardstatus() as u128; let cid2 = regs.respr(2).read().cardstatus() as u128; @@ -1056,10 +1075,10 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3); card.cid = cid.into(); - self.cmd(Cmd::send_rel_addr(), false)?; + Self::cmd(Cmd::send_rel_addr(), false)?; card.rca = regs.respr(0).read().cardstatus() >> 16; - self.cmd(Cmd::send_csd(card.rca << 16), false)?; + Self::cmd(Cmd::send_csd(card.rca << 16), false)?; let csd0 = regs.respr(0).read().cardstatus() as u128; let csd1 = regs.respr(1).read().cardstatus() as u128; let csd2 = regs.respr(2).read().cardstatus() as u128; @@ -1077,8 +1096,8 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2), _ => (BusWidth::One, 0), }; - self.cmd(Cmd::app_cmd(card.rca << 16), false)?; - self.cmd(Cmd::cmd6(acmd_arg), false)?; + Self::cmd(Cmd::app_cmd(card.rca << 16), false)?; + Self::cmd(Cmd::cmd6(acmd_arg), false)?; // CPSMACT and DPSMACT must be 0 to set WIDBUS Self::wait_idle(); @@ -1139,16 +1158,14 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { CardCapacity::SDSC => block_idx * 512, _ => block_idx, }; - self.cmd(Cmd::set_block_length(512), false)?; // CMD16 + Self::cmd(Cmd::set_block_length(512), false)?; // CMD16 let regs = T::regs(); let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); - unsafe { - self.prepare_datapath_read(buffer, 512, 9); - Self::data_interrupts(true); - } - self.cmd(Cmd::read_single_block(address), true)?; + let transfer = self.prepare_datapath_read(buffer, 512, 9); + Self::data_interrupts(true); + Self::cmd(Cmd::read_single_block(address), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -1169,6 +1186,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { if res.is_ok() { on_drop.defuse(); Self::stop_datapath(); + drop(transfer); } res } @@ -1185,22 +1203,20 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { CardCapacity::SDSC => block_idx * 512, _ => block_idx, }; - self.cmd(Cmd::set_block_length(512), false)?; // CMD16 + Self::cmd(Cmd::set_block_length(512), false)?; // CMD16 let regs = T::regs(); let on_drop = OnDrop::new(|| unsafe { Self::on_drop() }); // sdmmc_v1 uses different cmd/dma order than v2, but only for writes #[cfg(sdmmc_v1)] - self.cmd(Cmd::write_single_block(address), true)?; + Self::cmd(Cmd::write_single_block(address), true)?; - unsafe { - self.prepare_datapath_write(buffer as *const [u32; 128], 512, 9); - Self::data_interrupts(true); - } + let transfer = self.prepare_datapath_write(buffer, 512, 9); + Self::data_interrupts(true); #[cfg(sdmmc_v2)] - self.cmd(Cmd::write_single_block(address), true)?; + Self::cmd(Cmd::write_single_block(address), true)?; let res = poll_fn(|cx| { T::state().register(cx.waker()); @@ -1222,6 +1238,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { Ok(_) => { on_drop.defuse(); Self::stop_datapath(); + drop(transfer); // TODO: Make this configurable let mut timeout: u32 = 0x00FF_FFFF; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 481ea4abc..7858cb3e8 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -421,8 +421,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let tx_request = self.txdma.request(); let tx_dst = T::REGS.tx_ptr(); - unsafe { self.txdma.start_write(tx_request, data, tx_dst, Default::default()) } - let tx_f = Transfer::new(&mut self.txdma); + let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) }; unsafe { set_txdmaen(T::REGS, true); @@ -468,13 +467,21 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let rx_request = self.rxdma.request(); let rx_src = T::REGS.rx_ptr(); - unsafe { self.rxdma.start_read(rx_request, rx_src, data, Default::default()) }; - let rx_f = Transfer::new(&mut self.rxdma); + let rx_f = unsafe { Transfer::new_read(&mut self.rxdma, rx_request, rx_src, data, Default::default()) }; let tx_request = self.txdma.request(); let tx_dst = T::REGS.tx_ptr(); let clock_byte = 0x00u8; - let tx_f = crate::dma::write_repeated(&mut self.txdma, tx_request, &clock_byte, clock_byte_count, tx_dst); + let tx_f = unsafe { + Transfer::new_write_repeated( + &mut self.txdma, + tx_request, + &clock_byte, + clock_byte_count, + tx_dst, + Default::default(), + ) + }; unsafe { set_txdmaen(T::REGS, true); @@ -521,13 +528,11 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let rx_request = self.rxdma.request(); let rx_src = T::REGS.rx_ptr(); - unsafe { self.rxdma.start_read(rx_request, rx_src, read, Default::default()) }; - let rx_f = Transfer::new(&mut self.rxdma); + let rx_f = unsafe { Transfer::new_read_raw(&mut self.rxdma, rx_request, rx_src, read, Default::default()) }; let tx_request = self.txdma.request(); let tx_dst = T::REGS.tx_ptr(); - unsafe { self.txdma.start_write(tx_request, write, tx_dst, Default::default()) } - let tx_f = Transfer::new(&mut self.txdma); + let tx_f = unsafe { Transfer::new_write_raw(&mut self.txdma, tx_request, write, tx_dst, Default::default()) }; unsafe { set_txdmaen(T::REGS, true); diff --git a/embassy-stm32/src/traits.rs b/embassy-stm32/src/traits.rs index 45cc4e725..ffce7bd42 100644 --- a/embassy-stm32/src/traits.rs +++ b/embassy-stm32/src/traits.rs @@ -34,7 +34,7 @@ macro_rules! dma_trait_impl { (crate::$mod:ident::$trait:ident, $instance:ident, {dmamux: $dmamux:ident}, $request:expr) => { impl crate::$mod::$trait for T where - T: crate::dma::MuxChannel, + T: crate::dma::Channel + crate::dma::MuxChannel, { fn request(&self) -> crate::dma::Request { $request diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 8bbba305b..b8656b586 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -6,11 +6,11 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_cortex_m::interrupt::InterruptExt; -use embassy_futures::select::{select, Either}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; +use futures::future::{select, Either}; -use crate::dma::NoDma; +use crate::dma::{NoDma, Transfer}; use crate::gpio::sealed::AFType; #[cfg(any(lpuart_v1, lpuart_v2))] use crate::pac::lpuart::{regs, vals, Lpuart as Regs}; @@ -91,7 +91,7 @@ enum ReadCompletionEvent { // DMA Read transfer completed first DmaCompleted, // Idle line detected first - Idle, + Idle(usize), } pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { @@ -183,7 +183,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { } // If we don't assign future to a variable, the data register pointer // is held across an await and makes the future non-Send. - let transfer = crate::dma::write(ch, request, buffer, tdr(T::regs())); + let transfer = unsafe { Transfer::new_write(ch, request, buffer, tdr(T::regs()), Default::default()) }; transfer.await; Ok(()) } @@ -430,10 +430,12 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { let ch = &mut self.rx_dma; let request = ch.request(); + let buffer_len = buffer.len(); + // Start USART DMA // will not do anything yet because DMAR is not yet set // future which will complete when DMA Read request completes - let transfer = crate::dma::read(ch, request, rdr(T::regs()), buffer); + let transfer = unsafe { Transfer::new_read(ch, request, rdr(T::regs()), buffer, Default::default()) }; // SAFETY: The only way we might have a problem is using split rx and tx // here we only modify or read Rx related flags, interrupts and DMA channel @@ -565,13 +567,15 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // when transfer is dropped, it will stop the DMA request let r = match select(transfer, idle).await { // DMA transfer completed first - Either::First(()) => Ok(ReadCompletionEvent::DmaCompleted), + Either::Left(((), _)) => Ok(ReadCompletionEvent::DmaCompleted), // Idle line detected first - Either::Second(Ok(())) => Ok(ReadCompletionEvent::Idle), + Either::Right((Ok(()), transfer)) => Ok(ReadCompletionEvent::Idle( + buffer_len - transfer.get_remaining_transfers() as usize, + )), // error occurred - Either::Second(Err(e)) => Err(e), + Either::Right((Err(e), _)) => Err(e), }; drop(on_drop); @@ -594,14 +598,9 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { // wait for DMA to complete or IDLE line detection if requested let res = self.inner_read_run(buffer, enable_idle_line_detection).await; - let ch = &mut self.rx_dma; - match res { Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len), - Ok(ReadCompletionEvent::Idle) => { - let n = buffer_len - (ch.remaining_transfers() as usize); - Ok(n) - } + Ok(ReadCompletionEvent::Idle(n)) => Ok(n), Err(e) => Err(e), } } From efc70debb3bbf7fb7e9b1a23a42e5db149de8ed6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 18 Apr 2023 16:16:33 +0200 Subject: [PATCH 36/46] stm32/dma: add double buffered mode for DMA, update DCMI. --- embassy-stm32/src/dcmi.rs | 37 +++++--- embassy-stm32/src/dma/dma.rs | 159 ++++++++++++++++++++++++++++++++++- 2 files changed, 182 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index 0b34553cf..c19be86c6 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -434,9 +434,13 @@ where result } + #[cfg(not(dma))] async fn capture_giant(&mut self, _buffer: &mut [u32]) -> Result<(), Error> { - todo!() - /* + panic!("capturing to buffers larger than 0xffff is only supported on DMA for now, not on BDMA or GPDMA."); + } + + #[cfg(dma)] + async fn capture_giant(&mut self, buffer: &mut [u32]) -> Result<(), Error> { use crate::dma::TransferOptions; let data_len = buffer.len(); @@ -460,16 +464,24 @@ where let r = self.inner.regs(); let src = r.dr().ptr() as *mut u32; - unsafe { - channel.start_double_buffered_read(request, src, m0ar, m1ar, chunk_size, TransferOptions::default()); - } + let mut transfer = unsafe { + crate::dma::DoubleBuffered::new_read( + &mut self.dma, + request, + src, + m0ar, + m1ar, + chunk_size, + TransferOptions::default(), + ) + }; let mut last_chunk_set_for_transfer = false; let mut buffer0_last_accessible = false; let dma_result = poll_fn(|cx| { - channel.set_waker(cx.waker()); + transfer.set_waker(cx.waker()); - let buffer0_currently_accessible = unsafe { channel.is_buffer0_accessible() }; + let buffer0_currently_accessible = transfer.is_buffer0_accessible(); // check if the accessible buffer changed since last poll if buffer0_last_accessible == buffer0_currently_accessible { @@ -480,21 +492,21 @@ where if remaining_chunks != 0 { if remaining_chunks % 2 == 0 && buffer0_currently_accessible { m0ar = unsafe { m0ar.add(2 * chunk_size) }; - unsafe { channel.set_buffer0(m0ar) } + unsafe { transfer.set_buffer0(m0ar) } remaining_chunks -= 1; } else if !buffer0_currently_accessible { m1ar = unsafe { m1ar.add(2 * chunk_size) }; - unsafe { channel.set_buffer1(m1ar) }; + unsafe { transfer.set_buffer1(m1ar) }; remaining_chunks -= 1; } } else { if buffer0_currently_accessible { - unsafe { channel.set_buffer0(buffer.as_mut_ptr()) } + unsafe { transfer.set_buffer0(buffer.as_mut_ptr()) } } else { - unsafe { channel.set_buffer1(buffer.as_mut_ptr()) } + unsafe { transfer.set_buffer1(buffer.as_mut_ptr()) } } if last_chunk_set_for_transfer { - channel.request_stop(); + transfer.request_stop(); return Poll::Ready(()); } last_chunk_set_for_transfer = true; @@ -542,7 +554,6 @@ where unsafe { Self::toggle(false) }; result - */ } } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 9052aa110..62c092241 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -1,7 +1,8 @@ use core::future::Future; +use core::marker::PhantomData; use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; -use core::task::{Context, Poll}; +use core::task::{Context, Poll, Waker}; use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; @@ -440,3 +441,159 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { } } } + +// ================================== + +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct DoubleBuffered<'a, C: Channel, W: Word> { + channel: PeripheralRef<'a, C>, + _phantom: PhantomData, +} + +impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> { + pub unsafe fn new_read( + channel: impl Peripheral

+ 'a, + _request: Request, + peri_addr: *mut W, + buf0: *mut W, + buf1: *mut W, + len: usize, + options: TransferOptions, + ) -> Self { + into_ref!(channel); + assert!(len > 0 && len <= 0xFFFF); + + let dir = Dir::PeripheralToMemory; + let data_size = W::bits(); + + let channel_number = channel.num(); + let dma = channel.regs(); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + + let mut this = Self { + channel, + _phantom: PhantomData, + }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); + + let ch = dma.st(channel_number); + ch.par().write_value(peri_addr as u32); + ch.m0ar().write_value(buf0 as u32); + ch.m1ar().write_value(buf1 as u32); + ch.ndtr().write_value(regs::Ndtr(len as _)); + ch.fcr().write(|w| { + if let Some(fth) = options.fifo_threshold { + // FIFO mode + w.set_dmdis(vals::Dmdis::DISABLED); + w.set_fth(fth.into()); + } else { + // Direct mode + w.set_dmdis(vals::Dmdis::ENABLED); + } + }); + ch.cr().write(|w| { + w.set_dir(dir.into()); + w.set_msize(data_size.into()); + w.set_psize(data_size.into()); + w.set_pl(vals::Pl::VERYHIGH); + w.set_minc(vals::Inc::INCREMENTED); + w.set_pinc(vals::Inc::FIXED); + w.set_teie(true); + w.set_tcie(true); + #[cfg(dma_v1)] + w.set_trbuff(true); + + #[cfg(dma_v2)] + w.set_chsel(_request); + + w.set_pburst(options.pburst.into()); + w.set_mburst(options.mburst.into()); + w.set_pfctrl(options.flow_ctrl.into()); + + w.set_en(true); + }); + + this + } + + fn clear_irqs(&mut self) { + let channel_number = self.channel.num(); + let dma = self.channel.regs(); + let isrn = channel_number / 4; + let isrbit = channel_number % 4; + + unsafe { + dma.ifcr(isrn).write(|w| { + w.set_tcif(isrbit, true); + w.set_teif(isrbit, true); + }) + } + } + + pub unsafe fn set_buffer0(&mut self, buffer: *mut W) { + let ch = self.channel.regs().st(self.channel.num()); + ch.m0ar().write_value(buffer as _); + } + + pub unsafe fn set_buffer1(&mut self, buffer: *mut W) { + let ch = self.channel.regs().st(self.channel.num()); + ch.m1ar().write_value(buffer as _); + } + + pub fn is_buffer0_accessible(&mut self) -> bool { + let ch = self.channel.regs().st(self.channel.num()); + unsafe { ch.cr().read() }.ct() == vals::Ct::MEMORY1 + } + + pub fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.channel.index()].register(waker); + } + + pub fn request_stop(&mut self) { + let ch = self.channel.regs().st(self.channel.num()); + + // Disable the channel. Keep the IEs enabled so the irqs still fire. + unsafe { + ch.cr().write(|w| { + w.set_teie(true); + w.set_tcie(true); + }) + } + } + + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().st(self.channel.num()); + unsafe { ch.cr().read() }.en() + } + + /// Gets the total remaining transfers for the channel + /// Note: this will be zero for transfers that completed without cancellation. + pub fn get_remaining_transfers(&self) -> u16 { + let ch = self.channel.regs().st(self.channel.num()); + unsafe { ch.ndtr().read() }.ndt() + } + + pub fn blocking_wait(mut self) { + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + + core::mem::forget(self); + } +} + +impl<'a, C: Channel, W: Word> Drop for DoubleBuffered<'a, C, W> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } +} From dbded7a6ce1297f382b48c0d9e7c0b8b6ccfdad3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 14 Apr 2023 22:04:09 +0200 Subject: [PATCH 37/46] Update nightly. Includes this TAIT breaking change. https://github.com/rust-lang/rust/pull/110237 --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f7183d167..2301ddc8d 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-2023-04-11" +channel = "nightly-2023-04-18" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", From 21ea98810aed1a4a820ac8cc357d66821f80c3fc Mon Sep 17 00:00:00 2001 From: Jacob Davis-Hansson Date: Tue, 18 Apr 2023 17:44:19 +0200 Subject: [PATCH 38/46] Pass rx pin to right init arg --- embassy-rp/src/uart/mod.rs | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index f7a4c5a60..f9e30a78b 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -5,8 +5,8 @@ use embassy_hal_common::{into_ref, PeripheralRef}; use crate::dma::{AnyChannel, Channel}; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; -use crate::{pac, peripherals, Peripheral}; use crate::pac::io::vals::{Inover, Outover}; +use crate::{pac, peripherals, Peripheral}; #[cfg(feature = "nightly")] mod buffered; @@ -180,7 +180,7 @@ impl<'d, T: Instance> UartTx<'d, T, Async> { } impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { - /// Create a new DMA-enabled UART which can only send data + /// Create a new DMA-enabled UART which can only recieve data pub fn new( _uart: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, @@ -188,7 +188,7 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { config: Config, ) -> Self { into_ref!(rx, rx_dma); - Uart::::init(Some(rx.map_into()), None, None, None, config); + Uart::::init(None, Some(rx.map_into()), None, None, config); Self::new_inner(Some(rx_dma.map_into())) } @@ -396,28 +396,44 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { if let Some(pin) = &tx { pin.io().ctrl().write(|w| { w.set_funcsel(2); - w.set_outover(if config.invert_tx { Outover::INVERT } else { Outover::NORMAL }); + w.set_outover(if config.invert_tx { + Outover::INVERT + } else { + Outover::NORMAL + }); }); pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rx { pin.io().ctrl().write(|w| { w.set_funcsel(2); - w.set_inover(if config.invert_rx { Inover::INVERT } else { Inover::NORMAL }); + w.set_inover(if config.invert_rx { + Inover::INVERT + } else { + Inover::NORMAL + }); }); pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &cts { pin.io().ctrl().write(|w| { w.set_funcsel(2); - w.set_inover(if config.invert_cts { Inover::INVERT } else { Inover::NORMAL }); + w.set_inover(if config.invert_cts { + Inover::INVERT + } else { + Inover::NORMAL + }); }); pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rts { pin.io().ctrl().write(|w| { w.set_funcsel(2); - w.set_outover(if config.invert_rts { Outover::INVERT } else { Outover::NORMAL }); + w.set_outover(if config.invert_rts { + Outover::INVERT + } else { + Outover::NORMAL + }); }); pin.pad_ctrl().write(|w| w.set_ie(true)); } From 2080d8bb6de58eb2da2ca5df2437ac8cc6577661 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 18 Apr 2023 20:56:23 +0200 Subject: [PATCH 39/46] stm32/spi: add support for all word sizes. Co-Authored-By: anton smeenk --- embassy-stm32/src/dma/bdma.rs | 9 +- embassy-stm32/src/dma/dma.rs | 11 +- embassy-stm32/src/dma/gpdma.rs | 9 +- embassy-stm32/src/dma/mod.rs | 49 +-------- embassy-stm32/src/dma/word.rs | 79 +++++++++++++++ embassy-stm32/src/spi/mod.rs | 180 ++++++++++++++++++--------------- 6 files changed, 197 insertions(+), 140 deletions(-) create mode 100644 embassy-stm32/src/dma/word.rs diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index cf1222c46..a23bb8cd7 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -9,7 +9,8 @@ use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::{Dir, Word, WordSize}; +use super::word::{Word, WordSize}; +use super::Dir; use crate::_generated::BDMA_CHANNEL_COUNT; use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac; @@ -167,7 +168,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -202,7 +203,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -225,7 +226,7 @@ impl<'a, C: Channel> Transfer<'a, C> { repeated as *const W as *mut u32, count, false, - W::bits(), + W::size(), options, ) } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 62c092241..ef1d27573 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -9,7 +9,8 @@ use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::dma::regs; -use super::{Dir, Word, WordSize}; +use super::word::{Word, WordSize}; +use super::Dir; use crate::_generated::DMA_CHANNEL_COUNT; use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac::dma::vals; @@ -246,7 +247,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -281,7 +282,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -304,7 +305,7 @@ impl<'a, C: Channel> Transfer<'a, C> { repeated as *const W as *mut u32, count, false, - W::bits(), + W::size(), options, ) } @@ -464,7 +465,7 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> { assert!(len > 0 && len <= 0xFFFF); let dir = Dir::PeripheralToMemory; - let data_size = W::bits(); + let data_size = W::size(); let channel_number = channel.num(); let dma = channel.regs(); diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 5c6676a5f..5a516ccda 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -9,7 +9,8 @@ use embassy_cortex_m::interrupt::Priority; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::{Dir, Word, WordSize}; +use super::word::{Word, WordSize}; +use super::Dir; use crate::_generated::GPDMA_CHANNEL_COUNT; use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac; @@ -165,7 +166,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -200,7 +201,7 @@ impl<'a, C: Channel> Transfer<'a, C> { ptr as *mut u32, len, true, - W::bits(), + W::size(), options, ) } @@ -223,7 +224,7 @@ impl<'a, C: Channel> Transfer<'a, C> { repeated as *const W as *mut u32, count, false, - W::bits(), + W::size(), options, ) } diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index d29ef4a1f..3312ca752 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -21,6 +21,8 @@ pub use gpdma::*; #[cfg(dmamux)] mod dmamux; +pub mod word; + use core::mem; use embassy_cortex_m::interrupt::Priority; @@ -36,53 +38,6 @@ enum Dir { PeripheralToMemory, } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum WordSize { - OneByte, - TwoBytes, - FourBytes, -} - -impl WordSize { - pub fn bytes(&self) -> usize { - match self { - Self::OneByte => 1, - Self::TwoBytes => 2, - Self::FourBytes => 4, - } - } -} - -mod word_sealed { - pub trait Word {} -} - -pub trait Word: word_sealed::Word { - fn bits() -> WordSize; -} - -impl word_sealed::Word for u8 {} -impl Word for u8 { - fn bits() -> WordSize { - WordSize::OneByte - } -} - -impl word_sealed::Word for u16 {} -impl Word for u16 { - fn bits() -> WordSize { - WordSize::TwoBytes - } -} - -impl word_sealed::Word for u32 {} -impl Word for u32 { - fn bits() -> WordSize { - WordSize::FourBytes - } -} - pub struct NoDma; impl_peripheral!(NoDma); diff --git a/embassy-stm32/src/dma/word.rs b/embassy-stm32/src/dma/word.rs new file mode 100644 index 000000000..aef6e9700 --- /dev/null +++ b/embassy-stm32/src/dma/word.rs @@ -0,0 +1,79 @@ +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum WordSize { + OneByte, + TwoBytes, + FourBytes, +} + +impl WordSize { + pub fn bytes(&self) -> usize { + match self { + Self::OneByte => 1, + Self::TwoBytes => 2, + Self::FourBytes => 4, + } + } +} + +mod sealed { + pub trait Word {} +} + +pub trait Word: sealed::Word + Default + Copy + 'static { + fn size() -> WordSize; + fn bits() -> usize; +} + +macro_rules! impl_word { + (_, $T:ident, $bits:literal, $size:ident) => { + impl sealed::Word for $T {} + impl Word for $T { + fn bits() -> usize { + $bits + } + fn size() -> WordSize { + WordSize::$size + } + } + }; + ($T:ident, $uX:ident, $bits:literal, $size:ident) => { + #[repr(transparent)] + #[derive(Copy, Clone, Default)] + pub struct $T(pub $uX); + impl_word!(_, $T, $bits, $size); + }; +} + +impl_word!(U1, u8, 1, OneByte); +impl_word!(U2, u8, 2, OneByte); +impl_word!(U3, u8, 3, OneByte); +impl_word!(U4, u8, 4, OneByte); +impl_word!(U5, u8, 5, OneByte); +impl_word!(U6, u8, 6, OneByte); +impl_word!(U7, u8, 7, OneByte); +impl_word!(_, u8, 8, OneByte); +impl_word!(U9, u16, 9, TwoBytes); +impl_word!(U10, u16, 10, TwoBytes); +impl_word!(U11, u16, 11, TwoBytes); +impl_word!(U12, u16, 12, TwoBytes); +impl_word!(U13, u16, 13, TwoBytes); +impl_word!(U14, u16, 14, TwoBytes); +impl_word!(U15, u16, 15, TwoBytes); +impl_word!(_, u16, 16, TwoBytes); +impl_word!(U17, u32, 17, FourBytes); +impl_word!(U18, u32, 18, FourBytes); +impl_word!(U19, u32, 19, FourBytes); +impl_word!(U20, u32, 20, FourBytes); +impl_word!(U21, u32, 21, FourBytes); +impl_word!(U22, u32, 22, FourBytes); +impl_word!(U23, u32, 23, FourBytes); +impl_word!(U24, u32, 24, FourBytes); +impl_word!(U25, u32, 25, FourBytes); +impl_word!(U26, u32, 26, FourBytes); +impl_word!(U27, u32, 27, FourBytes); +impl_word!(U28, u32, 28, FourBytes); +impl_word!(U29, u32, 29, FourBytes); +impl_word!(U30, u32, 30, FourBytes); +impl_word!(U31, u32, 31, FourBytes); +impl_word!(_, u32, 32, FourBytes); diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 7858cb3e8..9ce0cebfe 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -7,8 +7,7 @@ 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 self::sealed::WordSize; -use crate::dma::{slice_ptr_parts, Transfer}; +use crate::dma::{slice_ptr_parts, word, Transfer}; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Pull}; use crate::pac::spi::{regs, vals, Spi as Regs}; @@ -78,7 +77,7 @@ pub struct Spi<'d, T: Instance, Tx, Rx> { miso: Option>, txdma: PeripheralRef<'d, Tx>, rxdma: PeripheralRef<'d, Rx>, - current_word_size: WordSize, + current_word_size: word_impl::Config, } impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { @@ -234,14 +233,15 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { if mosi.is_none() { w.set_rxonly(vals::Rxonly::OUTPUTDISABLED); } - w.set_dff(WordSize::EightBit.dff()) + w.set_dff(::CONFIG) }); } #[cfg(spi_v2)] unsafe { T::REGS.cr2().modify(|w| { - w.set_frxth(WordSize::EightBit.frxth()); - w.set_ds(WordSize::EightBit.ds()); + let (ds, frxth) = ::CONFIG; + w.set_frxth(frxth); + w.set_ds(ds); w.set_ssoe(false); }); T::REGS.cr1().modify(|w| { @@ -279,7 +279,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cfg1().modify(|w| { w.set_crcen(false); w.set_mbr(br); - w.set_dsize(WordSize::EightBit.dsize()); + w.set_dsize(::CONFIG); + w.set_fthlv(vals::Fthlv::ONEFRAME); }); T::REGS.cr2().modify(|w| { w.set_tsize(0); @@ -297,7 +298,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { miso, txdma, rxdma, - current_word_size: WordSize::EightBit, + current_word_size: ::CONFIG, } } @@ -355,7 +356,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } } - fn set_word_size(&mut self, word_size: WordSize) { + fn set_word_size(&mut self, word_size: word_impl::Config) { if self.current_word_size == word_size { return; } @@ -364,7 +365,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { unsafe { T::REGS.cr1().modify(|reg| { reg.set_spe(false); - reg.set_dff(word_size.dff()) + reg.set_dff(word_size) }); T::REGS.cr1().modify(|reg| { reg.set_spe(true); @@ -376,8 +377,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { w.set_spe(false); }); T::REGS.cr2().modify(|w| { - w.set_frxth(word_size.frxth()); - w.set_ds(word_size.ds()); + w.set_frxth(word_size.1); + w.set_ds(word_size.0); }); T::REGS.cr1().modify(|w| { w.set_spe(true); @@ -393,7 +394,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { w.set_spe(false); }); T::REGS.cfg1().modify(|w| { - w.set_dsize(word_size.dsize()); + w.set_dsize(word_size); }); T::REGS.cr1().modify(|w| { w.set_csusp(false); @@ -412,7 +413,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { return Ok(()); } - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); unsafe { T::REGS.cr1().modify(|w| { w.set_spe(false); @@ -450,7 +451,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { return Ok(()); } - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); unsafe { T::REGS.cr1().modify(|w| { w.set_spe(false); @@ -513,7 +514,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { return Ok(()); } - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); unsafe { T::REGS.cr1().modify(|w| { w.set_spe(false); @@ -571,7 +572,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn blocking_write(&mut self, words: &[W]) -> Result<(), Error> { unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } flush_rx_fifo(T::REGS); - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); for word in words.iter() { let _ = transfer_word(T::REGS, *word)?; } @@ -581,7 +582,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn blocking_read(&mut self, words: &mut [W]) -> Result<(), Error> { unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } flush_rx_fifo(T::REGS); - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); for word in words.iter_mut() { *word = transfer_word(T::REGS, W::default())?; } @@ -591,7 +592,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn blocking_transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Error> { unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } flush_rx_fifo(T::REGS); - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); for word in words.iter_mut() { *word = transfer_word(T::REGS, *word)?; } @@ -601,7 +602,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { pub fn blocking_transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) } flush_rx_fifo(T::REGS); - self.set_word_size(W::WORDSIZE); + self.set_word_size(W::CONFIG); let len = read.len().max(write.len()); for i in 0..len { let wb = write.get(i).copied().unwrap_or_default(); @@ -933,70 +934,89 @@ pub(crate) mod sealed { const REGS: Regs; } - pub trait Word: Copy + 'static { - const WORDSIZE: WordSize; - } - - impl Word for u8 { - const WORDSIZE: WordSize = WordSize::EightBit; - } - impl Word for u16 { - const WORDSIZE: WordSize = WordSize::SixteenBit; - } - - #[derive(Copy, Clone, PartialOrd, PartialEq)] - pub enum WordSize { - EightBit, - SixteenBit, - } - - impl WordSize { - #[cfg(any(spi_v1, spi_f1))] - pub fn dff(&self) -> vals::Dff { - match self { - WordSize::EightBit => vals::Dff::EIGHTBIT, - WordSize::SixteenBit => vals::Dff::SIXTEENBIT, - } - } - - #[cfg(spi_v2)] - pub fn ds(&self) -> vals::Ds { - match self { - WordSize::EightBit => vals::Ds::EIGHTBIT, - WordSize::SixteenBit => vals::Ds::SIXTEENBIT, - } - } - - #[cfg(spi_v2)] - pub fn frxth(&self) -> vals::Frxth { - match self { - WordSize::EightBit => vals::Frxth::QUARTER, - WordSize::SixteenBit => vals::Frxth::HALF, - } - } - - #[cfg(any(spi_v3, spi_v4, spi_v5))] - pub fn dsize(&self) -> u8 { - match self { - WordSize::EightBit => 0b0111, - WordSize::SixteenBit => 0b1111, - } - } - - #[cfg(any(spi_v3, spi_v4, spi_v5))] - pub fn _frxth(&self) -> vals::Fthlv { - match self { - WordSize::EightBit => vals::Fthlv::ONEFRAME, - WordSize::SixteenBit => vals::Fthlv::ONEFRAME, - } - } + pub trait Word { + const CONFIG: word_impl::Config; } } -pub trait Word: Copy + 'static + sealed::Word + Default + crate::dma::Word {} +pub trait Word: word::Word + sealed::Word {} -impl Word for u8 {} -impl Word for u16 {} +macro_rules! impl_word { + ($T:ty, $config:expr) => { + impl sealed::Word for $T { + const CONFIG: Config = $config; + } + impl Word for $T {} + }; +} + +#[cfg(any(spi_v1, spi_f1))] +mod word_impl { + use super::*; + + pub type Config = vals::Dff; + + impl_word!(u8, vals::Dff::EIGHTBIT); + impl_word!(u16, vals::Dff::SIXTEENBIT); +} + +#[cfg(any(spi_v2))] +mod word_impl { + use super::*; + + pub type Config = (vals::Ds, vals::Frxth); + + impl_word!(word::U4, (vals::Ds::FOURBIT, vals::Frxth::QUARTER)); + impl_word!(word::U5, (vals::Ds::FIVEBIT, vals::Frxth::QUARTER)); + impl_word!(word::U6, (vals::Ds::SIXBIT, vals::Frxth::QUARTER)); + impl_word!(word::U7, (vals::Ds::SEVENBIT, vals::Frxth::QUARTER)); + impl_word!(u8, (vals::Ds::EIGHTBIT, vals::Frxth::QUARTER)); + impl_word!(word::U9, (vals::Ds::NINEBIT, vals::Frxth::HALF)); + impl_word!(word::U10, (vals::Ds::TENBIT, vals::Frxth::HALF)); + impl_word!(word::U11, (vals::Ds::ELEVENBIT, vals::Frxth::HALF)); + impl_word!(word::U12, (vals::Ds::TWELVEBIT, vals::Frxth::HALF)); + impl_word!(word::U13, (vals::Ds::THIRTEENBIT, vals::Frxth::HALF)); + impl_word!(word::U14, (vals::Ds::FOURTEENBIT, vals::Frxth::HALF)); + impl_word!(word::U15, (vals::Ds::FIFTEENBIT, vals::Frxth::HALF)); + impl_word!(u16, (vals::Ds::SIXTEENBIT, vals::Frxth::HALF)); +} + +#[cfg(any(spi_v3, spi_v4, spi_v5))] +mod word_impl { + use super::*; + + pub type Config = u8; + + impl_word!(word::U4, 4 - 1); + impl_word!(word::U5, 5 - 1); + impl_word!(word::U6, 6 - 1); + impl_word!(word::U7, 7 - 1); + impl_word!(u8, 8 - 1); + impl_word!(word::U9, 9 - 1); + impl_word!(word::U10, 10 - 1); + impl_word!(word::U11, 11 - 1); + impl_word!(word::U12, 12 - 1); + impl_word!(word::U13, 13 - 1); + impl_word!(word::U14, 14 - 1); + impl_word!(word::U15, 15 - 1); + impl_word!(u16, 16 - 1); + impl_word!(word::U17, 17 - 1); + impl_word!(word::U18, 18 - 1); + impl_word!(word::U19, 19 - 1); + impl_word!(word::U20, 20 - 1); + impl_word!(word::U21, 21 - 1); + impl_word!(word::U22, 22 - 1); + impl_word!(word::U23, 23 - 1); + impl_word!(word::U24, 24 - 1); + impl_word!(word::U25, 25 - 1); + impl_word!(word::U26, 26 - 1); + impl_word!(word::U27, 27 - 1); + impl_word!(word::U28, 28 - 1); + impl_word!(word::U29, 29 - 1); + impl_word!(word::U30, 30 - 1); + impl_word!(word::U31, 31 - 1); + impl_word!(u32, 32 - 1); +} pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} pin_trait!(SckPin, Instance); From 3260f6b2aff824a5f7f2a81283b234bdb1bd3e24 Mon Sep 17 00:00:00 2001 From: anton smeenk Date: Tue, 18 Apr 2023 20:56:57 +0200 Subject: [PATCH 40/46] stm32/spi: add new_txonly_nosck constructor, for neopixels, with an example in the stm32g0 directory. --- embassy-stm32/src/spi/mod.rs | 17 ++++ examples/stm32g0/src/bin/spi_neopixel.rs | 101 +++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 examples/stm32g0/src/bin/spi_neopixel.rs diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 9ce0cebfe..492d0649a 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -177,6 +177,23 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { ) } + pub fn new_txonly_nosck( + peri: impl Peripheral

+ 'd, + mosi: impl Peripheral

> + 'd, + txdma: impl Peripheral

+ 'd, + rxdma: impl Peripheral

+ 'd, // TODO: remove + freq: Hertz, + config: Config, + ) -> Self { + into_ref!(mosi); + unsafe { + mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down); + mosi.set_speed(crate::gpio::Speed::Medium); + } + + Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, freq, config) + } + /// Useful for on chip peripherals like SUBGHZ which are hardwired. /// The bus can optionally be exposed externally with `Spi::new()` still. #[allow(dead_code)] diff --git a/examples/stm32g0/src/bin/spi_neopixel.rs b/examples/stm32g0/src/bin/spi_neopixel.rs new file mode 100644 index 000000000..81fdd15cb --- /dev/null +++ b/examples/stm32g0/src/bin/spi_neopixel.rs @@ -0,0 +1,101 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::dma::word::U5; +use embassy_stm32::dma::NoDma; +use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +const NR_PIXELS: usize = 15; +const BITS_PER_PIXEL: usize = 24; // 24 for rgb, 32 for rgbw +const TOTAL_BITS: usize = NR_PIXELS * BITS_PER_PIXEL; + +struct RGB { + r: u8, + g: u8, + b: u8, +} +impl Default for RGB { + fn default() -> RGB { + RGB { r: 0, g: 0, b: 0 } + } +} +pub struct Ws2812 { + // Note that the U5 type controls the selection of 5 bits to output + bitbuffer: [U5; TOTAL_BITS], +} + +impl Ws2812 { + pub fn new() -> Ws2812 { + Ws2812 { + bitbuffer: [U5(0); TOTAL_BITS], + } + } + fn len(&self) -> usize { + return NR_PIXELS; + } + fn set(&mut self, idx: usize, rgb: RGB) { + self.render_color(idx, 0, rgb.g); + self.render_color(idx, 8, rgb.r); + self.render_color(idx, 16, rgb.b); + } + // transform one color byte into an array of 8 byte. Each byte in the array does represent 1 neopixel bit pattern + fn render_color(&mut self, pixel_idx: usize, offset: usize, color: u8) { + let mut bits = color as usize; + let mut idx = pixel_idx * BITS_PER_PIXEL + offset; + + // render one bit in one spi byte. High time first, then the low time + // clock should be 4 Mhz, 5 bits, each bit is 0.25 us. + // a one bit is send as a pulse of 0.75 high -- 0.50 low + // a zero bit is send as a pulse of 0.50 high -- 0.75 low + // clock frequency for the neopixel is exact 800 khz + // note that the mosi output should have a resistor to ground of 10k, + // to assure that between the bursts the line is low + for _i in 0..8 { + if idx >= TOTAL_BITS { + return; + } + let pattern = match bits & 0x80 { + 0x80 => 0b0000_1110, + _ => 0b000_1100, + }; + bits = bits << 1; + self.bitbuffer[idx] = U5(pattern); + idx += 1; + } + } +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Start test using spi as neopixel driver"); + + let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, Hertz(4_000_000), Config::default()); + + let mut neopixels = Ws2812::new(); + + loop { + let mut cnt: usize = 0; + for _i in 0..10 { + for idx in 0..neopixels.len() { + let color = match (cnt + idx) % 3 { + 0 => RGB { r: 0x21, g: 0, b: 0 }, + 1 => RGB { r: 0, g: 0x31, b: 0 }, + _ => RGB { r: 0, g: 0, b: 0x41 }, + }; + neopixels.set(idx, color); + } + cnt += 1; + // start sending the neopixel bit patters over spi to the neopixel string + spi.write(&neopixels.bitbuffer).await.ok(); + Timer::after(Duration::from_millis(500)).await; + } + Timer::after(Duration::from_millis(1000)).await; + } +} From 8a9136e4e47a2d02084f343cd6d212f419c37f04 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 18 Apr 2023 21:07:36 +0200 Subject: [PATCH 41/46] enable inline-asm feature for cortex-m in examples inline assembly is supported since rust 1.59, we're way past that. enabling this makes the compiled code more compact, and on rp2040 even decreses memory usage by not needing thunks in sram. --- examples/boot/application/nrf/Cargo.toml | 4 ++-- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/boot/bootloader/nrf/Cargo.toml | 2 +- examples/boot/bootloader/rp/Cargo.toml | 2 +- examples/boot/bootloader/stm32/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- 31 files changed, 32 insertions(+), 32 deletions(-) diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index cda1917b3..d6a30602d 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -18,9 +18,9 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] ed25519-dalek = ["embassy-boot/ed25519-dalek"] -ed25519-salty = ["embassy-boot/ed25519-salty"] \ No newline at end of file +ed25519-salty = ["embassy-boot/ed25519-salty"] diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 9d34a3691..62ef42d6b 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -18,7 +18,7 @@ panic-probe = { version = "0.3", features = ["print-defmt"], optional = true } panic-reset = { version = "0.1.1", optional = true } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 8e978eb24..e5fb1b01d 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index fb55b166d..a6ac1cadf 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index ea6b905a4..5b8ee555f 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 6ba18564d..05ce5c10a 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index d5b8e3e01..14af99e96 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index ccd1fe2ee..90ae97725 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 128afd51e..08403a4ec 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -17,7 +17,7 @@ defmt-rtt = { version = "0.4", optional = true } panic-reset = { version = "0.1.1" } embedded-hal = { version = "0.2.6" } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" [features] diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index 8a6f53643..cd0be5b48 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -11,7 +11,7 @@ defmt-rtt = { version = "0.4", optional = true } embassy-nrf = { path = "../../../../embassy-nrf", default-features = false, features = ["nightly"] } embassy-boot-nrf = { path = "../../../../embassy-boot/nrf", default-features = false } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = { version = "0.7" } cfg-if = "1.0.0" diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index a16cebe31..b4167bcd8 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -13,7 +13,7 @@ embassy-rp = { path = "../../../../embassy-rp", default-features = false, featur embassy-boot-rp = { path = "../../../../embassy-boot/rp", default-features = false } embassy-time = { path = "../../../../embassy-time", features = ["nightly"] } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.0" embedded-storage-async = "0.4.0" diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index b1791620f..f2675aa73 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -11,7 +11,7 @@ defmt-rtt = { version = "0.4", optional = true } embassy-stm32 = { path = "../../../../embassy-stm32", default-features = false, features = ["nightly"] } embassy-boot-stm32 = { path = "../../../../embassy-boot/stm32", default-features = false } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.0" embedded-storage-async = "0.4.0" diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 6f7cb8875..0a7141c4e 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -21,7 +21,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature embassy-time = { version = "0.1.0", path = "../../embassy-time" } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3" } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index af19413cc..10c269a76 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -27,7 +27,7 @@ defmt = "0.3" defmt-rtt = "0.4" static_cell = "1.0" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 2c3a12964..ebbc25bc6 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -43,7 +43,7 @@ defmt = "0.3" defmt-rtt = "0.4" static_cell = "1.0" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = [ diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index f0fd27991..8067f7ba5 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -20,7 +20,7 @@ defmt = "0.3" defmt-rtt = "0.4" #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } -cortex-m = { version = "0.7.6" } +cortex-m = { version = "0.7.6", features = ["inline-asm"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 2ff252622..e969538d3 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index d08e00b0b..706b5a722 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -15,7 +15,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 02045a79f..62947bf49 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index a62eba9ec..7ba9ff0c1 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -15,7 +15,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 69dcab64c..77985a017 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -15,7 +15,7 @@ embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defm defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-io = "0.4.0" diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 898e05c12..d9e9d668c 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -16,7 +16,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index a522fb422..6bbd3a53f 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 149f8a58b..67e6c76a7 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -16,7 +16,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 8316498ca..bd175a5b1 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -16,7 +16,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index b04faf535..d08e2b61a 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -24,7 +24,7 @@ defmt-rtt = "0.4" embedded-storage = "0.3.0" embedded-io = "0.4.0" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index e071c5d27..ff95571e6 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index af305a19d..d3dee5250 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -18,7 +18,7 @@ defmt = "0.3" defmt-rtt = "0.4" panic-probe = { version = "0.3", features = ["print-defmt"] } -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 65fc1b988..86bc83dab 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -14,7 +14,7 @@ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defm defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 835985ec3..18b27b28e 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -13,7 +13,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index cb3526fa4..07f136b40 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -17,7 +17,7 @@ lorawan = { version = "0.7.2", default-features = false, features = ["default-cr defmt = "0.3" defmt-rtt = "0.4" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-storage = "0.3.0" From bfa3cbaf30fae513c7569a2f5c88a45b0911f02e Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Tue, 18 Apr 2023 21:47:28 +0200 Subject: [PATCH 42/46] Add embassy-net without dhcp to ci.sh --- ci.sh | 1 + ci_stable.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/ci.sh b/ci.sh index 1b3fac8bc..657975041 100755 --- a/ci.sh +++ b/ci.sh @@ -22,6 +22,7 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \ diff --git a/ci_stable.sh b/ci_stable.sh index b4b0b83e7..18271ee73 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -13,6 +13,7 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ From a2ac1eed1bd357f31c8a0cd5f8957f3017c5df21 Mon Sep 17 00:00:00 2001 From: Roy Buitenhuis Date: Tue, 18 Apr 2023 22:11:15 +0200 Subject: [PATCH 43/46] Add extra feature flags to fix build without dhcp. --- embassy-net/src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 7b9d0e773..5dfb5843e 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -27,12 +27,10 @@ use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; use heapless::Vec; +use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] -use smoltcp::iface::SocketHandle; -use smoltcp::iface::{Interface, SocketSet, SocketStorage}; +use smoltcp::socket::dhcpv4::{self, RetryConfig}; #[cfg(feature = "dhcpv4")] -use smoltcp::socket::dhcpv4; -use smoltcp::socket::dhcpv4::RetryConfig; use smoltcp::time::Duration; // smoltcp reexports pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; @@ -76,6 +74,7 @@ pub struct StaticConfig { pub dns_servers: Vec, } +#[cfg(feature = "dhcpv4")] #[derive(Debug, Clone, PartialEq, Eq)] pub struct DhcpConfig { pub max_lease_duration: Option, @@ -88,6 +87,7 @@ pub struct DhcpConfig { pub client_port: u16, } +#[cfg(feature = "dhcpv4")] impl Default for DhcpConfig { fn default() -> Self { Self { @@ -384,6 +384,7 @@ impl Inner { self.config = Some(config) } + #[cfg(feature = "dhcpv4")] fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) { socket.set_ignore_naks(config.ignore_naks); socket.set_max_lease_duration(config.max_lease_duration); From fdd6e08ed6b5445a53c8cd0752f80f203c4860ac Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 19 Apr 2023 01:57:37 +0200 Subject: [PATCH 44/46] rp: hook up softfloat rom intrinsics rp-hal has done this very well already, so we'll just copy their entire impl again. only div.rs needed some massaging because our sio access works a little differently, everything else worked as is. --- embassy-rp/src/float/add_sub.rs | 92 ++++++++++++ embassy-rp/src/float/cmp.rs | 201 +++++++++++++++++++++++++ embassy-rp/src/float/conv.rs | 157 ++++++++++++++++++++ embassy-rp/src/float/div.rs | 141 ++++++++++++++++++ embassy-rp/src/float/functions.rs | 239 ++++++++++++++++++++++++++++++ embassy-rp/src/float/mod.rs | 149 +++++++++++++++++++ embassy-rp/src/float/mul.rs | 70 +++++++++ embassy-rp/src/lib.rs | 1 + tests/rp/.cargo/config.toml | 6 +- tests/rp/Cargo.toml | 2 +- tests/rp/src/bin/float.rs | 53 +++++++ 11 files changed, 1108 insertions(+), 3 deletions(-) create mode 100644 embassy-rp/src/float/add_sub.rs create mode 100644 embassy-rp/src/float/cmp.rs create mode 100644 embassy-rp/src/float/conv.rs create mode 100644 embassy-rp/src/float/div.rs create mode 100644 embassy-rp/src/float/functions.rs create mode 100644 embassy-rp/src/float/mod.rs create mode 100644 embassy-rp/src/float/mul.rs create mode 100644 tests/rp/src/bin/float.rs diff --git a/embassy-rp/src/float/add_sub.rs b/embassy-rp/src/float/add_sub.rs new file mode 100644 index 000000000..673544cfe --- /dev/null +++ b/embassy-rp/src/float/add_sub.rs @@ -0,0 +1,92 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/add_sub.rs + +use super::{Float, Int}; +use crate::rom_data; + +trait ROMAdd { + fn rom_add(self, b: Self) -> Self; +} + +impl ROMAdd for f32 { + fn rom_add(self, b: Self) -> Self { + rom_data::float_funcs::fadd(self, b) + } +} + +impl ROMAdd for f64 { + fn rom_add(self, b: Self) -> Self { + rom_data::double_funcs::dadd(self, b) + } +} + +fn add(a: F, b: F) -> F { + if a.is_not_finite() { + if b.is_not_finite() { + let class_a = a.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK); + let class_b = b.repr() & (F::SIGNIFICAND_MASK | F::SIGN_MASK); + + if class_a == F::Int::ZERO && class_b == F::Int::ZERO { + // inf + inf = inf + return a; + } + if class_a == F::SIGN_MASK && class_b == F::SIGN_MASK { + // -inf + (-inf) = -inf + return a; + } + + // Sign mismatch, or either is NaN already + return F::NAN; + } + + // [-]inf/NaN + X = [-]inf/NaN + return a; + } + + if b.is_not_finite() { + // X + [-]inf/NaN = [-]inf/NaN + return b; + } + + a.rom_add(b) +} + +intrinsics! { + #[alias = __addsf3vfp] + #[aeabi = __aeabi_fadd] + extern "C" fn __addsf3(a: f32, b: f32) -> f32 { + add(a, b) + } + + #[bootrom_v2] + #[alias = __adddf3vfp] + #[aeabi = __aeabi_dadd] + extern "C" fn __adddf3(a: f64, b: f64) -> f64 { + add(a, b) + } + + // The ROM just implements subtraction the same way, so just do it here + // and save the work of implementing more complicated NaN/inf handling. + + #[alias = __subsf3vfp] + #[aeabi = __aeabi_fsub] + extern "C" fn __subsf3(a: f32, b: f32) -> f32 { + add(a, -b) + } + + #[bootrom_v2] + #[alias = __subdf3vfp] + #[aeabi = __aeabi_dsub] + extern "C" fn __subdf3(a: f64, b: f64) -> f64 { + add(a, -b) + } + + extern "aapcs" fn __aeabi_frsub(a: f32, b: f32) -> f32 { + add(b, -a) + } + + #[bootrom_v2] + extern "aapcs" fn __aeabi_drsub(a: f64, b: f64) -> f64 { + add(b, -a) + } +} diff --git a/embassy-rp/src/float/cmp.rs b/embassy-rp/src/float/cmp.rs new file mode 100644 index 000000000..e540e3918 --- /dev/null +++ b/embassy-rp/src/float/cmp.rs @@ -0,0 +1,201 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/cmp.rs + +use super::Float; +use crate::rom_data; + +trait ROMCmp { + fn rom_cmp(self, b: Self) -> i32; +} + +impl ROMCmp for f32 { + fn rom_cmp(self, b: Self) -> i32 { + rom_data::float_funcs::fcmp(self, b) + } +} + +impl ROMCmp for f64 { + fn rom_cmp(self, b: Self) -> i32 { + rom_data::double_funcs::dcmp(self, b) + } +} + +fn le_abi(a: F, b: F) -> i32 { + if a.is_nan() || b.is_nan() { + 1 + } else { + a.rom_cmp(b) + } +} + +fn ge_abi(a: F, b: F) -> i32 { + if a.is_nan() || b.is_nan() { + -1 + } else { + a.rom_cmp(b) + } +} + +intrinsics! { + #[slower_than_default] + #[bootrom_v2] + #[alias = __eqsf2, __ltsf2, __nesf2] + extern "C" fn __lesf2(a: f32, b: f32) -> i32 { + le_abi(a, b) + } + + #[slower_than_default] + #[bootrom_v2] + #[alias = __eqdf2, __ltdf2, __nedf2] + extern "C" fn __ledf2(a: f64, b: f64) -> i32 { + le_abi(a, b) + } + + #[slower_than_default] + #[bootrom_v2] + #[alias = __gtsf2] + extern "C" fn __gesf2(a: f32, b: f32) -> i32 { + ge_abi(a, b) + } + + #[slower_than_default] + #[bootrom_v2] + #[alias = __gtdf2] + extern "C" fn __gedf2(a: f64, b: f64) -> i32 { + ge_abi(a, b) + } + + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_fcmple(a: f32, b: f32) -> i32 { + (le_abi(a, b) <= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_fcmpge(a: f32, b: f32) -> i32 { + (ge_abi(a, b) >= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_fcmpeq(a: f32, b: f32) -> i32 { + (le_abi(a, b) == 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_fcmplt(a: f32, b: f32) -> i32 { + (le_abi(a, b) < 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_fcmpgt(a: f32, b: f32) -> i32 { + (ge_abi(a, b) > 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_dcmple(a: f64, b: f64) -> i32 { + (le_abi(a, b) <= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_dcmpge(a: f64, b: f64) -> i32 { + (ge_abi(a, b) >= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_dcmpeq(a: f64, b: f64) -> i32 { + (le_abi(a, b) == 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_dcmplt(a: f64, b: f64) -> i32 { + (le_abi(a, b) < 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "aapcs" fn __aeabi_dcmpgt(a: f64, b: f64) -> i32 { + (ge_abi(a, b) > 0) as i32 + } + + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __gesf2vfp(a: f32, b: f32) -> i32 { + (ge_abi(a, b) >= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __gedf2vfp(a: f64, b: f64) -> i32 { + (ge_abi(a, b) >= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __gtsf2vfp(a: f32, b: f32) -> i32 { + (ge_abi(a, b) > 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __gtdf2vfp(a: f64, b: f64) -> i32 { + (ge_abi(a, b) > 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __ltsf2vfp(a: f32, b: f32) -> i32 { + (le_abi(a, b) < 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __ltdf2vfp(a: f64, b: f64) -> i32 { + (le_abi(a, b) < 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __lesf2vfp(a: f32, b: f32) -> i32 { + (le_abi(a, b) <= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __ledf2vfp(a: f64, b: f64) -> i32 { + (le_abi(a, b) <= 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __nesf2vfp(a: f32, b: f32) -> i32 { + (le_abi(a, b) != 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __nedf2vfp(a: f64, b: f64) -> i32 { + (le_abi(a, b) != 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __eqsf2vfp(a: f32, b: f32) -> i32 { + (le_abi(a, b) == 0) as i32 + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn __eqdf2vfp(a: f64, b: f64) -> i32 { + (le_abi(a, b) == 0) as i32 + } +} diff --git a/embassy-rp/src/float/conv.rs b/embassy-rp/src/float/conv.rs new file mode 100644 index 000000000..021826e28 --- /dev/null +++ b/embassy-rp/src/float/conv.rs @@ -0,0 +1,157 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs + +use super::Float; +use crate::rom_data; + +// Some of these are also not connected in the Pico SDK. This is probably +// because the ROM version actually does a fixed point conversion, just with +// the fractional width set to zero. + +intrinsics! { + // Not connected in the Pico SDK + #[slower_than_default] + #[aeabi = __aeabi_i2f] + extern "C" fn __floatsisf(i: i32) -> f32 { + rom_data::float_funcs::int_to_float(i) + } + + // Not connected in the Pico SDK + #[slower_than_default] + #[aeabi = __aeabi_i2d] + extern "C" fn __floatsidf(i: i32) -> f64 { + rom_data::double_funcs::int_to_double(i) + } + + // Questionable gain + #[aeabi = __aeabi_l2f] + extern "C" fn __floatdisf(i: i64) -> f32 { + rom_data::float_funcs::int64_to_float(i) + } + + #[bootrom_v2] + #[aeabi = __aeabi_l2d] + extern "C" fn __floatdidf(i: i64) -> f64 { + rom_data::double_funcs::int64_to_double(i) + } + + // Not connected in the Pico SDK + #[slower_than_default] + #[aeabi = __aeabi_ui2f] + extern "C" fn __floatunsisf(i: u32) -> f32 { + rom_data::float_funcs::uint_to_float(i) + } + + // Questionable gain + #[bootrom_v2] + #[aeabi = __aeabi_ui2d] + extern "C" fn __floatunsidf(i: u32) -> f64 { + rom_data::double_funcs::uint_to_double(i) + } + + // Questionable gain + #[bootrom_v2] + #[aeabi = __aeabi_ul2f] + extern "C" fn __floatundisf(i: u64) -> f32 { + rom_data::float_funcs::uint64_to_float(i) + } + + #[bootrom_v2] + #[aeabi = __aeabi_ul2d] + extern "C" fn __floatundidf(i: u64) -> f64 { + rom_data::double_funcs::uint64_to_double(i) + } + + + // The Pico SDK does some optimization here (e.x. fast paths for zero and + // one), but we can just directly connect it. + #[aeabi = __aeabi_f2iz] + extern "C" fn __fixsfsi(f: f32) -> i32 { + rom_data::float_funcs::float_to_int(f) + } + + #[bootrom_v2] + #[aeabi = __aeabi_f2lz] + extern "C" fn __fixsfdi(f: f32) -> i64 { + rom_data::float_funcs::float_to_int64(f) + } + + // Not connected in the Pico SDK + #[slower_than_default] + #[bootrom_v2] + #[aeabi = __aeabi_d2iz] + extern "C" fn __fixdfsi(f: f64) -> i32 { + rom_data::double_funcs::double_to_int(f) + } + + // Like with the 32 bit version, there's optimization that we just + // skip. + #[bootrom_v2] + #[aeabi = __aeabi_d2lz] + extern "C" fn __fixdfdi(f: f64) -> i64 { + rom_data::double_funcs::double_to_int64(f) + } + + #[slower_than_default] + #[aeabi = __aeabi_f2uiz] + extern "C" fn __fixunssfsi(f: f32) -> u32 { + rom_data::float_funcs::float_to_uint(f) + } + + #[slower_than_default] + #[bootrom_v2] + #[aeabi = __aeabi_f2ulz] + extern "C" fn __fixunssfdi(f: f32) -> u64 { + rom_data::float_funcs::float_to_uint64(f) + } + + #[slower_than_default] + #[bootrom_v2] + #[aeabi = __aeabi_d2uiz] + extern "C" fn __fixunsdfsi(f: f64) -> u32 { + rom_data::double_funcs::double_to_uint(f) + } + + #[slower_than_default] + #[bootrom_v2] + #[aeabi = __aeabi_d2ulz] + extern "C" fn __fixunsdfdi(f: f64) -> u64 { + rom_data::double_funcs::double_to_uint64(f) + } + + #[bootrom_v2] + #[alias = __extendsfdf2vfp] + #[aeabi = __aeabi_f2d] + extern "C" fn __extendsfdf2(f: f32) -> f64 { + if f.is_not_finite() { + return f64::from_repr( + // Not finite + f64::EXPONENT_MASK | + // Preserve NaN or inf + ((f.repr() & f32::SIGNIFICAND_MASK) as u64) | + // Preserve sign + ((f.repr() & f32::SIGN_MASK) as u64) << (f64::BITS-f32::BITS) + ); + } + rom_data::float_funcs::float_to_double(f) + } + + #[bootrom_v2] + #[alias = __truncdfsf2vfp] + #[aeabi = __aeabi_d2f] + extern "C" fn __truncdfsf2(f: f64) -> f32 { + if f.is_not_finite() { + let mut repr: u32 = + // Not finite + f32::EXPONENT_MASK | + // Preserve sign + ((f.repr() & f64::SIGN_MASK) >> (f64::BITS-f32::BITS)) as u32; + // Set NaN + if (f.repr() & f64::SIGNIFICAND_MASK) != 0 { + repr |= 1; + } + return f32::from_repr(repr); + } + rom_data::double_funcs::double_to_float(f) + } +} diff --git a/embassy-rp/src/float/div.rs b/embassy-rp/src/float/div.rs new file mode 100644 index 000000000..094dec446 --- /dev/null +++ b/embassy-rp/src/float/div.rs @@ -0,0 +1,141 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/conv.rs + +use super::Float; +use crate::rom_data; + +// Make sure this stays as a separate call, because when it's inlined the +// compiler will move the save of the registers used to contain the divider +// state into the function prologue. That save and restore (push/pop) takes +// longer than the actual division, so doing it in the common case where +// they are not required wastes a lot of time. +#[inline(never)] +#[cold] +fn save_divider_and_call(f: F) -> R +where + F: FnOnce() -> R, +{ + let sio = rp_pac::SIO; + + unsafe { + // Since we can't save the signed-ness of the calculation, we have to make + // sure that there's at least an 8 cycle delay before we read the result. + // The Pico SDK ensures this by using a 6 cycle push and two 1 cycle reads. + // Since we can't be sure the Rust implementation will optimize to the same, + // just use an explicit wait. + while !sio.div().csr().read().ready() {} + + // Read the quotient last, since that's what clears the dirty flag + let dividend = sio.div().udividend().read(); + let divisor = sio.div().udivisor().read(); + let remainder = sio.div().remainder().read(); + let quotient = sio.div().quotient().read(); + + // If we get interrupted here (before a write sets the DIRTY flag) its fine, since + // we have the full state, so the interruptor doesn't have to restore it. Once the + // write happens and the DIRTY flag is set, the interruptor becomes responsible for + // restoring our state. + let result = f(); + + // If we are interrupted here, then the interruptor will start an incorrect calculation + // using a wrong divisor, but we'll restore the divisor and result ourselves correctly. + // This sets DIRTY, so any interruptor will save the state. + sio.div().udividend().write_value(dividend); + // If we are interrupted here, the the interruptor may start the calculation using + // incorrectly signed inputs, but we'll restore the result ourselves. + // This sets DIRTY, so any interruptor will save the state. + sio.div().udivisor().write_value(divisor); + // If we are interrupted here, the interruptor will have restored everything but the + // quotient may be wrongly signed. If the calculation started by the above writes is + // still ongoing it is stopped, so it won't replace the result we're restoring. + // DIRTY and READY set, but only DIRTY matters to make the interruptor save the state. + sio.div().remainder().write_value(remainder); + // State fully restored after the quotient write. This sets both DIRTY and READY, so + // whatever we may have interrupted can read the result. + sio.div().quotient().write_value(quotient); + + result + } +} + +fn save_divider(f: F) -> R +where + F: FnOnce() -> R, +{ + let sio = rp_pac::SIO; + if unsafe { !sio.div().csr().read().dirty() } { + // Not dirty, so nothing is waiting for the calculation. So we can just + // issue it directly without a save/restore. + f() + } else { + save_divider_and_call(f) + } +} + +trait ROMDiv { + fn rom_div(self, b: Self) -> Self; +} + +impl ROMDiv for f32 { + fn rom_div(self, b: Self) -> Self { + // ROM implementation uses the hardware divider, so we have to save it + save_divider(|| rom_data::float_funcs::fdiv(self, b)) + } +} + +impl ROMDiv for f64 { + fn rom_div(self, b: Self) -> Self { + // ROM implementation uses the hardware divider, so we have to save it + save_divider(|| rom_data::double_funcs::ddiv(self, b)) + } +} + +fn div(a: F, b: F) -> F { + if a.is_not_finite() { + if b.is_not_finite() { + // inf/NaN / inf/NaN = NaN + return F::NAN; + } + + if b.is_zero() { + // inf/NaN / 0 = NaN + return F::NAN; + } + + return if b.is_sign_negative() { + // [+/-]inf/NaN / (-X) = [-/+]inf/NaN + a.negate() + } else { + // [-]inf/NaN / X = [-]inf/NaN + a + }; + } + + if b.is_nan() { + // X / NaN = NaN + return b; + } + + // ROM handles X / 0 = [-]inf and X / [-]inf = [-]0, so we only + // need to catch 0 / 0 + if b.is_zero() && a.is_zero() { + return F::NAN; + } + + a.rom_div(b) +} + +intrinsics! { + #[alias = __divsf3vfp] + #[aeabi = __aeabi_fdiv] + extern "C" fn __divsf3(a: f32, b: f32) -> f32 { + div(a, b) + } + + #[bootrom_v2] + #[alias = __divdf3vfp] + #[aeabi = __aeabi_ddiv] + extern "C" fn __divdf3(a: f64, b: f64) -> f64 { + div(a, b) + } +} diff --git a/embassy-rp/src/float/functions.rs b/embassy-rp/src/float/functions.rs new file mode 100644 index 000000000..de29ce336 --- /dev/null +++ b/embassy-rp/src/float/functions.rs @@ -0,0 +1,239 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/functions.rs + +use crate::float::{Float, Int}; +use crate::rom_data; + +trait ROMFunctions { + fn sqrt(self) -> Self; + fn ln(self) -> Self; + fn exp(self) -> Self; + fn sin(self) -> Self; + fn cos(self) -> Self; + fn tan(self) -> Self; + fn atan2(self, y: Self) -> Self; + + fn to_trig_range(self) -> Self; +} + +impl ROMFunctions for f32 { + fn sqrt(self) -> Self { + rom_data::float_funcs::fsqrt(self) + } + + fn ln(self) -> Self { + rom_data::float_funcs::fln(self) + } + + fn exp(self) -> Self { + rom_data::float_funcs::fexp(self) + } + + fn sin(self) -> Self { + rom_data::float_funcs::fsin(self) + } + + fn cos(self) -> Self { + rom_data::float_funcs::fcos(self) + } + + fn tan(self) -> Self { + rom_data::float_funcs::ftan(self) + } + + fn atan2(self, y: Self) -> Self { + rom_data::float_funcs::fatan2(self, y) + } + + fn to_trig_range(self) -> Self { + // -128 < X < 128, logic from the Pico SDK + let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS; + if exponent < 134 { + self + } else { + self % (core::f32::consts::PI * 2.0) + } + } +} + +impl ROMFunctions for f64 { + fn sqrt(self) -> Self { + rom_data::double_funcs::dsqrt(self) + } + + fn ln(self) -> Self { + rom_data::double_funcs::dln(self) + } + + fn exp(self) -> Self { + rom_data::double_funcs::dexp(self) + } + + fn sin(self) -> Self { + rom_data::double_funcs::dsin(self) + } + + fn cos(self) -> Self { + rom_data::double_funcs::dcos(self) + } + fn tan(self) -> Self { + rom_data::double_funcs::dtan(self) + } + + fn atan2(self, y: Self) -> Self { + rom_data::double_funcs::datan2(self, y) + } + + fn to_trig_range(self) -> Self { + // -1024 < X < 1024, logic from the Pico SDK + let exponent = (self.repr() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS; + if exponent < 1033 { + self + } else { + self % (core::f64::consts::PI * 2.0) + } + } +} + +fn is_negative_nonzero_or_nan(f: F) -> bool { + let repr = f.repr(); + if (repr & F::SIGN_MASK) != F::Int::ZERO { + // Negative, so anything other than exactly zero + return (repr & (!F::SIGN_MASK)) != F::Int::ZERO; + } + // NaN + (repr & (F::EXPONENT_MASK | F::SIGNIFICAND_MASK)) > F::EXPONENT_MASK +} + +fn sqrt(f: F) -> F { + if is_negative_nonzero_or_nan(f) { + F::NAN + } else { + f.sqrt() + } +} + +fn ln(f: F) -> F { + if is_negative_nonzero_or_nan(f) { + F::NAN + } else { + f.ln() + } +} + +fn exp(f: F) -> F { + if f.is_nan() { + F::NAN + } else { + f.exp() + } +} + +fn sin(f: F) -> F { + if f.is_not_finite() { + F::NAN + } else { + f.to_trig_range().sin() + } +} + +fn cos(f: F) -> F { + if f.is_not_finite() { + F::NAN + } else { + f.to_trig_range().cos() + } +} + +fn tan(f: F) -> F { + if f.is_not_finite() { + F::NAN + } else { + f.to_trig_range().tan() + } +} + +fn atan2(x: F, y: F) -> F { + if x.is_nan() || y.is_nan() { + F::NAN + } else { + x.to_trig_range().atan2(y) + } +} + +// Name collisions +mod intrinsics { + intrinsics! { + extern "C" fn sqrtf(f: f32) -> f32 { + super::sqrt(f) + } + + #[bootrom_v2] + extern "C" fn sqrt(f: f64) -> f64 { + super::sqrt(f) + } + + extern "C" fn logf(f: f32) -> f32 { + super::ln(f) + } + + #[bootrom_v2] + extern "C" fn log(f: f64) -> f64 { + super::ln(f) + } + + extern "C" fn expf(f: f32) -> f32 { + super::exp(f) + } + + #[bootrom_v2] + extern "C" fn exp(f: f64) -> f64 { + super::exp(f) + } + + #[slower_than_default] + extern "C" fn sinf(f: f32) -> f32 { + super::sin(f) + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn sin(f: f64) -> f64 { + super::sin(f) + } + + #[slower_than_default] + extern "C" fn cosf(f: f32) -> f32 { + super::cos(f) + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn cos(f: f64) -> f64 { + super::cos(f) + } + + #[slower_than_default] + extern "C" fn tanf(f: f32) -> f32 { + super::tan(f) + } + + #[slower_than_default] + #[bootrom_v2] + extern "C" fn tan(f: f64) -> f64 { + super::tan(f) + } + + // Questionable gain + #[bootrom_v2] + extern "C" fn atan2f(a: f32, b: f32) -> f32 { + super::atan2(a, b) + } + + // Questionable gain + #[bootrom_v2] + extern "C" fn atan2(a: f64, b: f64) -> f64 { + super::atan2(a, b) + } + } +} diff --git a/embassy-rp/src/float/mod.rs b/embassy-rp/src/float/mod.rs new file mode 100644 index 000000000..945afff90 --- /dev/null +++ b/embassy-rp/src/float/mod.rs @@ -0,0 +1,149 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mod.rs + +use core::ops; + +// Borrowed and simplified from compiler-builtins so we can use bit ops +// on floating point without macro soup. +pub(crate) trait Int: + Copy + + core::fmt::Debug + + PartialEq + + PartialOrd + + ops::AddAssign + + ops::SubAssign + + ops::BitAndAssign + + ops::BitOrAssign + + ops::BitXorAssign + + ops::ShlAssign + + ops::ShrAssign + + ops::Add + + ops::Sub + + ops::Div + + ops::Shl + + ops::Shr + + ops::BitOr + + ops::BitXor + + ops::BitAnd + + ops::Not +{ + const ZERO: Self; +} + +macro_rules! int_impl { + ($ty:ty) => { + impl Int for $ty { + const ZERO: Self = 0; + } + }; +} + +int_impl!(u32); +int_impl!(u64); + +pub(crate) trait Float: + Copy + + core::fmt::Debug + + PartialEq + + PartialOrd + + ops::AddAssign + + ops::MulAssign + + ops::Add + + ops::Sub + + ops::Div + + ops::Rem +{ + /// A uint of the same with as the float + type Int: Int; + + /// NaN representation for the float + const NAN: Self; + + /// The bitwidth of the float type + const BITS: u32; + + /// The bitwidth of the significand + const SIGNIFICAND_BITS: u32; + + /// A mask for the sign bit + const SIGN_MASK: Self::Int; + + /// A mask for the significand + const SIGNIFICAND_MASK: Self::Int; + + /// A mask for the exponent + const EXPONENT_MASK: Self::Int; + + /// Returns `self` transmuted to `Self::Int` + fn repr(self) -> Self::Int; + + /// Returns a `Self::Int` transmuted back to `Self` + fn from_repr(a: Self::Int) -> Self; + + /// Return a sign swapped `self` + fn negate(self) -> Self; + + /// Returns true if `self` is either NaN or infinity + fn is_not_finite(self) -> bool { + (self.repr() & Self::EXPONENT_MASK) == Self::EXPONENT_MASK + } + + /// Returns true if `self` is infinity + fn is_infinity(self) -> bool { + (self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) == Self::EXPONENT_MASK + } + + /// Returns true if `self is NaN + fn is_nan(self) -> bool { + (self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) > Self::EXPONENT_MASK + } + + /// Returns true if `self` is negative + fn is_sign_negative(self) -> bool { + (self.repr() & Self::SIGN_MASK) != Self::Int::ZERO + } + + /// Returns true if `self` is zero (either sign) + fn is_zero(self) -> bool { + (self.repr() & (Self::SIGNIFICAND_MASK | Self::EXPONENT_MASK)) == Self::Int::ZERO + } +} + +macro_rules! float_impl { + ($ty:ident, $ity:ident, $bits:expr, $significand_bits:expr) => { + impl Float for $ty { + type Int = $ity; + + const NAN: Self = <$ty>::NAN; + + const BITS: u32 = $bits; + const SIGNIFICAND_BITS: u32 = $significand_bits; + + const SIGN_MASK: Self::Int = 1 << (Self::BITS - 1); + const SIGNIFICAND_MASK: Self::Int = (1 << Self::SIGNIFICAND_BITS) - 1; + const EXPONENT_MASK: Self::Int = !(Self::SIGN_MASK | Self::SIGNIFICAND_MASK); + + fn repr(self) -> Self::Int { + self.to_bits() + } + + fn from_repr(a: Self::Int) -> Self { + Self::from_bits(a) + } + + fn negate(self) -> Self { + -self + } + } + }; +} + +float_impl!(f32, u32, 32, 23); +float_impl!(f64, u64, 64, 52); + +mod add_sub; +mod cmp; +mod conv; +mod div; +mod functions; +mod mul; diff --git a/embassy-rp/src/float/mul.rs b/embassy-rp/src/float/mul.rs new file mode 100644 index 000000000..ceb0210e3 --- /dev/null +++ b/embassy-rp/src/float/mul.rs @@ -0,0 +1,70 @@ +// Credit: taken from `rp-hal` (also licensed Apache+MIT) +// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/float/mul.rs + +use super::Float; +use crate::rom_data; + +trait ROMMul { + fn rom_mul(self, b: Self) -> Self; +} + +impl ROMMul for f32 { + fn rom_mul(self, b: Self) -> Self { + rom_data::float_funcs::fmul(self, b) + } +} + +impl ROMMul for f64 { + fn rom_mul(self, b: Self) -> Self { + rom_data::double_funcs::dmul(self, b) + } +} + +fn mul(a: F, b: F) -> F { + if a.is_not_finite() { + if b.is_zero() { + // [-]inf/NaN * 0 = NaN + return F::NAN; + } + + return if b.is_sign_negative() { + // [+/-]inf/NaN * (-X) = [-/+]inf/NaN + a.negate() + } else { + // [-]inf/NaN * X = [-]inf/NaN + a + }; + } + + if b.is_not_finite() { + if a.is_zero() { + // 0 * [-]inf/NaN = NaN + return F::NAN; + } + + return if b.is_sign_negative() { + // (-X) * [+/-]inf/NaN = [-/+]inf/NaN + b.negate() + } else { + // X * [-]inf/NaN = [-]inf/NaN + b + }; + } + + a.rom_mul(b) +} + +intrinsics! { + #[alias = __mulsf3vfp] + #[aeabi = __aeabi_fmul] + extern "C" fn __mulsf3(a: f32, b: f32) -> f32 { + mul(a, b) + } + + #[bootrom_v2] + #[alias = __muldf3vfp] + #[aeabi = __aeabi_dmul] + extern "C" fn __muldf3(a: f64, b: f64) -> f64 { + mul(a, b) + } +} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 1d63f6c2e..3841bb83a 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -12,6 +12,7 @@ mod intrinsics; pub mod adc; pub mod dma; +mod float; pub mod gpio; pub mod i2c; pub mod interrupt; diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml index 9611db3a0..e1744c703 100644 --- a/tests/rp/.cargo/config.toml +++ b/tests/rp/.cargo/config.toml @@ -1,6 +1,8 @@ [unstable] -build-std = ["core"] -build-std-features = ["panic_immediate_abort"] +# enabling these breaks the float tests during linking, with intrinsics +# duplicated between embassy-rp and compilter_builtins +#build-std = ["core"] +#build-std-features = ["panic_immediate_abort"] [target.'cfg(all(target_arch = "arm", target_os = "none"))'] #runner = "teleprobe client run --target rpi-pico --elf" diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 36ff735ec..6778f53d7 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } -embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" diff --git a/tests/rp/src/bin/float.rs b/tests/rp/src/bin/float.rs new file mode 100644 index 000000000..6715271e6 --- /dev/null +++ b/tests/rp/src/bin/float.rs @@ -0,0 +1,53 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::pac; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + embassy_rp::init(Default::default()); + info!("Hello World!"); + + const PI_F: f32 = 3.1415926535f32; + const PI_D: f64 = 3.14159265358979323846f64; + + unsafe { + pac::BUSCTRL + .perfsel(0) + .write(|r| r.set_perfsel(pac::busctrl::vals::Perfsel::ROM)); + } + + for i in 0..=360 { + let rad_f = (i as f32) * PI_F / 180.0; + info!( + "{}° float: {=f32} / {=f32} / {=f32} / {=f32}", + i, + rad_f, + rad_f - PI_F, + rad_f + PI_F, + rad_f % PI_F + ); + let rad_d = (i as f64) * PI_D / 180.0; + info!( + "{}° double: {=f64} / {=f64} / {=f64} / {=f64}", + i, + rad_d, + rad_d - PI_D, + rad_d + PI_D, + rad_d % PI_D + ); + Timer::after(Duration::from_millis(10)).await; + } + + let rom_accesses = unsafe { pac::BUSCTRL.perfctr(0).read().perfctr() }; + // every float operation used here uses at least 10 cycles + defmt::assert!(rom_accesses >= 360 * 12 * 10); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From 64b80c2e4d390b16a384e197f1c7ed2a9a6fc946 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 19 Apr 2023 16:16:44 -0500 Subject: [PATCH 45/46] stm32/i2c: ignore wakes without interrupt --- embassy-stm32/src/i2c/v2.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 44237b890..92fc05591 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -485,6 +485,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { poll_fn(|cx| { state.waker.register(cx.waker()); + let isr = unsafe { T::regs().isr().read() }; if remaining_len == total_len { // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers if first_slice { @@ -503,6 +504,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { T::regs().cr1().modify(|w| w.set_tcie(true)); } } + } else if !(isr.tcr() || isr.tc()) { + // poll_fn was woken without an interrupt present + return Poll::Pending; } else if remaining_len == 0 { return Poll::Ready(Ok(())); } else { @@ -575,6 +579,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { poll_fn(|cx| { state.waker.register(cx.waker()); + + let isr = unsafe { T::regs().isr().read() }; if remaining_len == total_len { // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers unsafe { @@ -587,6 +593,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { &check_timeout, )?; } + } else if !(isr.tcr() || isr.tc()) { + // poll_fn was woken without an interrupt present + return Poll::Pending; } else if remaining_len == 0 { return Poll::Ready(Ok(())); } else { From 837cdacd16eaf10c70758f413867c251e68460d0 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 19 Apr 2023 21:56:18 +0200 Subject: [PATCH 46/46] rp: optimize rom-func-cache for runtime storing a full function pointer initialized to a resolver trampoline lets us avoid the runtime cost of checking whether we need to do the initialization. --- embassy-rp/src/rom_data.rs | 117 +++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 62 deletions(-) diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs index 757a27114..805c1f09f 100644 --- a/embassy-rp/src/rom_data.rs +++ b/embassy-rp/src/rom_data.rs @@ -56,50 +56,11 @@ macro_rules! declare_rom_function { 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),*) + declare_rom_function!{ + __internal , + $(#[$outer])* + fn $name( $($argname: $ty),* ) -> $ret + $lookup } }; @@ -107,6 +68,21 @@ macro_rules! declare_rom_function { $(#[$outer:meta])* unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty $lookup:block + ) => { + declare_rom_function!{ + __internal unsafe , + $(#[$outer])* + fn $name( $($argname: $ty),* ) -> $ret + $lookup + } + }; + + ( + __internal + $( $maybe_unsafe:ident )? , + $(#[$outer:meta])* + fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty + $lookup:block ) => { #[doc = r"Additional access for the `"] #[doc = stringify!($name)] @@ -114,43 +90,58 @@ macro_rules! declare_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 { + pub fn ptr() -> $( $maybe_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); + let func : $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret + = core::mem::transmute(p); func } } + #[cfg(feature = "rom-func-cache")] + // unlike rp2040-hal we store a full word, containing the full function pointer. + // rp2040-hal saves two bytes by storing only the rom offset, at the cost of + // having to do an indirection and an atomic operation on every rom call. + static mut CACHE: $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret + = trampoline; + + #[cfg(feature = "rom-func-cache")] + $( $maybe_unsafe )? extern "C" fn trampoline( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{compiler_fence, Ordering}; + + let p: *const u32 = $lookup; + #[allow(unused_unsafe)] + unsafe { + CACHE = core::mem::transmute(p); + compiler_fence(Ordering::Release); + CACHE($($argname),*) + } + } + /// 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}; + pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{compiler_fence, 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, - }; + // + // We easily get away with using only compiler fences here + // because RP2040 SRAM is not cached. If it were we'd need + // to make sure updates propagate quickly, or just take the + // hit and let each core resolve every function once. + compiler_fence(Ordering::Acquire); unsafe { - let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p); - func + CACHE } } } $(#[$outer])* - pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret { + pub $( $maybe_unsafe )? extern "C" fn $name( $($argname: $ty),* ) -> $ret { $name::ptr()($($argname),*) } }; @@ -369,6 +360,7 @@ pub fn fplib_start() -> *const u8 { } /// See Table 180 in the RP2040 datasheet for the contents of this table. +#[cfg_attr(feature = "rom-func-cache", inline(never))] pub fn soft_float_table() -> *const usize { rom_table_lookup(DATA_TABLE, *b"SF") } @@ -379,6 +371,7 @@ pub fn fplib_end() -> *const u8 { } /// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table. +#[cfg_attr(feature = "rom-func-cache", inline(never))] pub fn soft_double_table() -> *const usize { if rom_version_number() < 2 { panic!(