diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 47ff2831a7..7c636d4535 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Rng` and `Trng` now implement `Peripheral

` (#2992) - SPI, UART, I2C: `with_` functions of peripheral drivers now disconnect the previously assigned pins from the peripheral. (#3012) - SPI, UART, I2C: Dropping a driver now disconnects pins from their peripherals. (#3012) - +- Migrate PARL_IO driver to DMA move API (#0000) - `Async` drivers are no longer `Send` (#2980) - GPIO drivers now take configuration structs, and their constructors are fallible (#2990) - `flip-link` feature is now a config option diff --git a/esp-hal/MIGRATING-0.23.md b/esp-hal/MIGRATING-0.23.md index 86cb55ddb6..795aa39e94 100644 --- a/esp-hal/MIGRATING-0.23.md +++ b/esp-hal/MIGRATING-0.23.md @@ -162,6 +162,34 @@ config/config.toml + ESP_HAL_CONFIG_PSRAM_MODE = "octal" ``` +## PARL_IO changes +Parallel IO now uses the newer DMA Move API. + +Changes on the TX side +```diff + let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32000); ++ let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); + +- let transfer = parl_io_tx.write_dma(&tx_buffer).unwrap(); +- transfer.wait().unwrap(); ++ let transfer = parl_io_tx.write(Some(dma_tx_buf.len()), dma_tx_buf).unwrap(); ++ (result, parl_io_tx, dma_tx_buf) = transfer.wait(); ++ result.unwrap(); +``` + +Changes on the RX side +```diff + let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(32000, 0); ++ let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); +- let transfer = parl_io_rx.read_dma(&mut rx_buffer).unwrap(); +- transfer.wait().unwrap(); ++ let transfer = parl_io_rx.read(Some(dma_rx_buf.len()), dma_rx_buf).unwrap(); ++ (_, parl_io_rx, dma_rx_buf) = transfer.wait(); +``` + +On the RX side, the `EofMode` is now decided at transfer time, rather than config time. +- `EofMode::ByteLen` -> `Some()` +- `EofMode::EnableSignal` -> `None` ## UART halves have their configuration split too diff --git a/esp-hal/src/parl_io.rs b/esp-hal/src/parl_io.rs index 5b0414512b..fccb38890e 100644 --- a/esp-hal/src/parl_io.rs +++ b/esp-hal/src/parl_io.rs @@ -22,6 +22,7 @@ //! //! // Initialize DMA buffer and descriptors for data reception //! let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(32000, 0); +//! let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); //! let dma_channel = peripherals.DMA_CH0; //! //! // Configure the 4-bit input pins and clock pin @@ -38,7 +39,6 @@ //! let parl_io = ParlIoRxOnly::new( //! peripherals.PARL_IO, //! dma_channel, -//! rx_descriptors, //! 1.MHz(), //! ) //! .unwrap(); @@ -54,29 +54,28 @@ //! .unwrap(); //! //! // Initialize the buffer and delay -//! let mut buffer = rx_buffer; -//! buffer.fill(0u8); +//! dma_rx_buf.as_mut_slice().fill(0u8); //! let delay = Delay::new(); //! //! loop { //! // Read data via DMA and print received values -//! let transfer = parl_io_rx.read_dma(&mut buffer).unwrap(); -//! transfer.wait().unwrap(); +//! let transfer = parl_io_rx.read(Some(dma_rx_buf.len()), dma_rx_buf).unwrap(); +//! (_, parl_io_rx, dma_rx_buf) = transfer.wait(); //! //! delay.delay_millis(500); //! } //! # } //! ``` -//! +//! //! ### Initialization for TX //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::delay::Delay; -//! # use esp_hal::dma_buffers; +//! # use esp_hal::dma_tx_buffer; //! # use esp_hal::parl_io::{BitPackOrder, ParlIoTxOnly, TxFourBits, SampleEdge, ClkOutPin, TxPinConfigWithValidPin}; //! //! // Initialize DMA buffer and descriptors for data reception -//! let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32000); +//! let mut dma_tx_buf = dma_tx_buffer!(32000).unwrap(); //! let dma_channel = peripherals.DMA_CH0; //! //! // Configure the 4-bit input pins and clock pin @@ -94,7 +93,6 @@ //! let parl_io = ParlIoTxOnly::new( //! peripherals.PARL_IO, //! dma_channel, -//! tx_descriptors, //! 1.MHz(), //! ) //! .unwrap(); @@ -112,19 +110,21 @@ //! .unwrap(); //! //! let buffer = tx_buffer; -//! for i in 0..buffer.len() { -//! buffer[i] = (i % 255) as u8; +//! for i in 0..dma_tx_buf.len() { +//! dma_tx_buf.as_mut_slice()[i] = (i % 255) as u8; //! } //! //! let delay = Delay::new(); //! loop { -//! let transfer = parl_io_tx.write_dma(&buffer).unwrap(); -//! transfer.wait().unwrap(); +//! let transfer = parl_io_tx.write(Some(dma_tx_buf.len()), dma_tx_buf).unwrap(); +//! (_, parl_io_tx, dma_tx_buf) = transfer.wait(); //! delay.delay_millis(500); //! } //! # } //! ``` +use core::mem::ManuallyDrop; +use core::ops::{Deref, DerefMut}; use enumset::{EnumSet, EnumSetType}; use fugit::HertzU32; use peripheral::PeripheralRef; @@ -132,20 +132,14 @@ use private::*; use crate::{ dma::{ - dma_private::{DmaSupport, DmaSupportRx, DmaSupportTx}, Channel, ChannelRx, ChannelTx, - DescriptorChain, DmaChannelFor, - DmaDescriptor, DmaError, DmaPeripheral, - DmaTransferRx, - DmaTransferTx, PeripheralRxChannel, PeripheralTxChannel, - ReadBuffer, Rx, RxChannelFor, Tx, @@ -164,6 +158,8 @@ use crate::{ Blocking, DriverMode, }; +use crate::dma::{DmaRxBuffer, DmaTxBuffer}; +use crate::parl_io::asynch::interrupt_handler; #[allow(unused)] const MAX_DMA_SIZE: usize = 32736; @@ -355,16 +351,6 @@ impl EnableMode { } } -/// Generation of GDMA SUC EOF -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum EofMode { - /// Generate GDMA SUC EOF by data byte length - ByteLen, - /// Generate GDMA SUC EOF by the external enable signal - EnableSignal, -} - /// Used to configure no pin as clock output impl TxClkPin for NoPin { fn configure(&mut self) { @@ -631,7 +617,6 @@ where rx_pins: P, valid_pin: PeripheralRef<'d, InputConnection>, enable_mode: EnableMode, - eof_mode: EofMode, } impl<'d, P> RxPinConfigWithValidPin<'d, P> @@ -643,14 +628,12 @@ where rx_pins: P, valid_pin: impl Peripheral

