From eeda57a4245019ec1acd13afd9d826679d5ebb5e Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Thu, 12 Sep 2024 11:37:17 -0400 Subject: [PATCH 01/39] rp2350 pio pin fixes Disable pad isolation on any used pin. Use GPIOBASE and offset pin bases if all pins are > 16, panic if some pins are < 16 and some are > 31 --- embassy-rp/src/pio/mod.rs | 120 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index 68b1d6849..0d489763c 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -655,6 +655,10 @@ impl<'d, PIO: Instance> Config<'d, PIO> { /// Set pin used to signal jump. pub fn set_jmp_pin(&mut self, pin: &Pin<'d, PIO>) { + #[cfg(feature = "_rp235x")] + pin.pin.pad_ctrl().modify(|w| { + w.set_iso(false); + }); self.exec.jmp_pin = pin.pin(); } @@ -664,6 +668,12 @@ impl<'d, PIO: Instance> Config<'d, PIO> { pub fn set_set_pins(&mut self, pins: &[&Pin<'d, PIO>]) { assert!(pins.len() <= 5); assert_consecutive(pins); + #[cfg(feature = "_rp235x")] + for pin in pins { + pin.pin.pad_ctrl().modify(|w| { + w.set_iso(false); + }) + } self.pins.set_base = pins.first().map_or(0, |p| p.pin()); self.pins.set_count = pins.len() as u8; } @@ -673,6 +683,12 @@ impl<'d, PIO: Instance> Config<'d, PIO> { /// effective. pub fn set_out_pins(&mut self, pins: &[&Pin<'d, PIO>]) { assert_consecutive(pins); + #[cfg(feature = "_rp235x")] + for pin in pins { + pin.pin.pad_ctrl().modify(|w| { + w.set_iso(false); + }) + } self.pins.out_base = pins.first().map_or(0, |p| p.pin()); self.pins.out_count = pins.len() as u8; } @@ -682,6 +698,12 @@ impl<'d, PIO: Instance> Config<'d, PIO> { /// effective. pub fn set_in_pins(&mut self, pins: &[&Pin<'d, PIO>]) { assert_consecutive(pins); + #[cfg(feature = "_rp235x")] + for pin in pins { + pin.pin.pad_ctrl().modify(|w| { + w.set_iso(false); + }) + } self.pins.in_base = pins.first().map_or(0, |p| p.pin()); self.in_count = pins.len() as u8; } @@ -731,6 +753,8 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { w.set_autopull(config.shift_out.auto_fill); w.set_autopush(config.shift_in.auto_fill); }); + + #[cfg(feature = "rp2040")] sm.pinctrl().write(|w| { w.set_sideset_count(config.pins.sideset_count); w.set_set_count(config.pins.set_count); @@ -740,6 +764,102 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { w.set_set_base(config.pins.set_base); w.set_out_base(config.pins.out_base); }); + + #[cfg(feature = "_rp235x")] + { + let shift_gpio_base = { + match ( + if config.in_count > 0 { + Some(config.pins.in_base) + } else { + None + }, + if config.pins.sideset_count > 0 { + Some(config.pins.sideset_base) + } else { + None + }, + if config.pins.set_count > 0 { + Some(config.pins.set_base) + } else { + None + }, + if config.pins.out_count > 0 { + Some(config.pins.out_base) + } else { + None + }, + ) { + (None, None, None, None) => false, + + (Some(0..31), None, None, None) => false, + (None, Some(0..31), None, None) => false, + (None, None, Some(0..31), None) => false, + (None, None, None, Some(0..31)) => false, + + (Some(0..31), Some(0..31), None, None) => false, + (None, Some(0..31), Some(0..31), None) => false, + (None, None, Some(0..31), Some(0..31)) => false, + (Some(0..31), None, None, Some(0..31)) => false, + + (None, Some(0..31), Some(0..31), Some(0..31)) => false, + (Some(0..31), None, Some(0..31), Some(0..31)) => false, + (Some(0..31), Some(0..31), None, Some(0..31)) => false, + (Some(0..31), Some(0..31), Some(0..31), None) => false, + + (Some(0..31), Some(0..31), Some(0..31), Some(0..31)) => false, + + (Some(16..48), None, None, None) => true, + (None, Some(16..48), None, None) => true, + (None, None, Some(16..48), None) => true, + (None, None, None, Some(16..48)) => true, + + (Some(16..48), Some(16..48), None, None) => true, + (None, Some(16..48), Some(16..48), None) => true, + (None, None, Some(16..48), Some(16..48)) => true, + (Some(16..48), None, None, Some(16..48)) => true, + + (None, Some(16..48), Some(16..48), Some(16..48)) => true, + (Some(16..48), None, Some(16..48), Some(16..48)) => true, + (Some(16..48), Some(16..48), None, Some(16..48)) => true, + (Some(16..48), Some(16..48), Some(16..48), None) => true, + + (Some(16..48), Some(16..48), Some(16..48), Some(16..48)) => true, + + (i, side, set, out) => panic!( + "All pins must either be < 31 or >16, in:{}, side:{}, set:{}, out:{}", + i, side, set, out + ), + } + }; + + info!("shift: {}", shift_gpio_base); + + if shift_gpio_base { + sm.pinctrl().write(|w| { + w.set_sideset_count(config.pins.sideset_count); + w.set_set_count(config.pins.set_count); + w.set_out_count(config.pins.out_count); + w.set_in_base(config.pins.in_base - 16); + w.set_sideset_base(config.pins.sideset_base - 16); + w.set_set_base(config.pins.set_base - 16); + w.set_out_base(config.pins.out_base - 16); + }); + } else { + sm.pinctrl().write(|w| { + w.set_sideset_count(config.pins.sideset_count); + w.set_set_count(config.pins.set_count); + w.set_out_count(config.pins.out_count); + w.set_in_base(config.pins.in_base); + w.set_sideset_base(config.pins.sideset_base); + w.set_set_base(config.pins.set_base); + w.set_out_base(config.pins.out_base); + }); + } + + PIO::PIO.gpiobase().write(|w| w.set_gpiobase(shift_gpio_base)); + } + if let Some(origin) = config.origin { unsafe { instr::exec_jmp(self, origin) } } From 823a82adb7206435794c769be699e1b972d6f966 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Thu, 12 Sep 2024 11:43:15 -0400 Subject: [PATCH 02/39] Fixup formatting to work with log --- embassy-rp/src/pio/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index 0d489763c..ec29a690c 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -827,7 +827,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { (Some(16..48), Some(16..48), Some(16..48), Some(16..48)) => true, (i, side, set, out) => panic!( - "All pins must either be < 31 or >16, in:{}, side:{}, set:{}, out:{}", + "All pins must either be < 31 or >16, in:{:?}, side:{:?}, set:{:?}, out:{:?}", i, side, set, out ), } From e359b24121d9747ea8260d8887829b6a97c312f1 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Thu, 12 Sep 2024 11:50:36 -0400 Subject: [PATCH 03/39] remove debug print --- embassy-rp/src/pio/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index ec29a690c..2b1ed554e 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -833,8 +833,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } }; - info!("shift: {}", shift_gpio_base); - if shift_gpio_base { sm.pinctrl().write(|w| { w.set_sideset_count(config.pins.sideset_count); From ed6cbc7a3a1082b2063162cbf30fb6f9979d81ad Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Sun, 15 Sep 2024 20:09:18 -0400 Subject: [PATCH 04/39] Fix upper bound of case where pins should be <=31 --- embassy-rp/src/pio/mod.rs | 54 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index 2b1ed554e..e8e411a25 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -792,42 +792,42 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { ) { (None, None, None, None) => false, - (Some(0..31), None, None, None) => false, - (None, Some(0..31), None, None) => false, - (None, None, Some(0..31), None) => false, - (None, None, None, Some(0..31)) => false, + (Some(..32), None, None, None) => false, + (None, Some(..32), None, None) => false, + (None, None, Some(..32), None) => false, + (None, None, None, Some(..32)) => false, - (Some(0..31), Some(0..31), None, None) => false, - (None, Some(0..31), Some(0..31), None) => false, - (None, None, Some(0..31), Some(0..31)) => false, - (Some(0..31), None, None, Some(0..31)) => false, + (Some(..32), Some(..32), None, None) => false, + (None, Some(..32), Some(..32), None) => false, + (None, None, Some(..32), Some(..32)) => false, + (Some(..32), None, None, Some(..32)) => false, - (None, Some(0..31), Some(0..31), Some(0..31)) => false, - (Some(0..31), None, Some(0..31), Some(0..31)) => false, - (Some(0..31), Some(0..31), None, Some(0..31)) => false, - (Some(0..31), Some(0..31), Some(0..31), None) => false, + (None, Some(..32), Some(..32), Some(..32)) => false, + (Some(..32), None, Some(..32), Some(..32)) => false, + (Some(..32), Some(..32), None, Some(..32)) => false, + (Some(..32), Some(..32), Some(..32), None) => false, - (Some(0..31), Some(0..31), Some(0..31), Some(0..31)) => false, + (Some(..32), Some(..32), Some(..32), Some(..32)) => false, - (Some(16..48), None, None, None) => true, - (None, Some(16..48), None, None) => true, - (None, None, Some(16..48), None) => true, - (None, None, None, Some(16..48)) => true, + (Some(16..), None, None, None) => true, + (None, Some(16..), None, None) => true, + (None, None, Some(16..), None) => true, + (None, None, None, Some(16..)) => true, - (Some(16..48), Some(16..48), None, None) => true, - (None, Some(16..48), Some(16..48), None) => true, - (None, None, Some(16..48), Some(16..48)) => true, - (Some(16..48), None, None, Some(16..48)) => true, + (Some(16..), Some(16..), None, None) => true, + (None, Some(16..), Some(16..), None) => true, + (None, None, Some(16..), Some(16..)) => true, + (Some(16..), None, None, Some(16..)) => true, - (None, Some(16..48), Some(16..48), Some(16..48)) => true, - (Some(16..48), None, Some(16..48), Some(16..48)) => true, - (Some(16..48), Some(16..48), None, Some(16..48)) => true, - (Some(16..48), Some(16..48), Some(16..48), None) => true, + (None, Some(16..), Some(16..), Some(16..)) => true, + (Some(16..), None, Some(16..), Some(16..)) => true, + (Some(16..), Some(16..), None, Some(16..)) => true, + (Some(16..), Some(16..), Some(16..), None) => true, - (Some(16..48), Some(16..48), Some(16..48), Some(16..48)) => true, + (Some(16..), Some(16..), Some(16..), Some(16..)) => true, (i, side, set, out) => panic!( - "All pins must either be < 31 or >16, in:{:?}, side:{:?}, set:{:?}, out:{:?}", + "All pins must either be <=31 or >=16, in:{:?}, side:{:?}, set:{:?}, out:{:?}", i, side, set, out ), } From 55c3da5a4f48ea6c66371484c83d6298cde3befe Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Sun, 15 Sep 2024 20:48:54 -0400 Subject: [PATCH 05/39] Properly drop pins >30 --- embassy-rp/src/gpio.rs | 4 ++-- embassy-rp/src/pio/mod.rs | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 31397172c..520043b07 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -16,9 +16,9 @@ use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; const NEW_AW: AtomicWaker = AtomicWaker::new(); #[cfg(any(feature = "rp2040", feature = "rp235xa"))] -const BANK0_PIN_COUNT: usize = 30; +pub(crate) const BANK0_PIN_COUNT: usize = 30; #[cfg(feature = "rp235xb")] -const BANK0_PIN_COUNT: usize = 48; +pub(crate) const BANK0_PIN_COUNT: usize = 48; static BANK0_WAKERS: [AtomicWaker; BANK0_PIN_COUNT] = [NEW_AW; BANK0_PIN_COUNT]; #[cfg(feature = "qspi-as-gpio")] diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index e8e411a25..fc940b045 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -5,7 +5,7 @@ use core::pin::Pin as FuturePin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; -use atomic_polyfill::{AtomicU32, AtomicU8}; +use atomic_polyfill::{AtomicU64, AtomicU8}; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::extra::U8; @@ -1305,7 +1305,7 @@ impl<'d, PIO: Instance> Pio<'d, PIO> { // other way. pub struct State { users: AtomicU8, - used_pins: AtomicU32, + used_pins: AtomicU64, } fn on_pio_drop() { @@ -1313,8 +1313,7 @@ fn on_pio_drop() { if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { let used_pins = state.used_pins.load(Ordering::Relaxed); let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _; - // we only have 30 pins. don't test the other two since gpio() asserts. - for i in 0..30 { + for i in 0..crate::gpio::BANK0_PIN_COUNT { if used_pins & (1 << i) != 0 { pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null)); } @@ -1339,7 +1338,7 @@ trait SealedInstance { fn state() -> &'static State { static STATE: State = State { users: AtomicU8::new(0), - used_pins: AtomicU32::new(0), + used_pins: AtomicU64::new(0), }; &STATE From 48fd0550d1569a44a6b58a41423b0f86270fb373 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Mon, 16 Sep 2024 12:41:12 -0400 Subject: [PATCH 06/39] Review fixes Don't overflow on subtract Replace giant match with for loop dedupe register write --- embassy-rp/src/pio/mod.rs | 130 +++++++++++++------------------------- 1 file changed, 43 insertions(+), 87 deletions(-) diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index fc940b045..ace597f00 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -767,95 +767,51 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { #[cfg(feature = "_rp235x")] { - let shift_gpio_base = { - match ( - if config.in_count > 0 { - Some(config.pins.in_base) - } else { - None - }, - if config.pins.sideset_count > 0 { - Some(config.pins.sideset_base) - } else { - None - }, - if config.pins.set_count > 0 { - Some(config.pins.set_base) - } else { - None - }, - if config.pins.out_count > 0 { - Some(config.pins.out_base) - } else { - None - }, - ) { - (None, None, None, None) => false, - - (Some(..32), None, None, None) => false, - (None, Some(..32), None, None) => false, - (None, None, Some(..32), None) => false, - (None, None, None, Some(..32)) => false, - - (Some(..32), Some(..32), None, None) => false, - (None, Some(..32), Some(..32), None) => false, - (None, None, Some(..32), Some(..32)) => false, - (Some(..32), None, None, Some(..32)) => false, - - (None, Some(..32), Some(..32), Some(..32)) => false, - (Some(..32), None, Some(..32), Some(..32)) => false, - (Some(..32), Some(..32), None, Some(..32)) => false, - (Some(..32), Some(..32), Some(..32), None) => false, - - (Some(..32), Some(..32), Some(..32), Some(..32)) => false, - - (Some(16..), None, None, None) => true, - (None, Some(16..), None, None) => true, - (None, None, Some(16..), None) => true, - (None, None, None, Some(16..)) => true, - - (Some(16..), Some(16..), None, None) => true, - (None, Some(16..), Some(16..), None) => true, - (None, None, Some(16..), Some(16..)) => true, - (Some(16..), None, None, Some(16..)) => true, - - (None, Some(16..), Some(16..), Some(16..)) => true, - (Some(16..), None, Some(16..), Some(16..)) => true, - (Some(16..), Some(16..), None, Some(16..)) => true, - (Some(16..), Some(16..), Some(16..), None) => true, - - (Some(16..), Some(16..), Some(16..), Some(16..)) => true, - - (i, side, set, out) => panic!( - "All pins must either be <=31 or >=16, in:{:?}, side:{:?}, set:{:?}, out:{:?}", - i, side, set, out - ), - } - }; - - if shift_gpio_base { - sm.pinctrl().write(|w| { - w.set_sideset_count(config.pins.sideset_count); - w.set_set_count(config.pins.set_count); - w.set_out_count(config.pins.out_count); - w.set_in_base(config.pins.in_base - 16); - w.set_sideset_base(config.pins.sideset_base - 16); - w.set_set_base(config.pins.set_base - 16); - w.set_out_base(config.pins.out_base - 16); - }); - } else { - sm.pinctrl().write(|w| { - w.set_sideset_count(config.pins.sideset_count); - w.set_set_count(config.pins.set_count); - w.set_out_count(config.pins.out_count); - w.set_in_base(config.pins.in_base); - w.set_sideset_base(config.pins.sideset_base); - w.set_set_base(config.pins.set_base); - w.set_out_base(config.pins.out_base); - }); + let mut low_ok = true; + let mut high_ok = true; + for pin in [ + config.pins.in_base, + config.pins.in_base + config.in_count, + config.pins.sideset_base, + config.pins.sideset_base + config.pins.sideset_count, + config.pins.set_base, + config.pins.set_base + config.pins.set_count, + config.pins.out_base, + config.pins.out_base + config.pins.out_count, + ] + .iter() + .flatten() + { + low_ok &= pin < 32; + high_ok &= pin >= 16; } - PIO::PIO.gpiobase().write(|w| w.set_gpiobase(shift_gpio_base)); + if !low_ok && !high_ok { + panic!( + "All pins must either be <32 or >=16, in:{:?}-{:?}, side:{:?}-{:?}, set:{:?}-{:?}, out:{:?}-{:?}", + config.pins.in_base, + config.pins.in_base + config.in_count, + config.pins.sideset_base, + config.pins.sideset_base + config.pins.sideset_count, + config.pins.set_base, + config.pins.set_base + config.pins.set_count, + config.pins.out_base, + config.pins.out_base + config.pins.out_count, + ) + } + let shift = if low_ok { 0 } else { 16 }; + + sm.pinctrl().write(|w| { + w.set_sideset_count(config.pins.sideset_count); + w.set_set_count(config.pins.set_count); + w.set_out_count(config.pins.out_count); + w.set_in_base(config.pins.in_base.checked_sub(shift).unwrap_or_default()); + w.set_sideset_base(config.pins.sideset_base.checked_sub(shift).unwrap_or_default()); + w.set_set_base(config.pins.set_base.checked_sub(shift).unwrap_or_default()); + w.set_out_base(config.pins.out_base.checked_sub(shift).unwrap_or_default()); + }); + + PIO::PIO.gpiobase().write(|w| w.set_gpiobase(shift == 16)); } if let Some(origin) = config.origin { From 9cfde66446ca5451a6cab80e0f2f783199fbeb62 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Mon, 16 Sep 2024 12:47:07 -0400 Subject: [PATCH 07/39] Move pin isolation config to make_pio_pin --- embassy-rp/src/pio/mod.rs | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index ace597f00..b08f2df6b 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -655,10 +655,6 @@ impl<'d, PIO: Instance> Config<'d, PIO> { /// Set pin used to signal jump. pub fn set_jmp_pin(&mut self, pin: &Pin<'d, PIO>) { - #[cfg(feature = "_rp235x")] - pin.pin.pad_ctrl().modify(|w| { - w.set_iso(false); - }); self.exec.jmp_pin = pin.pin(); } @@ -668,12 +664,6 @@ impl<'d, PIO: Instance> Config<'d, PIO> { pub fn set_set_pins(&mut self, pins: &[&Pin<'d, PIO>]) { assert!(pins.len() <= 5); assert_consecutive(pins); - #[cfg(feature = "_rp235x")] - for pin in pins { - pin.pin.pad_ctrl().modify(|w| { - w.set_iso(false); - }) - } self.pins.set_base = pins.first().map_or(0, |p| p.pin()); self.pins.set_count = pins.len() as u8; } @@ -683,12 +673,6 @@ impl<'d, PIO: Instance> Config<'d, PIO> { /// effective. pub fn set_out_pins(&mut self, pins: &[&Pin<'d, PIO>]) { assert_consecutive(pins); - #[cfg(feature = "_rp235x")] - for pin in pins { - pin.pin.pad_ctrl().modify(|w| { - w.set_iso(false); - }) - } self.pins.out_base = pins.first().map_or(0, |p| p.pin()); self.pins.out_count = pins.len() as u8; } @@ -698,12 +682,6 @@ impl<'d, PIO: Instance> Config<'d, PIO> { /// effective. pub fn set_in_pins(&mut self, pins: &[&Pin<'d, PIO>]) { assert_consecutive(pins); - #[cfg(feature = "_rp235x")] - for pin in pins { - pin.pin.pad_ctrl().modify(|w| { - w.set_iso(false); - }) - } self.pins.in_base = pins.first().map_or(0, |p| p.pin()); self.in_count = pins.len() as u8; } @@ -778,10 +756,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { config.pins.set_base + config.pins.set_count, config.pins.out_base, config.pins.out_base + config.pins.out_count, - ] - .iter() - .flatten() - { + ] { low_ok &= pin < 32; high_ok &= pin >= 16; } @@ -1080,6 +1055,10 @@ impl<'d, PIO: Instance> Common<'d, PIO> { pub fn make_pio_pin(&mut self, pin: impl Peripheral

+ 'd) -> Pin<'d, PIO> { into_ref!(pin); pin.gpio().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _)); + #[cfg(feature = "_rp235x")] + pin.pad_ctrl().modify(|w| { + w.set_iso(false); + }); // we can be relaxed about this because we're &mut here and nothing is cached PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); Pin { From 6e0c3e25fd8e07ce3ee46e0ba74064a4c4574e16 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Mon, 16 Sep 2024 12:58:23 -0400 Subject: [PATCH 08/39] Only check ping groups that have count >0 --- embassy-rp/src/pio/mod.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index b08f2df6b..29a5bfab3 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -743,22 +743,21 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { w.set_out_base(config.pins.out_base); }); - #[cfg(feature = "_rp235x")] + //#[cfg(feature = "_rp235x")] { let mut low_ok = true; let mut high_ok = true; - for pin in [ - config.pins.in_base, - config.pins.in_base + config.in_count, - config.pins.sideset_base, - config.pins.sideset_base + config.pins.sideset_count, - config.pins.set_base, - config.pins.set_base + config.pins.set_count, - config.pins.out_base, - config.pins.out_base + config.pins.out_count, - ] { - low_ok &= pin < 32; - high_ok &= pin >= 16; + + let in_pins = config.pins.in_base..config.pins.in_base + config.in_count; + let side_pins = config.pins.sideset_base..config.pins.sideset_base + config.pins.sideset_count; + let set_pins = config.pins.set_base..config.pins.set_base + config.pins.set_count; + let out_pins = config.pins.out_base..config.pins.out_base + config.pins.out_count; + + for pin_range in [in_pins, side_pins, set_pins, out_pins] { + for pin in pin_range { + low_ok &= pin < 32; + high_ok &= pin >= 16; + } } if !low_ok && !high_ok { From 8b34c94ef3640b2ec28b3de3c5d90db40b0b0ecd Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Mon, 16 Sep 2024 13:02:35 -0400 Subject: [PATCH 09/39] Improve error message when pin groups are not allowed --- embassy-rp/src/pio/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index 29a5bfab3..b2f9abaa0 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -764,13 +764,13 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { panic!( "All pins must either be <32 or >=16, in:{:?}-{:?}, side:{:?}-{:?}, set:{:?}-{:?}, out:{:?}-{:?}", config.pins.in_base, - config.pins.in_base + config.in_count, + config.pins.in_base + config.in_count - 1, config.pins.sideset_base, - config.pins.sideset_base + config.pins.sideset_count, + config.pins.sideset_base + config.pins.sideset_count - 1, config.pins.set_base, - config.pins.set_base + config.pins.set_count, + config.pins.set_base + config.pins.set_count - 1, config.pins.out_base, - config.pins.out_base + config.pins.out_count, + config.pins.out_base + config.pins.out_count - 1, ) } let shift = if low_ok { 0 } else { 16 }; From d1508cc49c9ac029ececb1ae5e4c326ac678d1ec Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Mon, 16 Sep 2024 13:07:16 -0400 Subject: [PATCH 10/39] oops --- embassy-rp/src/pio/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index b2f9abaa0..72aa8f104 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -743,7 +743,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { w.set_out_base(config.pins.out_base); }); - //#[cfg(feature = "_rp235x")] + #[cfg(feature = "_rp235x")] { let mut low_ok = true; let mut high_ok = true; From 2855e65cc6ac5a3a102f3a309b4c450efffe1417 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Mon, 16 Sep 2024 13:24:47 -0400 Subject: [PATCH 11/39] Disable pad isolation on clock in/out pins --- embassy-rp/src/clocks.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index ed146844c..f229a5acd 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -847,6 +847,10 @@ impl<'d, T: GpinPin> Gpin<'d, T> { into_ref!(gpin); gpin.gpio().ctrl().write(|w| w.set_funcsel(0x08)); + #[cfg(feature = "_rp235x")] + gpin.pad_ctrl().write(|w| { + w.set_iso(false); + }); Gpin { gpin: gpin.map_into(), @@ -861,6 +865,7 @@ impl<'d, T: GpinPin> Gpin<'d, T> { impl<'d, T: GpinPin> Drop for Gpin<'d, T> { fn drop(&mut self) { + self.gpin.pad_ctrl().write(|_| {}); self.gpin .gpio() .ctrl() @@ -921,11 +926,15 @@ pub struct Gpout<'d, T: GpoutPin> { } impl<'d, T: GpoutPin> Gpout<'d, T> { - /// Create new general purpose cloud output. + /// Create new general purpose clock output. pub fn new(gpout: impl Peripheral

