#![macro_use]
use stm32_metapac::gpdma::regs;
use stm32_metapac::gpdma::vals::Dreq;
use crate::dma::word::{Word, WordSize};
use crate::dma::{Dir, Request};
#[derive(Debug)]
pub enum RunMode {
Unlinked,
Once,
Circular,
}
#[derive(Debug, Copy, Clone, Default)]
#[repr(C)]
pub struct LinearItem {
pub tr1: regs::ChTr1,
pub tr2: regs::ChTr2,
pub br1: regs::ChBr1,
pub sar: u32,
pub dar: u32,
pub llr: regs::ChLlr,
}
impl LinearItem {
pub unsafe fn new_read<'d, W: Word>(request: Request, peri_addr: *mut W, buf: &'d mut [W]) -> Self {
Self::new_inner(
request,
Dir::PeripheralToMemory,
peri_addr as *const u32,
buf as *mut [W] as *mut W as *mut u32,
buf.len(),
true,
W::size(),
W::size(),
)
}
pub unsafe fn new_write<'d, MW: Word, PW: Word>(request: Request, buf: &'d [MW], peri_addr: *mut PW) -> Self {
Self::new_inner(
request,
Dir::MemoryToPeripheral,
peri_addr as *const u32,
buf as *const [MW] as *const MW as *mut u32,
buf.len(),
true,
MW::size(),
PW::size(),
)
}
unsafe fn new_inner(
request: Request,
dir: Dir,
peri_addr: *const u32,
mem_addr: *mut u32,
mem_len: usize,
incr_mem: bool,
data_size: WordSize,
dst_size: WordSize,
) -> Self {
let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else {
panic!("DMA transfers may not be larger than 65535 bytes.");
};
let mut br1 = regs::ChBr1(0);
br1.set_bndt(bndt);
let mut tr1 = regs::ChTr1(0);
tr1.set_sdw(data_size.into());
tr1.set_ddw(dst_size.into());
tr1.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem);
tr1.set_dinc(dir == Dir::PeripheralToMemory && incr_mem);
let mut tr2 = regs::ChTr2(0);
tr2.set_dreq(match dir {
Dir::MemoryToPeripheral => Dreq::DESTINATION_PERIPHERAL,
Dir::PeripheralToMemory => Dreq::SOURCE_PERIPHERAL,
Dir::MemoryToMemory => panic!("memory-to-memory transfers are not valid for LinearItem"),
});
tr2.set_reqsel(request);
let (sar, dar) = match dir {
Dir::MemoryToPeripheral => (mem_addr as _, peri_addr as _),
Dir::PeripheralToMemory => (peri_addr as _, mem_addr as _),
Dir::MemoryToMemory => panic!("memory-to-memory transfers are not valid for LinearItem"),
};
let llr = regs::ChLlr(0);
Self {
tr1,
tr2,
br1,
sar,
dar,
llr,
}
}
fn link_to(&mut self, next: u16) {
let mut llr = regs::ChLlr(0);
llr.set_ut1(true);
llr.set_ut2(true);
llr.set_ub1(true);
llr.set_usa(true);
llr.set_uda(true);
llr.set_ull(true);
llr.set_la(next >> 2);
self.llr = llr;
}
fn unlink(&mut self) {
self.llr = regs::ChLlr(0);
}
fn transfer_count(&self) -> usize {
let word_size: WordSize = self.tr1.ddw().into();
self.br1.bndt() as usize / word_size.bytes()
}
}
#[repr(C)]
pub struct Table<const ITEM_COUNT: usize> {
pub items: [LinearItem; ITEM_COUNT],
}
impl<const ITEM_COUNT: usize> Table<ITEM_COUNT> {
pub fn new(items: [LinearItem; ITEM_COUNT]) -> Self {
assert!(!items.is_empty());
Self { items }
}
pub unsafe fn new_ping_pong<W: Word>(
request: Request,
peri_addr: *mut W,
buffer: &mut [W],
direction: Dir,
) -> Table<2> {
let half_len = buffer.len() / 2;
assert_eq!(half_len * 2, buffer.len());
let items = match direction {
Dir::MemoryToPeripheral => [
LinearItem::new_write(request, &mut buffer[..half_len], peri_addr),
LinearItem::new_write(request, &mut buffer[half_len..], peri_addr),
],
Dir::PeripheralToMemory => [
LinearItem::new_read(request, peri_addr, &mut buffer[..half_len]),
LinearItem::new_read(request, peri_addr, &mut buffer[half_len..]),
],
Dir::MemoryToMemory => panic!("memory-to-memory transfers are not valid for LinearItem"),
};
Table::new(items)
}
pub fn link(&mut self, run_mode: RunMode) {
if matches!(run_mode, RunMode::Once | RunMode::Circular) {
self.link_sequential();
}
if matches!(run_mode, RunMode::Circular) {
self.link_repeat();
}
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn transfer_count(&self) -> usize {
let mut count = 0;
for item in self.items {
count += item.transfer_count() as usize
}
count
}
pub fn link_indices(&mut self, first: usize, second: usize) {
assert!(first < self.len());
assert!(second < self.len());
let second_item = self.offset_address(second);
self.items[first].link_to(second_item);
}
pub fn link_sequential(&mut self) {
if self.len() > 1 {
for index in 0..(self.items.len() - 1) {
let next = self.offset_address(index + 1);
self.items[index].link_to(next);
}
}
}
pub fn link_repeat(&mut self) {
let first_address = self.offset_address(0);
self.items.last_mut().unwrap().link_to(first_address);
}
pub fn unlink(&mut self) {
for item in self.items.iter_mut() {
item.unlink();
}
}
pub fn base_address(&self) -> u16 {
((&raw const self.items as u32) >> 16) as _
}
pub fn offset_address(&self, index: usize) -> u16 {
assert!(self.items.len() > index);
let address = &raw const self.items[index] as _;
assert_eq!(address & 0b11, 0);
address
}
}