Skip to content

Commit

Permalink
feat(dma): dma transfer impl
Browse files Browse the repository at this point in the history
  • Loading branch information
andelf committed Jul 6, 2024
1 parent 173713e commit e2841dc
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 64 deletions.
3 changes: 2 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,9 @@ fn main() {

quote! {
#[cfg(feature = "rt")]
#[allow(non_snake_case)]
#[no_mangle]
unsafe fn #irq() {
unsafe extern "riscv-interrupt-m" fn #irq() {
<crate::peripherals::#peri_name as crate::dma::ControllerInterrupt>::on_irq();
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/dma/dmamux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ pub(crate) fn configure_dmamux(mux_num: usize, request: u8) {
let ch_mux_regs = pac::DMAMUX.muxcfg(mux_num);
ch_mux_regs.write(|reg| {
reg.set_enable(true);
reg.set_source(request);
reg.set_source(request); // peripheral request number
});
}
103 changes: 49 additions & 54 deletions src/dma/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub struct TransferOptions {
impl Default for TransferOptions {
fn default() -> Self {
Self {
burst: Burst::Liner(1),
burst: Burst::Liner(0),
priority: false,
circular: false,
half_transfer_irq: false,
Expand Down Expand Up @@ -104,48 +104,54 @@ pub(crate) unsafe fn init(cs: critical_section::CriticalSection) {

crate::peripherals::HDMA::add_resource_group(0);

pac::HDMA.dmactrl().modify(|w| w.set_reset(true));

interrupt::typelevel::HDMA::set_priority_with_cs(cs, interrupt::Priority::P1);
interrupt::typelevel::HDMA::enable();
}

impl super::ControllerInterrupt for crate::peripherals::HDMA {
unsafe fn on_irq() {
defmt::info!("in irq");
let r = pac::HDMA;

let num_base = 0; // for XDMA, it's 32

for i in BitIter(pac::HDMA.ch_en().read().0) {
dma_on_irq(crate::pac::HDMA, i as usize, (i + num_base) as usize);
let half = pac::HDMA.inthalfsts().read().0;
let tc = pac::HDMA.inttcsts().read().0;
let err = pac::HDMA.interrsts().read().0;
let abort = pac::HDMA.intabortsts().read().0;

// possible errors:
// - bus error
// - memory alignment error
// - bit width alignment error
// - invalid configuration
// DMA error: this is normally a hardware error(memory alignment or access), but we can't do anything about it
if err != 0 {
panic!(
"DMA: error on DMA@{:08x}, errsts=0x{:08x}",
r.as_ptr() as u32,
pac::HDMA.interrsts().read().0
);
}

crate::interrupt::HDMA.complete(); // notify PLIC
}
}

/// Safety: Must be called with a matching set of parameters for a valid dma channel
pub(crate) unsafe fn dma_on_irq(r: crate::pac::dma::Dma, num: usize, id: usize) {
let state = &STATE[id];

defmt::info!("dma_on_irq");

// DMA error: this is normally a hardware error(memory alignment or access), but we can't do anything about it
if r.interrsts().read().sts(num) {
panic!("DMA: error on DMA@{:08x} channel {}", r.as_ptr() as u32, num);
}
if half != 0 {
r.inthalfsts().modify(|w| w.0 = half); // W1C
}
if tc != 0 {
r.inttcsts().modify(|w| w.0 = tc); // W1C
}
if abort != 0 {
r.intabortsts().modify(|w| w.0 = abort); // W1C
}

if r.inthalfsts().read().sts(num) {
// ack half transfer
r.inthalfsts().modify(|w| w.set_sts(num, true)); // W1C
} else if r.inttcsts().read().sts(num) {
// ack transfer complete
r.inttcsts().modify(|w| w.set_sts(num, true)); // W1C
for i in BitIter(half | tc | abort) {
let id = (i + num_base) as usize;
STATE[id].waker.wake();
}

state.complete_count.fetch_add(1, Ordering::Relaxed);
} else {
return; // not this channel
crate::interrupt::HDMA.complete(); // notify PLIC
}

state.waker.wake();
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -212,11 +218,15 @@ impl AnyChannel {

let ch_cr = r.chctrl(ch);

// configure DMAMUX request and output channel
super::dmamux::configure_dmamux(info.mux_num, request);

ch_cr.src_addr().write_value(src_addr as u32);
ch_cr.dst_addr().write_value(dst_addr as u32);
ch_cr.tran_size().modify(|w| size_in_bytes / src_width.bytes());
// TODO: LLPointer

ch_cr
.tran_size()
.modify(|w| w.0 = (size_in_bytes / src_width.bytes()) as u32);
ch_cr.llpointer().modify(|w| w.0 = 0x0);
ch_cr.chan_req_ctrl().write(|w| {
if dir == Dir::MemoryToPeripheral {
w.set_dstreqsel(mux_ch as u8);
Expand All @@ -225,8 +235,8 @@ impl AnyChannel {
}
});

ch_cr.llpointer().modify(|w| w.0 = 0x0);
// TODO: handle SwapTable here
// TODO: LLPointer handling

// clear transfer irq status (W1C)
// dma_clear_transfer_status
Expand All @@ -235,7 +245,7 @@ impl AnyChannel {
r.intabortsts().modify(|w| w.set_sts(ch, true));
r.interrsts().modify(|w| w.set_sts(ch, true));

ch_cr.ctrl().modify(|w| {
ch_cr.ctrl().write(|w| {
w.set_infiniteloop(options.circular);
w.set_handshakeopt(handshake != HandshakeMode::Normal);

Expand All @@ -250,19 +260,18 @@ impl AnyChannel {
w.set_srcaddrctrl(src_addr_ctrl);
w.set_dstaddrctrl(dst_addr_ctrl);

w.set_inthalfcntmask(options.half_transfer_irq);
w.set_inttcmask(options.complete_transfer_irq);
// unmask
w.set_inthalfcntmask(!options.half_transfer_irq);
w.set_inttcmask(!options.complete_transfer_irq);
w.set_interrmask(false);
w.set_intabtmask(false);

w.set_enable(false); // don't start yet
});

// configure DMAMUX request and output channel
super::dmamux::configure_dmamux(info.mux_num, request);
}

fn start(&self) {
let info = self.info();

let r = info.dma.regs();
let ch = info.num; // channel number in current dma controller

Expand Down Expand Up @@ -445,20 +454,6 @@ impl<'a> Transfer<'a> {
) -> Self {
assert!(mem_len > 0);

/*
request: Request, // DMA request number in DMAMUX
dir: Dir,
src_addr: *const u32,
src_width: WordSize,
src_addr_ctrl: AddrCtrl,
dst_addr: *mut u32,
dst_width: WordSize,
dst_addr_ctrl: AddrCtrl,
// TRANSIZE
size_in_bytes: usize,
// handshake: HandshakeMode,
options: TransferOptions,
*/
let src_addr;
let dst_addr;
let mut src_addr_ctrl = AddrCtrl::FIXED;
Expand Down
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ pub fn init(config: Config) -> Peripherals {
gpio::input_future::init_gpio0_irq();
}

unsafe {
critical_section::with(|cs| {
dma::init(cs);
});
}

#[cfg(feature = "embassy")]
embassy::init();

Expand Down
11 changes: 11 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,14 @@ macro_rules! dma_trait_impl {
}
};
}

macro_rules! new_dma {
($name:ident) => {{
let dma = $name.into_ref();
let request = dma.request();
Some(crate::dma::ChannelAndRequest {
channel: dma.map_into(),
request,
})
}};
}
80 changes: 72 additions & 8 deletions src/uart/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ use core::sync::atomic::{AtomicU8, Ordering};
use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;

use crate::dma::ChannelAndRequest;
use crate::gpio::AnyPin;
use crate::interrupt::typelevel::Interrupt as _;
use crate::interrupt::InterruptExt as _;
use crate::mode::{Blocking, Mode};
use crate::mode::{Async, Blocking, Mode};
pub use crate::pac::uart::vals::{RxFifoTrigger, TxFifoTrigger};
use crate::pac::Interrupt;
use crate::time::Hertz;
Expand Down Expand Up @@ -193,10 +194,73 @@ pub struct UartTx<'d, M: Mode> {
tx: Option<PeripheralRef<'d, AnyPin>>,
cts: Option<PeripheralRef<'d, AnyPin>>,
de: Option<PeripheralRef<'d, AnyPin>>,
// tx_dma: Option<ChannelAndRequest<'d>>,
tx_dma: Option<ChannelAndRequest<'d>>,
_phantom: PhantomData<M>,
}

impl<'d> UartTx<'d, Async> {
/// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power.
pub fn new<T: Instance>(
peri: impl Peripheral<P = T> + 'd,
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
config: Config,
) -> Result<Self, ConfigError> {
into_ref!(tx);
tx.set_as_alt(tx.alt_num());

Self::new_inner(peri, Some(tx.map_into()), None, new_dma!(tx_dma), config)
}

/// Create a new tx-only UART with a clear-to-send pin
pub fn new_with_cts<T: Instance>(
peri: impl Peripheral<P = T> + 'd,
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
cts: impl Peripheral<P = impl CtsPin<T>> + 'd,
tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd,
config: Config,
) -> Result<Self, ConfigError> {
into_ref!(tx, cts);
tx.set_as_alt(tx.alt_num());
cts.set_as_alt(cts.alt_num());

Self::new_inner(
peri,
Some(tx.map_into()),
Some(cts.map_into()),
new_dma!(tx_dma),
config,
)
}

/// Initiate an asynchronous UART write
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
let r = self.info.regs;

r.fcrr().modify(|w| {
w.set_dmae(true);
});
#[cfg(not(any(hpm53, hpm68, hpm6e)))]
r.fcr().modify(|w| w.set_dmae(true));

let ch = self.tx_dma.as_mut().unwrap();

// If we don't assign future to a variable, the data register pointer
// is held across an await and makes the future non-Send.
let transfer = unsafe { ch.write(buffer, r.thr().as_ptr() as *mut u8, Default::default()) };
transfer.await;

// BUG: only a batch of FIFO size is sent, "handshake" not working

Ok(())
}

/// Wait until transmission complete
pub async fn flush(&mut self) -> Result<(), Error> {
self.blocking_flush()
}
}

impl<'d> UartTx<'d, Blocking> {
/// Create a new blocking tx-only UART with no hardware flow control.
///
Expand All @@ -209,7 +273,7 @@ impl<'d> UartTx<'d, Blocking> {
into_ref!(tx);
tx.set_as_alt(tx.alt_num());

Self::new_inner(peri, Some(tx.map_into()), None, config)
Self::new_inner(peri, Some(tx.map_into()), None, None, config)
}

/// Create a new blocking tx-only UART with a clear-to-send pin
Expand All @@ -223,7 +287,7 @@ impl<'d> UartTx<'d, Blocking> {
tx.set_as_alt(tx.alt_num());
cts.set_as_alt(cts.alt_num());

Self::new_inner(peri, Some(tx.map_into()), Some(cts.map_into()), config)
Self::new_inner(peri, Some(tx.map_into()), Some(cts.map_into()), None, config)
}
}

Expand All @@ -232,7 +296,7 @@ impl<'d, M: Mode> UartTx<'d, M> {
_peri: impl Peripheral<P = T> + 'd,
tx: Option<PeripheralRef<'d, AnyPin>>,
cts: Option<PeripheralRef<'d, AnyPin>>,
// tx_dma: Option<ChannelAndRequest<'d>>,
tx_dma: Option<ChannelAndRequest<'d>>,
config: Config,
) -> Result<Self, ConfigError> {
let mut this = Self {
Expand All @@ -242,7 +306,7 @@ impl<'d, M: Mode> UartTx<'d, M> {
tx,
cts,
de: None,
// tx_dma,
tx_dma,
_phantom: PhantomData,
};
this.enable_and_configure(&config)?;
Expand Down Expand Up @@ -507,7 +571,7 @@ impl<'d, M: Mode> Uart<'d, M> {
rts: Option<PeripheralRef<'d, AnyPin>>,
cts: Option<PeripheralRef<'d, AnyPin>>,
de: Option<PeripheralRef<'d, AnyPin>>,
//tx_dma: Option<ChannelAndRequest<'d>>,
// tx_dma: Option<ChannelAndRequest<'d>>,
//rx_dma: Option<ChannelAndRequest<'d>>,
config: Config,
) -> Result<Self, ConfigError> {
Expand All @@ -528,7 +592,7 @@ impl<'d, M: Mode> Uart<'d, M> {
tx,
cts,
de,
// tx_dma,
tx_dma: None,
},
rx: UartRx {
_phantom: PhantomData,
Expand Down

0 comments on commit e2841dc

Please sign in to comment.