use core::{marker::PhantomData, task::Poll};
use bitbybit::{bitenum, bitfield};
use cortex_m::interrupt::InterruptNumber;
use embassy_hal_internal::{Peri, PeripheralType, interrupt::InterruptExt as _};
use embassy_sync::waitqueue::AtomicWaker;
use crate::{
NUM_INTERRUPTS,
event_link::{IcuInterrupt as _, InterruptEvent},
interrupt::{
self,
typelevel::{Handler as InterruptHandler, Interrupt as InterruptType},
},
module_stop::ModuleStop as _,
pac,
};
static mut DTC_VECTOR_TABLE: VectorTable = VectorTable::new();
cfg_select! {
feature = "defmt" => {
#[allow(private_bounds)]
pub trait Word: SealedWord + defmt::Format {}
}
_ => {
#[allow(private_bounds)]
pub trait Word: SealedWord {}
}
}
trait SealedWord: Copy {
const WORD_SIZE: WordSize;
}
impl Word for u8 {}
impl SealedWord for u8 {
const WORD_SIZE: WordSize = WordSize::Byte;
}
impl Word for u16 {}
impl SealedWord for u16 {
const WORD_SIZE: WordSize = WordSize::HalfWord;
}
impl Word for u32 {}
impl SealedWord for u32 {
const WORD_SIZE: WordSize = WordSize::Word;
}
pub struct DtcInterruptHandler<C: Instance> {
phantom: PhantomData<C>,
}
pub struct Channel<C: Instance> {
entry: DtcEntry,
phantom: PhantomData<C>,
}
#[bitenum(u2)]
#[allow(dead_code)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum AddressMode {
Fixed = 0b00,
Increment = 0b10,
Decrement = 0b11,
}
#[bitenum(u1, exhaustive = true)]
#[allow(dead_code)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum RepeatMode {
Destination = 0,
Source = 1,
}
#[bitenum(u1, exhaustive = true)]
#[allow(dead_code)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum InterruptMode {
OnCompletion = 0,
PerTransfer = 1,
}
#[bitenum(u2)]
#[allow(dead_code)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum ChainMode {
Disabled = 0b00,
Continuous = 0b01,
Penultimate = 0b11,
}
#[bitenum(u2)]
#[allow(dead_code)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum WordSize {
Byte = 0b00,
HalfWord = 0b01,
Word = 0b10,
}
#[bitenum(u2)]
#[allow(dead_code)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum TransferMode {
Normal = 0b00,
Repeat = 0b01,
Block = 0b10,
}
#[bitfield(u128, default = 0)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct DtcEntry {
#[bits(0..=15)]
reserved: u16,
#[bits(16..=17)]
reserved: u2,
#[bits(18..=19, rw)]
dest_address_mode: Option<AddressMode>,
#[bit(20, rw)]
repeat_mode: RepeatMode,
#[bit(21, rw)]
interrupt_mode: InterruptMode,
#[bits(22..=23, rw)]
chain_mode: Option<ChainMode>,
#[bits(24..=25)]
reserved: u2,
#[bits(26..=27, rw)]
source_address_mode: Option<AddressMode>,
#[bits(28..=29, rw)]
word_size: Option<WordSize>,
#[bits(30..=31, rw)]
transfer_mode: Option<TransferMode>,
#[bits(32..=63, rw)]
source_address: u32,
#[bits(64..=95, rw)]
dest_address: u32,
#[bits(96..=111, rw)]
count_b: u16,
#[bits(112..=119, rw)]
count_a_low: u8,
#[bits(120..=127, rw)]
count_a_high: u8,
}
#[repr(C, align(1024))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct VectorTable {
vectors: [u32; NUM_INTERRUPTS],
}
impl Default for VectorTable {
fn default() -> Self {
Self {
vectors: [0; NUM_INTERRUPTS],
}
}
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance {
type Int: interrupt::typelevel::Interrupt;
}
pub(crate) trait SealedInstance: PeripheralType {}
impl VectorTable {
const fn new() -> Self {
Self {
vectors: [0; NUM_INTERRUPTS],
}
}
}
pub struct Transfer<'d, C: Instance> {
event: InterruptEvent,
phantom: PhantomData<&'d C>,
}
impl<'d, C: Instance> Transfer<'d, C> {
fn new(event: InterruptEvent) -> Self {
Self {
event,
phantom: PhantomData,
}
}
#[inline(always)]
pub fn start(&mut self) {
trace!("DTC{}: Enable for: {}", C::Int::IRQ.number(), self.event);
C::Int::IRQ.dtc_enable(self.event);
}
#[inline(always)]
pub fn stop(&mut self) {
C::Int::IRQ.icu_disable();
}
}
pub(crate) fn init() {
let dtc = pac::DTC;
crate::peripherals::DTC::start_module();
let vector_base = unsafe { core::ptr::addr_of!(DTC_VECTOR_TABLE.vectors) as u32 };
cfg_select! {
trust_zone => dtc.dtcvbr_sec().write_value(vector_base),
_ => dtc.dtcvbr().write_value(vector_base),
}
cfg_select! {
all(trust_zone_v2, secure) => {
dtc.dtccr_sec().modify(|r| r.set_rrs(false));
dtc.dtcst().modify(|r| r.set_dtcst(true));
},
_ => {
dtc.dtccr().modify(|r| r.set_rrs(false));
dtc.dtcst().modify(|r| r.set_dtcst(true));
}
}
}
impl<C: Instance> Channel<C> {
fn waker() -> &'static AtomicWaker {
static WAKER: AtomicWaker = AtomicWaker::new();
&WAKER
}
#[allow(private_bounds)]
pub fn new<'d>(
peri: Peri<'d, C>,
irqs: impl interrupt::typelevel::Binding<C::Int, DtcInterruptHandler<C>>,
) -> Self {
let _ = peri;
let _ = irqs;
let this = Self {
entry: Default::default(),
phantom: PhantomData,
};
unsafe { C::Int::IRQ.enable() };
C::Int::IRQ.icu_disable();
this
}
pub unsafe fn copy_slice<'d, W: Word>(
&mut self,
source: &'d [W],
dest: &'d mut [W],
increment_on: InterruptEvent,
) -> Transfer<'d, C> {
let len = source.len() as u8;
let transfer_entry = DtcEntry::builder()
.with_chain_mode(ChainMode::Disabled)
.with_source_address_mode(AddressMode::Increment)
.with_dest_address_mode(AddressMode::Increment)
.with_word_size(W::WORD_SIZE)
.with_transfer_mode(TransferMode::Block)
.with_repeat_mode(RepeatMode::Destination)
.with_interrupt_mode(InterruptMode::OnCompletion)
.with_source_address(source.as_ptr() as u32)
.with_dest_address(dest.as_ptr() as u32)
.with_count_b(1)
.with_count_a_low(len)
.with_count_a_high(len)
.build();
self.update_entry(transfer_entry);
let mut transfer = Transfer::new(increment_on);
transfer.start();
transfer
}
pub unsafe fn write<'d, W: Word>(
&mut self,
source: &'d [W],
dest: *mut W,
increment_on: InterruptEvent,
repeat: bool,
) -> Transfer<'d, C> {
let (transfer_mode, low_count, high_count) = match repeat {
false => (TransferMode::Normal, source.len() as u8, 0),
true => (TransferMode::Repeat, source.len() as u8, source.len() as u8),
};
let transfer_entry = DtcEntry::builder()
.with_chain_mode(ChainMode::Disabled)
.with_source_address_mode(AddressMode::Increment)
.with_dest_address_mode(AddressMode::Fixed)
.with_word_size(W::WORD_SIZE)
.with_transfer_mode(transfer_mode)
.with_repeat_mode(RepeatMode::Source)
.with_interrupt_mode(InterruptMode::OnCompletion)
.with_source_address(source.as_ptr() as u32)
.with_dest_address(dest as u32)
.with_count_b(0)
.with_count_a_low(low_count)
.with_count_a_high(high_count)
.build();
self.update_entry(transfer_entry);
let mut transfer = Transfer::new(increment_on);
transfer.start();
transfer
}
pub(crate) fn prepare_write<'d, W: Word>(
&'d mut self,
source: &'d [W],
dest: *mut W,
last: bool,
) -> DtcEntry {
let chain_mode = match last {
true => ChainMode::Disabled,
false => ChainMode::Penultimate,
};
DtcEntry::builder()
.with_chain_mode(chain_mode)
.with_source_address_mode(AddressMode::Increment)
.with_dest_address_mode(AddressMode::Fixed)
.with_word_size(W::WORD_SIZE)
.with_transfer_mode(TransferMode::Normal)
.with_repeat_mode(RepeatMode::Source)
.with_interrupt_mode(InterruptMode::OnCompletion)
.with_source_address(source.as_ptr() as u32)
.with_dest_address(dest as u32)
.with_count_b(0)
.with_count_a_low(source.len() as u8)
.with_count_a_high(0)
.build()
}
pub(crate) unsafe fn write_chain<'d>(
&mut self,
chain: &'d [DtcEntry],
increment_on: InterruptEvent,
) -> Transfer<'d, C> {
unsafe { DTC_VECTOR_TABLE.vectors[C::Int::IRQ.number() as usize] = chain.as_ptr() as u32 };
let dtc = crate::pac::DTC;
dtc.dtccr().modify(|r| r.set_rrs(false));
dtc.dtccr().modify(|r| r.set_rrs(true));
let mut transfer = Transfer::new(increment_on);
transfer.start();
transfer
}
pub unsafe fn read<'d, W: Word>(
&mut self,
source: *const W,
dest: &'d mut [W],
increment_on: InterruptEvent,
repeat: bool,
) -> Transfer<'d, C> {
let (transfer_mode, low_count, high_count) = match repeat {
false => (TransferMode::Normal, dest.len() as u8, 0),
true => (TransferMode::Repeat, dest.len() as u8, dest.len() as u8),
};
let transfer_entry = DtcEntry::builder()
.with_chain_mode(ChainMode::Disabled)
.with_source_address_mode(AddressMode::Fixed)
.with_dest_address_mode(AddressMode::Increment)
.with_word_size(W::WORD_SIZE)
.with_transfer_mode(transfer_mode)
.with_repeat_mode(RepeatMode::Destination)
.with_interrupt_mode(InterruptMode::OnCompletion)
.with_source_address(source as u32)
.with_dest_address(dest.as_ptr() as u32)
.with_count_b(0)
.with_count_a_low(low_count)
.with_count_a_high(high_count)
.build();
self.update_entry(transfer_entry);
let mut transfer = Transfer::new(increment_on);
transfer.start();
transfer
}
#[inline(always)]
fn update_entry(&mut self, entry: DtcEntry) {
self.entry = entry;
let entry_addr = &self.entry as *const _;
unsafe { DTC_VECTOR_TABLE.vectors[C::Int::IRQ.number() as usize] = entry_addr as u32 };
let dtc = crate::pac::DTC;
dtc.dtccr().modify(|r| r.set_rrs(false));
dtc.dtccr().modify(|r| r.set_rrs(true));
}
}
impl<'d, C: Instance> Drop for Transfer<'d, C> {
fn drop(&mut self) {
trace!(
"DTC{}: Dropping transfer status={}",
C::Int::IRQ.number(),
pac::DTC.dtcsts().read()
);
}
}
impl<C: Instance> Drop for Channel<C> {
fn drop(&mut self) {
trace!("DTC{}: Drop", C::Int::IRQ.number());
unsafe { DTC_VECTOR_TABLE.vectors[C::Int::IRQ.number() as usize] = 0 };
C::Int::IRQ.dtc_disable();
}
}
impl<C: Instance> InterruptHandler<C::Int> for DtcInterruptHandler<C> {
unsafe fn on_interrupt() {
C::Int::IRQ.icu_unpend();
Channel::<C>::waker().wake();
}
}
impl<'d, C: Instance> Future for Transfer<'d, C> {
type Output = Result<(), ()>;
fn poll(
self: core::pin::Pin<&mut Self>,
ctx: &mut core::task::Context<'_>,
) -> Poll<Self::Output> {
Channel::<C>::waker().register(ctx.waker());
#[cfg(dtc_sec)]
{
let dtc = crate::pac::DTC;
let err = dtc.dtevr().read();
if err.dtesta() && u16::from(err.dtev()) == C::Int::IRQ.number() {
error!("DTC Error: {}", err);
let bus = pac::BUS;
return Poll::Ready(Err(()));
}
}
if !C::Int::IRQ.is_dtc() {
C::Int::IRQ.icu_disable();
return Poll::Ready(Ok(()));
}
Poll::Pending
}
}
macro_rules! dtc_link {
($index:literal) => {
paste::paste! {
impl crate::dtc::Instance for crate::peripherals::[< DTC_CHAN $index >] {
type Int = interrupt::typelevel::[< IEL $index >];
}
impl crate::dtc::SealedInstance for crate::peripherals::[< DTC_CHAN $index >] {
}
}
};
}
pub(crate) use dtc_link;