+ 'd, enable_mode: EnableMode, - eof_mode: EofMode, ) -> Self { crate::into_mapped_ref!(valid_pin); Self { rx_pins, valid_pin, enable_mode, - eof_mode, } } } @@ -678,7 +661,6 @@ where if let Some(sel) = self.enable_mode.smp_model_sel() { Instance::set_rx_sample_mode(sel); } - Instance::set_eof_gen_sel(self.eof_mode); Ok(()) } @@ -691,7 +673,6 @@ where { rx_pins: P, enable_mode: EnableMode, - eof_mode: EofMode, } impl

RxPinConfigIncludingValidPin

@@ -699,11 +680,10 @@ where P: ContainsValidSignalPin + RxPins + ConfigurePins, { /// Create a new [RxPinConfigIncludingValidPin] - pub fn new(rx_pins: P, enable_mode: EnableMode, eof_mode: EofMode) -> Self { + pub fn new(rx_pins: P, enable_mode: EnableMode) -> Self { Self { rx_pins, enable_mode, - eof_mode, } } } @@ -729,7 +709,6 @@ where if let Some(sel) = self.enable_mode.smp_model_sel() { Instance::set_rx_sample_mode(sel); } - Instance::set_eof_gen_sel(self.eof_mode); Ok(()) } @@ -867,7 +846,6 @@ where Ok(ParlIoTx { tx_channel: self.tx_channel, - tx_chain: DescriptorChain::new(self.descriptors), _guard: self._guard, }) } @@ -899,7 +877,6 @@ where Ok(ParlIoTx { tx_channel: self.tx_channel, - tx_chain: DescriptorChain::new(self.descriptors), _guard: self._guard, }) } @@ -912,7 +889,6 @@ where Dm: DriverMode, { tx_channel: ChannelTx<'d, Dm, PeripheralTxChannel>, - tx_chain: DescriptorChain, _guard: GenericPeripheralGuard<{ crate::system::Peripheral::ParlIo as u8 }>, } @@ -951,7 +927,6 @@ where Ok(ParlIoRx { rx_channel: self.rx_channel, - rx_chain: DescriptorChain::new(self.descriptors), _guard: guard, }) } @@ -981,7 +956,6 @@ where Ok(ParlIoRx { rx_channel: self.rx_channel, - rx_chain: DescriptorChain::new(self.descriptors), _guard: self._guard, }) } @@ -994,7 +968,6 @@ where Dm: DriverMode, { rx_channel: ChannelRx<'d, Dm, PeripheralRxChannel>, - rx_chain: DescriptorChain, _guard: GenericPeripheralGuard<{ crate::system::Peripheral::ParlIo as u8 }>, } @@ -1109,8 +1082,6 @@ impl<'d> ParlIoFullDuplex<'d, Blocking> { pub fn new( _parl_io: impl Peripheral

+ 'd, dma_channel: impl Peripheral

+ 'd, - tx_descriptors: &'static mut [DmaDescriptor], - rx_descriptors: &'static mut [DmaDescriptor], frequency: HertzU32, ) -> Result where @@ -1124,12 +1095,10 @@ impl<'d> ParlIoFullDuplex<'d, Blocking> { Ok(Self { tx: TxCreatorFullDuplex { tx_channel: dma_channel.tx, - descriptors: tx_descriptors, _guard: tx_guard, }, rx: RxCreatorFullDuplex { rx_channel: dma_channel.rx, - descriptors: rx_descriptors, _guard: rx_guard, }, }) @@ -1148,15 +1117,32 @@ impl<'d> ParlIoFullDuplex<'d, Blocking> { crate::interrupt::disable(core, Interrupt::PARL_IO_TX); } } + + #[cfg(esp32c6)] + { + unsafe { + crate::interrupt::bind_interrupt(Interrupt::PARL_IO, interrupt_handler.handler()); + } + unwrap!(crate::interrupt::enable(Interrupt::PARL_IO, interrupt_handler.priority())); + } + #[cfg(esp32h2)] + { + unsafe { + crate::interrupt::bind_interrupt(Interrupt::PARL_IO_TX, interrupt_handler.handler()); + } + unwrap!(crate::interrupt::enable( + Interrupt::PARL_IO_TX, + interrupt_handler.priority() + )); + } + ParlIoFullDuplex { tx: TxCreatorFullDuplex { tx_channel: self.tx.tx_channel.into_async(), - descriptors: self.tx.descriptors, _guard: self.tx._guard, }, rx: RxCreatorFullDuplex { rx_channel: self.rx.rx_channel.into_async(), - descriptors: self.rx.descriptors, _guard: self.rx._guard, }, } @@ -1205,12 +1191,10 @@ impl<'d> ParlIoFullDuplex<'d, Async> { ParlIoFullDuplex { tx: TxCreatorFullDuplex { tx_channel: self.tx.tx_channel.into_blocking(), - descriptors: self.tx.descriptors, _guard: self.tx._guard, }, rx: RxCreatorFullDuplex { rx_channel: self.rx.rx_channel.into_blocking(), - descriptors: self.rx.descriptors, _guard: self.rx._guard, }, } @@ -1232,7 +1216,6 @@ impl<'d> ParlIoTxOnly<'d, Blocking> { pub fn new( _parl_io: impl Peripheral

+ 'd, dma_channel: impl Peripheral

+ 'd, - descriptors: &'static mut [DmaDescriptor], frequency: HertzU32, ) -> Result where @@ -1245,7 +1228,6 @@ impl<'d> ParlIoTxOnly<'d, Blocking> { Ok(Self { tx: TxCreator { tx_channel, - descriptors, _guard: guard, }, }) @@ -1264,10 +1246,27 @@ impl<'d> ParlIoTxOnly<'d, Blocking> { crate::interrupt::disable(core, Interrupt::PARL_IO_TX); } } + #[cfg(esp32c6)] + { + unsafe { + crate::interrupt::bind_interrupt(Interrupt::PARL_IO, interrupt_handler.handler()); + } + unwrap!(crate::interrupt::enable(Interrupt::PARL_IO, interrupt_handler.priority())); + } + #[cfg(esp32h2)] + { + unsafe { + crate::interrupt::bind_interrupt(Interrupt::PARL_IO_TX, interrupt_handler.handler()); + } + unwrap!(crate::interrupt::enable( + Interrupt::PARL_IO_TX, + interrupt_handler.priority() + )); + } + ParlIoTxOnly { tx: TxCreator { tx_channel: self.tx.tx_channel.into_async(), - descriptors: self.tx.descriptors, _guard: self.tx._guard, }, } @@ -1308,7 +1307,6 @@ impl<'d> ParlIoTxOnly<'d, Async> { ParlIoTxOnly { tx: TxCreator { tx_channel: self.tx.tx_channel.into_blocking(), - descriptors: self.tx.descriptors, _guard: self.tx._guard, }, } @@ -1338,7 +1336,6 @@ impl<'d> ParlIoRxOnly<'d, Blocking> { pub fn new( _parl_io: impl Peripheral

