diff --git a/examples/usart_dma.rs b/examples/usart_dma.rs index c8de60627..5fb682f1c 100644 --- a/examples/usart_dma.rs +++ b/examples/usart_dma.rs @@ -34,18 +34,36 @@ fn main() -> ! { .u0_txd .assign(p.pins.pio0_25.into_swm_pin(), &mut swm_handle); - let serial = + let mut serial = p.USART0 .enable(&clock_config, &mut syscon.handle, u0_rxd, u0_txd); - let channel = dma.channels.channel1.enable(&dma_handle); + let mut rx_channel = dma.channels.channel0.enable(&dma_handle); + let mut tx_channel = dma.channels.channel1.enable(&dma_handle); - serial - .tx - .write_all(b"Hello, world!\r\n", channel) - .wait() - .expect("USART write shouldn't fail"); + static mut BUF: [u8; 4] = [0; 4]; - // We're done. Let's do nothing until someone resets the microcontroller. - loop {} + loop { + { + // Sound, as the mutable reference is dropped after this block. + let rx_buf = unsafe { &mut BUF }; + + let res = serial.rx.read_all(rx_buf, rx_channel).wait().unwrap(); + rx_channel = res.channel; + serial.rx = res.source; + } + + { + // Sound, as the mutable reference is dropped after this block. + let tx_buf = unsafe { &BUF }; + + let res = serial + .tx + .write_all(tx_buf, tx_channel) + .wait() + .expect("USART write shouldn't fail"); + tx_channel = res.channel; + serial.tx = res.dest; + } + } } diff --git a/src/dma.rs b/src/dma.rs index 960faac4a..d63f2bf2c 100644 --- a/src/dma.rs +++ b/src/dma.rs @@ -2,6 +2,7 @@ //! //! The DMA controller is described in the user manual, chapter 12. +mod buffer; mod channels; mod descriptors; mod peripheral; @@ -11,5 +12,5 @@ pub use self::{ channels::*, descriptors::DescriptorTable, peripheral::{Handle, DMA}, - transfer::{Dest, Payload, Transfer}, + transfer::{Dest, Payload, Source, Transfer}, }; diff --git a/src/dma/buffer.rs b/src/dma/buffer.rs new file mode 100644 index 000000000..9cc141b9b --- /dev/null +++ b/src/dma/buffer.rs @@ -0,0 +1,75 @@ +use crate::{ + pac::dma0::channel::xfercfg::{DSTINC_A, SRCINC_A}, + void::Void, +}; + +use super::{Dest, Source}; + +impl crate::private::Sealed for &'static [u8] {} + +impl Source for &'static [u8] { + fn is_valid(&self) -> bool { + self.len() <= 1024 + } + + fn is_empty(&self) -> bool { + self.len() == 0 + } + + fn increment(&self) -> SRCINC_A { + SRCINC_A::WIDTH_X_1 + } + + fn transfer_count(&self) -> Option { + if self.is_empty() { + None + } else { + // The cast should be fine, as DMA buffers are restricted to a + // length of 1024. + Some(self.len() as u16 - 1) + } + } + + fn end_addr(&self) -> *const u8 { + // Sound, as we stay within the bounds of the slice. + unsafe { self.as_ptr().add(self.len() - 1) } + } +} + +impl crate::private::Sealed for &'static mut [u8] {} + +impl Dest for &'static mut [u8] { + /// The error that can occur while waiting for the destination to be idle + type Error = Void; + + fn is_valid(&self) -> bool { + self.len() <= 1024 + } + + fn is_full(&self) -> bool { + self.len() == 0 + } + + fn increment(&self) -> DSTINC_A { + DSTINC_A::WIDTH_X_1 + } + + fn transfer_count(&self) -> Option { + if self.is_full() { + None + } else { + // The cast should be fine, as DMA buffers are restricted to a + // length of 1024. + Some(self.len() as u16 - 1) + } + } + + fn wait(&mut self) -> nb::Result<(), Self::Error> { + Ok(()) + } + + fn end_addr(&mut self) -> *mut u8 { + // Sound, as we stay within the bounds of the slice. + unsafe { self.as_mut_ptr().add(self.len() - 1) } + } +} diff --git a/src/dma/channels.rs b/src/dma/channels.rs index 76c1292c5..d9cd467a4 100644 --- a/src/dma/channels.rs +++ b/src/dma/channels.rs @@ -13,7 +13,8 @@ use crate::{ }; use super::{ - descriptors::ChannelDescriptor, DescriptorTable, Dest, Handle, Transfer, + descriptors::ChannelDescriptor, DescriptorTable, Dest, Handle, Source, + Transfer, }; /// A DMA channel @@ -66,28 +67,48 @@ where { /// Starts a DMA transfer /// - /// # Limitations + /// # Panics + /// + /// Panics, if any buffer passed to this function has a length larger than + /// 1024. /// - /// The length of `source` must be 1024 or less. + /// # Limitations /// /// The caller must make sure to call this method only for the correct /// combination of channel and target. - pub(crate) fn start_transfer( + pub(crate) fn start_transfer( self, - source: &'static [u8], + source: S, mut dest: D, - ) -> Transfer<'dma, C, D> + ) -> Transfer<'dma, C, S, D> where + S: Source, D: Dest, { + assert!(source.is_valid()); + assert!(dest.is_valid()); + compiler_fence(Ordering::SeqCst); - // We need to substract 1 from the length below. If the source is empty, - // return early to prevent underflow. - if source.is_empty() { + // To compute the transfer count, source or destination buffers need to + // subtract 1 from their length. This early return makes sure that + // this won't lead to an underflow. + if source.is_empty() || dest.is_full() { return Transfer::new(self, source, dest); } + // Currently we don't support memory-to-memory transfers, which means + // exactly one participant is providing the transfer count. + let source_count = source.transfer_count(); + let dest_count = dest.transfer_count(); + let transfer_count = match (source_count, dest_count) { + (Some(transfer_count), None) => transfer_count, + (None, Some(transfer_count)) => transfer_count, + _ => { + panic!("Unsupported transfer type"); + } + }; + // Configure channel // See user manual, section 12.6.16. self.cfg.write(|w| { @@ -106,16 +127,14 @@ where w.setinta().no_effect(); w.setintb().no_effect(); w.width().bit_8(); - w.srcinc().width_x_1(); - w.dstinc().no_increment(); - unsafe { w.xfercount().bits(source.len() as u16 - 1) } + w.srcinc().variant(source.increment()); + w.dstinc().variant(dest.increment()); + unsafe { w.xfercount().bits(transfer_count) } }); - let source_end = unsafe { source.as_ptr().add(source.len() - 1) }; - // Configure channel descriptor // See user manual, sections 12.5.2 and 12.5.3. - self.descriptor.source_end = source_end; + self.descriptor.source_end = source.end_addr(); self.descriptor.dest_end = dest.end_addr(); // Enable channel diff --git a/src/dma/transfer.rs b/src/dma/transfer.rs index e431f6acc..cc14e7265 100644 --- a/src/dma/transfer.rs +++ b/src/dma/transfer.rs @@ -3,26 +3,29 @@ use core::{ sync::atomic::{compiler_fence, Ordering}, }; -use crate::init_state::Enabled; +use crate::{ + init_state::Enabled, + pac::dma0::channel::xfercfg::{DSTINC_A, SRCINC_A}, +}; use super::{channels::ChannelTrait, Channel, Handle}; /// A DMA transfer -pub struct Transfer<'dma, C, D> +pub struct Transfer<'dma, C, S, D> where C: ChannelTrait, { - payload: Payload<'dma, C, D>, + payload: Payload<'dma, C, S, D>, } -impl<'dma, C, D> Transfer<'dma, C, D> +impl<'dma, C, S, D> Transfer<'dma, C, S, D> where C: ChannelTrait, D: Dest, { pub(super) fn new( channel: Channel>, - source: &'static [u8], + source: S, dest: D, ) -> Self { Self { @@ -37,7 +40,8 @@ where /// Waits for the transfer to finish pub fn wait( mut self, - ) -> Result, (D::Error, Payload<'dma, C, D>)> { + ) -> Result, (D::Error, Payload<'dma, C, S, D>)> + { // There's an error interrupt status register. Maybe we should check // this here, but I have no idea whether that actually makes sense: // 1. As of this writing, we're not enabling any interrupts. I don't @@ -68,7 +72,7 @@ where } /// The payload of a `Transfer` -pub struct Payload<'dma, C, D> +pub struct Payload<'dma, C, S, D> where C: ChannelTrait, { @@ -76,13 +80,13 @@ where pub channel: Channel>, /// The source of the transfer - pub source: &'static [u8], + pub source: S, /// The destination of the transfer pub dest: D, } -impl<'dma, C, D> fmt::Debug for Payload<'dma, C, D> +impl<'dma, C, S, D> fmt::Debug for Payload<'dma, C, S, D> where C: ChannelTrait, { @@ -94,14 +98,83 @@ where } } +/// The source of a DMA transfer +/// +/// This trait is intended for internal use only. It is implemented for +/// immutable static buffers and peripherals that support being read from using +/// DMA. +pub trait Source: crate::private::Sealed { + /// Indicates whether the source is valid + /// + /// Buffers are valid, if they have a length of 1024 or less. Peripherals + /// are always valid. + fn is_valid(&self) -> bool; + + /// Indicates whether the source is empty + /// + /// Buffers are empty, if they have a length of 0. Peripherals are never + /// empty. + fn is_empty(&self) -> bool; + + /// The address increment during the transfer + /// + /// Buffers will return the word size here. Peripherals will indicate no + /// increment. + fn increment(&self) -> SRCINC_A; + + /// The transfer count, as defined by XFERCFG.XFERCOUNT + /// + /// Only buffers will return a value here, and only if `is_empty` returns + /// false. Peripherals will always return `None`. + fn transfer_count(&self) -> Option; + + /// The end address + /// + /// This is not the actual end of the buffer, but the starting address plus + /// `transfer_count` times address increment. See LPC845 user manual, + /// section 16.5.2, for example. + fn end_addr(&self) -> *const u8; +} + /// A destination for a DMA transfer -pub trait Dest { +/// +/// This trait is intended for internal use only. It is implemented for mutable +/// static buffers and peripherals that support being written to using DMA. +pub trait Dest: crate::private::Sealed { /// The error that can occur while waiting for the destination to be idle type Error; + /// Indicates whether the destination is valid + /// + /// Buffers are valid if they have a length of 1024 or less. Peripherals are + /// always valid. + fn is_valid(&self) -> bool; + + /// Indicates whether the destination is full + /// + /// Buffers are empty, if they have a length of 0. Peripherals are never + /// empty. + fn is_full(&self) -> bool; + + /// The address increment during the transfer + /// + /// Buffers will return the word size here. Peripherals will indicate no + /// increment. + fn increment(&self) -> DSTINC_A; + + /// The transfer count, as defined by XFERCFG.XFERCOUNT + /// + /// Only buffers will return a value here, and only if `if_full` returns + /// `false`. Peripherals will always return `None`. + fn transfer_count(&self) -> Option; + /// Wait for the destination to be idle fn wait(&mut self) -> nb::Result<(), Self::Error>; - /// The last byte of the destination's memory range + /// The end address + /// + /// This is not the actual end of the buffer, but the starting address plus + /// `transfer_count` times address increment. See LPC845 user manual, + /// section 16.5.2, for example. fn end_addr(&mut self) -> *mut u8; } diff --git a/src/lib.rs b/src/lib.rs index 4930f7314..ffd770dad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,6 +103,7 @@ pub extern crate cortex_m; pub extern crate cortex_m_rt; pub extern crate embedded_hal; pub extern crate nb; +pub extern crate void; #[macro_use] pub(crate) mod reg_proxy; @@ -564,3 +565,7 @@ pub mod init_state { /// Indicates that the hardware component is disabled pub struct Disabled; } + +mod private { + pub trait Sealed {} +} diff --git a/src/usart/instances.rs b/src/usart/instances.rs index 50aee6f26..65e176dc4 100644 --- a/src/usart/instances.rs +++ b/src/usart/instances.rs @@ -27,6 +27,9 @@ pub trait Instance: /// The movable function that needs to be assigned to this USART's TX pin type Tx; + /// The DMA channel used with this instance for receiving + type RxChannel: dma::ChannelTrait; + /// The DMA channel used with this instance for transmissions type TxChannel: dma::ChannelTrait; } @@ -40,6 +43,7 @@ macro_rules! instances { $interrupt:ident, $rx:ident, $tx:ident, + $rx_channel:ident, $tx_channel:ident; )* ) => { @@ -54,6 +58,7 @@ macro_rules! instances { type Rx = swm::$rx; type Tx = swm::$tx; + type RxChannel = dma::$rx_channel; type TxChannel = dma::$tx_channel; } @@ -65,15 +70,15 @@ macro_rules! instances { } instances!( - USART0, 0, usart0, USART0, U0_RXD, U0_TXD, Channel1; - USART1, 1, usart1, USART1, U1_RXD, U1_TXD, Channel3; - USART2, 2, usart2, USART2, U2_RXD, U2_TXD, Channel5; + USART0, 0, usart0, USART0, U0_RXD, U0_TXD, Channel0, Channel1; + USART1, 1, usart1, USART1, U1_RXD, U1_TXD, Channel2, Channel3; + USART2, 2, usart2, USART2, U2_RXD, U2_TXD, Channel4, Channel5; ); #[cfg(feature = "845")] instances!( - USART3, 3, usart3, PIN_INT6_USART3, U3_RXD, U3_TXD, Channel7; - USART4, 4, usart4, PIN_INT7_USART4, U4_RXD, U4_TXD, Channel9; + USART3, 3, usart3, PIN_INT6_USART3, U3_RXD, U3_TXD, Channel6, Channel7; + USART4, 4, usart4, PIN_INT7_USART4, U4_RXD, U4_TXD, Channel8, Channel9; ); mod private { diff --git a/src/usart/peripheral.rs b/src/usart/peripheral.rs index df104bdec..f759926ae 100644 --- a/src/usart/peripheral.rs +++ b/src/usart/peripheral.rs @@ -7,7 +7,7 @@ use embedded_hal::{ use void::Void; use crate::{ - dma, init_state, + init_state, pac::NVIC, pins, swm::{self, FunctionTrait}, @@ -306,18 +306,3 @@ where self.tx.write_str(s) } } - -impl dma::Dest for USART -where - I: Instance, -{ - type Error = Void; - - fn wait(&mut self) -> nb::Result<(), Self::Error> { - self.tx.wait() - } - - fn end_addr(&mut self) -> *mut u8 { - self.tx.end_addr() - } -} diff --git a/src/usart/rx.rs b/src/usart/rx.rs index bbc99d531..7002ac406 100644 --- a/src/usart/rx.rs +++ b/src/usart/rx.rs @@ -1,6 +1,9 @@ use core::marker::PhantomData; -use crate::{embedded_hal::serial::Read, init_state}; +use crate::{ + dma, embedded_hal::serial::Read, init_state::Enabled, + pac::dma0::channel::xfercfg::SRCINC_A, +}; use super::instances::Instance; @@ -11,7 +14,7 @@ use super::instances::Instance; /// /// /// [`embedded_hal::serial::Read`]: #impl-Read%3Cu8%3E -pub struct Rx { +pub struct Rx { _instance: PhantomData, _state: PhantomData, } @@ -28,7 +31,7 @@ where } } -impl Rx +impl Rx where I: Instance, { @@ -53,9 +56,22 @@ where usart.intenclr.write(|w| w.rxrdyclr().set_bit()); } + + /// Reads until the provided buffer is full, using DMA + /// + /// # Panics + /// + /// Panics, if `buffer` has a length larger than 1024. + pub fn read_all<'dma>( + self, + buffer: &'static mut [u8], + channel: dma::Channel>, + ) -> dma::Transfer<'dma, I::RxChannel, Self, &'static mut [u8]> { + channel.start_transfer(self, buffer) + } } -impl Read for Rx +impl Read for Rx where I: Instance, { @@ -97,6 +113,35 @@ where } } +impl crate::private::Sealed for Rx {} + +impl dma::Source for Rx +where + I: Instance, +{ + fn is_valid(&self) -> bool { + true + } + + fn is_empty(&self) -> bool { + false + } + + fn increment(&self) -> SRCINC_A { + SRCINC_A::NO_INCREMENT + } + + fn transfer_count(&self) -> Option { + None + } + + fn end_addr(&self) -> *const u8 { + // Sound, because we're dereferencing a register address that is always + // valid on the target hardware. + (unsafe { &(*I::REGISTERS).rxdat }) as *const _ as *mut u8 + } +} + /// A USART error #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Error { diff --git a/src/usart/tx.rs b/src/usart/tx.rs index 1a4b5a012..2efb71115 100644 --- a/src/usart/tx.rs +++ b/src/usart/tx.rs @@ -6,7 +6,7 @@ use embedded_hal::{ use nb::block; use void::Void; -use crate::{dma, init_state::Enabled}; +use crate::{dma, init_state::Enabled, pac::dma0::channel::xfercfg::DSTINC_A}; use super::instances::Instance; @@ -63,14 +63,14 @@ where /// Writes the provided buffer using DMA /// - /// # Limitations + /// # Panics /// - /// The length of `buffer` must be 1024 or less. + /// Panics, if `buffer` has a length larger than 1024. pub fn write_all<'dma>( self, buffer: &'static [u8], channel: dma::Channel>, - ) -> dma::Transfer<'dma, I::TxChannel, Self> { + ) -> dma::Transfer<'dma, I::TxChannel, &'static [u8], Self> { channel.start_transfer(buffer, self) } } @@ -126,12 +126,30 @@ where } } +impl crate::private::Sealed for Tx {} + impl dma::Dest for Tx where I: Instance, { type Error = Void; + fn is_valid(&self) -> bool { + true + } + + fn is_full(&self) -> bool { + false + } + + fn increment(&self) -> DSTINC_A { + DSTINC_A::NO_INCREMENT + } + + fn transfer_count(&self) -> Option { + None + } + fn wait(&mut self) -> nb::Result<(), Self::Error> { self.flush() }