Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for DMA-based reading from USART #261

Merged
merged 18 commits into from
Jul 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 27 additions & 9 deletions examples/usart_dma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
3 changes: 2 additions & 1 deletion src/dma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//!
//! The DMA controller is described in the user manual, chapter 12.

mod buffer;
mod channels;
mod descriptors;
mod peripheral;
Expand All @@ -11,5 +12,5 @@ pub use self::{
channels::*,
descriptors::DescriptorTable,
peripheral::{Handle, DMA},
transfer::{Dest, Payload, Transfer},
transfer::{Dest, Payload, Source, Transfer},
};
75 changes: 75 additions & 0 deletions src/dma/buffer.rs
Original file line number Diff line number Diff line change
@@ -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<u16> {
david-sawatzke marked this conversation as resolved.
Show resolved Hide resolved
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<u16> {
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) }
}
}
49 changes: 34 additions & 15 deletions src/dma/channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use crate::{
};

use super::{
descriptors::ChannelDescriptor, DescriptorTable, Dest, Handle, Transfer,
descriptors::ChannelDescriptor, DescriptorTable, Dest, Handle, Source,
Transfer,
};

/// A DMA channel
Expand Down Expand Up @@ -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<D>(
pub(crate) fn start_transfer<S, D>(
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| {
Expand All @@ -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
Expand Down
95 changes: 84 additions & 11 deletions src/dma/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<C, Enabled<&'dma Handle>>,
source: &'static [u8],
source: S,
dest: D,
) -> Self {
Self {
Expand All @@ -37,7 +40,8 @@ where
/// Waits for the transfer to finish
pub fn wait(
mut self,
) -> Result<Payload<'dma, C, D>, (D::Error, Payload<'dma, C, D>)> {
) -> Result<Payload<'dma, C, S, D>, (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
Expand Down Expand Up @@ -68,21 +72,21 @@ where
}

/// The payload of a `Transfer`
pub struct Payload<'dma, C, D>
pub struct Payload<'dma, C, S, D>
where
C: ChannelTrait,
{
/// The channel used for this transfer
pub channel: Channel<C, Enabled<&'dma Handle>>,

/// 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,
{
Expand All @@ -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<u16>;

/// 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<u16>;

/// 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;
}
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -564,3 +565,7 @@ pub mod init_state {
/// Indicates that the hardware component is disabled
pub struct Disabled;
}

mod private {
pub trait Sealed {}
}
Loading