+ 'd, dma_channel: impl Peripheral

+ 'd, - descriptors: &'static mut [DmaDescriptor], frequency: HertzU32, ) -> Result where @@ -1351,7 +1348,6 @@ impl<'d> ParlIoRxOnly<'d, Blocking> { Ok(Self { rx: RxCreator { rx_channel, - descriptors, _guard: guard, }, }) @@ -1370,11 +1366,27 @@ impl<'d> ParlIoRxOnly<'d, Blocking> { crate::interrupt::disable(core, Interrupt::PARL_IO_TX); } } + #[cfg(esp32c6)] + { + unsafe { + crate::interrupt::bind_interrupt(Interrupt::PARL_IO, interrupt_handler.handler()); + } + unwrap!(crate::interrupt::enable(Interrupt::PARL_IO, interrupt_handler.priority())); + } + #[cfg(esp32h2)] + { + unsafe { + crate::interrupt::bind_interrupt(Interrupt::PARL_IO_TX, interrupt_handler.handler()); + } + unwrap!(crate::interrupt::enable( + Interrupt::PARL_IO_TX, + interrupt_handler.priority() + )); + } ParlIoRxOnly { rx: RxCreator { rx_channel: self.rx.rx_channel.into_async(), - descriptors: self.rx.descriptors, _guard: self.rx._guard, }, } @@ -1415,7 +1427,6 @@ impl<'d> ParlIoRxOnly<'d, Async> { ParlIoRxOnly { rx: RxCreator { rx_channel: self.rx.rx_channel.into_blocking(), - descriptors: self.rx.descriptors, _guard: self.rx._guard, }, } @@ -1458,47 +1469,37 @@ fn internal_init(frequency: HertzU32) -> Result<(), Error> { Ok(()) } -impl ParlIoTx<'_, Dm> +impl<'d, Dm> ParlIoTx<'d, Dm> where Dm: DriverMode, { /// Perform a DMA write. /// - /// This will return a [DmaTransferTx] + /// This will return a [ParlIoTxTransfer] /// /// The maximum amount of data to be sent is 32736 bytes. - pub fn write_dma<'t, TXBUF>( - &'t mut self, - words: &'t TXBUF, - ) -> Result, Error> + pub fn write( + mut self, + number_of_bytes: usize, + mut buffer: BUF, + ) -> Result, Error> where - TXBUF: ReadBuffer, + BUF: DmaTxBuffer, { - let (ptr, len) = unsafe { words.read_buffer() }; - - if len > MAX_DMA_SIZE { + if number_of_bytes > MAX_DMA_SIZE { return Err(Error::MaxDmaTransferSizeExceeded); } - self.start_write_bytes_dma(ptr, len)?; - - Ok(DmaTransferTx::new(self)) - } - - fn start_write_bytes_dma(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> { PCR::regs() .parl_clk_tx_conf() .modify(|_, w| w.parl_tx_rst_en().set_bit()); Instance::clear_tx_interrupts(); - Instance::set_tx_bytes(len as u16); - - self.tx_channel.is_done(); + Instance::set_tx_bytes(number_of_bytes as u16); unsafe { - self.tx_chain.fill_for_tx(false, ptr, len)?; self.tx_channel - .prepare_transfer_without_start(DmaPeripheral::ParlIo, &self.tx_chain) + .prepare_transfer(DmaPeripheral::ParlIo, &mut buffer) .and_then(|_| self.tx_channel.start_transfer())?; } @@ -1510,37 +1511,82 @@ where .parl_clk_tx_conf() .modify(|_, w| w.parl_tx_rst_en().clear_bit()); - Ok(()) + Ok(ParlIoTxTransfer { + parl_io: ManuallyDrop::new(self), + buf_view: ManuallyDrop::new(buffer.into_view()), + }) } } -impl DmaSupport for ParlIoTx<'_, Dm> -where - Dm: DriverMode, -{ - fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) { - while !Instance::is_tx_eof() {} +/// Represents an ongoing (or potentially finished) transfer using the PARL_IO TX. +pub struct ParlIoTxTransfer<'d, BUF: DmaTxBuffer, Dm: DriverMode> { + parl_io: ManuallyDrop>, + buf_view: ManuallyDrop, +} + +impl<'d, BUF: DmaTxBuffer, Dm: DriverMode> ParlIoTxTransfer<'d, BUF, Dm> { + /// Returns true when [Self::wait] will not block. + pub fn is_done(&self) -> bool { + Instance::is_tx_eof() + } + + /// Waits for the transfer to finish and returns the peripheral and buffer. + pub fn wait(mut self) -> (Result<(), DmaError>, ParlIoTx<'d, Dm>, BUF) { + while !self.is_done() {} Instance::set_tx_start(false); + + // Stop the DMA as it doesn't know that the parl io has stopped. + self.parl_io.tx_channel.stop_transfer(); + + let (parl_io, view) = self.release(); + + let result = if parl_io.tx_channel.has_error() { + Err(DmaError::DescriptorError) + } else { + Ok(()) + }; + + (result, parl_io, BUF::from_view(view)) } - fn peripheral_dma_stop(&mut self) { - unreachable!("unsupported") + fn release(mut self) -> (ParlIoTx<'d, Dm>, BUF::View) { + let (parl_io, view) = unsafe { + (ManuallyDrop::take(&mut self.parl_io), + ManuallyDrop::take(&mut self.buf_view)) + }; + core::mem::forget(self); + (parl_io, view) } } -impl<'d, Dm> DmaSupportTx for ParlIoTx<'d, Dm> -where - Dm: DriverMode, -{ - type TX = ChannelTx<'d, Dm, PeripheralTxChannel>; +impl<'d, BUF: DmaTxBuffer, Dm: DriverMode> Deref for ParlIoTxTransfer<'d, BUF, Dm> { + type Target = BUF::View; + + fn deref(&self) -> &Self::Target { + &self.buf_view + } +} - fn tx(&mut self) -> &mut Self::TX { - &mut self.tx_channel +impl<'d, BUF: DmaTxBuffer, Dm: DriverMode> DerefMut for ParlIoTxTransfer<'d, BUF, Dm> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buf_view } +} + +impl<'d, BUF: DmaTxBuffer, Dm: DriverMode> Drop for ParlIoTxTransfer<'d, BUF, Dm> { + fn drop(&mut self) { + // There's no documented way to cancel the PARL IO transfer, so we'll just stop the DMA to + // stop the memory access. + self.parl_io.tx_channel.stop_transfer(); - fn chain(&mut self) -> &mut DescriptorChain { - &mut self.tx_chain + // SAFETY: This is Drop, we know that self.parl_io and self.buf_view + // won't be touched again. + let view = unsafe { + ManuallyDrop::drop(&mut self.parl_io); + ManuallyDrop::take(&mut self.buf_view) + }; + let _ = BUF::from_view(view); } } @@ -1550,36 +1596,22 @@ where { /// Perform a DMA read. /// - /// This will return a [DmaTransferRx] + /// This will return a [ParlIoRxTransfer] /// - /// The maximum amount of data is 32736 bytes when using [EofMode::ByteLen]. + /// When the number of bytes is specified, the maximum amount of data is 32736 bytes and + /// the transfer ends when the number of specified bytes is received. /// - /// It's only limited by the size of the DMA buffer when using - /// [EofMode::EnableSignal]. - pub fn read_dma<'t, RXBUF>( - &'t mut self, - words: &'t mut RXBUF, - ) -> Result, Error> + /// When the number of bytes is unspecified, there's no limit the amount of data transferred + /// and the transfer ends when the enable signal signals the end or the DMA buffer runs out of + /// space. + pub fn read( + mut self, + number_of_bytes: Option, + mut buffer: BUF, + ) -> Result, Error> where - RXBUF: WriteBuffer, + BUF: DmaRxBuffer, { - let (ptr, len) = unsafe { words.write_buffer() }; - - if !Instance::is_suc_eof_generated_externally() && len > MAX_DMA_SIZE { - return Err(Error::MaxDmaTransferSizeExceeded); - } - - Self::start_receive_bytes_dma(&mut self.rx_channel, &mut self.rx_chain, ptr, len)?; - - Ok(DmaTransferRx::new(self)) - } - - fn start_receive_bytes_dma( - rx_channel: &mut ChannelRx<'d, Dm, PeripheralRxChannel>, - rx_chain: &mut DescriptorChain, - ptr: *mut u8, - len: usize, - ) -> Result<(), Error> { PCR::regs() .parl_clk_rx_conf() .modify(|_, w| w.parl_rx_rst_en().set_bit()); @@ -1588,56 +1620,110 @@ where .modify(|_, w| w.parl_rx_rst_en().clear_bit()); Instance::clear_rx_interrupts(); - Instance::set_rx_bytes(len as u16); + if let Some(number_of_bytes) = number_of_bytes { + if number_of_bytes > MAX_DMA_SIZE { + return Err(Error::MaxDmaTransferSizeExceeded); + } + Instance::set_rx_bytes(number_of_bytes as u16); + Instance::set_eof_gen_sel(EofMode::ByteLen); + } else { + Instance::set_eof_gen_sel(EofMode::EnableSignal); + } unsafe { - rx_chain.fill_for_rx(false, ptr, len)?; - rx_channel - .prepare_transfer_without_start(DmaPeripheral::ParlIo, rx_chain) - .and_then(|_| rx_channel.start_transfer())?; + self.rx_channel + .prepare_transfer(DmaPeripheral::ParlIo, &mut buffer) + .and_then(|_| self.rx_channel.start_transfer())?; } Instance::set_rx_reg_update(); Instance::set_rx_start(true); - Ok(()) + + Ok(ParlIoRxTransfer { + parl_io: ManuallyDrop::new(self), + buf_view: ManuallyDrop::new(buffer.into_view()), + dma_result: None, + }) } } -impl DmaSupport for ParlIoRx<'_, Dm> -where - Dm: DriverMode, -{ - fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) { - loop { - if self.rx_channel.is_done() - || self.rx_channel.has_eof_error() - || self.rx_channel.has_dscr_empty_error() - { - break; - } +/// Represents an ongoing (or potentially finished) transfer using the PARL_IO TX. +pub struct ParlIoRxTransfer<'d, BUF: DmaRxBuffer, Dm: DriverMode> { + parl_io: ManuallyDrop>, + buf_view: ManuallyDrop, + // Needed to use DmaRxFuture, which clear the bits we check in is_done() + dma_result: Option>, +} + +impl<'d, BUF: DmaRxBuffer, Dm: DriverMode> ParlIoRxTransfer<'d, BUF, Dm> { + /// Returns true when [Self::wait] will not block. + pub fn is_done(&self) -> bool { + if self.dma_result.is_some() { + return true; } + let ch = &self.parl_io.rx_channel; + ch.is_done() || ch.has_eof_error() || ch.has_dscr_empty_error() + } + + /// Waits for the transfer to finish and returns the peripheral and buffer. + pub fn wait(mut self) -> (Result<(), DmaError>, ParlIoRx<'d, Dm>, BUF) { + while !self.is_done() {} Instance::set_rx_start(false); + + // Stop the DMA as it doesn't know that the parl io has stopped. + self.parl_io.rx_channel.stop_transfer(); + + let dma_result = self.dma_result.take(); + let (parl_io, view) = self.release(); + + let result = if parl_io.rx_channel.has_error() { + Err(DmaError::DescriptorError) + } else { + dma_result.unwrap_or(Ok(())) + }; + + (result, parl_io, BUF::from_view(view)) } - fn peripheral_dma_stop(&mut self) { - unreachable!("unsupported") + fn release(mut self) -> (ParlIoRx<'d, Dm>, BUF::View) { + let (parl_io, view) = unsafe { + (ManuallyDrop::take(&mut self.parl_io), + ManuallyDrop::take(&mut self.buf_view)) + }; + core::mem::forget(self); + (parl_io, view) } } -impl<'d, Dm> DmaSupportRx for ParlIoRx<'d, Dm> -where - Dm: DriverMode, -{ - type RX = ChannelRx<'d, Dm, PeripheralRxChannel>; +impl<'d, BUF: DmaRxBuffer, Dm: DriverMode> Deref for ParlIoRxTransfer<'d, BUF, Dm> { + type Target = BUF::View; - fn rx(&mut self) -> &mut Self::RX { - &mut self.rx_channel + fn deref(&self) -> &Self::Target { + &self.buf_view } +} + +impl<'d, BUF: DmaRxBuffer, Dm: DriverMode> DerefMut for ParlIoRxTransfer<'d, BUF, Dm> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buf_view + } +} - fn chain(&mut self) -> &mut DescriptorChain { - &mut self.rx_chain +impl<'d, BUF: DmaRxBuffer, Dm: DriverMode> Drop for ParlIoRxTransfer<'d, BUF, Dm> { + fn drop(&mut self) { + // There's no documented way to cancel the PARL IO transfer, so we'll just stop the DMA to + // stop the memory access. + self.parl_io.rx_channel.stop_transfer(); + + // SAFETY: This is Drop, we know that self.parl_io and self.buf_view + // won't be touched again. + let view = unsafe { + ManuallyDrop::drop(&mut self.parl_io); + ManuallyDrop::take(&mut self.buf_view) + }; + let _ = BUF::from_view(view); } } @@ -1647,7 +1733,6 @@ where Dm: DriverMode, { tx_channel: ChannelTx<'d, Dm, PeripheralTxChannel>, - descriptors: &'static mut [DmaDescriptor], _guard: GenericPeripheralGuard<{ system::Peripheral::ParlIo as u8 }>, } @@ -1657,7 +1742,6 @@ where Dm: DriverMode, { rx_channel: ChannelRx<'d, Dm, PeripheralRxChannel>, - descriptors: &'static mut [DmaDescriptor], _guard: GenericPeripheralGuard<{ system::Peripheral::ParlIo as u8 }>, } @@ -1667,7 +1751,6 @@ where Dm: DriverMode, { tx_channel: ChannelTx<'d, Dm, PeripheralTxChannel>, - descriptors: &'static mut [DmaDescriptor], _guard: GenericPeripheralGuard<{ system::Peripheral::ParlIo as u8 }>, } @@ -1677,7 +1760,6 @@ where Dm: DriverMode, { rx_channel: ChannelRx<'d, Dm, PeripheralRxChannel>, - descriptors: &'static mut [DmaDescriptor], _guard: GenericPeripheralGuard<{ system::Peripheral::ParlIo as u8 }>, } @@ -1687,12 +1769,12 @@ pub mod asynch { use procmacros::handler; - use super::{private::Instance, Error, ParlIoRx, ParlIoTx, MAX_DMA_SIZE}; + use super::{private::Instance, ParlIoRxTransfer, ParlIoTxTransfer}; use crate::{ asynch::AtomicWaker, - dma::{asynch::DmaRxFuture, ReadBuffer, WriteBuffer}, - peripherals::{Interrupt, PARL_IO}, + dma::asynch::DmaRxFuture, }; + use crate::dma::{DmaRxBuffer, DmaTxBuffer}; static TX_WAKER: AtomicWaker = AtomicWaker::new(); @@ -1701,26 +1783,6 @@ pub mod asynch { impl TxDoneFuture { pub fn new() -> Self { - Instance::listen_tx_done(); - let mut parl_io = unsafe { PARL_IO::steal() }; - - #[cfg(esp32c6)] - { - parl_io.bind_parl_io_interrupt(interrupt_handler.handler()); - unwrap!(crate::interrupt::enable( - Interrupt::PARL_IO, - interrupt_handler.priority() - )); - } - #[cfg(esp32h2)] - { - parl_io.bind_parl_io_tx_interrupt(interrupt_handler.handler()); - unwrap!(crate::interrupt::enable( - Interrupt::PARL_IO_TX, - interrupt_handler.priority() - )); - } - Self {} } } @@ -1733,73 +1795,51 @@ pub mod asynch { cx: &mut core::task::Context<'_>, ) -> Poll { TX_WAKER.register(cx.waker()); - if Instance::is_listening_tx_done() { - Poll::Pending - } else { + if Instance::is_tx_done_set() { Poll::Ready(()) + } else { + Instance::listen_tx_done(); + Poll::Pending } } } + impl Drop for TxDoneFuture { + fn drop(&mut self) { + Instance::unlisten_tx_done(); + } + } + #[handler] - fn interrupt_handler() { + pub(super) fn interrupt_handler() { if Instance::is_tx_done_set() { - Instance::clear_is_tx_done(); Instance::unlisten_tx_done(); TX_WAKER.wake() } } - impl ParlIoTx<'_, crate::Async> { - /// Perform a DMA write. - /// - /// The maximum amount of data to be sent is 32736 bytes. - pub async fn write_dma_async(&mut self, words: &TXBUF) -> Result<(), Error> - where - TXBUF: ReadBuffer, - { - let (ptr, len) = unsafe { words.read_buffer() }; - - if len > MAX_DMA_SIZE { - return Err(Error::MaxDmaTransferSizeExceeded); - } - + impl ParlIoTxTransfer<'_, BUF, crate::Async> { + /// Waits for [Self::is_done] to return true. + pub async fn wait_for_done(&mut self) { let future = TxDoneFuture::new(); - self.start_write_bytes_dma(ptr, len)?; future.await; - - Ok(()) } } - impl ParlIoRx<'_, crate::Async> { - /// Perform a DMA write. - /// - /// The maximum amount of data to be sent is 32736 bytes. - pub async fn read_dma_async<'t, RXBUF>( - &'t mut self, - words: &'t mut RXBUF, - ) -> Result<(), Error> - where - RXBUF: WriteBuffer, - { - let (ptr, len) = unsafe { words.write_buffer() }; - - if !Instance::is_suc_eof_generated_externally() && len > MAX_DMA_SIZE { - return Err(Error::MaxDmaTransferSizeExceeded); + impl ParlIoRxTransfer<'_, BUF, crate::Async> { + /// Waits for [Self::is_done] to return true. + pub async fn wait_for_done(&mut self) { + if self.dma_result.is_some() { + return; } - - let future = DmaRxFuture::new(&mut self.rx_channel); - Self::start_receive_bytes_dma(future.rx, &mut self.rx_chain, ptr, len)?; - future.await?; - - Ok(()) + let future = DmaRxFuture::new(&mut self.parl_io.rx_channel); + self.dma_result = Some(future.await); } } } mod private { - use super::{BitPackOrder, EofMode, Error, SampleEdge}; + use super::{BitPackOrder, Error, SampleEdge}; use crate::peripherals::PARL_IO; pub trait FullDuplex {} @@ -1847,6 +1887,14 @@ mod private { InternalSoftwareEnable = 2, } + /// Generation of GDMA SUC EOF + pub(super) enum EofMode { + /// Generate GDMA SUC EOF by data byte length + ByteLen, + /// Generate GDMA SUC EOF by the external enable signal + EnableSignal, + } + pub(super) struct Instance; #[cfg(esp32c6)] @@ -1976,7 +2024,7 @@ mod private { reg_block .rx_cfg0() - .modify(|_, w| w.rx_eof_gen_sel().bit(mode == EofMode::EnableSignal)); + .modify(|_, w| w.rx_eof_gen_sel().bit(matches!(mode, EofMode::EnableSignal))); } pub fn set_rx_pulse_submode_sel(sel: u8) { @@ -2205,7 +2253,7 @@ mod private { reg_block .rx_genrl_cfg() - .modify(|_, w| w.rx_eof_gen_sel().bit(mode == EofMode::EnableSignal)); + .modify(|_, w| w.rx_eof_gen_sel().bit(matches!(mode, EofMode::EnableSignal))); } pub fn set_rx_pulse_submode_sel(sel: u8) { diff --git a/hil-test/tests/parl_io_tx.rs b/hil-test/tests/parl_io_tx.rs index e361f5dce5..4d4db42ccc 100644 --- a/hil-test/tests/parl_io_tx.rs +++ b/hil-test/tests/parl_io_tx.rs @@ -9,7 +9,8 @@ #[cfg(esp32c6)] use esp_hal::parl_io::{TxPinConfigWithValidPin, TxSixteenBits}; use esp_hal::{ - dma::DmaChannel0, + dma::{DmaChannel0, DmaTxBuf}, + dma_tx_buffer, gpio::{ interconnect::{InputSignal, OutputSignal}, NoPin, @@ -78,8 +79,8 @@ mod tests { #[test] fn test_parl_io_tx_16bit_valid_clock_count(ctx: Context) { const BUFFER_SIZE: usize = 64; - let tx_buffer = [0u16; BUFFER_SIZE]; - let (_, tx_descriptors) = esp_hal::dma_descriptors!(0, 2 * BUFFER_SIZE); + + let mut dma_tx_buf: DmaTxBuf = dma_tx_buffer!(2 * BUFFER_SIZE).unwrap(); let pins = TxSixteenBits::new( NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, @@ -88,8 +89,7 @@ mod tests { let mut pins = TxPinConfigIncludingValidPin::new(pins); let mut clock_pin = ClkOutPin::new(ctx.clock); - let pio = - ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, tx_descriptors, 10.MHz()).unwrap(); + let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, 10.MHz()).unwrap(); let mut pio = pio .tx @@ -102,7 +102,7 @@ mod tests { ) .unwrap(); // TODO: handle error - // use a PCNT unit to count the negitive clock edges only when valid is high + // use a PCNT unit to count the negative clock edges only when valid is high let clock_unit = ctx.pcnt_unit; clock_unit.channel0.set_edge_signal(ctx.clock_loopback); clock_unit.channel0.set_ctrl_signal(ctx.valid_loopback); @@ -115,8 +115,8 @@ mod tests { for _ in 0..100 { clock_unit.clear(); - let xfer = pio.write_dma(&tx_buffer).unwrap(); - xfer.wait().unwrap(); + let xfer = pio.write(dma_tx_buf.len(), dma_tx_buf).unwrap(); + (_, pio, dma_tx_buf) = xfer.wait(); info!("clock count: {}", clock_unit.value()); assert_eq!(clock_unit.value(), BUFFER_SIZE as _); } @@ -125,8 +125,7 @@ mod tests { #[test] fn test_parl_io_tx_8bit_valid_clock_count(ctx: Context) { const BUFFER_SIZE: usize = 64; - let tx_buffer = [0u8; BUFFER_SIZE]; - let (_, tx_descriptors) = esp_hal::dma_descriptors!(0, 2 * BUFFER_SIZE); + let mut dma_tx_buf: DmaTxBuf = dma_tx_buffer!(BUFFER_SIZE).unwrap(); let pins = TxEightBits::new( NoPin, @@ -149,8 +148,7 @@ mod tests { let mut clock_pin = ClkOutPin::new(ctx.clock); - let pio = - ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, tx_descriptors, 10.MHz()).unwrap(); + let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, 10.MHz()).unwrap(); let mut pio = pio .tx @@ -176,8 +174,8 @@ mod tests { for _ in 0..100 { clock_unit.clear(); - let xfer = pio.write_dma(&tx_buffer).unwrap(); - xfer.wait().unwrap(); + let xfer = pio.write(dma_tx_buf.len(), dma_tx_buf).unwrap(); + (_, pio, dma_tx_buf) = xfer.wait(); info!("clock count: {}", clock_unit.value()); assert_eq!(clock_unit.value(), BUFFER_SIZE as _); } diff --git a/hil-test/tests/parl_io_tx_async.rs b/hil-test/tests/parl_io_tx_async.rs index ecde95ae75..d60b2bd1e3 100644 --- a/hil-test/tests/parl_io_tx_async.rs +++ b/hil-test/tests/parl_io_tx_async.rs @@ -9,7 +9,8 @@ #[cfg(esp32c6)] use esp_hal::parl_io::{TxPinConfigWithValidPin, TxSixteenBits}; use esp_hal::{ - dma::DmaChannel0, + dma::{DmaChannel0, DmaTxBuf}, + dma_tx_buffer, gpio::{ interconnect::{InputSignal, OutputSignal}, NoPin, @@ -78,8 +79,7 @@ mod tests { #[test] async fn test_parl_io_tx_async_16bit_valid_clock_count(ctx: Context) { const BUFFER_SIZE: usize = 64; - let tx_buffer = [0u16; BUFFER_SIZE]; - let (_, tx_descriptors) = esp_hal::dma_descriptors!(0, 2 * BUFFER_SIZE); + let mut dma_tx_buf: DmaTxBuf = dma_tx_buffer!(2 * BUFFER_SIZE).unwrap(); let pins = TxSixteenBits::new( NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, @@ -88,7 +88,7 @@ mod tests { let mut pins = TxPinConfigIncludingValidPin::new(pins); let mut clock_pin = ClkOutPin::new(ctx.clock); - let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, tx_descriptors, 10.MHz()) + let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, 10.MHz()) .unwrap() .into_async(); @@ -116,7 +116,9 @@ mod tests { for _ in 0..100 { clock_unit.clear(); - pio.write_dma_async(&tx_buffer).await.unwrap(); + let mut xfer = pio.write(dma_tx_buf.len(), dma_tx_buf).unwrap(); + xfer.wait_for_done().await; + (_, pio, dma_tx_buf) = xfer.wait(); info!("clock count: {}", clock_unit.value()); assert_eq!(clock_unit.value(), BUFFER_SIZE as _); } @@ -125,8 +127,8 @@ mod tests { #[test] async fn test_parl_io_tx_async_8bit_valid_clock_count(ctx: Context) { const BUFFER_SIZE: usize = 64; - let tx_buffer = [0u8; BUFFER_SIZE]; - let (_, tx_descriptors) = esp_hal::dma_descriptors!(0, 2 * BUFFER_SIZE); + + let mut dma_tx_buf: DmaTxBuf = dma_tx_buffer!(BUFFER_SIZE).unwrap(); let pins = TxEightBits::new( NoPin, @@ -149,7 +151,7 @@ mod tests { let mut clock_pin = ClkOutPin::new(ctx.clock); - let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, tx_descriptors, 10.MHz()) + let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, 10.MHz()) .unwrap() .into_async(); @@ -178,7 +180,9 @@ mod tests { for _ in 0..100 { clock_unit.clear(); - pio.write_dma_async(&tx_buffer).await.unwrap(); + let mut xfer = pio.write(dma_tx_buf.len(), dma_tx_buf).unwrap(); + xfer.wait_for_done().await; + (_, pio, dma_tx_buf) = xfer.wait(); info!("clock count: {}", clock_unit.value()); assert_eq!(clock_unit.value(), BUFFER_SIZE as _); }