diff --git a/on-target-tests/build.rs b/on-target-tests/build.rs index d88d873a8..b952d7a88 100644 --- a/on-target-tests/build.rs +++ b/on-target-tests/build.rs @@ -15,6 +15,9 @@ fn main() { let memory_x = include_bytes!("memory_rp2040.x"); #[cfg(feature = "rp235x")] let memory_x = include_bytes!("memory_rp235x.x"); + #[cfg(not(any(feature = "rp2040", feature = "rp235x")))] + compile_error!("No target feature enabled"); + let mut f = File::create(out.join("memory.x")).unwrap(); f.write_all(memory_x).unwrap(); println!("cargo:rerun-if-changed=memory.x"); diff --git a/rp2040-hal/src/async_utils.rs b/rp2040-hal/src/async_utils.rs index 11f82760d..d0a793093 100644 --- a/rp2040-hal/src/async_utils.rs +++ b/rp2040-hal/src/async_utils.rs @@ -64,13 +64,12 @@ pub trait AsyncPeripheral: sealed::Wakeable { pub(crate) struct CancellablePollFn<'periph, Periph, PFn, EnIrqFn, CancelFn, OutputTy> where Periph: sealed::Wakeable, - CancelFn: FnMut(&mut Periph), + CancelFn: FnOnce(&mut Periph), { periph: &'periph mut Periph, poll: PFn, enable_irq: EnIrqFn, - cancel: CancelFn, - done: bool, + cancel: Option, // captures F's return type. phantom: PhantomData, } @@ -80,7 +79,7 @@ where Periph: sealed::Wakeable, PFn: FnMut(&mut Periph) -> Poll, EnIrqFn: FnMut(&mut Periph), - CancelFn: FnMut(&mut Periph), + CancelFn: FnOnce(&mut Periph), { pub(crate) fn new( periph: &'p mut Periph, @@ -92,8 +91,7 @@ where periph, poll, enable_irq, - cancel, - done: false, + cancel: Some(cancel), phantom: PhantomData, } } @@ -105,7 +103,7 @@ where Periph: sealed::Wakeable, PFn: FnMut(&mut Periph) -> Poll, EnIrqFn: FnMut(&mut Periph), - CancelFn: FnMut(&mut Periph), + CancelFn: FnOnce(&mut Periph), { type Output = OutputTy; @@ -115,7 +113,7 @@ where ref mut periph, poll: ref mut is_ready, enable_irq: ref mut setup_flags, - ref mut done, + ref mut cancel, .. } = unsafe { self.get_unchecked_mut() }; let r = (is_ready)(periph); @@ -123,7 +121,7 @@ where Periph::waker().register(cx.waker()); (setup_flags)(periph); } else { - *done = true; + *cancel = None; } r } @@ -132,12 +130,12 @@ impl Drop for CancellablePollFn<'_, Periph, PFn, EnIrqFn, CancelFn, OutputTy> where Periph: sealed::Wakeable, - CancelFn: FnMut(&mut Periph), + CancelFn: FnOnce(&mut Periph), { fn drop(&mut self) { - if !self.done { + if let Some(cancel) = self.cancel.take() { Periph::waker().clear(); - (self.cancel)(self.periph); + cancel(self.periph); } } } diff --git a/rp2040-hal/src/i2c.rs b/rp2040-hal/src/i2c.rs index 11455db6b..3143cb1c0 100644 --- a/rp2040-hal/src/i2c.rs +++ b/rp2040-hal/src/i2c.rs @@ -115,6 +115,7 @@ impl ValidAddress for u16 { /// I2C error #[non_exhaustive] +#[derive(PartialEq, Eq)] pub enum Error { /// I2C abort with error Abort(u32), diff --git a/rp2040-hal/src/i2c/peripheral.rs b/rp2040-hal/src/i2c/peripheral.rs index 3055786ee..5be8bbd32 100644 --- a/rp2040-hal/src/i2c/peripheral.rs +++ b/rp2040-hal/src/i2c/peripheral.rs @@ -210,10 +210,10 @@ impl, PINS> Iterator for I2C, PINS> I2C { - /// Returns the next i2c event if any. - pub fn next_event(&mut self) -> Option { - let stat = self.i2c.ic_raw_intr_stat().read(); - + fn internal_next_event( + &mut self, + stat: rp2040_pac::i2c0::ic_raw_intr_stat::R, + ) -> Option { match self.mode.state { State::Idle if stat.start_det().bit_is_set() => { self.i2c.ic_clr_start_det().read(); @@ -241,7 +241,7 @@ impl, PINS> I2C { State::Read if stat.rd_req().bit_is_set() => Some(Event::TransferRead), State::Write if !self.rx_fifo_empty() => Some(Event::TransferWrite), - State::Read | State::Write if stat.restart_det().bit_is_set() => { + State::Read | State::Write | State::Active if stat.restart_det().bit_is_set() => { self.i2c.ic_clr_restart_det().read(); self.i2c.ic_clr_start_det().read(); self.mode.state = State::Active; @@ -258,6 +258,13 @@ impl, PINS> I2C { _ => None, } } + + /// Returns the next i2c event if any. + pub fn next_event(&mut self) -> Option { + let stat = self.i2c.ic_raw_intr_stat().read(); + + self.internal_next_event(stat) + } } macro_rules! impl_wakeable { @@ -291,12 +298,13 @@ where { /// Asynchronously waits for an Event. pub async fn wait_next(&mut self) -> Event { + let mut stat = self.i2c.ic_raw_intr_stat().read(); loop { - if let Some(evt) = self.next_event() { + if let Some(evt) = self.internal_next_event(stat) { return evt; } - CancellablePollFn::new( + stat = CancellablePollFn::new( self, |me| { let stat = me.i2c.ic_raw_intr_stat().read(); @@ -306,7 +314,7 @@ where || stat.rd_req().bit_is_set() || stat.rx_full().bit_is_set() { - Poll::Ready(()) + Poll::Ready(stat) } else { Poll::Pending } diff --git a/rp2040-hal/src/pio.rs b/rp2040-hal/src/pio.rs index 980c281ee..b0a70df80 100644 --- a/rp2040-hal/src/pio.rs +++ b/rp2040-hal/src/pio.rs @@ -14,6 +14,8 @@ use crate::{ typelevel::Sealed, }; +mod non_blocking; + const PIO_INSTRUCTION_COUNT: usize = 32; impl Sealed for PIO0 {} @@ -21,9 +23,18 @@ impl Sealed for PIO1 {} /// PIO Instance pub trait PIOExt: Deref + SubsystemReset + Sized + Send + Sealed { + /// RX FIFO depth + const RX_FIFO_DEPTH: usize; + + /// TX FIFO depth + const TX_FIFO_DEPTH: usize; + /// Associated Pin Function. type PinFunction: Function; + /// Returns a pointer to the PIO’s Register Block + fn ptr() -> *const RegisterBlock; + /// Create a new PIO wrapper and split the state machines into individual objects. #[allow(clippy::type_complexity)] // Required for symmetry with PIO::free(). fn split( @@ -76,13 +87,23 @@ pub trait PIOExt: Deref + SubsystemReset + Sized + Send } impl PIOExt for PIO0 { + const RX_FIFO_DEPTH: usize = 4; + const TX_FIFO_DEPTH: usize = 4; type PinFunction = FunctionPio0; + fn ptr() -> *const RegisterBlock { + PIO0::ptr() + } fn id() -> usize { 0 } } impl PIOExt for PIO1 { + const RX_FIFO_DEPTH: usize = 4; + const TX_FIFO_DEPTH: usize = 4; type PinFunction = FunctionPio1; + fn ptr() -> *const RegisterBlock { + PIO1::ptr() + } fn id() -> usize { 1 } @@ -576,9 +597,9 @@ pub struct Running; #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum PioIRQ { #[allow(missing_docs)] - Irq0, + Irq0 = 0, #[allow(missing_docs)] - Irq1, + Irq1 = 1, } impl PioIRQ { const fn to_index(self) -> usize { @@ -644,8 +665,8 @@ impl StateMachine { pub fn clear_fifos(&mut self) { // Safety: all accesses to these registers are controlled by this instance unsafe { - let sm = &self.sm.sm(); - let sm_shiftctrl = &sm.sm_shiftctrl(); + let sm = self.sm.sm(); + let sm_shiftctrl = sm.sm_shiftctrl(); let mut current = false; // Toggling the FIFO join state clears the fifo sm_shiftctrl.modify(|r, w| { @@ -679,10 +700,10 @@ impl StateMachine { // Safety: all accesses to these registers are controlled by this instance unsafe { - let sm = &self.sm.sm(); - let sm_pinctrl = &sm.sm_pinctrl(); - let sm_instr = &sm.sm_instr(); - let fstat = &self.sm.pio().fstat(); + let sm = self.sm.sm(); + let sm_pinctrl = sm.sm_pinctrl(); + let sm_instr = sm.sm_instr(); + let fstat = self.sm.pio().fstat(); let operands = if sm.sm_shiftctrl().read().autopull().bit_is_set() { OUT @@ -771,9 +792,9 @@ impl StateMachine { // Safety: all accesses to these registers are controlled by this instance unsafe { let sm = self.sm.sm(); - let sm_pinctrl = &sm.sm_pinctrl(); - let sm_execctrl = &sm.sm_execctrl(); - let sm_instr = &sm.sm_instr(); + let sm_pinctrl = sm.sm_pinctrl(); + let sm_execctrl = sm.sm_execctrl(); + let sm_instr = sm.sm_instr(); // sideset_count is implicitly set to 0 when the set_base/set_count are written (rather // than modified) @@ -1271,8 +1292,8 @@ impl StateMachine { // Safety: all accesses to these registers are controlled by this instance unsafe { let sm = self.sm.sm(); - let sm_pinctrl = &sm.sm_pinctrl(); - let sm_instr = &sm.sm_instr(); + let sm_pinctrl = sm.sm_pinctrl(); + let sm_instr = sm.sm_instr(); // save exec_ctrl & make side_set optional let mut saved_sideset_count = 0; @@ -1380,6 +1401,32 @@ impl Rx { unsafe { self.block().fstat().read().rxfull().bits() & (1 << SM::id()) != 0 } } + /// Reads the number of word in the fifo + pub fn fifo_level(&self) -> usize { + // Safety: read-only access without side-effect + let flevel = unsafe { self.block().flevel().read() }; + (match SM::id() { + 0 => flevel.rx0().bits(), + 1 => flevel.rx1().bits(), + 2 => flevel.rx2().bits(), + 3 => flevel.rx3().bits(), + _ => unreachable!(), + }) as usize + } + + /// Returns the FIFO depth. + pub fn fifo_depth(&self) -> usize { + // Safety: read-only access without side-effect + let block = unsafe { self.block() }; + let join_rx = block.sm(SM::id()).sm_shiftctrl().read().fjoin_rx().bit(); + let depth = block.dbg_cfginfo().read().fifo_depth().bits() as usize; + if join_rx { + depth * 2 + } else { + depth + } + } + /// Enable RX FIFO not empty interrupt. /// /// This interrupt is raised when the RX FIFO is not empty, i.e. one could read more data from it. @@ -1498,7 +1545,7 @@ impl Tx { /// This is a value between 0 and 39. Each FIFO on each state machine on /// each PIO has a unique value. pub fn dreq_value(&self) -> u8 { - if self.block as usize == 0x5020_0000usize { + if self.block == PIO0::ptr() { TREQ_SEL_A::PIO0_TX0 as u8 + (SM::id() as u8) } else { TREQ_SEL_A::PIO1_TX0 as u8 + (SM::id() as u8) @@ -1588,6 +1635,32 @@ impl Tx { unsafe { self.block().fstat().read().txfull().bits() & (1 << SM::id()) != 0 } } + /// Reads the number of word in the FIFO + pub fn fifo_level(&self) -> usize { + // Safety: read-only access without side-effect + let flevel = unsafe { self.block().flevel().read() }; + (match SM::id() { + 0 => flevel.tx0().bits(), + 1 => flevel.tx1().bits(), + 2 => flevel.tx2().bits(), + 3 => flevel.tx3().bits(), + _ => unreachable!(), + }) as usize + } + + /// Returns the FIFO depth. + pub fn fifo_depth(&self) -> usize { + // Safety: read-only access without side-effect + let block = unsafe { self.block() }; + let join_tx = block.sm(SM::id()).sm_shiftctrl().read().fjoin_tx().bit(); + let depth = block.dbg_cfginfo().read().fifo_depth().bits() as usize; + if join_tx { + depth * 2 + } else { + depth + } + } + /// Enable TX FIFO not full interrupt. /// /// This interrupt is raised when the TX FIFO is not full, i.e. one could push more data to it. diff --git a/rp2040-hal/src/pio/non_blocking.rs b/rp2040-hal/src/pio/non_blocking.rs new file mode 100644 index 000000000..321edd38a --- /dev/null +++ b/rp2040-hal/src/pio/non_blocking.rs @@ -0,0 +1,196 @@ +use core::task::Poll; + +use crate::async_utils::{ + sealed::{IrqWaker, Wakeable}, + CancellablePollFn, +}; +use crate::dma::TransferSize; + +use super::{Interrupt, PioIRQ, Rx, Tx}; +use super::{PIOExt, StateMachineIndex, ValidStateMachine}; +use super::{PIO0, PIO1, SM0, SM1, SM2, SM3}; + +macro_rules! impl_wakeable { + ($($pio:ident => ( $($sm:ident),* )),*) => { + $( + impl Wakeable for Interrupt<'_, $pio, IRQ> { + fn waker() -> &'static IrqWaker { + static WAKER: IrqWaker = IrqWaker::new(); + &WAKER + } + } + $( + impl_wakeable!(Rx, 0, "Rx Not Empty", $pio, $sm); + impl_wakeable!(Tx, 4, "Tx Not Full", $pio, $sm); + )* + )* + }; + ($t:ident, $offset:expr, $doc:expr, $pio:ident, $sm:ident) => { + impl Wakeable for $t<($pio, $sm), W> + where + W: TransferSize, + { + fn waker() -> &'static IrqWaker { + static WAKER: IrqWaker = IrqWaker::new(); + &WAKER + } + } + }; +} +impl_wakeable!( + PIO0 => ( SM0, SM1, SM2, SM3 ), + PIO1 => ( SM0, SM1, SM2, SM3 ) +); + +impl Interrupt<'_, P, IRQ> +where + Self: Wakeable, + P: PIOExt, +{ + /// Wakes a task awaiting of this interrupt. + pub fn on_interrupt() { + use crate::atomic_register_access::write_bitmask_clear; + unsafe { + let mask = 1 << (IRQ + 8); + let sm_irq = (*P::ptr()).sm_irq(IRQ); + if sm_irq.irq_ints().read().bits() & mask == mask { + write_bitmask_clear(sm_irq.irq_inte().as_ptr(), mask); + } + } + Self::waker().wake(); + } +} + +macro_rules! on_interrupts { + ($t:ident, $offset:expr, $event:expr) => { + impl $t<(P, SM), W> + where + W: TransferSize, + P: PIOExt, + SM: StateMachineIndex, + Self: Wakeable, + { + /// Wakes a task awaiting on + #[doc = $event] + pub fn on_interrupt(irq: PioIRQ) { + use crate::atomic_register_access::write_bitmask_clear; + unsafe { + let mask = 1 << ($offset + <(P, SM)>::id()); + let irq = (&*P::ptr()).sm_irq(irq as usize); + if irq.irq_ints().read().bits() & mask == mask { + write_bitmask_clear(irq.irq_inte().as_ptr(), mask); + } + } + Self::waker().wake(); + } + } + }; +} +on_interrupts!(Rx, 0, "Rx Not Empty"); +on_interrupts!(Tx, 4, "Tx Not Full"); + +impl Interrupt<'_, P, IRQ> +where + P: PIOExt, + Self: Wakeable, +{ + /// Stalls until the IRQ fires + pub async fn sm_interrupt(&mut self, id: u8) { + assert!(id < 4, "invalid state machine interrupt number"); + let mask = 1 << id; + + CancellablePollFn::new( + self, + |me| unsafe { + if (me.block().irq().read().irq().bits() & mask) == mask { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| me.enable_sm_interrupt(id), + |me| { + me.disable_sm_interrupt(id); + }, + ) + .await + } +} + +impl Rx +where + Self: Wakeable, +{ + /// Reads a u32 from the FIFO. + pub async fn async_read(&mut self, irq: PioIRQ) -> u32 { + self.rx_not_empty(irq).await; + let fifo_address = self.fifo_address(); + unsafe { core::ptr::read_volatile(fifo_address) } + } + + /// Used to `.await` until the fifo is no longer empty. + pub async fn rx_not_empty(&mut self, irq: PioIRQ) { + CancellablePollFn::new( + self, + |me| { + if me.is_empty() { + Poll::Pending + } else { + Poll::Ready(()) + } + }, + |me| me.enable_rx_not_empty_interrupt(irq), + |me| { + me.disable_rx_not_empty_interrupt(irq); + }, + ) + .await + } +} + +impl Tx +where + Self: Wakeable, +{ + /// Writes a u8 to the FIFO replicated 4 times in a 32bits word. + pub async fn async_write_u8_replicated(&mut self, irq: PioIRQ, value: u8) { + self.async_write_generic(irq, value).await + } + /// Writes a u16 to the FIFO replicated 2 times in a 32bits word. + pub async fn async_write_u16_replicated(&mut self, irq: PioIRQ, value: u16) { + self.async_write_generic(irq, value).await + } + + /// Writes a u32 to the FIFO. + pub async fn async_write(&mut self, irq: PioIRQ, value: u32) { + self.async_write_generic(irq, value).await + } + + async fn async_write_generic(&mut self, irq: PioIRQ, value: T) { + self.tx_not_full(irq).await; + // Safety: Only accessed by this instance (unless DMA is used). + unsafe { + let reg_ptr = self.fifo_address() as *mut T; + reg_ptr.write_volatile(value); + } + } + + /// Used to `.await` until the fifo is no longer full. + pub async fn tx_not_full(&mut self, irq: PioIRQ) { + CancellablePollFn::new( + self, + |me| { + if me.is_full() { + Poll::Pending + } else { + Poll::Ready(()) + } + }, + |me| me.enable_tx_not_full_interrupt(irq), + |me| { + me.disable_tx_not_full_interrupt(irq); + }, + ) + .await + } +} diff --git a/rp235x-hal/src/i2c/peripheral.rs b/rp235x-hal/src/i2c/peripheral.rs index 27c8450d7..7f347840e 100644 --- a/rp235x-hal/src/i2c/peripheral.rs +++ b/rp235x-hal/src/i2c/peripheral.rs @@ -210,10 +210,10 @@ impl, PINS> Iterator for I2C, PINS> I2C { - /// Returns the next i2c event if any. - pub fn next_event(&mut self) -> Option { - let stat = self.i2c.ic_raw_intr_stat().read(); - + fn internal_next_event( + &mut self, + stat: rp235x_pac::i2c0::ic_raw_intr_stat::R, + ) -> Option { match self.mode.state { State::Idle if stat.start_det().bit_is_set() => { self.i2c.ic_clr_start_det().read(); @@ -241,7 +241,7 @@ impl, PINS> I2C { State::Read if stat.rd_req().bit_is_set() => Some(Event::TransferRead), State::Write if !self.rx_fifo_empty() => Some(Event::TransferWrite), - State::Read | State::Write if stat.restart_det().bit_is_set() => { + State::Read | State::Write | State::Active if stat.restart_det().bit_is_set() => { self.i2c.ic_clr_restart_det().read(); self.i2c.ic_clr_start_det().read(); self.mode.state = State::Active; @@ -258,6 +258,13 @@ impl, PINS> I2C { _ => None, } } + + /// Returns the next i2c event if any. + pub fn next_event(&mut self) -> Option { + let stat = self.i2c.ic_raw_intr_stat().read(); + + self.internal_next_event(stat) + } } macro_rules! impl_wakeable { @@ -291,12 +298,13 @@ where { /// Asynchronously waits for an Event. pub async fn wait_next(&mut self) -> Event { + let mut stat = self.i2c.ic_raw_intr_stat().read(); loop { - if let Some(evt) = self.next_event() { + if let Some(evt) = self.internal_next_event(stat) { return evt; } - CancellablePollFn::new( + stat = CancellablePollFn::new( self, |me| { let stat = me.i2c.ic_raw_intr_stat().read(); @@ -306,7 +314,7 @@ where || stat.rd_req().bit_is_set() || stat.rx_full().bit_is_set() { - Poll::Ready(()) + Poll::Ready(stat) } else { Poll::Pending } diff --git a/rp235x-hal/src/pio.rs b/rp235x-hal/src/pio.rs index deadda1d3..9dc94d885 100644 --- a/rp235x-hal/src/pio.rs +++ b/rp235x-hal/src/pio.rs @@ -15,6 +15,8 @@ use crate::{ typelevel::Sealed, }; +mod non_blocking; + const PIO_INSTRUCTION_COUNT: usize = 32; impl Sealed for PIO0 {} @@ -23,9 +25,18 @@ impl Sealed for PIO2 {} /// PIO Instance pub trait PIOExt: Deref + SubsystemReset + Sized + Send + Sealed { + /// RX FIFO depth + const RX_FIFO_DEPTH: usize; + + /// TX FIFO depth + const TX_FIFO_DEPTH: usize; + /// Associated Pin Function. type PinFunction: Function; + /// Returns a pointer to the PIO’s Register Block + fn ptr() -> *const RegisterBlock; + /// Create a new PIO wrapper and split the state machines into individual objects. #[allow(clippy::type_complexity)] // Required for symmetry with PIO::free(). fn split( @@ -78,19 +89,34 @@ pub trait PIOExt: Deref + SubsystemReset + Sized + Send } impl PIOExt for PIO0 { + const RX_FIFO_DEPTH: usize = 4; + const TX_FIFO_DEPTH: usize = 4; type PinFunction = FunctionPio0; + fn ptr() -> *const RegisterBlock { + PIO0::ptr() + } fn id() -> usize { 0 } } impl PIOExt for PIO1 { + const RX_FIFO_DEPTH: usize = 4; + const TX_FIFO_DEPTH: usize = 4; type PinFunction = FunctionPio1; + fn ptr() -> *const RegisterBlock { + PIO1::ptr() + } fn id() -> usize { 1 } } impl PIOExt for PIO2 { + const RX_FIFO_DEPTH: usize = 4; + const TX_FIFO_DEPTH: usize = 4; type PinFunction = FunctionPio2; + fn ptr() -> *const RegisterBlock { + PIO1::ptr() + } fn id() -> usize { 2 } @@ -601,9 +627,9 @@ pub struct Running; #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum PioIRQ { #[allow(missing_docs)] - Irq0, + Irq0 = 0, #[allow(missing_docs)] - Irq1, + Irq1 = 1, } impl PioIRQ { const fn to_index(self) -> usize { @@ -1408,6 +1434,32 @@ impl Rx { unsafe { self.block().fstat().read().rxfull().bits() & (1 << SM::id()) != 0 } } + /// Reads the number of word in the fifo + pub fn fifo_level(&self) -> usize { + // Safety: read-only access without side-effect + let flevel = unsafe { self.block().flevel().read() }; + (match SM::id() { + 0 => flevel.rx0().bits(), + 1 => flevel.rx1().bits(), + 2 => flevel.rx2().bits(), + 3 => flevel.rx3().bits(), + _ => unreachable!(), + }) as usize + } + + /// Returns the FIFO depth. + pub fn fifo_depth(&self) -> usize { + // Safety: read-only access without side-effect + let block = unsafe { self.block() }; + let join_rx = block.sm(SM::id()).sm_shiftctrl().read().fjoin_rx().bit(); + let depth = block.dbg_cfginfo().read().fifo_depth().bits() as usize; + if join_rx { + depth * 2 + } else { + depth + } + } + /// Enable RX FIFO not empty interrupt. /// /// This interrupt is raised when the RX FIFO is not empty, i.e. one could read more data from it. @@ -1526,7 +1578,7 @@ impl Tx { /// This is a value between 0 and 39. Each FIFO on each state machine on /// each PIO has a unique value. pub fn dreq_value(&self) -> u8 { - if self.block as usize == 0x5020_0000usize { + if self.block == PIO0::ptr() { TREQ_SEL_A::PIO0_TX0 as u8 + (SM::id() as u8) } else if self.block as usize == 0x5030_0000usize { TREQ_SEL_A::PIO1_TX0 as u8 + (SM::id() as u8) @@ -1619,6 +1671,32 @@ impl Tx { unsafe { self.block().fstat().read().txfull().bits() & (1 << SM::id()) != 0 } } + /// Reads the number of word in the FIFO + pub fn fifo_level(&self) -> usize { + // Safety: read-only access without side-effect + let flevel = unsafe { self.block().flevel().read() }; + (match SM::id() { + 0 => flevel.tx0().bits(), + 1 => flevel.tx1().bits(), + 2 => flevel.tx2().bits(), + 3 => flevel.tx3().bits(), + _ => unreachable!(), + }) as usize + } + + /// Returns the FIFO depth. + pub fn fifo_depth(&self) -> usize { + // Safety: read-only access without side-effect + let block = unsafe { self.block() }; + let join_tx = block.sm(SM::id()).sm_shiftctrl().read().fjoin_tx().bit(); + let depth = block.dbg_cfginfo().read().fifo_depth().bits() as usize; + if join_tx { + depth * 2 + } else { + depth + } + } + /// Enable TX FIFO not full interrupt. /// /// This interrupt is raised when the TX FIFO is not full, i.e. one could push more data to it. diff --git a/rp235x-hal/src/pio/non_blocking.rs b/rp235x-hal/src/pio/non_blocking.rs new file mode 100644 index 000000000..4773b42ea --- /dev/null +++ b/rp235x-hal/src/pio/non_blocking.rs @@ -0,0 +1,197 @@ +use core::task::Poll; + +use crate::async_utils::{ + sealed::{IrqWaker, Wakeable}, + CancellablePollFn, +}; +use crate::dma::TransferSize; + +use super::{Interrupt, PioIRQ, Rx, Tx}; +use super::{PIOExt, StateMachineIndex, ValidStateMachine}; +use super::{PIO0, PIO1, PIO2, SM0, SM1, SM2, SM3}; + +macro_rules! impl_wakeable { + ($($pio:ident => ( $($sm:ident),* )),*) => { + $( + impl Wakeable for Interrupt<'_, $pio, IRQ> { + fn waker() -> &'static IrqWaker { + static WAKER: IrqWaker = IrqWaker::new(); + &WAKER + } + } + $( + impl_wakeable!(Rx, 0, "Rx Not Empty", $pio, $sm); + impl_wakeable!(Tx, 4, "Tx Not Full", $pio, $sm); + )* + )* + }; + ($t:ident, $offset:expr, $doc:expr, $pio:ident, $sm:ident) => { + impl Wakeable for $t<($pio, $sm), W> + where + W: TransferSize, + { + fn waker() -> &'static IrqWaker { + static WAKER: IrqWaker = IrqWaker::new(); + &WAKER + } + } + }; +} +impl_wakeable!( + PIO0 => ( SM0, SM1, SM2, SM3 ), + PIO1 => ( SM0, SM1, SM2, SM3 ), + PIO2 => ( SM0, SM1, SM2, SM3 ) +); + +impl Interrupt<'_, P, IRQ> +where + Self: Wakeable, + P: PIOExt, +{ + /// Wakes a task awaiting of this interrupt. + pub fn on_interrupt() { + use crate::atomic_register_access::write_bitmask_clear; + unsafe { + let mask = 1 << (IRQ + 8); + let sm_irq = (*P::ptr()).sm_irq(IRQ); + if sm_irq.irq_ints().read().bits() & mask == mask { + write_bitmask_clear(sm_irq.irq_inte().as_ptr(), mask); + } + } + Self::waker().wake(); + } +} + +macro_rules! on_interrupts { + ($t:ident, $offset:expr, $event:expr) => { + impl $t<(P, SM), W> + where + W: TransferSize, + P: PIOExt, + SM: StateMachineIndex, + Self: Wakeable, + { + /// Wakes a task awaiting on + #[doc = $event] + pub fn on_interrupt(irq: PioIRQ) { + use crate::atomic_register_access::write_bitmask_clear; + unsafe { + let mask = 1 << ($offset + <(P, SM)>::id()); + let irq = (&*P::ptr()).sm_irq(irq as usize); + if irq.irq_ints().read().bits() & mask == mask { + write_bitmask_clear(irq.irq_inte().as_ptr(), mask); + } + } + Self::waker().wake(); + } + } + }; +} +on_interrupts!(Rx, 0, "Rx Not Empty"); +on_interrupts!(Tx, 4, "Tx Not Full"); + +impl Interrupt<'_, P, IRQ> +where + P: PIOExt, + Self: Wakeable, +{ + /// Stalls until the IRQ fires + pub async fn sm_interrupt(&mut self, id: u8) { + assert!(id < 4, "invalid state machine interrupt number"); + let mask = 1 << id; + + CancellablePollFn::new( + self, + |me| unsafe { + if (me.block().irq().read().irq().bits() & mask) == mask { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| me.enable_sm_interrupt(id), + |me| { + me.disable_sm_interrupt(id); + }, + ) + .await + } +} + +impl Rx +where + Self: Wakeable, +{ + /// Reads a u32 from the FIFO. + pub async fn async_read(&mut self, irq: PioIRQ) -> u32 { + self.rx_not_empty(irq).await; + let fifo_address = self.fifo_address(); + unsafe { core::ptr::read_volatile(fifo_address) } + } + + /// Used to `.await` until the fifo is no longer empty. + pub async fn rx_not_empty(&mut self, irq: PioIRQ) { + CancellablePollFn::new( + self, + |me| { + if me.is_empty() { + Poll::Pending + } else { + Poll::Ready(()) + } + }, + |me| me.enable_rx_not_empty_interrupt(irq), + |me| { + me.disable_rx_not_empty_interrupt(irq); + }, + ) + .await + } +} + +impl Tx +where + Self: Wakeable, +{ + /// Writes a u8 to the FIFO replicated 4 times in a 32bits word. + pub async fn async_write_u8_replicated(&mut self, irq: PioIRQ, value: u8) { + self.async_write_generic(irq, value).await + } + /// Writes a u16 to the FIFO replicated 2 times in a 32bits word. + pub async fn async_write_u16_replicated(&mut self, irq: PioIRQ, value: u16) { + self.async_write_generic(irq, value).await + } + + /// Writes a u32 to the FIFO. + pub async fn async_write(&mut self, irq: PioIRQ, value: u32) { + self.async_write_generic(irq, value).await + } + + async fn async_write_generic(&mut self, irq: PioIRQ, value: T) { + self.tx_not_full(irq).await; + // Safety: Only accessed by this instance (unless DMA is used). + unsafe { + let reg_ptr = self.fifo_address() as *mut T; + reg_ptr.write_volatile(value); + } + } + + /// Used to `.await` until the fifo is no longer full. + pub async fn tx_not_full(&mut self, irq: PioIRQ) { + CancellablePollFn::new( + self, + |me| { + if me.is_full() { + Poll::Pending + } else { + Poll::Ready(()) + } + }, + |me| me.enable_tx_not_full_interrupt(irq), + |me| { + me.disable_tx_not_full_interrupt(irq); + }, + ) + .await + } +}