use crate::peripherals::{Dma, Sdma};
use core::marker::PhantomData;
pub trait DmaInstance {
fn ptr() -> *const ws63_pac::dma::RegisterBlock;
const CHANNEL_BASE: u8;
}
pub struct Dma0;
impl DmaInstance for Dma0 {
fn ptr() -> *const ws63_pac::dma::RegisterBlock {
Dma::ptr()
}
const CHANNEL_BASE: u8 = 0;
}
pub struct Sdma0;
impl DmaInstance for Sdma0 {
fn ptr() -> *const ws63_pac::dma::RegisterBlock {
Sdma::ptr()
}
const CHANNEL_BASE: u8 = 8;
}
#[inline]
fn physical_channel_index(base: u8, channel: u8) -> usize {
assert!(channel >= base && channel < base + 4, "DMA channel out of range for this controller");
(channel - base) as usize
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TransferWidth {
Width8 = 0,
Width16 = 1,
Width32 = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BurstSize {
Beats1 = 0,
Beats4 = 1,
Beats8 = 2,
Beats16 = 3,
Beats32 = 4,
Beats64 = 5,
Beats128 = 6,
Beats256 = 7,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FlowControl {
MemToMem = 0,
MemToPeripheral = 1,
PeripheralToMem = 2,
PeripheralToPeripheral = 3,
}
#[derive(Debug, Clone, Copy)]
pub struct DmaChannelConfig {
pub src_peripheral: u8,
pub dst_peripheral: u8,
pub flow_control: FlowControl,
pub src_width: TransferWidth,
pub dst_width: TransferWidth,
pub src_burst: BurstSize,
pub dst_burst: BurstSize,
pub src_inc: bool,
pub dst_inc: bool,
pub transfer_int: bool,
pub error_int: bool,
pub bus_lock: bool,
}
impl Default for DmaChannelConfig {
fn default() -> Self {
Self {
src_peripheral: 0,
dst_peripheral: 0,
flow_control: FlowControl::MemToMem,
src_width: TransferWidth::Width32,
dst_width: TransferWidth::Width32,
src_burst: BurstSize::Beats1,
dst_burst: BurstSize::Beats1,
src_inc: true,
dst_inc: true,
transfer_int: false,
error_int: false,
bus_lock: false,
}
}
}
pub struct DmaDriver<'d, T: DmaInstance> {
_instance: PhantomData<&'d T>,
}
impl<'d, T: DmaInstance> DmaDriver<'d, T> {
pub fn new(_dma: impl Into<PhantomData<&'d T>>) -> Self {
Self { _instance: PhantomData }
}
fn regs() -> &'static ws63_pac::dma::RegisterBlock {
unsafe { &*T::ptr() }
}
#[inline]
fn physical_channel(channel: u8) -> usize {
physical_channel_index(T::CHANNEL_BASE, channel)
}
pub fn enable_controller(&mut self) {
let r = Self::regs();
unsafe {
r.dmac_config().write(|w| w.bits(0x01));
}
}
pub fn disable_controller(&mut self) {
unsafe {
Self::regs().dmac_config().write(|w| w.bits(0));
}
}
pub fn configure_channel(
&mut self,
channel: u8,
src_addr: u32,
dst_addr: u32,
transfer_size: u16,
config: &DmaChannelConfig,
) {
let ch = Self::physical_channel(channel);
let r = Self::regs();
unsafe {
r.dmac_chn_config_0(ch).write(|w| w.bits(0));
}
unsafe {
r.dmac_s_addr_0(ch).write(|w| w.bits(src_addr));
}
unsafe {
r.dmac_d_addr_0(ch).write(|w| w.bits(dst_addr));
}
unsafe {
r.dmac_lli_0(ch).write(|w| w.bits(0));
}
let mut control: u32 = 0;
control |= (transfer_size as u32) & 0xFFF; control |= ((config.src_burst as u32) & 0x07) << 12; control |= ((config.dst_burst as u32) & 0x07) << 15; control |= ((config.src_width as u32) & 0x07) << 18; control |= ((config.dst_width as u32) & 0x07) << 21; control |= 0 << 24; control |= 0 << 25; if config.src_inc {
control |= 1 << 26;
}
if config.dst_inc {
control |= 1 << 27;
}
control |= 0 << 28; if config.transfer_int {
control |= 1 << 31;
}
unsafe {
r.dmac_chn_control_0(ch).write(|w| w.bits(control));
}
let mut ch_cfg: u32 = 0;
ch_cfg |= 0x01; ch_cfg |= ((config.src_peripheral as u32) & 0x0F) << 1; ch_cfg |= ((config.dst_peripheral as u32) & 0x0F) << 5; ch_cfg |= ((config.flow_control as u32) & 0x07) << 9; if config.error_int {
ch_cfg |= 1 << 12; }
if config.transfer_int {
ch_cfg |= 1 << 13; }
if config.bus_lock {
ch_cfg |= 1 << 14; }
unsafe {
r.dmac_chn_config_0(ch).write(|w| w.bits(ch_cfg));
}
}
pub fn enable_channel(&mut self, channel: u8) {
let ch = Self::physical_channel(channel);
let r = Self::regs();
let cfg = r.dmac_chn_config_0(ch).read().bits();
unsafe {
r.dmac_chn_config_0(ch).write(|w| w.bits(cfg | 0x01));
}
}
pub fn disable_channel(&mut self, channel: u8) {
let ch = Self::physical_channel(channel);
let r = Self::regs();
let cfg = r.dmac_chn_config_0(ch).read().bits();
unsafe {
r.dmac_chn_config_0(ch).write(|w| w.bits(cfg & !0x01));
}
}
pub fn channel_enabled(&self, channel: u8) -> bool {
let ch = Self::physical_channel(channel);
let mask = 1u32 << ch;
Self::regs().dmac_en_chns().read().bits() & mask != 0
}
pub fn channel_active(&self, channel: u8) -> bool {
let ch = Self::physical_channel(channel);
Self::regs().dmac_chn_config_0(ch).read().bits() & (1 << 15) != 0
}
pub fn halt_channel(&mut self, channel: u8) {
let ch = Self::physical_channel(channel);
let r = Self::regs();
let cfg = r.dmac_chn_config_0(ch).read().bits();
unsafe {
r.dmac_chn_config_0(ch).write(|w| w.bits(cfg | (1 << 16)));
}
}
pub fn resume_channel(&mut self, channel: u8) {
let ch = Self::physical_channel(channel);
let r = Self::regs();
let cfg = r.dmac_chn_config_0(ch).read().bits();
unsafe {
r.dmac_chn_config_0(ch).write(|w| w.bits(cfg & !(1 << 16)));
}
}
pub fn burst_request(&mut self, channel: u8) {
let ch = Self::physical_channel(channel);
unsafe {
Self::regs().dmac_burst_req().write(|w| w.bits(1 << ch));
}
}
pub fn single_request(&mut self, channel: u8) {
let ch = Self::physical_channel(channel);
unsafe {
Self::regs().dmac_single_req().write(|w| w.bits(1 << ch));
}
}
pub fn raw_interrupt_status(&self) -> (u8, u8) {
let sts = Self::regs().dmac_ori_int_st().read().bits();
((sts & 0xFF) as u8, ((sts >> 8) & 0xFF) as u8)
}
pub fn interrupt_status(&self) -> (u8, u8) {
let sts = Self::regs().dmac_int_st().read().bits();
((sts & 0xFF) as u8, ((sts >> 16) & 0xFF) as u8)
}
pub fn clear_transfer_interrupt(&mut self, channel: u8) {
let ch = Self::physical_channel(channel);
unsafe {
Self::regs().dmac_int_clr().write(|w| w.bits(1 << ch));
}
}
pub fn clear_error_interrupt(&mut self, channel: u8) {
let ch = Self::physical_channel(channel);
unsafe {
Self::regs().dmac_int_clr().write(|w| w.bits(1 << (ch + 8)));
}
}
pub fn set_sync(&mut self, sync_mask: u16) {
unsafe {
Self::regs().dmac_sync().write(|w| w.bits(sync_mask as u32));
}
}
}
impl<'d> DmaDriver<'d, Dma0> {
pub fn new_dma(_dma: Dma<'d>) -> Self {
Self { _instance: PhantomData }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum DmaPeripheral {
Tie0 = 0,
Uart0Tx = 1,
Uart0Rx = 2,
Uart1Tx = 3,
Uart1Rx = 4,
Uart2Tx = 5,
Uart2Rx = 6,
Spi0Tx = 7,
Spi0Rx = 8,
I2sTx = 11,
I2sRx = 12,
Spi1Tx = 13,
Spi1Rx = 14,
}
impl DmaPeripheral {
pub const fn request_id(self) -> u8 {
self as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DmaDirection {
Tx,
Rx,
}
impl DmaChannelConfig {
pub fn mem_to_peripheral(mut self, peri: DmaPeripheral) -> Self {
self.flow_control = FlowControl::MemToPeripheral;
self.dst_peripheral = peri.request_id();
self.dst_inc = false;
self
}
pub fn peripheral_to_mem(mut self, peri: DmaPeripheral) -> Self {
self.flow_control = FlowControl::PeripheralToMem;
self.src_peripheral = peri.request_id();
self.src_inc = false;
self
}
}
impl<'d> DmaDriver<'d, Sdma0> {
pub fn new_sdma(_sdma: Sdma<'d>) -> Self {
Self { _instance: PhantomData }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dma_direction_tx_rx_distinct() {
assert_ne!(DmaDirection::Tx as u8, DmaDirection::Rx as u8);
}
#[test]
fn test_dma_peripheral_spi_handshaking_ids() {
assert_eq!(DmaPeripheral::Spi0Tx.request_id(), 7);
assert_eq!(DmaPeripheral::Spi0Rx.request_id(), 8);
assert_eq!(DmaPeripheral::Spi1Tx.request_id(), 13);
assert_eq!(DmaPeripheral::Spi1Rx.request_id(), 14);
}
#[test]
fn test_dma_peripheral_uart_handshaking_ids() {
assert_eq!(DmaPeripheral::Uart0Tx.request_id(), 1);
assert_eq!(DmaPeripheral::Uart0Rx.request_id(), 2);
assert_eq!(DmaPeripheral::Uart1Tx.request_id(), 3);
assert_eq!(DmaPeripheral::Uart1Rx.request_id(), 4);
assert_eq!(DmaPeripheral::Uart2Tx.request_id(), 5);
assert_eq!(DmaPeripheral::Uart2Rx.request_id(), 6);
}
#[test]
fn test_dma_peripheral_i2s_handshaking_ids() {
assert_eq!(DmaPeripheral::I2sTx.request_id(), 11);
assert_eq!(DmaPeripheral::I2sRx.request_id(), 12);
}
#[test]
fn test_dma_peripheral_ids_fit_4bit_field() {
for p in [
DmaPeripheral::Uart0Tx,
DmaPeripheral::Uart2Rx,
DmaPeripheral::Spi0Tx,
DmaPeripheral::Spi1Rx,
DmaPeripheral::I2sTx,
DmaPeripheral::I2sRx,
] {
assert!(p.request_id() <= 0x0F, "{:?} id {} > 4 bits", p, p.request_id());
}
}
#[test]
fn test_dma_channel_config_peripheral_wiring() {
let tx = DmaChannelConfig::default().mem_to_peripheral(DmaPeripheral::Spi0Tx);
assert_eq!(tx.flow_control, FlowControl::MemToPeripheral);
assert_eq!(tx.dst_peripheral, 7);
assert!(!tx.dst_inc);
let rx = DmaChannelConfig::default().peripheral_to_mem(DmaPeripheral::Uart1Rx);
assert_eq!(rx.flow_control, FlowControl::PeripheralToMem);
assert_eq!(rx.src_peripheral, 4);
assert!(!rx.src_inc);
}
#[test]
fn test_channel_base_consts() {
assert_eq!(Dma0::CHANNEL_BASE, 0);
assert_eq!(Sdma0::CHANNEL_BASE, 8);
}
#[test]
fn test_mdma_logical_to_physical() {
for ch in 0u8..4 {
assert_eq!(physical_channel_index(Dma0::CHANNEL_BASE, ch), ch as usize);
}
}
#[test]
fn test_sdma_logical_to_physical() {
assert_eq!(physical_channel_index(Sdma0::CHANNEL_BASE, 8), 0);
assert_eq!(physical_channel_index(Sdma0::CHANNEL_BASE, 9), 1);
assert_eq!(physical_channel_index(Sdma0::CHANNEL_BASE, 10), 2);
assert_eq!(physical_channel_index(Sdma0::CHANNEL_BASE, 11), 3);
}
#[test]
#[should_panic(expected = "out of range")]
fn test_mdma_channel_4_panics() {
physical_channel_index(Dma0::CHANNEL_BASE, 4);
}
#[test]
#[should_panic(expected = "out of range")]
fn test_sdma_channel_7_panics() {
physical_channel_index(Sdma0::CHANNEL_BASE, 7);
}
#[test]
#[should_panic(expected = "out of range")]
fn test_sdma_channel_12_panics() {
physical_channel_index(Sdma0::CHANNEL_BASE, 12);
}
}