+ 'd) -> Self { into_ref!(gpout); gpout.gpio().ctrl().write(|w| w.set_funcsel(0x08)); + #[cfg(feature = "_rp235x")] + gpout.pad_ctrl().write(|w| { + w.set_iso(false); + }); Self { gpout } } @@ -1005,6 +1014,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { impl<'d, T: GpoutPin> Drop for Gpout<'d, T> { fn drop(&mut self) { self.disable(); + self.gpout.pad_ctrl().write(|_| {}); self.gpout .gpio() .ctrl() From 0bfc98a3e526075ad14517589e4879d14f50ad12 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 17 Sep 2024 19:41:58 +0200 Subject: [PATCH 12/39] rp: Add PIO example for one-wire temperature sensor --- examples/rp/src/bin/pio_onewire.rs | 155 +++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 examples/rp/src/bin/pio_onewire.rs diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs new file mode 100644 index 000000000..5076101ec --- /dev/null +++ b/examples/rp/src/bin/pio_onewire.rs @@ -0,0 +1,155 @@ +//! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor. + +#![no_std] +#![no_main] +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{self, Common, Config, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut pio = Pio::new(p.PIO0, Irqs); + let mut sensor = Ds18b20::new(&mut pio.common, pio.sm0, p.PIN_2); + + loop { + sensor.start().await; // Start a new measurement + Timer::after_secs(1).await; // Allow 1s for the measurement to finish + match sensor.temperature().await { + Ok(temp) => info!("temp = {:?} deg C", temp), + _ => error!("sensor error"), + } + Timer::after_secs(1).await; + } +} + +/// DS18B20 temperature sensor driver +pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { + sm: StateMachine<'d, PIO, SM>, +} + +impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { + /// Create a new instance the driver + pub fn new(common: &mut Common<'d, PIO>, mut sm: StateMachine<'d, PIO, SM>, pin: impl PioPin) -> Self { + let prg = pio_proc::pio_asm!( + r#" + .wrap_target + again: + pull block + mov x, osr + jmp !x, read + write: + set pindirs, 1 + set pins, 0 + loop1: + jmp x--,loop1 + set pindirs, 0 [31] + wait 1 pin 0 [31] + pull block + mov x, osr + bytes1: + pull block + set y, 7 + set pindirs, 1 + bit1: + set pins, 0 [1] + out pins,1 [31] + set pins, 1 [20] + jmp y--,bit1 + jmp x--,bytes1 + set pindirs, 0 [31] + jmp again + read: + pull block + mov x, osr + bytes2: + set y, 7 + bit2: + set pindirs, 1 + set pins, 0 [1] + set pindirs, 0 [5] + in pins,1 [10] + jmp y--,bit2 + jmp x--,bytes2 + .wrap + "#, + ); + + let pin = common.make_pio_pin(pin); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&prg.program), &[]); + cfg.set_out_pins(&[&pin]); + cfg.set_in_pins(&[&pin]); + cfg.set_set_pins(&[&pin]); + cfg.shift_in = ShiftConfig { + auto_fill: true, + direction: ShiftDirection::Right, + threshold: 8, + }; + cfg.clock_divider = 255_u8.into(); + sm.set_config(&cfg); + sm.set_enable(true); + Self { sm } + } + + /// Write bytes over the wire + async fn write_bytes(&mut self, bytes: &[u8]) { + self.sm.tx().wait_push(250).await; + self.sm.tx().wait_push(bytes.len() as u32 - 1).await; + for b in bytes { + self.sm.tx().wait_push(*b as u32).await; + } + } + + /// Read bytes from the wire + async fn read_bytes(&mut self, bytes: &mut [u8]) { + self.sm.tx().wait_push(0).await; + self.sm.tx().wait_push(bytes.len() as u32 - 1).await; + for b in bytes.iter_mut() { + *b = (self.sm.rx().wait_pull().await >> 24) as u8; + } + } + + /// Calculate CRC8 of the data + fn crc8(data: &[u8]) -> u8 { + let mut temp; + let mut data_byte; + let mut crc = 0; + for b in data { + data_byte = *b; + for _ in 0..8 { + temp = (crc ^ data_byte) & 0x01; + crc >>= 1; + if temp != 0 { + crc ^= 0x8C; + } + data_byte >>= 1; + } + } + crc + } + + /// Start a new measurement. Allow at least 1000ms before getting `temperature`. + pub async fn start(&mut self) { + self.write_bytes(&[0xCC, 0x44]).await; + } + + /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. + pub async fn temperature(&mut self) -> Result { + self.write_bytes(&[0xCC, 0xBE]).await; + let mut data = [0; 9]; + self.read_bytes(&mut data).await; + match Self::crc8(&data) == 0 { + true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), + false => Err(()), + } + } +} From 2bc49763c6c36026feeaf681078d06106b73e0b0 Mon Sep 17 00:00:00 2001 From: Ugljesa Jovanovic Date: Sat, 14 Sep 2024 14:57:10 +0200 Subject: [PATCH 13/39] TRNG support for 235x --- embassy-rp/src/lib.rs | 4 + embassy-rp/src/trng.rs | 405 ++++++++++++++++++++++++++++++++++ examples/rp23/src/bin/trng.rs | 64 ++++++ 3 files changed, 473 insertions(+) create mode 100644 embassy-rp/src/trng.rs create mode 100644 examples/rp23/src/bin/trng.rs diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index c357c14c2..d402cf793 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -42,6 +42,8 @@ pub mod rtc; pub mod spi; #[cfg(feature = "time-driver")] pub mod time_driver; +#[cfg(feature = "_rp235x")] +pub mod trng; pub mod uart; pub mod usb; pub mod watchdog; @@ -402,6 +404,8 @@ embassy_hal_internal::peripherals! { WATCHDOG, BOOTSEL, + + TRNG } #[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))] diff --git a/embassy-rp/src/trng.rs b/embassy-rp/src/trng.rs new file mode 100644 index 000000000..9f2f33c4b --- /dev/null +++ b/embassy-rp/src/trng.rs @@ -0,0 +1,405 @@ +//! True Random Number Generator (TRNG) driver. + +use core::future::poll_fn; +use core::marker::PhantomData; +use core::ops::Not; +use core::task::Poll; + +use embassy_hal_internal::Peripheral; +use embassy_sync::waitqueue::AtomicWaker; +use rand_core::Error; + +use crate::interrupt::typelevel::{Binding, Interrupt}; +use crate::peripherals::TRNG; +use crate::{interrupt, pac}; + +trait SealedInstance { + fn regs() -> pac::trng::Trng; + fn waker() -> &'static AtomicWaker; +} + +/// TRNG peripheral instance. +#[allow(private_bounds)] +pub trait Instance: SealedInstance { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +impl SealedInstance for TRNG { + fn regs() -> rp_pac::trng::Trng { + pac::TRNG + } + + fn waker() -> &'static AtomicWaker { + static WAKER: AtomicWaker = AtomicWaker::new(); + &WAKER + } +} + +impl Instance for TRNG { + type Interrupt = interrupt::typelevel::TRNG_IRQ; +} + +#[derive(Copy, Clone, Debug)] +#[allow(missing_docs)] +/// TRNG ROSC Inverter chain length options. +pub enum InverterChainLength { + None = 0, + One, + Two, + Three, + Four, +} + +impl From for u8 { + fn from(value: InverterChainLength) -> Self { + value as u8 + } +} + +/// Configuration for the TRNG. +/// +/// - Three built in entropy checks +/// - ROSC frequency controlled by selecting one of ROSC chain lengths +/// - Sample period in terms of system clock ticks +/// +/// +/// Default configuration is based on the following from documentation: +/// +/// ---- +/// +/// RP2350 Datasheet 12.12.2 +/// +/// ... +/// +/// When configuring the TRNG block, consider the following principles: +/// • As average generation time increases, result quality increases and failed entropy checks decrease. +/// • A low sample count decreases average generation time, but increases the chance of NIST test-failing results and +/// failed entropy checks. +/// For acceptable results with an average generation time of about 2 milliseconds, use ROSC chain length settings of 0 or +/// 1 and sample count settings of 20-25. +/// +/// --- +/// +/// Note, Pico SDK and Bootrom don't use any of the entropy checks and sample the ROSC directly +/// by setting the sample period to 0. Random data collected this way is then passed through +/// either hardware accelerated SHA256 (Bootrom) or xoroshiro128** (version 1.0!). +#[non_exhaustive] +#[derive(Copy, Clone, Debug)] +pub struct Config { + /// Bypass TRNG autocorrelation test + pub disable_autocorrelation_test: bool, + /// Bypass CRNGT test + pub disable_crngt_test: bool, + /// When set, the Von-Neuman balancer is bypassed (including the + /// 32 consecutive bits test) + pub disable_von_neumann_balancer: bool, + /// Sets the number of rng_clk cycles between two consecutive + /// ring oscillator samples. + /// Note: If the von Neumann decorrelator is bypassed, the minimum value for + /// sample counter must not be less than seventeen + pub sample_count: u32, + /// Selects the number of inverters (out of four possible + /// selections) in the ring oscillator (the entropy source). Higher values select + /// longer inverter chain lengths. + pub inverter_chain_length: InverterChainLength, +} + +impl Default for Config { + fn default() -> Self { + Config { + disable_autocorrelation_test: true, + disable_crngt_test: true, + disable_von_neumann_balancer: true, + sample_count: 25, + inverter_chain_length: InverterChainLength::One, + } + } +} + +/// True Random Number Generator Driver for RP2350 +/// +/// This driver provides async and blocking options. +/// +/// See [Config] for configuration details. +/// +/// Usage example: +/// ```no_run +/// use embassy_executor::Spawner; +/// use embassy_rp::trng::Trng; +/// use embassy_rp::peripherals::TRNG; +/// use embassy_rp::bind_interrupts; +/// +/// bind_interrupts!(struct Irqs { +/// TRNG_IRQ => embassy_rp::trng::InterruptHandler; +/// }); +/// +/// #[embassy_executor::main] +/// async fn main(spawner: Spawner) { +/// let peripherals = embassy_rp::init(Default::default()); +/// let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default()); +/// +/// let mut randomness = [0u8; 58]; +/// loop { +/// trng.fill_bytes(&mut randomness).await; +/// assert_ne!(randomness, [0u8; 58]); +/// } +///} +/// ``` +pub struct Trng<'d, T: Instance> { + phantom: PhantomData<&'d mut T>, +} + +/// 12.12.1. Overview +/// On request, the TRNG block generates a block of 192 entropy bits generated by automatically processing a series of +/// periodic samples from the TRNG block’s internal Ring Oscillator (ROSC). +const TRNG_BLOCK_SIZE_BITS: usize = 192; +const TRNG_BLOCK_SIZE_BYTES: usize = TRNG_BLOCK_SIZE_BITS / 8; + +impl<'d, T: Instance> Trng<'d, T> { + /// Create a new TRNG driver. + pub fn new( + _trng: impl Peripheral

+ 'd, + _irq: impl Binding> + 'd, + config: Config, + ) -> Self { + let regs = T::regs(); + + regs.rng_imr().write(|w| w.set_ehr_valid_int_mask(false)); + + let trng_config_register = regs.trng_config(); + trng_config_register.write(|w| { + w.set_rnd_src_sel(config.inverter_chain_length.clone().into()); + }); + + let sample_count_register = regs.sample_cnt1(); + sample_count_register.write(|w| { + *w = config.sample_count; + }); + + let debug_control_register = regs.trng_debug_control(); + debug_control_register.write(|w| { + w.set_auto_correlate_bypass(config.disable_autocorrelation_test); + w.set_trng_crngt_bypass(config.disable_crngt_test); + w.set_vnc_bypass(config.disable_von_neumann_balancer) + }); + + Trng { phantom: PhantomData } + } + + fn start_rng(&self) { + let regs = T::regs(); + let source_enable_register = regs.rnd_source_enable(); + // Enable TRNG ROSC + source_enable_register.write(|w| w.set_rnd_src_en(true)); + } + + fn stop_rng(&self) { + let regs = T::regs(); + let source_enable_register = regs.rnd_source_enable(); + source_enable_register.write(|w| w.set_rnd_src_en(false)); + let reset_bits_counter_register = regs.rst_bits_counter(); + reset_bits_counter_register.write(|w| w.set_rst_bits_counter(true)); + } + + fn enable_irq(&self) { + unsafe { T::Interrupt::enable() } + } + + fn disable_irq(&self) { + T::Interrupt::disable(); + } + + fn blocking_wait_for_successful_generation(&self) { + let regs = T::regs(); + + let trng_busy_register = regs.trng_busy(); + let trng_valid_register = regs.trng_valid(); + + let mut success = false; + while success.not() { + while trng_busy_register.read().trng_busy() {} + if trng_valid_register.read().ehr_valid().not() { + if regs.rng_isr().read().autocorr_err() { + regs.trng_sw_reset().write(|w| w.set_trng_sw_reset(true)); + } else { + panic!("RNG not busy, but ehr is not valid!") + } + } else { + success = true + } + } + } + + fn read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) { + let regs = T::regs(); + let ehr_data_regs = [ + regs.ehr_data0(), + regs.ehr_data1(), + regs.ehr_data2(), + regs.ehr_data3(), + regs.ehr_data4(), + regs.ehr_data5(), + ]; + + for (i, reg) in ehr_data_regs.iter().enumerate() { + buffer[i * 4..i * 4 + 4].copy_from_slice(®.read().to_ne_bytes()); + } + } + + fn blocking_read_ehr_registers_into_array(&mut self, buffer: &mut [u8; TRNG_BLOCK_SIZE_BYTES]) { + self.blocking_wait_for_successful_generation(); + self.read_ehr_registers_into_array(buffer); + } + + /// Fill the buffer with random bytes, async version. + pub async fn fill_bytes(&mut self, destination: &mut [u8]) { + if destination.is_empty() { + return; // Nothing to fill + } + + self.start_rng(); + self.enable_irq(); + + let mut bytes_transferred = 0usize; + let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES]; + + let regs = T::regs(); + + let trng_busy_register = regs.trng_busy(); + let trng_valid_register = regs.trng_valid(); + + let waker = T::waker(); + + let destination_length = destination.len(); + + poll_fn(|context| { + waker.register(context.waker()); + if bytes_transferred == destination_length { + self.stop_rng(); + self.disable_irq(); + Poll::Ready(()) + } else { + if trng_busy_register.read().trng_busy() { + Poll::Pending + } else { + if trng_valid_register.read().ehr_valid().not() { + panic!("RNG not busy, but ehr is not valid!") + } + self.read_ehr_registers_into_array(&mut buffer); + let remaining = destination_length - bytes_transferred; + if remaining > TRNG_BLOCK_SIZE_BYTES { + destination[bytes_transferred..bytes_transferred + TRNG_BLOCK_SIZE_BYTES] + .copy_from_slice(&buffer); + bytes_transferred += TRNG_BLOCK_SIZE_BYTES + } else { + destination[bytes_transferred..bytes_transferred + remaining] + .copy_from_slice(&buffer[0..remaining]); + bytes_transferred += remaining + } + if bytes_transferred == destination_length { + self.stop_rng(); + self.disable_irq(); + Poll::Ready(()) + } else { + Poll::Pending + } + } + } + }) + .await + } + + /// Fill the buffer with random bytes, blocking version. + pub fn blocking_fill_bytes(&mut self, destination: &mut [u8]) { + if destination.is_empty() { + return; // Nothing to fill + } + self.start_rng(); + + let mut buffer = [0u8; TRNG_BLOCK_SIZE_BYTES]; + + for chunk in destination.chunks_mut(TRNG_BLOCK_SIZE_BYTES) { + self.blocking_wait_for_successful_generation(); + self.blocking_read_ehr_registers_into_array(&mut buffer); + chunk.copy_from_slice(&buffer[..chunk.len()]) + } + self.stop_rng() + } + + /// Return a random u32, blocking. + pub fn blocking_next_u32(&mut self) -> u32 { + let regs = T::regs(); + self.start_rng(); + self.blocking_wait_for_successful_generation(); + // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to + // clear all of the result registers. + let result = regs.ehr_data5().read(); + self.stop_rng(); + result + } + + /// Return a random u64, blocking. + pub fn blocking_next_u64(&mut self) -> u64 { + let regs = T::regs(); + self.start_rng(); + self.blocking_wait_for_successful_generation(); + + let low = regs.ehr_data4().read() as u64; + // 12.12.3 After successful generation, read the last result register, EHR_DATA[5] to + // clear all of the result registers. + let result = (regs.ehr_data5().read() as u64) << 32 | low; + self.stop_rng(); + result + } +} + +impl<'d, T: Instance> rand_core::RngCore for Trng<'d, T> { + fn next_u32(&mut self) -> u32 { + self.blocking_next_u32() + } + + fn next_u64(&mut self) -> u64 { + self.blocking_next_u64() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.blocking_fill_bytes(dest) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.blocking_fill_bytes(dest); + Ok(()) + } +} +/// TRNG interrupt handler. +pub struct InterruptHandler { + _trng: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = T::regs(); + let isr = regs.rng_isr().read(); + // Clear ehr bit + regs.rng_icr().write(|w| { + w.set_ehr_valid(true); + }); + if isr.ehr_valid() { + T::waker().wake(); + } else { + // 12.12.5. List of Registers + // ... + // TRNG: RNG_ISR Register + // ... + // AUTOCORR_ERR: 1 indicates Autocorrelation test failed four times in a row. + // When set, RNG ceases functioning until next reset + if isr.autocorr_err() { + warn!("TRNG Autocorrect error! Resetting TRNG"); + regs.trng_sw_reset().write(|w| { + w.set_trng_sw_reset(true); + }); + } + } + } +} diff --git a/examples/rp23/src/bin/trng.rs b/examples/rp23/src/bin/trng.rs new file mode 100644 index 000000000..e146baa2e --- /dev/null +++ b/examples/rp23/src/bin/trng.rs @@ -0,0 +1,64 @@ +//! This example shows TRNG usage + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::block::ImageDef; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::peripherals::TRNG; +use embassy_rp::trng::Trng; +use embassy_time::Timer; +use rand::RngCore; +use {defmt_rtt as _, panic_probe as _}; + +#[link_section = ".start_block"] +#[used] +pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); + +// Program metadata for `picotool info` +#[link_section = ".bi_entries"] +#[used] +pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ + embassy_rp::binary_info::rp_program_name!(c"example"), + embassy_rp::binary_info::rp_cargo_version!(), + embassy_rp::binary_info::rp_program_description!(c"Blinky"), + embassy_rp::binary_info::rp_program_build_attribute!(), +]; + +bind_interrupts!(struct Irqs { + TRNG_IRQ => embassy_rp::trng::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let peripherals = embassy_rp::init(Default::default()); + + // Initialize the TRNG with default configuration + let mut trng = Trng::new(peripherals.TRNG, Irqs, embassy_rp::trng::Config::default()); + // A buffer to collect random bytes in. + let mut randomness = [0u8; 58]; + + let mut led = Output::new(peripherals.PIN_25, Level::Low); + + loop { + trng.fill_bytes(&mut randomness).await; + info!("Random bytes async {}", &randomness); + trng.blocking_fill_bytes(&mut randomness); + info!("Random bytes blocking {}", &randomness); + let random_u32 = trng.next_u32(); + let random_u64 = trng.next_u64(); + info!("Random u32 {} u64 {}", random_u32, random_u64); + // Random number of blinks between 0 and 31 + let blinks = random_u32 % 32; + for _ in 0..blinks { + led.set_high(); + Timer::after_millis(20).await; + led.set_low(); + Timer::after_millis(20).await; + } + Timer::after_millis(1000).await; + } +} From a406a01459a5c759c044a181ba992fb2bc9e1150 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 18 Sep 2024 21:24:35 +0200 Subject: [PATCH 14/39] net-esp-hosted: remove useless fn init. --- embassy-net-esp-hosted/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index c78578bf1..f05e2a70a 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -137,7 +137,7 @@ where let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6])); let state_ch = ch_runner.state_runner(); - let mut runner = Runner { + let runner = Runner { ch: ch_runner, state_ch, shared: &state.shared, @@ -148,7 +148,6 @@ where spi, heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP, }; - runner.init().await; (device, Control::new(state_ch, &state.shared), runner) } @@ -174,8 +173,6 @@ where IN: InputPin + Wait, OUT: OutputPin, { - async fn init(&mut self) {} - /// Run the packet processing. pub async fn run(mut self) -> ! { debug!("resetting..."); From 60f93b42e262dc30bf7bd3767b1912a92f9e0f43 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 18 Sep 2024 21:24:50 +0200 Subject: [PATCH 15/39] net-esp-hosted: set wpa3_supported=true. I've noticed wpa3 still works without this flag, so I'm not sure what this does tbh... --- embassy-net-esp-hosted/src/control.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index c8cea8503..b1838a425 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -120,7 +120,7 @@ impl<'a> Control<'a> { pwd: unwrap!(String::try_from(password)), bssid: String::new(), listen_interval: 3, - is_wpa3_supported: false, + is_wpa3_supported: true, }; ioctl!(self, ReqConnectAp, RespConnectAp, req, resp); self.state_ch.set_link_state(LinkState::Up); From bee53af36a91d984558cd22dd96195537fd61fd6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 18 Sep 2024 21:37:42 +0200 Subject: [PATCH 16/39] net: add all combinations of wait methods for link/config up/down. --- embassy-net/src/lib.rs | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index ef53fb905..a7b7efa87 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -260,7 +260,10 @@ pub struct Stack<'d> { pub(crate) struct Inner { pub(crate) sockets: SocketSet<'static>, // Lifetime type-erased. pub(crate) iface: Interface, + /// Waker used for triggering polls. pub(crate) waker: WakerRegistration, + /// Waker used for waiting for link up or config up. + state_waker: WakerRegistration, hardware_address: HardwareAddress, next_local_port: u16, link_up: bool, @@ -270,7 +273,6 @@ pub(crate) struct Inner { static_v6: Option, #[cfg(feature = "dhcpv4")] dhcp_socket: Option, - config_waker: WakerRegistration, #[cfg(feature = "dns")] dns_socket: SocketHandle, #[cfg(feature = "dns")] @@ -326,6 +328,7 @@ pub fn new<'d, D: Driver, const SOCK: usize>( sockets, iface, waker: WakerRegistration::new(), + state_waker: WakerRegistration::new(), next_local_port, hardware_address, link_up: false, @@ -335,7 +338,6 @@ pub fn new<'d, D: Driver, const SOCK: usize>( static_v6: None, #[cfg(feature = "dhcpv4")] dhcp_socket: None, - config_waker: WakerRegistration::new(), #[cfg(feature = "dns")] dns_socket, #[cfg(feature = "dns")] @@ -421,10 +423,20 @@ impl<'d> Stack<'d> { v4_up || v6_up } + /// Wait for the network device to obtain a link signal. + pub async fn wait_link_up(&self) { + self.wait(|| self.is_link_up()).await + } + + /// Wait for the network device to lose link signal. + pub async fn wait_link_down(&self) { + self.wait(|| !self.is_link_up()).await + } + /// Wait for the network stack to obtain a valid IP configuration. /// /// ## Notes: - /// - Ensure [`Stack::run`] has been called before using this function. + /// - Ensure [`Runner::run`] has been started before using this function. /// /// - This function may never return (e.g. if no configuration is obtained through DHCP). /// The caller is supposed to handle a timeout for this case. @@ -451,13 +463,17 @@ impl<'d> Stack<'d> { /// // ... /// ``` pub async fn wait_config_up(&self) { - // If the config is up already, we can return immediately. - if self.is_config_up() { - return; - } + self.wait(|| self.is_config_up()).await + } - poll_fn(|cx| { - if self.is_config_up() { + /// Wait for the network stack to lose a valid IP configuration. + pub async fn wait_config_down(&self) { + self.wait(|| !self.is_config_up()).await + } + + fn wait<'a>(&'a self, mut predicate: impl FnMut() -> bool + 'a) -> impl Future + 'a { + poll_fn(move |cx| { + if predicate() { Poll::Ready(()) } else { // If the config is not up, we register a waker that is woken up @@ -465,13 +481,12 @@ impl<'d> Stack<'d> { trace!("Waiting for config up"); self.with_mut(|i| { - i.config_waker.register(cx.waker()); + i.state_waker.register(cx.waker()); }); Poll::Pending } }) - .await; } /// Get the current IPv4 configuration. @@ -775,7 +790,7 @@ impl Inner { .update_servers(&dns_servers[..count]); } - self.config_waker.wake(); + self.state_waker.wake(); } fn poll(&mut self, cx: &mut Context<'_>, driver: &mut D) { @@ -813,6 +828,7 @@ impl Inner { // Print when changed if old_link_up != self.link_up { info!("link_up = {:?}", self.link_up); + self.state_waker.wake(); } #[allow(unused_mut)] From b1897c58fa617dbab02b39e7f5e399ed1c9d54b4 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 18 Sep 2024 16:14:53 +0200 Subject: [PATCH 17/39] Add revert state in embassy-boot The revert state signals that a firmware revert has taken place, allowing the application to know if a firmware update attempt was reverted. --- embassy-boot/src/boot_loader.rs | 6 ++++-- embassy-boot/src/firmware_updater/asynch.rs | 3 ++- embassy-boot/src/firmware_updater/blocking.rs | 3 ++- embassy-boot/src/lib.rs | 6 ++++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/embassy-boot/src/boot_loader.rs b/embassy-boot/src/boot_loader.rs index 61d61b96e..5bffdc5ea 100644 --- a/embassy-boot/src/boot_loader.rs +++ b/embassy-boot/src/boot_loader.rs @@ -5,7 +5,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; -use crate::{State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; +use crate::{State, DFU_DETACH_MAGIC, REVERT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; /// Errors returned by bootloader #[derive(PartialEq, Eq, Debug)] @@ -276,7 +276,7 @@ impl BootLoader BootLoader FirmwareState<'d, STATE> { // Make sure we are running a booted firmware to avoid reverting to a bad state. async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { - if self.get_state().await? == State::Boot { + let state = self.get_state().await?; + if state == State::Boot || state == State::DfuDetach || state == State::Revert { Ok(()) } else { Err(FirmwareUpdaterError::BadState) diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index d3c723456..5f64b4be9 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs @@ -324,7 +324,8 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { // Make sure we are running a booted firmware to avoid reverting to a bad state. fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { - if self.get_state()? == State::Boot || self.get_state()? == State::DfuDetach { + let state = self.get_state()?; + if state == State::Boot || state == State::DfuDetach || state == State::Revert { Ok(()) } else { Err(FirmwareUpdaterError::BadState) diff --git a/embassy-boot/src/lib.rs b/embassy-boot/src/lib.rs index 8849055e8..7d5cc58f9 100644 --- a/embassy-boot/src/lib.rs +++ b/embassy-boot/src/lib.rs @@ -25,6 +25,7 @@ pub use firmware_updater::{ FirmwareUpdaterError, }; +pub(crate) const REVERT_MAGIC: u8 = 0xC0; pub(crate) const BOOT_MAGIC: u8 = 0xD0; pub(crate) const SWAP_MAGIC: u8 = 0xF0; pub(crate) const DFU_DETACH_MAGIC: u8 = 0xE0; @@ -37,6 +38,8 @@ pub enum State { Boot, /// Bootloader has swapped the active partition with the dfu partition and will attempt boot. Swap, + /// Bootloader has reverted the active partition with the dfu partition and will attempt boot. + Revert, /// Application has received a request to reboot into DFU mode to apply an update. DfuDetach, } @@ -157,6 +160,9 @@ mod tests { // Running again should cause a revert assert_eq!(State::Swap, bootloader.prepare_boot(&mut page).unwrap()); + // Next time we know it was reverted + assert_eq!(State::Revert, bootloader.prepare_boot(&mut page).unwrap()); + let mut read_buf = [0; FIRMWARE_SIZE]; flash.active().read(0, &mut read_buf).unwrap(); assert_eq!(ORIGINAL, read_buf); From ab0a227e4c02137bc3a621907d17ede0ace4cb1d Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 19 Sep 2024 09:15:08 +0200 Subject: [PATCH 18/39] Ensure bootloader state is parsed correctly --- embassy-boot/src/firmware_updater/asynch.rs | 7 +------ embassy-boot/src/firmware_updater/blocking.rs | 9 +-------- embassy-boot/src/lib.rs | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index b23857e2f..d9d15b004 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs @@ -304,12 +304,7 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { /// `mark_booted`. pub async fn get_state(&mut self) -> Result { self.state.read(0, &mut self.aligned).await?; - - if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } + Ok(State::from(&self.aligned)) } /// Mark to trigger firmware swap on next boot. diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index 5f64b4be9..08062b0d0 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs @@ -339,14 +339,7 @@ impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { /// `mark_booted`. pub fn get_state(&mut self) -> Result { self.state.read(0, &mut self.aligned)?; - - if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else if !self.aligned.iter().any(|&b| b != DFU_DETACH_MAGIC) { - Ok(State::DfuDetach) - } else { - Ok(State::Boot) - } + Ok(State::from(&self.aligned)) } /// Mark to trigger firmware swap on next boot. diff --git a/embassy-boot/src/lib.rs b/embassy-boot/src/lib.rs index 7d5cc58f9..e2c4cf771 100644 --- a/embassy-boot/src/lib.rs +++ b/embassy-boot/src/lib.rs @@ -44,6 +44,24 @@ pub enum State { DfuDetach, } +impl From for State +where + T: AsRef<[u8]>, +{ + fn from(magic: T) -> State { + let magic = magic.as_ref(); + if !magic.iter().any(|&b| b != SWAP_MAGIC) { + State::Swap + } else if !magic.iter().any(|&b| b != REVERT_MAGIC) { + State::Revert + } else if !magic.iter().any(|&b| b != DFU_DETACH_MAGIC) { + State::DfuDetach + } else { + State::Boot + } + } +} + /// Buffer aligned to 32 byte boundary, largest known alignment requirement for embassy-boot. #[repr(align(32))] pub struct AlignedBuffer(pub [u8; N]); From df23a77bfc3c8b5d8ab6adbd12842fa4cfe3675d Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 19 Sep 2024 09:15:35 +0200 Subject: [PATCH 19/39] Add led to example to demonstrate revert state detection --- examples/boot/application/nrf/src/bin/a.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 851a3d721..60cf3cd1a 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -8,6 +8,7 @@ use embassy_executor::Spawner; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; use embassy_nrf::nvmc::Nvmc; use embassy_nrf::wdt::{self, Watchdog}; +use embassy_boot::State; use embassy_sync::mutex::Mutex; use panic_reset as _; @@ -22,6 +23,7 @@ async fn main(_spawner: Spawner) { let mut button = Input::new(p.P0_11, Pull::Up); let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); + let mut led_reverted = Output::new(p.P0_14, Level::High, OutputDrive::Standard); //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); //let mut button = Input::new(p.P1_02, Pull::Up); @@ -53,6 +55,13 @@ async fn main(_spawner: Spawner) { let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc, &nvmc); let mut magic = [0; 4]; let mut updater = FirmwareUpdater::new(config, &mut magic); + let state = updater.get_state().await.unwrap(); + if state == State::Revert { + led_reverted.set_low(); + } else { + led_reverted.set_high(); + } + loop { led.set_low(); button.wait_for_any_edge().await; From 4e1efd93fd4dc8dd692daf419d901ae22413e091 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 19 Sep 2024 09:15:55 +0200 Subject: [PATCH 20/39] Fix defmt support for example boot app --- examples/boot/application/nrf/build.rs | 3 +++ examples/boot/application/nrf/src/bin/a.rs | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/boot/application/nrf/build.rs b/examples/boot/application/nrf/build.rs index cd1a264c4..e1da69328 100644 --- a/examples/boot/application/nrf/build.rs +++ b/examples/boot/application/nrf/build.rs @@ -31,4 +31,7 @@ fn main() { println!("cargo:rustc-link-arg-bins=--nmagic"); println!("cargo:rustc-link-arg-bins=-Tlink.x"); + if env::var("CARGO_FEATURE_DEFMT").is_ok() { + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + } } diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 60cf3cd1a..2c1d1a7bb 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -2,13 +2,15 @@ #![no_main] #![macro_use] +#[cfg(feature = "defmt")] +use defmt_rtt as _; +use embassy_boot::State; use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; use embassy_nrf::nvmc::Nvmc; use embassy_nrf::wdt::{self, Watchdog}; -use embassy_boot::State; use embassy_sync::mutex::Mutex; use panic_reset as _; From 893b8d79e8bab8dae0f46a0182443df9160592b5 Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Thu, 19 Sep 2024 08:17:33 -0400 Subject: [PATCH 21/39] embassy_sync/pubsub: fix PubSubBehavior visibility https://github.com/embassy-rs/embassy/pull/2969 appears to have broken direct `publish_immediate()` on `pubsub::Channel`, as it functionally made `PubSubBehavior` private and didn't delegate this method to the new (private) `SealedPubSubBehavior`. This change moves `publish_immediate`, `capacity`, and `is_full` from `SealedPubSubBehavior` to `PubSubBehavior` in order to restore them to `pub` visibility. --- embassy-sync/src/pubsub/mod.rs | 56 +++++++++++++++++----------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index a97eb7d5b..812302e2b 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs @@ -194,6 +194,25 @@ impl crate::pubsub::PubSubBehavior + for PubSubChannel +{ + fn publish_immediate(&self, message: T) { + self.inner.lock(|s| { + let mut s = s.borrow_mut(); + s.publish_immediate(message) + }) + } + + fn capacity(&self) -> usize { + self.capacity() + } + + fn is_full(&self) -> bool { + self.is_full() + } +} + impl SealedPubSubBehavior for PubSubChannel { @@ -246,13 +265,6 @@ impl usize { - self.capacity() - } - fn free_capacity(&self) -> usize { self.free_capacity() } @@ -286,10 +294,6 @@ impl bool { self.is_empty() } - - fn is_full(&self) -> bool { - self.is_full() - } } /// Internal state for the PubSub channel @@ -445,8 +449,6 @@ pub enum Error { MaximumPublishersReached, } -/// 'Middle level' behaviour of the pubsub channel. -/// This trait is used so that Sub and Pub can be generic over the channel. trait SealedPubSubBehavior { /// Try to get a message from the queue with the given message id. /// @@ -462,12 +464,6 @@ trait SealedPubSubBehavior { /// If the queue is full and a context is given, then its waker is registered in the publisher wakers. fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T>; - /// Publish a message immediately - fn publish_immediate(&self, message: T); - - /// Returns the maximum number of elements the channel can hold. - fn capacity(&self) -> usize; - /// Returns the free capacity of the channel. /// /// This is equivalent to `capacity() - len()` @@ -482,9 +478,6 @@ trait SealedPubSubBehavior { /// Returns whether the channel is empty. fn is_empty(&self) -> bool; - /// Returns whether the channel is full. - fn is_full(&self) -> bool; - /// Let the channel know that a subscriber has dropped fn unregister_subscriber(&self, subscriber_next_message_id: u64); @@ -495,9 +488,16 @@ trait SealedPubSubBehavior { /// 'Middle level' behaviour of the pubsub channel. /// This trait is used so that Sub and Pub can be generic over the channel. #[allow(private_bounds)] -pub trait PubSubBehavior: SealedPubSubBehavior {} +pub trait PubSubBehavior: SealedPubSubBehavior { + /// Publish a message immediately + fn publish_immediate(&self, message: T); -impl> PubSubBehavior for C {} + /// Returns the maximum number of elements the channel can hold. + fn capacity(&self) -> usize; + + /// Returns whether the channel is full. + fn is_full(&self) -> bool; +} /// The result of the subscriber wait procedure #[derive(Debug, Clone, PartialEq, Eq)] From 907d55ea82ac09b507afdc7ccb4d5997f827a748 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Thu, 19 Sep 2024 18:14:09 +0200 Subject: [PATCH 22/39] stm32: Added request_pause to DMA, and use it for RingBufferedUartRx --- embassy-stm32/src/dma/dma_bdma.rs | 54 ++++++++++++++++++++++++- embassy-stm32/src/usart/ringbuffered.rs | 2 +- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index df041c4e9..2887536c3 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -493,6 +493,26 @@ impl AnyChannel { } } + fn request_pause(&self) { + let info = self.info(); + match self.info().dma { + #[cfg(dma)] + DmaInfo::Dma(r) => { + r.st(info.num).cr().modify(|w| { + // Disable the channel without overwriting the existing configuration + w.set_en(false); + }); + } + #[cfg(bdma)] + DmaInfo::Bdma(r) => { + r.ch(info.num).cr().modify(|w| { + // Disable the channel without overwriting the existing configuration + w.set_en(false); + }); + } + } + } + fn is_running(&self) -> bool { let info = self.info(); match self.info().dma { @@ -667,12 +687,22 @@ impl<'a> Transfer<'a> { } /// Request the transfer to stop. + /// The configuration for this channel will **not be preserved**. If you need to restart the transfer + /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. /// /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. pub fn request_stop(&mut self) { self.channel.request_stop() } + /// Request the transfer to pause, keeping the existing configuration for this channel. + /// To restart the transfer, call [`start`](Self::start) again. + /// + /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. + pub fn request_pause(&mut self) { + self.channel.request_pause() + } + /// Return whether this transfer is still running. /// /// If this returns `false`, it can be because either the transfer finished, or @@ -846,13 +876,23 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); } - /// Request DMA to stop. + /// Request the DMA to stop. + /// The configuration for this channel will **not be preserved**. If you need to restart the transfer + /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. /// /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. pub fn request_stop(&mut self) { self.channel.request_stop() } + /// Request the transfer to pause, keeping the existing configuration for this channel. + /// To restart the transfer, call [`start`](Self::start) again. + /// + /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. + pub fn request_pause(&mut self) { + self.channel.request_pause() + } + /// Return whether DMA is still running. /// /// If this returns `false`, it can be because either the transfer finished, or @@ -977,13 +1017,23 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); } - /// Request DMA to stop. + /// Request the DMA to stop. + /// The configuration for this channel will **not be preserved**. If you need to restart the transfer + /// at a later point with the same configuration, see [`request_pause`](Self::request_pause) instead. /// /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. pub fn request_stop(&mut self) { self.channel.request_stop() } + /// Request the transfer to pause, keeping the existing configuration for this channel. + /// To restart the transfer, call [`start`](Self::start) again. + /// + /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. + pub fn request_pause(&mut self) { + self.channel.request_pause() + } + /// Return whether DMA is still running. /// /// If this returns `false`, it can be because either the transfer finished, or diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index b0652046c..bb95af966 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -120,7 +120,7 @@ impl<'d> RingBufferedUartRx<'d> { /// Stop uart background receive fn teardown_uart(&mut self) { - self.ring_buf.request_stop(); + self.ring_buf.request_pause(); let r = self.info.regs; // clear all interrupts and DMA Rx Request From 2a9cdaabaa315795722886f94fa553e8f3e3c14d Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Thu, 19 Sep 2024 18:25:08 +0200 Subject: [PATCH 23/39] stm32: Moved comment to match request_stop --- embassy-stm32/src/dma/dma_bdma.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 2887536c3..d10b5554f 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -498,15 +498,15 @@ impl AnyChannel { match self.info().dma { #[cfg(dma)] DmaInfo::Dma(r) => { + // Disable the channel without overwriting the existing configuration r.st(info.num).cr().modify(|w| { - // Disable the channel without overwriting the existing configuration w.set_en(false); }); } #[cfg(bdma)] DmaInfo::Bdma(r) => { + // Disable the channel without overwriting the existing configuration r.ch(info.num).cr().modify(|w| { - // Disable the channel without overwriting the existing configuration w.set_en(false); }); } From 4fcc8e39d6b3e486d2b94aa45d255d9c645d028a Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Thu, 19 Sep 2024 19:21:34 +0200 Subject: [PATCH 24/39] stm32: Only check errors on running RingBufferedUartRx, reduce number of small one-time functions --- embassy-stm32/src/usart/ringbuffered.rs | 49 ++++++++++--------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index bb95af966..2d9c63820 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -71,33 +71,18 @@ impl<'d> UartRx<'d, Async> { } impl<'d> RingBufferedUartRx<'d> { - /// Clear the ring buffer and start receiving in the background - pub fn start(&mut self) -> Result<(), Error> { - // Clear the ring buffer so that it is ready to receive data - self.ring_buf.clear(); - - self.setup_uart(); - - Ok(()) - } - - fn stop(&mut self, err: Error) -> Result { - self.teardown_uart(); - - Err(err) - } - /// Reconfigure the driver pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { reconfigure(self.info, self.kernel_clock, config) } - /// Start uart background receive - fn setup_uart(&mut self) { - // fence before starting DMA. + /// Configure and start the DMA backed UART receiver + /// + /// Note: This is also done automatically by [`read()`] if required. + pub fn start_uart(&mut self) { + // Clear the buffer so that it is ready to receive data + self.ring_buf.clear(); compiler_fence(Ordering::SeqCst); - - // start the dma controller self.ring_buf.start(); let r = self.info.regs; @@ -118,8 +103,8 @@ impl<'d> RingBufferedUartRx<'d> { }); } - /// Stop uart background receive - fn teardown_uart(&mut self) { + /// Stop DMA backed UART receiver + fn stop_uart(&mut self) { self.ring_buf.request_pause(); let r = self.info.regs; @@ -153,13 +138,15 @@ impl<'d> RingBufferedUartRx<'d> { pub async fn read(&mut self, buf: &mut [u8]) -> Result { let r = self.info.regs; - // Start background receive if it was not already started + // Start DMA and Uart if it was not already started, + // otherwise check for errors in status register. + let sr = clear_idle_flag(r); if !r.cr3().read().dmar() { - self.start()?; + self.start_uart(); + } else { + check_for_errors(sr)?; } - check_for_errors(clear_idle_flag(r))?; - loop { match self.ring_buf.read(buf) { Ok((0, _)) => {} @@ -167,14 +154,16 @@ impl<'d> RingBufferedUartRx<'d> { return Ok(len); } Err(_) => { - return self.stop(Error::Overrun); + self.stop_uart(); + return Err(Error::Overrun); } } match self.wait_for_data_or_idle().await { Ok(_) => {} Err(err) => { - return self.stop(err); + self.stop_uart(); + return Err(err); } } } @@ -228,7 +217,7 @@ impl<'d> RingBufferedUartRx<'d> { impl Drop for RingBufferedUartRx<'_> { fn drop(&mut self) { - self.teardown_uart(); + self.stop_uart(); self.rx.as_ref().map(|x| x.set_as_disconnected()); self.rts.as_ref().map(|x| x.set_as_disconnected()); super::drop_tx_rx(self.info, self.state); From 3aeeeb0d784d843b521b4b8b222114ef7ba71363 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Thu, 19 Sep 2024 20:07:08 +0200 Subject: [PATCH 25/39] stm32: Start DMA before clearing, avoid panic in updater ringbuffer impl --- embassy-stm32/src/usart/ringbuffered.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 2d9c63820..75834bf37 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -81,9 +81,9 @@ impl<'d> RingBufferedUartRx<'d> { /// Note: This is also done automatically by [`read()`] if required. pub fn start_uart(&mut self) { // Clear the buffer so that it is ready to receive data - self.ring_buf.clear(); compiler_fence(Ordering::SeqCst); self.ring_buf.start(); + self.ring_buf.clear(); let r = self.info.regs; // clear all interrupts and DMA Rx Request From 5ea934d4ba6f0bc0e5e47b16f17dd8e881b528a3 Mon Sep 17 00:00:00 2001 From: Gerhard de Clercq Date: Fri, 20 Sep 2024 09:57:31 +0200 Subject: [PATCH 26/39] embassy_stm32/eth: support compliance testing This change adds the possibility to perform compliance testing with STM32 systems by directly exposing SMI when needed. Users can then use this to configure PHY registers for test modes. --- embassy-stm32/src/eth/mod.rs | 14 ++++ .../stm32f4/src/bin/eth_compliance_test.rs | 77 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 examples/stm32f4/src/bin/eth_compliance_test.rs diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index bfe8a60d6..6442176da 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -177,6 +177,20 @@ pub unsafe trait PHY { fn poll_link(&mut self, sm: &mut S, cx: &mut Context) -> bool; } +impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { + /// Directly expose the SMI interface used by the Ethernet driver. + /// + /// This can be used to for example configure special PHY registers for compliance testing. + /// + /// # Safety + /// + /// Revert any temporary PHY register changes such as to enable test modes before handing + /// the Ethernet device over to the networking stack otherwise things likely won't work. + pub unsafe fn station_management(&mut self) -> &mut impl StationManagement { + &mut self.station_management + } +} + trait SealedInstance { fn regs() -> crate::pac::eth::Eth; } diff --git a/examples/stm32f4/src/bin/eth_compliance_test.rs b/examples/stm32f4/src/bin/eth_compliance_test.rs new file mode 100644 index 000000000..5946fed79 --- /dev/null +++ b/examples/stm32f4/src/bin/eth_compliance_test.rs @@ -0,0 +1,77 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::eth::generic_smi::GenericSMI; +use embassy_stm32::eth::{Ethernet, PacketQueue, StationManagement}; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; +use embassy_time::Timer; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + ETH => eth::InterruptHandler; + HASH_RNG => rng::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL180, + divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 180 / 2 = 180Mhz. + divq: None, + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } + let p = embassy_stm32::init(config); + + info!("Hello Compliance World!"); + + let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; + + const PHY_ADDR: u8 = 0; + static PACKETS: StaticCell> = StaticCell::new(); + let mut device = Ethernet::new( + PACKETS.init(PacketQueue::<4, 4>::new()), + p.ETH, + Irqs, + p.PA1, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PG13, + p.PB13, + p.PG11, + GenericSMI::new(PHY_ADDR), + mac_addr, + ); + + let sm = unsafe { device.station_management() }; + + // Just an example. Exact register settings depend on the specific PHY and test. + sm.smi_write(PHY_ADDR, 0, 0x2100); + sm.smi_write(PHY_ADDR, 11, 0xA000); + + // NB: Remember to reset the PHY after testing before starting the networking stack + + loop { + Timer::after_secs(1).await; + } +} From 3328c5d6567bb5b1df7ca26d818a91e0740e56c1 Mon Sep 17 00:00:00 2001 From: Reed Date: Sat, 21 Sep 2024 12:31:38 +1200 Subject: [PATCH 27/39] Correctly gate `time` feature of embassy-embedded-hal in embassy-stm32 --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 3c6484c96..3f4a5e3c4 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -47,7 +47,7 @@ embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } -embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal" } +embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal", default-features = false } embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" } embassy-usb-synopsys-otg = {version = "0.1.0", path = "../embassy-usb-synopsys-otg" } @@ -129,7 +129,7 @@ unstable-pac = [] #! ## Time ## Enables additional driver features that depend on embassy-time -time = ["dep:embassy-time"] +time = ["dep:embassy-time", "embassy-embedded-hal/time"] # Features starting with `_` are for internal use only. They're not intended # to be enabled by other crates, and are not covered by semver guarantees. From 2f60d78ea318f51ff59868c348b77cf880012198 Mon Sep 17 00:00:00 2001 From: Kevin Date: Sun, 15 Sep 2024 02:44:16 +0200 Subject: [PATCH 28/39] Add OTG_HS support for STM32H7R/S --- embassy-stm32/Cargo.toml | 6 +- embassy-stm32/src/rcc/h.rs | 4 +- embassy-stm32/src/usb/mod.rs | 20 +++ embassy-stm32/src/usb/otg.rs | 53 +++++++ embassy-usb-synopsys-otg/src/lib.rs | 16 +++ embassy-usb-synopsys-otg/src/otg_v1.rs | 171 +++++++++++++++++++++++ examples/stm32h7rs/src/bin/usb_serial.rs | 139 ++++++++++++++++++ 7 files changed, 405 insertions(+), 4 deletions(-) create mode 100644 examples/stm32h7rs/src/bin/usb_serial.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 3f4a5e3c4..2f7f373af 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,8 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364" } +# stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364" } +stm32-metapac = { path = "../../stm32-data/build/stm32-metapac" } vcell = "0.1.3" nb = "1.0.0" @@ -99,7 +100,8 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364", default-features = false, features = ["metadata"] } +# stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364", default-features = false, features = ["metadata"] } +stm32-metapac = { path = "../../stm32-data/build/stm32-metapac", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 376a0b454..27fc2b8d7 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -698,7 +698,7 @@ pub(crate) unsafe fn init(config: Config) { #[cfg(stm32h7rs)] clk48mohci: None, // TODO #[cfg(stm32h7rs)] - usb: None, // TODO + usb: Some(Hertz(48_000_000)), ); } @@ -769,7 +769,7 @@ fn init_pll(num: usize, config: Option, input: &PllInput) -> PllOutput { if num == 0 { // on PLL1, DIVP must be even for most series. // The enum value is 1 less than the divider, so check it's odd. - #[cfg(not(pwr_h7rm0468))] + #[cfg(not(any(pwr_h7rm0468, stm32h7rs)))] assert!(div.to_bits() % 2 == 1); #[cfg(pwr_h7rm0468)] assert!(div.to_bits() % 2 == 1 || div.to_bits() == 0); diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index ce9fe0a9b..7d8c79618 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs @@ -48,6 +48,26 @@ fn common_init() { while !crate::pac::PWR.cr3().read().usb33rdy() {} } + #[cfg(stm32h7rs)] + { + // If true, VDD33USB is generated by internal regulator from VDD50USB + // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo) + // TODO: unhardcode + let internal_regulator = false; + + // Enable USB power + critical_section::with(|_| { + crate::pac::PWR.csr2().modify(|w| { + w.set_usbregen(internal_regulator); + w.set_usb33den(true); + w.set_usbhsregen(true); + }) + }); + + // Wait for USB power to stabilize + while !crate::pac::PWR.csr2().read().usb33rdy() {} + } + #[cfg(stm32u5)] { // Enable USB power diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index e27b164e4..59b5401cc 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -97,6 +97,45 @@ impl<'d, T: Instance> Driver<'d, T> { } } + /// Initializes USB OTG peripheral with internal High-Speed PHY. + /// + /// # Arguments + /// + /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets. + /// Must be large enough to fit all OUT endpoint max packet sizes. + /// Endpoint allocation will fail if it is too small. + pub fn new_hs( + _peri: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + dp: impl Peripheral

> + 'd, + dm: impl Peripheral

> + 'd, + ep_out_buffer: &'d mut [u8], + config: Config, + ) -> Self { + into_ref!(dp, dm); + + dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + + let regs = T::regs(); + + let instance = OtgInstance { + regs, + state: T::state(), + fifo_depth_words: T::FIFO_DEPTH_WORDS, + extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, + endpoint_count: T::ENDPOINT_COUNT, + phy_type: PhyType::InternalHighSpeed, + quirk_setup_late_cnak: quirk_setup_late_cnak(regs), + calculate_trdt_fn: calculate_trdt::, + }; + + Self { + inner: OtgDriver::new(ep_out_buffer, instance, config), + phantom: PhantomData, + } + } + /// Initializes USB OTG peripheral with external Full-speed PHY (usually, a High-speed PHY in Full-speed mode). /// /// # Arguments @@ -272,6 +311,19 @@ impl<'d, T: Instance> Bus<'d, T> { } }); + #[cfg(stm32h7rs)] + critical_section::with(|_| { + let rcc = crate::pac::RCC; + rcc.ahb1enr().modify(|w| { + w.set_usbphycen(true); + w.set_usb_otg_hsen(true); + }); + rcc.ahb1lpenr().modify(|w| { + w.set_usbphyclpen(true); + w.set_usb_otg_hslpen(true); + }); + }); + let r = T::regs(); let core_id = r.cid().read().0; trace!("Core id {:08x}", core_id); @@ -286,6 +338,7 @@ impl<'d, T: Instance> Bus<'d, T> { match core_id { 0x0000_1200 | 0x0000_1100 => self.inner.config_v1(), 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => self.inner.config_v2v3(), + 0x0000_5000 => self.inner.config_v5(), _ => unimplemented!("Unknown USB core id {:X}", core_id), } } diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index b145f4aa8..3ff965149 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs @@ -584,6 +584,22 @@ impl<'d, const MAX_EP_COUNT: usize> Bus<'d, MAX_EP_COUNT> { }); } + pub fn config_v5(&mut self) { + let r = self.instance.regs; + + r.gccfg_v3().modify(|w| { + w.set_vbvaloven(true); + w.set_vbvaloval(true); + w.set_vbden(self.config.vbus_detection); + }); + + // Force B-peripheral session + r.gotgctl().modify(|w| { + w.set_vbvaloen(!self.config.vbus_detection); + w.set_bvaloval(true); + }); + } + fn init(&mut self) { let r = self.instance.regs; let phy_type = self.instance.phy_type; diff --git a/embassy-usb-synopsys-otg/src/otg_v1.rs b/embassy-usb-synopsys-otg/src/otg_v1.rs index d3abc328d..18e760fd1 100644 --- a/embassy-usb-synopsys-otg/src/otg_v1.rs +++ b/embassy-usb-synopsys-otg/src/otg_v1.rs @@ -186,6 +186,11 @@ impl Otg { pub const fn gccfg_v2(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) } } + #[doc = "General core configuration register, for core_id 0x0000_5xxx"] + #[inline(always)] + pub const fn gccfg_v3(self) -> Reg { + unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) } + } #[doc = "Core ID register"] #[inline(always)] pub const fn cid(self) -> Reg { @@ -1831,6 +1836,172 @@ pub mod regs { GccfgV2(0) } } + #[doc = "OTG general core configuration register."] + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq)] + pub struct GccfgV3(pub u32); + impl GccfgV3 { + #[doc = "Charger detection, result of the current mode (primary or secondary)."] + #[inline(always)] + pub const fn chgdet(&self) -> bool { + let val = (self.0 >> 0usize) & 0x01; + val != 0 + } + #[doc = "Charger detection, result of the current mode (primary or secondary)."] + #[inline(always)] + pub fn set_chgdet(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 0usize)) | (((val as u32) & 0x01) << 0usize); + } + #[doc = "Single-Ended DP indicator This bit gives the voltage level on DP (also result of the comparison with VLGC threshold as defined in BC v1.2 standard)."] + #[inline(always)] + pub const fn fsvplus(&self) -> bool { + let val = (self.0 >> 1usize) & 0x01; + val != 0 + } + #[doc = "Single-Ended DP indicator This bit gives the voltage level on DP (also result of the comparison with VLGC threshold as defined in BC v1.2 standard)."] + #[inline(always)] + pub fn set_fsvplus(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 1usize)) | (((val as u32) & 0x01) << 1usize); + } + #[doc = "Single-Ended DM indicator This bit gives the voltage level on DM (also result of the comparison with VLGC threshold as defined in BC v1.2 standard)."] + #[inline(always)] + pub const fn fsvminus(&self) -> bool { + let val = (self.0 >> 2usize) & 0x01; + val != 0 + } + #[doc = "Single-Ended DM indicator This bit gives the voltage level on DM (also result of the comparison with VLGC threshold as defined in BC v1.2 standard)."] + #[inline(always)] + pub fn set_fsvminus(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 2usize)) | (((val as u32) & 0x01) << 2usize); + } + #[doc = "VBUS session indicator Indicates if VBUS is above VBUS session threshold."] + #[inline(always)] + pub const fn sessvld(&self) -> bool { + let val = (self.0 >> 3usize) & 0x01; + val != 0 + } + #[doc = "VBUS session indicator Indicates if VBUS is above VBUS session threshold."] + #[inline(always)] + pub fn set_sessvld(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 3usize)) | (((val as u32) & 0x01) << 3usize); + } + #[doc = "Host CDP behavior enable."] + #[inline(always)] + pub const fn hcdpen(&self) -> bool { + let val = (self.0 >> 16usize) & 0x01; + val != 0 + } + #[doc = "Host CDP behavior enable."] + #[inline(always)] + pub fn set_hcdpen(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 16usize)) | (((val as u32) & 0x01) << 16usize); + } + #[doc = "Host CDP port voltage detector enable on DP."] + #[inline(always)] + pub const fn hcdpdeten(&self) -> bool { + let val = (self.0 >> 17usize) & 0x01; + val != 0 + } + #[doc = "Host CDP port voltage detector enable on DP."] + #[inline(always)] + pub fn set_hcdpdeten(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 17usize)) | (((val as u32) & 0x01) << 17usize); + } + #[doc = "Host CDP port Voltage source enable on DM."] + #[inline(always)] + pub const fn hvdmsrcen(&self) -> bool { + let val = (self.0 >> 18usize) & 0x01; + val != 0 + } + #[doc = "Host CDP port Voltage source enable on DM."] + #[inline(always)] + pub fn set_hvdmsrcen(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 18usize)) | (((val as u32) & 0x01) << 18usize); + } + #[doc = "Data Contact Detection enable."] + #[inline(always)] + pub const fn dcden(&self) -> bool { + let val = (self.0 >> 19usize) & 0x01; + val != 0 + } + #[doc = "Data Contact Detection enable."] + #[inline(always)] + pub fn set_dcden(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 19usize)) | (((val as u32) & 0x01) << 19usize); + } + #[doc = "Primary detection enable."] + #[inline(always)] + pub const fn pden(&self) -> bool { + let val = (self.0 >> 20usize) & 0x01; + val != 0 + } + #[doc = "Primary detection enable."] + #[inline(always)] + pub fn set_pden(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 20usize)) | (((val as u32) & 0x01) << 20usize); + } + #[doc = "VBUS detection enable Enables VBUS Sensing Comparators in order to detect VBUS presence and/or perform OTG operation."] + #[inline(always)] + pub const fn vbden(&self) -> bool { + let val = (self.0 >> 21usize) & 0x01; + val != 0 + } + #[doc = "VBUS detection enable Enables VBUS Sensing Comparators in order to detect VBUS presence and/or perform OTG operation."] + #[inline(always)] + pub fn set_vbden(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 21usize)) | (((val as u32) & 0x01) << 21usize); + } + #[doc = "Secondary detection enable."] + #[inline(always)] + pub const fn sden(&self) -> bool { + let val = (self.0 >> 22usize) & 0x01; + val != 0 + } + #[doc = "Secondary detection enable."] + #[inline(always)] + pub fn set_sden(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 22usize)) | (((val as u32) & 0x01) << 22usize); + } + #[doc = "Software override value of the VBUS B-session detection."] + #[inline(always)] + pub const fn vbvaloval(&self) -> bool { + let val = (self.0 >> 23usize) & 0x01; + val != 0 + } + #[doc = "Software override value of the VBUS B-session detection."] + #[inline(always)] + pub fn set_vbvaloval(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 23usize)) | (((val as u32) & 0x01) << 23usize); + } + #[doc = "Enables a software override of the VBUS B-session detection."] + #[inline(always)] + pub const fn vbvaloven(&self) -> bool { + let val = (self.0 >> 24usize) & 0x01; + val != 0 + } + #[doc = "Enables a software override of the VBUS B-session detection."] + #[inline(always)] + pub fn set_vbvaloven(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 24usize)) | (((val as u32) & 0x01) << 24usize); + } + #[doc = "Force host mode pull-downs If the ID pin functions are enabled, the host mode pull-downs on DP and DM activate automatically. However, whenever that is not the case, yet host mode is required, this bit must be used to force the pull-downs active."] + #[inline(always)] + pub const fn forcehostpd(&self) -> bool { + let val = (self.0 >> 25usize) & 0x01; + val != 0 + } + #[doc = "Force host mode pull-downs If the ID pin functions are enabled, the host mode pull-downs on DP and DM activate automatically. However, whenever that is not the case, yet host mode is required, this bit must be used to force the pull-downs active."] + #[inline(always)] + pub fn set_forcehostpd(&mut self, val: bool) { + self.0 = (self.0 & !(0x01 << 25usize)) | (((val as u32) & 0x01) << 25usize); + } + } + impl Default for GccfgV3 { + #[inline(always)] + fn default() -> GccfgV3 { + GccfgV3(0) + } + } #[doc = "I2C access register"] #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq)] diff --git a/examples/stm32h7rs/src/bin/usb_serial.rs b/examples/stm32h7rs/src/bin/usb_serial.rs new file mode 100644 index 000000000..5a234e898 --- /dev/null +++ b/examples/stm32h7rs/src/bin/usb_serial.rs @@ -0,0 +1,139 @@ +#![no_std] +#![no_main] + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_stm32::time::Hertz; +use embassy_stm32::usb::{Driver, Instance}; +use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + OTG_HS => usb::InterruptHandler; +}); + +// If you are trying this and your USB device doesn't connect, the most +// common issues are the RCC config and vbus_detection +// +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// for more information. +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(24_000_000), + mode: HseMode::Oscillator, + }); + config.rcc.pll1 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV12, + mul: PllMul::MUL300, + divp: Some(PllDiv::DIV1), //600 MHz + divq: Some(PllDiv::DIV2), // 300 MHz + divr: Some(PllDiv::DIV2), // 300 MHz + }); + config.rcc.sys = Sysclk::PLL1_P; // 600 MHz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 MHz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 150 MHz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 150 MHz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 150 MHz + config.rcc.apb5_pre = APBPrescaler::DIV2; // 150 MHz + config.rcc.voltage_scale = VoltageScale::HIGH; + } + + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let mut ep_out_buffer = [0u8; 256]; + let mut config = embassy_stm32::usb::Config::default(); + + // Do not enable vbus_detection. This is a safe default that works in all boards. + // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need + // to enable vbus_detection to comply with the USB spec. If you enable it, the board + // has to support it or USB won't work at all. See docs on `vbus_detection` for details. + config.vbus_detection = false; + + let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PM6, p.PM5, &mut ep_out_buffer, config); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut config_descriptor, + &mut bos_descriptor, + &mut [], // no msos descriptors + &mut control_buf, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} From 6d9af8304cf88dbfa3713acfef4d89ba3a95c2d8 Mon Sep 17 00:00:00 2001 From: Kevin Date: Sun, 15 Sep 2024 20:09:42 +0200 Subject: [PATCH 29/39] Add USBPHYC clock configuration for H7RS series --- embassy-stm32/Cargo.toml | 6 ++--- embassy-stm32/src/rcc/h.rs | 32 +++++++++++++++++++++++- embassy-stm32/src/usb/mod.rs | 10 ++++++++ embassy-stm32/src/usb/otg.rs | 2 +- embassy-usb-synopsys-otg/src/lib.rs | 29 +++++++++++++-------- examples/stm32h7rs/src/bin/usb_serial.rs | 1 + 6 files changed, 63 insertions(+), 17 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 2f7f373af..575c4f20c 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,8 +72,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -# stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364" } -stm32-metapac = { path = "../../stm32-data/build/stm32-metapac" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-acaf04256034066bd5b3a8426224ccf3e4cb7d19" } vcell = "0.1.3" nb = "1.0.0" @@ -100,8 +99,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -# stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad00827345b4b758b2453082809d6e3b634b5364", default-features = false, features = ["metadata"] } -stm32-metapac = { path = "../../stm32-data/build/stm32-metapac", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-acaf04256034066bd5b3a8426224ccf3e4cb7d19", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 27fc2b8d7..cd1c10407 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -35,7 +35,10 @@ pub enum VoltageScale { Scale3, } #[cfg(any(stm32h7rs))] -pub use crate::pac::pwr::vals::Vos as VoltageScale; +pub use crate::pac::{ + pwr::vals::Vos as VoltageScale, + rcc::vals::{Usbphycsel, Usbrefcksel}, +}; #[derive(Clone, Copy, Eq, PartialEq)] pub enum HseMode { @@ -557,6 +560,27 @@ pub(crate) unsafe fn init(config: Config) { let rtc = config.ls.init(); + #[cfg(stm32h7rs)] + let usb_refck = match config.mux.usbphycsel { + Usbphycsel::HSE => hse, + Usbphycsel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8), + Usbphycsel::PLL3_Q => pll3.q, + _ => None, + }; + #[cfg(stm32h7rs)] + let usb_refck_sel = match usb_refck { + Some(clk_val) => match clk_val { + Hertz(16_000_000) => Usbrefcksel::MHZ16, + Hertz(19_200_000) => Usbrefcksel::MHZ19_2, + Hertz(20_000_000) => Usbrefcksel::MHZ20, + Hertz(24_000_000) => Usbrefcksel::MHZ24, + Hertz(26_000_000) => Usbrefcksel::MHZ26, + Hertz(32_000_000) => Usbrefcksel::MHZ32, + _ => panic!("cannot select USBPHYC reference clock with source frequency of {} Hz, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), + }, + None => Usbrefcksel::MHZ24, + }; + #[cfg(stm32h7)] { RCC.d1cfgr().modify(|w| { @@ -593,6 +617,10 @@ pub(crate) unsafe fn init(config: Config) { w.set_ppre4(config.apb4_pre); w.set_ppre5(config.apb5_pre); }); + + RCC.ahbperckselr().modify(|w| { + w.set_usbrefcksel(usb_refck_sel); + }); } #[cfg(stm32h5)] { @@ -698,6 +726,8 @@ pub(crate) unsafe fn init(config: Config) { #[cfg(stm32h7rs)] clk48mohci: None, // TODO #[cfg(stm32h7rs)] + hse_div_2: hse.map(|clk| clk / 2u32), + #[cfg(stm32h7rs)] usb: Some(Hertz(48_000_000)), ); } diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index 7d8c79618..a473285bf 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs @@ -13,9 +13,19 @@ fn common_init() { // Check the USB clock is enabled and running at exactly 48 MHz. // frequency() will panic if not enabled let freq = T::frequency(); + + // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally + #[cfg(stm32h7rs)] + if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) { + panic!( + "USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.", + freq.0 + ) + } // Check frequency is within the 0.25% tolerance allowed by the spec. // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user // has tight clock restrictions due to something else (like audio). + #[cfg(not(stm32h7rs))] if freq.0.abs_diff(48_000_000) > 120_000 { panic!( "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.", diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 59b5401cc..00cafe6e4 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -554,7 +554,7 @@ fn calculate_trdt(speed: Dspd) -> u8 { match speed { Dspd::HIGH_SPEED => { // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446) - if ahb_freq >= 30_000_000 { + if ahb_freq >= 30_000_000 || cfg!(stm32h7rs) { 0x9 } else { panic!("AHB frequency is too low") diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index 3ff965149..f90403936 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs @@ -584,20 +584,27 @@ impl<'d, const MAX_EP_COUNT: usize> Bus<'d, MAX_EP_COUNT> { }); } + /// Applies configuration specific to + /// Core ID 0x0000_5000 pub fn config_v5(&mut self) { let r = self.instance.regs; + let phy_type = self.instance.phy_type; - r.gccfg_v3().modify(|w| { - w.set_vbvaloven(true); - w.set_vbvaloval(true); - w.set_vbden(self.config.vbus_detection); - }); - - // Force B-peripheral session - r.gotgctl().modify(|w| { - w.set_vbvaloen(!self.config.vbus_detection); - w.set_bvaloval(true); - }); + if phy_type == PhyType::InternalHighSpeed { + r.gccfg_v3().modify(|w| { + w.set_vbvaloven(!self.config.vbus_detection); + w.set_vbvaloval(!self.config.vbus_detection); + w.set_vbden(self.config.vbus_detection); + }); + } else { + r.gotgctl().modify(|w| { + w.set_bvaloen(!self.config.vbus_detection); + w.set_bvaloval(!self.config.vbus_detection); + }); + r.gccfg_v3().modify(|w| { + w.set_vbden(self.config.vbus_detection); + }); + } } fn init(&mut self) { diff --git a/examples/stm32h7rs/src/bin/usb_serial.rs b/examples/stm32h7rs/src/bin/usb_serial.rs index 5a234e898..6773f7843 100644 --- a/examples/stm32h7rs/src/bin/usb_serial.rs +++ b/examples/stm32h7rs/src/bin/usb_serial.rs @@ -48,6 +48,7 @@ async fn main(_spawner: Spawner) { config.rcc.apb4_pre = APBPrescaler::DIV2; // 150 MHz config.rcc.apb5_pre = APBPrescaler::DIV2; // 150 MHz config.rcc.voltage_scale = VoltageScale::HIGH; + config.rcc.mux.usbphycsel = mux::Usbphycsel::HSE; } let p = embassy_stm32::init(config); From 85b7c8957cce3fef6011e63d7cb6ff85912ccb50 Mon Sep 17 00:00:00 2001 From: Kevin Date: Sun, 22 Sep 2024 01:11:32 +0200 Subject: [PATCH 30/39] Add presence check for OTG_HS peripheral on STM32H7R/S series --- embassy-stm32/build.rs | 4 +++- embassy-stm32/src/rcc/h.rs | 14 +++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 19cf193d9..28c619c6b 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -55,7 +55,7 @@ fn main() { let mut singletons: Vec = Vec::new(); for p in METADATA.peripherals { if let Some(r) = &p.registers { - if r.kind == "adccommon" || r.kind == "sai" || r.kind == "ucpd" { + if r.kind == "adccommon" || r.kind == "sai" || r.kind == "ucpd" || r.kind == "otg" { // TODO: should we emit this for all peripherals? if so, we will need a list of all // possible peripherals across all chips, so that we can declare the configs // (replacing the hard-coded list of `peri_*` cfgs below) @@ -111,6 +111,8 @@ fn main() { "peri_sai4", "peri_ucpd1", "peri_ucpd2", + "peri_usb_otg_fs", + "peri_usb_otg_hs", ]); cfgs.declare_all(&["mco", "mco1", "mco2"]); diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index cd1c10407..55fe8ca9d 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -34,11 +34,10 @@ pub enum VoltageScale { Scale2, Scale3, } -#[cfg(any(stm32h7rs))] -pub use crate::pac::{ - pwr::vals::Vos as VoltageScale, - rcc::vals::{Usbphycsel, Usbrefcksel}, -}; +#[cfg(stm32h7rs)] +pub use crate::pac::pwr::vals::Vos as VoltageScale; +#[cfg(all(stm32h7rs, peri_usb_otg_hs))] +pub use crate::pac::rcc::vals::{Usbphycsel, Usbrefcksel}; #[derive(Clone, Copy, Eq, PartialEq)] pub enum HseMode { @@ -560,14 +559,14 @@ pub(crate) unsafe fn init(config: Config) { let rtc = config.ls.init(); - #[cfg(stm32h7rs)] + #[cfg(all(stm32h7rs, peri_usb_otg_hs))] let usb_refck = match config.mux.usbphycsel { Usbphycsel::HSE => hse, Usbphycsel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8), Usbphycsel::PLL3_Q => pll3.q, _ => None, }; - #[cfg(stm32h7rs)] + #[cfg(all(stm32h7rs, peri_usb_otg_hs))] let usb_refck_sel = match usb_refck { Some(clk_val) => match clk_val { Hertz(16_000_000) => Usbrefcksel::MHZ16, @@ -618,6 +617,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_ppre5(config.apb5_pre); }); + #[cfg(peri_usb_otg_hs)] RCC.ahbperckselr().modify(|w| { w.set_usbrefcksel(usb_refck_sel); }); From db31e3648500bc7f36e8ac0d8c101b88cd4b956c Mon Sep 17 00:00:00 2001 From: Shaw Drastin <168159404+showier-drastic@users.noreply.github.com> Date: Sun, 22 Sep 2024 02:05:17 +0800 Subject: [PATCH 31/39] stm32/spi: issue correct DMA word length when reading Currently, when calling read() of the SPI bus, DMA always transmits u8, which will cause hang if SPI transfer size > 8bit. Use matching word size for TX DMA instead. --- embassy-stm32/src/spi/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 20718147a..dfac4bc89 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -783,7 +783,7 @@ impl<'d> Spi<'d, Async> { let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read(rx_src, data, Default::default()) }; let tx_dst = self.info.regs.tx_ptr(); - let clock_byte = 0x00u8; + let clock_byte = W::default(); let tx_f = unsafe { self.tx_dma .as_mut() @@ -1195,7 +1195,7 @@ trait SealedWord { /// Word sizes usable for SPI. #[allow(private_bounds)] -pub trait Word: word::Word + SealedWord {} +pub trait Word: word::Word + SealedWord + Default {} macro_rules! impl_word { ($T:ty, $config:expr) => { From e2d2b0f36281326772678f0e903675927faaaddb Mon Sep 17 00:00:00 2001 From: Hans Josephsen Date: Sun, 22 Sep 2024 12:30:38 +0200 Subject: [PATCH 32/39] Currently the return value of `write` is broken, it never returns the previous frame even when present. This happens because a slice of length 64 is always passed to Frame::new from within the `abort_pending_mailbox` function, causing `Frame::new` to return None. The fix is to take a subslice of length `data_length`. --- .gitignore | 1 + embassy-stm32/src/can/fd/peripheral.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1c221e876..a0b5d6a70 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ Cargo.lock third_party /Cargo.toml out/ +.zed diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 07e3dddad..1c7abfcb2 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -200,7 +200,7 @@ impl Registers { if header_reg.rtr().bit() { F::new_remote(id, len as usize) } else { - F::new(id, &data) + F::new(id, &data[0..(len as usize)]) } } else { // Abort request failed because the frame was already sent (or being sent) on From f2f96a731c1c08045a210083d307bc63814ae6bf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 23 Sep 2024 00:32:17 +0200 Subject: [PATCH 33/39] stm32/gpdma: ensure bndt in bytes doesn't overflow. --- embassy-stm32/src/dma/gpdma.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index f9d66ca86..792ddc4e8 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -216,7 +216,10 @@ impl<'a> Transfer<'a> { data_size: WordSize, _options: TransferOptions, ) -> Self { - assert!(mem_len > 0 && mem_len <= 0xFFFF); + // BNDT is specified as bytes, not as number of transfers. + let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else { + panic!("DMA transfers may not be larger than 65535 bytes."); + }; let info = channel.info(); let ch = info.dma.ch(info.num); @@ -226,9 +229,6 @@ impl<'a> Transfer<'a> { let this = Self { channel }; - #[cfg(dmamux)] - super::dmamux::configure_dmamux(&*this.channel, request); - ch.cr().write(|w| w.set_reset(true)); ch.fcr().write(|w| w.0 = 0xFFFF_FFFF); // clear all irqs ch.llr().write(|_| {}); // no linked list @@ -245,10 +245,7 @@ impl<'a> Transfer<'a> { }); w.set_reqsel(request); }); - ch.br1().write(|w| { - // BNDT is specified as bytes, not as number of transfers. - w.set_bndt((mem_len * data_size.bytes()) as u16) - }); + ch.br1().write(|w| w.set_bndt(bndt)); match dir { Dir::MemoryToPeripheral => { From 59dcffbc6021ab1aaef1dd546aa7e707b438e45f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 23 Sep 2024 01:32:55 +0200 Subject: [PATCH 34/39] stm32/gpdma: clear tr3 just in case. --- embassy-stm32/src/dma/gpdma.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index 792ddc4e8..a877bb8d4 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -245,6 +245,7 @@ impl<'a> Transfer<'a> { }); w.set_reqsel(request); }); + ch.tr3().write(|_| {}); // no address offsets. ch.br1().write(|w| w.set_bndt(bndt)); match dir { From 68b783aedfd7c85505d5961ad3030719676210d1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 23 Sep 2024 01:59:05 +0200 Subject: [PATCH 35/39] stm32/spi: fix hang/corruption of word sizes other than 8bit. --- embassy-stm32/src/spi/mod.rs | 72 +++++++++++++----------------------- 1 file changed, 26 insertions(+), 46 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index dfac4bc89..d034c028e 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -311,51 +311,29 @@ impl<'d, M: PeriMode> Spi<'d, M> { } } + /// Set SPI word size. Disables SPI if needed, you have to enable it back yourself. fn set_word_size(&mut self, word_size: word_impl::Config) { if self.current_word_size == word_size { return; } + self.info.regs.cr1().modify(|w| { + w.set_spe(false); + }); + #[cfg(any(spi_v1, spi_f1))] - { - self.info.regs.cr1().modify(|reg| { - reg.set_spe(false); - reg.set_dff(word_size) - }); - self.info.regs.cr1().modify(|reg| { - reg.set_spe(true); - }); - } + self.info.regs.cr1().modify(|reg| { + reg.set_dff(word_size); + }); #[cfg(spi_v2)] - { - self.info.regs.cr1().modify(|w| { - w.set_spe(false); - }); - self.info.regs.cr2().modify(|w| { - w.set_frxth(word_size.1); - w.set_ds(word_size.0); - }); - self.info.regs.cr1().modify(|w| { - w.set_spe(true); - }); - } + self.info.regs.cr2().modify(|w| { + w.set_frxth(word_size.1); + w.set_ds(word_size.0); + }); #[cfg(any(spi_v3, spi_v4, spi_v5))] - { - self.info.regs.cr1().modify(|w| { - w.set_csusp(true); - }); - while self.info.regs.sr().read().eot() {} - self.info.regs.cr1().modify(|w| { - w.set_spe(false); - }); - self.info.regs.cfg1().modify(|w| { - w.set_dsize(word_size); - }); - self.info.regs.cr1().modify(|w| { - w.set_csusp(false); - w.set_spe(true); - }); - } + self.info.regs.cfg1().modify(|w| { + w.set_dsize(word_size); + }); self.current_word_size = word_size; } @@ -365,9 +343,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? #[cfg(any(spi_v3, spi_v4, spi_v5))] self.info.regs.cr1().modify(|w| w.set_spe(false)); + self.set_word_size(W::CONFIG); self.info.regs.cr1().modify(|w| w.set_spe(true)); flush_rx_fifo(self.info.regs); - self.set_word_size(W::CONFIG); for word in words.iter() { // this cannot use `transfer_word` because on SPIv2 and higher, // the SPI RX state machine hangs if no physical pin is connected to the SCK AF. @@ -402,9 +380,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? #[cfg(any(spi_v3, spi_v4, spi_v5))] self.info.regs.cr1().modify(|w| w.set_spe(false)); + self.set_word_size(W::CONFIG); self.info.regs.cr1().modify(|w| w.set_spe(true)); flush_rx_fifo(self.info.regs); - self.set_word_size(W::CONFIG); for word in words.iter_mut() { *word = transfer_word(self.info.regs, W::default())?; } @@ -418,9 +396,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? #[cfg(any(spi_v3, spi_v4, spi_v5))] self.info.regs.cr1().modify(|w| w.set_spe(false)); + self.set_word_size(W::CONFIG); self.info.regs.cr1().modify(|w| w.set_spe(true)); flush_rx_fifo(self.info.regs); - self.set_word_size(W::CONFIG); for word in words.iter_mut() { *word = transfer_word(self.info.regs, *word)?; } @@ -437,9 +415,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? #[cfg(any(spi_v3, spi_v4, spi_v5))] self.info.regs.cr1().modify(|w| w.set_spe(false)); + self.set_word_size(W::CONFIG); self.info.regs.cr1().modify(|w| w.set_spe(true)); flush_rx_fifo(self.info.regs); - 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(); @@ -648,10 +626,10 @@ impl<'d> Spi<'d, Async> { return Ok(()); } - self.set_word_size(W::CONFIG); self.info.regs.cr1().modify(|w| { w.set_spe(false); }); + self.set_word_size(W::CONFIG); let tx_dst = self.info.regs.tx_ptr(); let tx_f = unsafe { self.tx_dma.as_mut().unwrap().write(data, tx_dst, Default::default()) }; @@ -685,6 +663,8 @@ impl<'d> Spi<'d, Async> { w.set_spe(false); }); + self.set_word_size(W::CONFIG); + let comm = regs.cfg2().modify(|w| { let prev = w.comm(); w.set_comm(vals::Comm::RECEIVER); @@ -707,7 +687,6 @@ impl<'d> Spi<'d, Async> { let rx_src = regs.rx_ptr(); for mut chunk in data.chunks_mut(u16::max_value().into()) { - self.set_word_size(W::CONFIG); set_rxdmaen(regs, true); let tsize = chunk.len(); @@ -765,12 +744,12 @@ impl<'d> Spi<'d, Async> { return Ok(()); } - self.set_word_size(W::CONFIG); - self.info.regs.cr1().modify(|w| { w.set_spe(false); }); + self.set_word_size(W::CONFIG); + // SPIv3 clears rxfifo on SPE=0 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] flush_rx_fifo(self.info.regs); @@ -813,11 +792,12 @@ impl<'d> Spi<'d, Async> { return Ok(()); } - self.set_word_size(W::CONFIG); self.info.regs.cr1().modify(|w| { w.set_spe(false); }); + self.set_word_size(W::CONFIG); + // SPIv3 clears rxfifo on SPE=0 #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] flush_rx_fifo(self.info.regs); From fb0afa8a0fbc47e76a5f9357da2898e0c7a0ee27 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 23 Sep 2024 02:06:11 +0200 Subject: [PATCH 36/39] stm32: update metapac. Fixes SPI version on L0. --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 575c4f20c..8fc8da006 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-acaf04256034066bd5b3a8426224ccf3e4cb7d19" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9b7414490b10ffbd5beb1b0dcf14adb018cbe37f" } vcell = "0.1.3" nb = "1.0.0" @@ -99,7 +99,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-acaf04256034066bd5b3a8426224ccf3e4cb7d19", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9b7414490b10ffbd5beb1b0dcf14adb018cbe37f", default-features = false, features = ["metadata"] } [features] default = ["rt"] From a71098d824346a25ab9fb376627c0383d0f9250a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 22 Sep 2024 10:53:04 +0200 Subject: [PATCH 37/39] stm32/tests: test spi u8 and u16 word sizes. --- tests/stm32/src/bin/spi.rs | 100 ++++++++++++++++++---------- tests/stm32/src/bin/spi_dma.rs | 116 +++++++++++++++++++++------------ 2 files changed, 141 insertions(+), 75 deletions(-) diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 53d44a94a..9712a8c5a 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -7,7 +7,8 @@ use common::*; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::spi::{self, Spi}; +use embassy_stm32::mode::Blocking; +use embassy_stm32::spi::{self, Spi, Word}; use embassy_stm32::time::Hertz; #[embassy_executor::main] @@ -31,11 +32,58 @@ async fn main(_spawner: Spawner) { spi_config, ); - let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE]; + test_txrx::(&mut spi); + test_txrx::(&mut spi); + + // Assert the RCC bit gets disabled on drop. + #[cfg(feature = "stm32f429zi")] + defmt::assert!(embassy_stm32::pac::RCC.apb2enr().read().spi1en()); + drop(spi); + #[cfg(feature = "stm32f429zi")] + defmt::assert!(!embassy_stm32::pac::RCC.apb2enr().read().spi1en()); + + // test rx-only configuration + let mut spi = Spi::new_blocking_rxonly(&mut spi_peri, &mut sck, &mut miso, spi_config); + let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); + + test_rx::(&mut spi, &mut mosi_out); + test_rx::(&mut spi, &mut mosi_out); + drop(spi); + drop(mosi_out); + + let mut spi = Spi::new_blocking_txonly(&mut spi_peri, &mut sck, &mut mosi, spi_config); + test_tx::(&mut spi); + test_tx::(&mut spi); + drop(spi); + + let mut spi = Spi::new_blocking_txonly_nosck(&mut spi_peri, &mut mosi, spi_config); + test_tx::(&mut spi); + test_tx::(&mut spi); + drop(spi); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +fn test_txrx + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking>) +where + W: core::ops::Not, +{ + let data: [W; 9] = [ + 0x00u8.into(), + 0xFFu8.into(), + 0xAAu8.into(), + 0x55u8.into(), + 0xC0u8.into(), + 0xFFu8.into(), + 0xEEu8.into(), + 0xC0u8.into(), + 0xDEu8.into(), + ]; // Arduino pins D11 and D12 (MOSI-MISO) are connected together with a 1K resistor. // so we should get the data we sent back. - let mut buf = [0; 9]; + let mut buf = [W::default(); 9]; spi.blocking_transfer(&mut buf, &data).unwrap(); assert_eq!(buf, data); @@ -59,47 +107,33 @@ async fn main(_spawner: Spawner) { spi.blocking_transfer_in_place::(&mut []).unwrap(); spi.blocking_read::(&mut []).unwrap(); spi.blocking_write::(&[]).unwrap(); +} - // Assert the RCC bit gets disabled on drop. - #[cfg(feature = "stm32f429zi")] - defmt::assert!(embassy_stm32::pac::RCC.apb2enr().read().spi1en()); - drop(spi); - #[cfg(feature = "stm32f429zi")] - defmt::assert!(!embassy_stm32::pac::RCC.apb2enr().read().spi1en()); +fn test_rx + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking>, mosi_out: &mut Output<'_>) +where + W: core::ops::Not, +{ + let mut buf = [W::default(); 9]; - // test rx-only configuration - let mut spi = Spi::new_blocking_rxonly(&mut spi_peri, &mut sck, &mut miso, spi_config); - let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); mosi_out.set_high(); spi.blocking_read(&mut buf).unwrap(); - assert_eq!(buf, [0xff; 9]); + assert_eq!(buf, [!W::default(); 9]); mosi_out.set_low(); spi.blocking_read(&mut buf).unwrap(); - assert_eq!(buf, [0x00; 9]); + assert_eq!(buf, [W::default(); 9]); spi.blocking_read::(&mut []).unwrap(); spi.blocking_read::(&mut []).unwrap(); - drop(mosi_out); - drop(spi); +} + +fn test_tx + defmt::Format + Eq>(spi: &mut Spi<'_, Blocking>) +where + W: core::ops::Not, +{ + let buf = [W::default(); 9]; // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave. - let mut spi = Spi::new_blocking_txonly(&mut spi_peri, &mut sck, &mut mosi, spi_config); - spi.blocking_transfer(&mut buf, &data).unwrap(); - spi.blocking_transfer_in_place(&mut buf).unwrap(); - spi.blocking_write(&buf).unwrap(); - spi.blocking_read(&mut buf).unwrap(); - spi.blocking_transfer::(&mut [], &[]).unwrap(); - spi.blocking_transfer_in_place::(&mut []).unwrap(); - spi.blocking_read::(&mut []).unwrap(); - spi.blocking_write::(&[]).unwrap(); - drop(spi); - - // Test tx-only nosck. - let mut spi = Spi::new_blocking_txonly_nosck(&mut spi_peri, &mut mosi, spi_config); spi.blocking_write(&buf).unwrap(); spi.blocking_write::(&[]).unwrap(); spi.blocking_write(&buf).unwrap(); - drop(spi); - - info!("Test OK"); - cortex_m::asm::bkpt(); + spi.blocking_write::(&[]).unwrap(); } diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index a1cbc0ed1..307409a16 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -7,7 +7,8 @@ use common::*; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::spi::{self, Spi}; +use embassy_stm32::mode::Async; +use embassy_stm32::spi::{self, Spi, Word}; use embassy_stm32::time::Hertz; #[embassy_executor::main] @@ -35,11 +36,61 @@ async fn main(_spawner: Spawner) { spi_config, ); - let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE]; + test_txrx::(&mut spi).await; + test_txrx::(&mut spi).await; + drop(spi); + + // test rx-only configuration + let mut spi = Spi::new_rxonly( + &mut spi_peri, + &mut sck, + &mut miso, + // SPIv1/f1 requires txdma even if rxonly. + #[cfg(not(feature = "spi-v345"))] + &mut tx_dma, + &mut rx_dma, + spi_config, + ); + let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); + + test_rx::(&mut spi, &mut mosi_out).await; + test_rx::(&mut spi, &mut mosi_out).await; + drop(spi); + drop(mosi_out); + + let mut spi = Spi::new_txonly(&mut spi_peri, &mut sck, &mut mosi, &mut tx_dma, spi_config); + test_tx::(&mut spi).await; + test_tx::(&mut spi).await; + drop(spi); + + let mut spi = Spi::new_txonly_nosck(&mut spi_peri, &mut mosi, &mut tx_dma, spi_config); + test_tx::(&mut spi).await; + test_tx::(&mut spi).await; + drop(spi); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +async fn test_txrx + defmt::Format + Eq>(spi: &mut Spi<'_, Async>) +where + W: core::ops::Not, +{ + let data: [W; 9] = [ + 0x00u8.into(), + 0xFFu8.into(), + 0xAAu8.into(), + 0x55u8.into(), + 0xC0u8.into(), + 0xFFu8.into(), + 0xEEu8.into(), + 0xC0u8.into(), + 0xDEu8.into(), + ]; // Arduino pins D11 and D12 (MOSI-MISO) are connected together with a 1K resistor. // so we should get the data we sent back. - let mut buf = [0; 9]; + let mut buf = [W::default(); 9]; spi.transfer(&mut buf, &data).await.unwrap(); assert_eq!(buf, data); @@ -83,44 +134,41 @@ async fn main(_spawner: Spawner) { spi.blocking_write(&buf).unwrap(); spi.blocking_read(&mut buf).unwrap(); spi.write(&buf).await.unwrap(); +} - core::mem::drop(spi); +async fn test_rx + defmt::Format + Eq>(spi: &mut Spi<'_, Async>, mosi_out: &mut Output<'_>) +where + W: core::ops::Not, +{ + let mut buf = [W::default(); 9]; - // test rx-only configuration - let mut spi = Spi::new_rxonly( - &mut spi_peri, - &mut sck, - &mut miso, - // SPIv1/f1 requires txdma even if rxonly. - #[cfg(not(feature = "spi-v345"))] - &mut tx_dma, - &mut rx_dma, - spi_config, - ); - let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); mosi_out.set_high(); spi.read(&mut buf).await.unwrap(); - assert_eq!(buf, [0xff; 9]); + assert_eq!(buf, [!W::default(); 9]); spi.blocking_read(&mut buf).unwrap(); - assert_eq!(buf, [0xff; 9]); + assert_eq!(buf, [!W::default(); 9]); spi.read(&mut buf).await.unwrap(); - assert_eq!(buf, [0xff; 9]); + assert_eq!(buf, [!W::default(); 9]); spi.read(&mut buf).await.unwrap(); - assert_eq!(buf, [0xff; 9]); + assert_eq!(buf, [!W::default(); 9]); spi.blocking_read(&mut buf).unwrap(); - assert_eq!(buf, [0xff; 9]); + assert_eq!(buf, [!W::default(); 9]); spi.blocking_read(&mut buf).unwrap(); - assert_eq!(buf, [0xff; 9]); + assert_eq!(buf, [!W::default(); 9]); mosi_out.set_low(); spi.read(&mut buf).await.unwrap(); - assert_eq!(buf, [0x00; 9]); + assert_eq!(buf, [W::default(); 9]); spi.read::(&mut []).await.unwrap(); spi.blocking_read::(&mut []).unwrap(); - drop(mosi_out); - drop(spi); +} + +async fn test_tx + defmt::Format + Eq>(spi: &mut Spi<'_, Async>) +where + W: core::ops::Not, +{ + let buf = [W::default(); 9]; // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave. - let mut spi = Spi::new_txonly(&mut spi_peri, &mut sck, &mut mosi, &mut tx_dma, spi_config); spi.blocking_write(&buf).unwrap(); spi.write(&buf).await.unwrap(); spi.blocking_write(&buf).unwrap(); @@ -129,20 +177,4 @@ async fn main(_spawner: Spawner) { spi.write(&buf).await.unwrap(); spi.write::(&[]).await.unwrap(); spi.blocking_write::(&[]).unwrap(); - drop(spi); - - // Test tx-only nosck. - let mut spi = Spi::new_txonly_nosck(&mut spi_peri, &mut mosi, &mut tx_dma, spi_config); - spi.blocking_write(&buf).unwrap(); - spi.write(&buf).await.unwrap(); - spi.blocking_write(&buf).unwrap(); - spi.blocking_write(&buf).unwrap(); - spi.write(&buf).await.unwrap(); - spi.write(&buf).await.unwrap(); - spi.write::(&[]).await.unwrap(); - spi.blocking_write::(&[]).unwrap(); - drop(spi); - - info!("Test OK"); - cortex_m::asm::bkpt(); } From 05d453bfd81bce49d541b2eeee3a8788e5a5fd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20H=C3=A9riveaux?= Date: Mon, 23 Sep 2024 16:11:45 +0200 Subject: [PATCH 38/39] Fixed signature script in bootloader documentation --- docs/pages/bootloader.adoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/pages/bootloader.adoc b/docs/pages/bootloader.adoc index 3b0cdb182..a80e75c4c 100644 --- a/docs/pages/bootloader.adoc +++ b/docs/pages/bootloader.adoc @@ -86,8 +86,7 @@ Then, to sign your firmware given a declaration of `FIRMWARE_DIR` and a firmware [source, bash] ---- -shasum -a 512 -b $FIRMWARE_DIR/myfirmware > $SECRETS_DIR/message.txt -cat $SECRETS_DIR/message.txt | dd ibs=128 count=1 | xxd -p -r > $SECRETS_DIR/message.txt +shasum -a 512 -b $FIRMWARE_DIR/myfirmware | head -c128 | xxd -p -r > $SECRETS_DIR/message.txt signify -S -s $SECRETS_DIR/key.sec -m $SECRETS_DIR/message.txt -x $SECRETS_DIR/message.txt.sig cp $FIRMWARE_DIR/myfirmware $FIRMWARE_DIR/myfirmware+signed tail -n1 $SECRETS_DIR/message.txt.sig | base64 -d -i - | dd ibs=10 skip=1 >> $FIRMWARE_DIR/myfirmware+signed From 0b8c4587c2726e4dd9186debb515034d687a22d3 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Mon, 23 Sep 2024 13:38:43 -0400 Subject: [PATCH 39/39] Fix rp2350b pins >31 on debug builds --- embassy-rp/src/gpio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 520043b07..cb54375e4 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -603,7 +603,7 @@ impl<'d> Flex<'d> { #[inline] fn bit(&self) -> u32 { - 1 << self.pin.pin() + 1 << (self.pin.pin() % 32) } /// Set the pin's pull.