update async SPI module #70

Merged
muellerr merged 1 commits from update-asynch-spi-module into main 2026-05-07 18:02:28 +02:00
3 changed files with 273 additions and 185 deletions
+228 -161
View File
@@ -1,11 +1,10 @@
//! Asynchronous PS SPI driver.
use core::{cell::RefCell, convert::Infallible, sync::atomic::AtomicBool};
use core::{cell::RefCell, sync::atomic::AtomicBool};
use critical_section::Mutex;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_hal_async::spi::SpiBus;
use raw_slice::{RawBufSlice, RawBufSliceMut};
use zynq7000::spi::InterruptStatus;
use super::{ChipSelect, FIFO_DEPTH, Spi, SpiId, SpiLowLevel};
@@ -16,6 +15,17 @@ static TRANSFER_CONTEXTS: [Mutex<RefCell<TransferContext>>; 2] =
// critical section.
static DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
#[derive(Debug, Clone, Copy, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("SPI RX FIFO overrun")]
pub struct RxOverrunError;
impl embedded_hal_async::spi::Error for RxOverrunError {
fn kind(&self) -> embedded_hal::spi::ErrorKind {
embedded_hal::spi::ErrorKind::Overrun
}
}
/// This is a generic interrupt handler to handle asynchronous SPI operations for a given
/// SPI peripheral.
///
@@ -23,18 +33,27 @@ static DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
/// the given SPI bank.
pub fn on_interrupt(peripheral: SpiId) {
let mut spi = unsafe { SpiLowLevel::steal(peripheral) };
let idx = peripheral as usize;
let imr = spi.read_imr();
// IRQ is not related.
if !imr.tx_trig() && !imr.tx_full() && !imr.tx_underflow() && !imr.rx_ovr() && !imr.rx_full() {
return;
}
let index = peripheral as usize;
let enabled_irqs = spi.read_enabled_interrupts();
// Prevent spurious interrupts from messing with out logic here.
spi.disable_interrupts();
let isr = spi.read_isr();
let interrupt_status = spi.read_isr();
spi.clear_interrupts();
// IRQ is not related.
if !enabled_irqs.tx_below_threshold()
&& !enabled_irqs.tx_full()
&& !enabled_irqs.tx_underflow()
&& !enabled_irqs.rx_ovr()
&& !enabled_irqs.rx_full()
{
return;
}
if interrupt_status.rx_overrun() {
// Not sure how to otherwise handle this cleanly..
return handle_rx_overrun(&mut spi, index);
}
let mut context = critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
*context_ref.borrow()
});
// No transfer active.
@@ -43,92 +62,82 @@ pub fn on_interrupt(peripheral: SpiId) {
}
let transfer_type = context.transfer_type.unwrap();
match transfer_type {
TransferType::Read => on_interrupt_read(idx, &mut context, &mut spi, isr),
TransferType::Write => on_interrupt_write(idx, &mut context, &mut spi, isr),
TransferType::Transfer => on_interrupt_transfer(idx, &mut context, &mut spi, isr),
TransferType::Read => on_interrupt_read(index, &mut context, &mut spi),
TransferType::Write => on_interrupt_write(index, &mut context, &mut spi),
TransferType::Transfer => on_interrupt_transfer(index, &mut context, &mut spi),
TransferType::TransferInPlace => {
on_interrupt_transfer_in_place(idx, &mut context, &mut spi, isr)
on_interrupt_transfer_in_place(index, &mut context, &mut spi)
}
};
}
fn on_interrupt_read(
idx: usize,
context: &mut TransferContext,
spi: &mut SpiLowLevel,
mut isr: InterruptStatus,
) {
fn handle_rx_overrun(spi: &mut SpiLowLevel, idx: usize) {
critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
context_ref.borrow_mut().rx_overrun = true;
});
// Clean state is re-configured by drop handler.
reset_trigger_levels(spi);
// At the very least disable the peripheral.
spi.disable();
// Interrupts were already disabled and cleared.
DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
WAKERS[idx].wake();
}
#[inline]
fn reset_trigger_levels(spi: &mut SpiLowLevel) {
spi.write_rx_trig(0x1);
spi.write_tx_trig(0x1);
}
fn on_interrupt_read(idx: usize, context: &mut TransferContext, spi: &mut SpiLowLevel) {
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
let transfer_len = read_slice.len();
// Read data from RX FIFO first.
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
(0..read_len).for_each(|_| {
while spi.read_interrupt_status().rx_not_empty() {
read_slice[context.rx_progress] = spi.read_fifo_unchecked();
context.rx_progress += 1;
});
}
// The FIFO still needs to be pumped.
while context.tx_progress < read_slice.len() && !isr.tx_full() {
while context.tx_progress < read_slice.len() && !spi.read_interrupt_status().tx_full() {
spi.write_fifo_unchecked(0);
context.tx_progress += 1;
isr = spi.read_isr();
}
isr_finish_handler(idx, spi, context, transfer_len)
}
fn on_interrupt_write(
idx: usize,
context: &mut TransferContext,
spi: &mut SpiLowLevel,
mut isr: InterruptStatus,
) {
fn on_interrupt_write(idx: usize, context: &mut TransferContext, spi: &mut SpiLowLevel) {
let write_slice = unsafe { context.tx_slice.get().unwrap() };
let transfer_len = write_slice.len();
// Read data from RX FIFO first.
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
(0..read_len).for_each(|_| {
while spi.read_interrupt_status().rx_not_empty() {
spi.read_fifo_unchecked();
context.rx_progress += 1;
});
}
// Data still needs to be sent
while context.tx_progress < transfer_len && !isr.tx_full() {
while context.tx_progress < transfer_len && !spi.read_interrupt_status().tx_full() {
spi.write_fifo_unchecked(write_slice[context.tx_progress]);
context.tx_progress += 1;
isr = spi.read_isr();
}
isr_finish_handler(idx, spi, context, transfer_len)
}
fn on_interrupt_transfer(
idx: usize,
context: &mut TransferContext,
spi: &mut SpiLowLevel,
mut isr: InterruptStatus,
) {
fn on_interrupt_transfer(idx: usize, context: &mut TransferContext, spi: &mut SpiLowLevel) {
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
let read_len = read_slice.len();
let write_slice = unsafe { context.tx_slice.get().unwrap() };
let write_len = write_slice.len();
let transfer_len = core::cmp::max(read_len, write_len);
// Read data from RX FIFO first.
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
(0..read_len).for_each(|_| {
if context.rx_progress < read_len {
read_slice[context.rx_progress] = spi.read_fifo_unchecked();
} else {
spi.read_fifo_unchecked();
}
context.rx_progress += 1;
});
// Data still needs to be sent
while context.tx_progress < transfer_len && !isr.tx_full() {
while context.tx_progress < transfer_len && !spi.read_interrupt_status().tx_full() {
if context.tx_progress < write_len {
spi.write_fifo_unchecked(write_slice[context.tx_progress]);
} else {
@@ -136,7 +145,16 @@ fn on_interrupt_transfer(
spi.write_fifo_unchecked(0);
}
context.tx_progress += 1;
isr = spi.read_isr();
}
// Read data from RX FIFO first.
while spi.read_interrupt_status().rx_not_empty() {
if context.rx_progress < read_len {
read_slice[context.rx_progress] = spi.read_fifo_unchecked();
} else {
spi.read_fifo_unchecked();
}
context.rx_progress += 1;
}
isr_finish_handler(idx, spi, context, transfer_len)
@@ -146,43 +164,25 @@ fn on_interrupt_transfer_in_place(
idx: usize,
context: &mut TransferContext,
spi: &mut SpiLowLevel,
mut isr: InterruptStatus,
) {
let transfer_slice = unsafe { context.rx_slice.get_mut().unwrap() };
let transfer_len = transfer_slice.len();
// Read data from RX FIFO first.
let read_len = calculate_read_len(spi, isr, transfer_len, context.rx_progress);
(0..read_len).for_each(|_| {
transfer_slice[context.rx_progress] = spi.read_fifo_unchecked();
context.rx_progress += 1;
});
// Data still needs to be sent
while context.tx_progress < transfer_len && !isr.tx_full() {
// Send data first to avoid overwriting data that still needs to be sent.
while context.tx_progress < transfer_len && !spi.read_interrupt_status().tx_full() {
spi.write_fifo_unchecked(transfer_slice[context.tx_progress]);
context.tx_progress += 1;
isr = spi.read_isr();
}
// Read data from RX FIFO.
while spi.read_interrupt_status().rx_not_empty() {
transfer_slice[context.rx_progress] = spi.read_fifo_unchecked();
context.rx_progress += 1;
}
isr_finish_handler(idx, spi, context, transfer_len)
}
fn calculate_read_len(
spi: &mut SpiLowLevel,
isr: InterruptStatus,
total_read_len: usize,
rx_progress: usize,
) -> usize {
if isr.rx_full() {
core::cmp::min(FIFO_DEPTH, total_read_len - rx_progress)
} else if isr.rx_not_empty() {
let trigger = spi.read_rx_not_empty_threshold();
core::cmp::min(total_read_len - rx_progress, trigger as usize)
} else {
0
}
}
/// Generic handler after RX FIFO and TX FIFO were handled. Checks and handles finished
/// and unfinished conditions.
fn isr_finish_handler(
@@ -197,7 +197,7 @@ fn isr_finish_handler(
return;
}
unfinished_transfer(spi, transfer_len, context.rx_progress);
unfinished_transfer(spi, transfer_len, context);
// If the transfer is done, the context structure was already written back.
// Write back updated context structure.
@@ -213,6 +213,7 @@ fn finish_transfer(idx: usize, context: &mut TransferContext, spi: &mut SpiLowLe
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
*context_ref.borrow_mut() = *context;
});
// Default reset values.
spi.set_rx_fifo_trigger(1).unwrap();
spi.set_tx_fifo_trigger(1).unwrap();
// Interrupts were already disabled and cleared.
@@ -220,11 +221,30 @@ fn finish_transfer(idx: usize, context: &mut TransferContext, spi: &mut SpiLowLe
WAKERS[idx].wake();
}
fn unfinished_transfer(spi: &mut SpiLowLevel, transfer_len: usize, rx_progress: usize) {
let new_trig_level = core::cmp::min(FIFO_DEPTH, transfer_len - rx_progress);
spi.set_rx_fifo_trigger(new_trig_level as u32).unwrap();
// Re-enable interrupts with the new RX FIFO trigger level.
spi.enable_interrupts();
fn unfinished_transfer(spi: &mut SpiLowLevel, transfer_len: usize, context: &TransferContext) {
// Unwraps okay, checks ensure number is below FIFO depth.
// By using the FIFO depth divided by two, give the software some time and allow more
// latency in the system.
spi.set_rx_fifo_trigger(
core::cmp::min(FIFO_DEPTH / 2, transfer_len - context.rx_progress) as u32,
)
.unwrap();
let tx_pending = context.tx_progress < transfer_len;
if tx_pending {
let remaining_tx = transfer_len - context.tx_progress;
let tx_trigger = if remaining_tx < FIFO_DEPTH / 2 {
FIFO_DEPTH as u32 - remaining_tx as u32
} else {
FIFO_DEPTH as u32 / 2
};
spi.set_tx_fifo_trigger(tx_trigger).unwrap();
} else {
spi.set_tx_fifo_trigger(1).unwrap();
}
// Re-enable interrupts with the new RX FIFO trigger level. Only enable TX threshold interrupt
// if we are not done yet with pushing all bytes to the FIFO.
spi.enable_interrupts(tx_pending);
}
#[derive(Debug, Clone, Copy)]
@@ -242,6 +262,7 @@ pub struct TransferContext {
rx_progress: usize,
tx_slice: RawBufSlice,
rx_slice: RawBufSliceMut,
rx_overrun: bool,
}
#[allow(clippy::new_without_default)]
@@ -253,145 +274,163 @@ impl TransferContext {
rx_progress: 0,
tx_slice: RawBufSlice::new_nulled(),
rx_slice: RawBufSliceMut::new_nulled(),
rx_overrun: false,
}
}
}
pub struct SpiFuture {
pub struct SpiFuture<'spi> {
id: super::SpiId,
spi: super::SpiLowLevel,
spi: &'spi mut super::SpiLowLevel,
config: super::Config,
finished_regularly: core::cell::Cell<bool>,
}
impl SpiFuture {
fn new_for_read(spi: &mut Spi, spi_id: SpiId, words: &mut [u8]) -> Self {
impl<'spi> SpiFuture<'spi> {
fn new_for_read(spi: &'spi mut Spi, spi_id: SpiId, words: &mut [u8]) -> Self {
if words.is_empty() {
panic!("words length unexpectedly 0");
}
let idx = spi_id as usize;
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
spi.inner.disable_interrupts();
Self::generic_init_transfer(spi, spi_id);
let write_idx = core::cmp::min(super::FIFO_DEPTH, words.len());
let write_index = core::cmp::min(super::FIFO_DEPTH, words.len());
// Send dummy bytes.
(0..write_idx).for_each(|_| {
(0..write_index).for_each(|_| {
spi.inner.write_fifo_unchecked(0);
});
Self::set_triggers(spi, write_idx, words.len());
Self::set_triggers(spi, write_index, words.len());
// We assume that the slave select configuration was already performed, but we take
// care of issuing a start if necessary.
spi.issue_manual_start_for_manual_cfg();
critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let context_ref = TRANSFER_CONTEXTS[spi_id as usize].borrow(cs);
let mut context = context_ref.borrow_mut();
context.transfer_type = Some(TransferType::Read);
unsafe {
context.rx_slice.set(words);
}
context.tx_slice.set_null();
context.tx_progress = write_idx;
context.tx_progress = write_index;
context.rx_progress = 0;
spi.inner.clear_interrupts();
spi.inner.enable_interrupts();
spi.inner.enable_interrupts(write_index < words.len());
spi.inner.enable();
});
Self {
id: spi_id,
config: spi.config,
spi: unsafe { spi.inner.clone() },
spi: &mut spi.inner,
finished_regularly: core::cell::Cell::new(false),
}
}
fn new_for_write(spi: &mut Spi, spi_id: SpiId, words: &[u8]) -> Self {
fn new_for_write(spi: &'spi mut Spi, spi_id: SpiId, words: &[u8]) -> Self {
if words.is_empty() {
panic!("words length unexpectedly 0");
}
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, words);
let write_index = Self::generic_init_transfer_write_transfer_in_place(spi, spi_id, words);
critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let context_ref = TRANSFER_CONTEXTS[spi_id as usize].borrow(cs);
let mut context = context_ref.borrow_mut();
context.transfer_type = Some(TransferType::Write);
unsafe {
context.tx_slice.set(words);
}
context.rx_slice.set_null();
context.tx_progress = write_idx;
context.tx_progress = write_index;
context.rx_progress = 0;
spi.inner.clear_interrupts();
spi.inner.enable_interrupts();
spi.inner.enable_interrupts(write_index < words.len());
spi.inner.enable();
});
Self {
id: spi_id,
config: spi.config,
spi: unsafe { spi.inner.clone() },
spi: &mut spi.inner,
finished_regularly: core::cell::Cell::new(false),
}
}
fn new_for_transfer(spi: &mut Spi, spi_id: SpiId, read: &mut [u8], write: &[u8]) -> Self {
fn new_for_transfer(spi: &'spi mut Spi, spi_id: SpiId, read: &mut [u8], write: &[u8]) -> Self {
if read.is_empty() || write.is_empty() {
panic!("read or write buffer unexpectedly empty");
}
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, write);
let full_write_len = core::cmp::max(read.len(), write.len());
let fifo_prefill = core::cmp::min(super::FIFO_DEPTH, full_write_len);
Self::generic_init_transfer(spi, spi_id);
for write_index in 0..fifo_prefill {
let value = write.get(write_index).copied().unwrap_or(0);
spi.inner.write_fifo_unchecked(value);
}
Self::set_triggers(spi, fifo_prefill, full_write_len);
critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let context_ref = TRANSFER_CONTEXTS[spi_id as usize].borrow(cs);
let mut context = context_ref.borrow_mut();
context.transfer_type = Some(TransferType::Transfer);
unsafe {
context.tx_slice.set(write);
context.rx_slice.set(read);
}
context.tx_progress = write_idx;
context.tx_progress = fifo_prefill;
context.rx_progress = 0;
spi.inner.clear_interrupts();
spi.inner.enable_interrupts();
spi.inner.enable_interrupts(fifo_prefill < write.len());
spi.inner.enable();
});
Self {
id: spi_id,
config: spi.config,
spi: unsafe { spi.inner.clone() },
spi: &mut spi.inner,
finished_regularly: core::cell::Cell::new(false),
}
}
fn new_for_transfer_in_place(spi: &mut Spi, spi_id: SpiId, words: &mut [u8]) -> Self {
fn new_for_transfer_in_place(spi: &'spi mut Spi, spi_id: SpiId, words: &mut [u8]) -> Self {
if words.is_empty() {
panic!("read and write buffer unexpectedly empty");
}
let (idx, write_idx) = Self::generic_init_transfer(spi, spi_id, words);
let write_index = Self::generic_init_transfer_write_transfer_in_place(spi, spi_id, words);
critical_section::with(|cs| {
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
let context_ref = TRANSFER_CONTEXTS[spi_id as usize].borrow(cs);
let mut context = context_ref.borrow_mut();
context.transfer_type = Some(TransferType::TransferInPlace);
unsafe {
context.rx_slice.set(words);
}
context.tx_slice.set_null();
context.tx_progress = write_idx;
context.tx_progress = write_index;
context.rx_progress = 0;
spi.inner.clear_interrupts();
spi.inner.enable_interrupts();
spi.inner.enable_interrupts(write_index < words.len());
spi.inner.enable();
});
Self {
id: spi_id,
config: spi.config,
spi: unsafe { spi.inner.clone() },
spi: &mut spi.inner,
finished_regularly: core::cell::Cell::new(false),
}
}
fn generic_init_transfer(spi: &mut Spi, spi_id: SpiId, write: &[u8]) -> (usize, usize) {
let idx = spi_id as usize;
fn generic_init_transfer(spi: &mut Spi, id: SpiId) {
let idx = id as usize;
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
spi.inner.disable();
spi.inner.disable_interrupts();
}
// Returns amount of bytes written to FIFO.
fn generic_init_transfer_write_transfer_in_place(
spi: &mut Spi,
id: SpiId,
write: &[u8],
) -> usize {
Self::generic_init_transfer(spi, id);
let write_idx = core::cmp::min(super::FIFO_DEPTH, write.len());
(0..write_idx).for_each(|idx| {
@@ -402,24 +441,31 @@ impl SpiFuture {
// We assume that the slave select configuration was already performed, but we take
// care of issuing a start if necessary.
spi.issue_manual_start_for_manual_cfg();
(idx, write_idx)
write_idx
}
fn set_triggers(spi: &mut Spi, write_idx: usize, write_len: usize) {
// This should never fail because it is never larger than the FIFO depth.
spi.inner.set_rx_fifo_trigger(write_idx as u32).unwrap();
spi.inner
.set_rx_fifo_trigger(core::cmp::min(
super::FIFO_DEPTH as u32 / 2,
write_idx as u32,
))
.unwrap();
// We want to re-fill the TX FIFO before it is completely empty if the full transfer size
// is larger than the FIFO depth. I am not sure whether the default value of 1 ensures
// this because the TMR says that this interrupt is triggered when the FIFO has less than
// threshold entries.
// is larger than the FIFO depth. Otherwise, set it to 0. Not exactly sure what that does,
// but we do not enable interrupts anyway.
if write_len > super::FIFO_DEPTH {
spi.inner.set_tx_fifo_trigger(2).unwrap();
spi.inner
.set_tx_fifo_trigger(super::FIFO_DEPTH as u32 / 2)
.unwrap();
} else {
spi.inner.set_tx_fifo_trigger(1).unwrap();
}
}
}
impl Future for SpiFuture {
type Output = ();
impl Future for SpiFuture<'_> {
type Output = Result<(), RxOverrunError>;
fn poll(
self: core::pin::Pin<&mut Self>,
@@ -427,18 +473,23 @@ impl Future for SpiFuture {
) -> core::task::Poll<Self::Output> {
WAKERS[self.id as usize].register(cx.waker());
if DONE[self.id as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
critical_section::with(|cs| {
let rx_overrun = critical_section::with(|cs| {
let mut ctx = TRANSFER_CONTEXTS[self.id as usize].borrow(cs).borrow_mut();
let overrun = ctx.rx_overrun;
*ctx = TransferContext::default();
overrun
});
self.finished_regularly.set(true);
return core::task::Poll::Ready(());
self.finished_regularly.set(!rx_overrun);
if rx_overrun {
return core::task::Poll::Ready(Err(RxOverrunError));
}
return core::task::Poll::Ready(Ok(()));
}
core::task::Poll::Pending
}
}
impl Drop for SpiFuture {
impl Drop for SpiFuture<'_> {
fn drop(&mut self) {
if !self.finished_regularly.get() {
// It might be sufficient to disable and enable the SPI.. But this definitely
@@ -459,65 +510,73 @@ impl SpiAsync {
Self(spi)
}
async fn read(&mut self, words: &mut [u8]) {
pub fn read(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
if words.is_empty() {
return;
return None;
}
let id = self.0.inner.id;
let spi_fut = SpiFuture::new_for_read(&mut self.0, id, words);
spi_fut.await;
Some(SpiFuture::new_for_read(&mut self.0, id, words))
}
async fn write(&mut self, words: &[u8]) {
pub fn write(&mut self, words: &[u8]) -> Option<SpiFuture<'_>> {
if words.is_empty() {
return;
return None;
}
let id = self.0.inner.id;
let spi_fut = SpiFuture::new_for_write(&mut self.0, id, words);
spi_fut.await;
Some(SpiFuture::new_for_write(&mut self.0, id, words))
}
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) {
pub fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Option<SpiFuture<'_>> {
if read.is_empty() || write.is_empty() {
return;
return None;
}
let id = self.0.inner.id;
let spi_fut = SpiFuture::new_for_transfer(&mut self.0, id, read, write);
spi_fut.await;
Some(SpiFuture::new_for_transfer(&mut self.0, id, read, write))
}
async fn transfer_in_place(&mut self, words: &mut [u8]) {
pub fn transfer_in_place(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
if words.is_empty() {
return;
return None;
}
let id = self.0.inner.id;
let spi_fut = SpiFuture::new_for_transfer_in_place(&mut self.0, id, words);
spi_fut.await;
Some(SpiFuture::new_for_transfer_in_place(&mut self.0, id, words))
}
}
impl embedded_hal_async::spi::ErrorType for SpiAsync {
type Error = Infallible;
type Error = RxOverrunError;
}
impl embedded_hal_async::spi::SpiBus for SpiAsync {
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.read(words).await;
if words.is_empty() {
return Ok(());
}
self.read(words).unwrap().await?;
Ok(())
}
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.write(words).await;
if words.is_empty() {
return Ok(());
}
self.write(words).unwrap().await?;
Ok(())
}
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
self.transfer(read, write).await;
if read.is_empty() && write.is_empty() {
return Ok(());
}
self.transfer(read, write).unwrap().await?;
Ok(())
}
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.transfer_in_place(words).await;
if words.is_empty() {
return Ok(());
}
self.transfer_in_place(words).unwrap().await?;
Ok(())
}
@@ -547,7 +606,7 @@ impl<Delay: embedded_hal_async::delay::DelayNs> SpiWithHwCsAsync<Delay> {
impl<Delay: embedded_hal_async::delay::DelayNs> embedded_hal_async::spi::ErrorType
for SpiWithHwCsAsync<Delay>
{
type Error = Infallible;
type Error = RxOverrunError;
}
impl<Delay: embedded_hal_async::delay::DelayNs> embedded_hal_async::spi::SpiDevice
@@ -561,16 +620,24 @@ impl<Delay: embedded_hal_async::delay::DelayNs> embedded_hal_async::spi::SpiDevi
for op in operations {
match op {
embedded_hal::spi::Operation::Read(items) => {
self.spi.read(items).await;
if let Some(fut) = self.spi.read(items) {
fut.await?;
}
}
embedded_hal::spi::Operation::Write(items) => {
self.spi.write(items).await;
if let Some(fut) = self.spi.write(items) {
fut.await?;
}
}
embedded_hal::spi::Operation::Transfer(read, write) => {
self.spi.transfer(read, write).await;
if let Some(fut) = self.spi.transfer(read, write) {
fut.await?;
}
}
embedded_hal::spi::Operation::TransferInPlace(items) => {
self.spi.transfer_in_place(items).await;
if let Some(fut) = self.spi.transfer_in_place(items) {
fut.await?;
}
}
embedded_hal::spi::Operation::DelayNs(delay) => {
self.delay.delay_ns(*delay).await;
+29 -15
View File
@@ -20,7 +20,7 @@ use embedded_hal::delay::DelayNs;
pub use embedded_hal::spi::Mode;
use zynq7000::slcr::reset::DualRefAndClockResetSpiUart;
use zynq7000::spi::{
BaudDivSel, DelayControl, FifoWrite, InterruptControl, InterruptMask, InterruptStatus,
BaudDivSel, DelayControl, FifoWrite, InterruptControl, InterruptEnabled, InterruptStatus,
MmioRegisters, SPI_0_BASE_ADDR, SPI_1_BASE_ADDR,
};
@@ -538,12 +538,12 @@ impl SpiLowLevel {
#[inline]
pub fn read_isr(&self) -> InterruptStatus {
self.regs.read_isr()
self.regs.read_interrupt_status()
}
#[inline]
pub fn read_imr(&self) -> InterruptMask {
self.regs.read_imr()
pub fn read_enabled_interrupts(&self) -> InterruptEnabled {
self.regs.read_enabled_interrupts()
}
#[inline]
@@ -573,13 +573,13 @@ impl SpiLowLevel {
/// in SPI master mode.
#[inline]
pub fn disable_interrupts(&mut self) {
self.regs.write_idr(
self.regs.write_interupt_disable(
InterruptControl::builder()
.with_tx_underflow(true)
.with_rx_full(true)
.with_rx_not_empty(true)
.with_tx_full(false)
.with_tx_trig(true)
.with_tx_below_threshold(true)
.with_mode_fault(false)
.with_rx_ovr(true)
.build(),
@@ -589,14 +589,14 @@ impl SpiLowLevel {
/// This enables all interrupts relevant for non-blocking interrupt driven SPI operation
/// in SPI master mode.
#[inline]
pub fn enable_interrupts(&mut self) {
self.regs.write_ier(
pub fn enable_interrupts(&mut self, tx_below_threshold: bool) {
self.regs.write_interrupt_enable(
InterruptControl::builder()
.with_tx_underflow(true)
.with_rx_full(true)
.with_rx_not_empty(true)
.with_tx_full(false)
.with_tx_trig(true)
.with_tx_below_threshold(tx_below_threshold)
.with_mode_fault(false)
.with_rx_ovr(true)
.build(),
@@ -607,20 +607,34 @@ impl SpiLowLevel {
/// in SPI master mode.
#[inline]
pub fn clear_interrupts(&mut self) {
self.regs.write_isr(
self.regs.write_interrupt_status(
InterruptStatus::builder()
.with_tx_underflow(true)
.with_rx_full(true)
.with_rx_not_empty(true)
.with_tx_full(false)
.with_tx_not_full(true)
.with_tx_below_threshold(true)
.with_mode_fault(false)
.with_rx_ovr(true)
.with_rx_overrun(true)
.build(),
);
}
}
impl core::ops::Deref for SpiLowLevel {
type Target = MmioRegisters<'static>;
fn deref(&self) -> &Self::Target {
&self.regs
}
}
impl core::ops::DerefMut for SpiLowLevel {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.regs
}
}
/// Blocking Driver for the PS SPI peripheral in master mode.
pub struct Spi {
inner: SpiLowLevel,
@@ -908,7 +922,7 @@ impl Spi {
let mut read_idx = 0;
while read_idx < words.len() {
let status = self.regs().read_isr();
let status = self.regs().read_interrupt_status();
if status.rx_not_empty() {
words[read_idx] = self.inner.read_fifo_unchecked();
read_idx += 1;
@@ -929,7 +943,7 @@ impl Spi {
let mut read_idx = 0;
while written < words.len() {
let status = self.regs().read_isr();
let status = self.regs().read_interrupt_status();
// We empty the FIFO to prevent it filling up completely, as long as we have to write
// bytes
if status.rx_not_empty() {
@@ -958,7 +972,7 @@ impl Spi {
let mut writes_finished = write_idx == max_idx;
let mut reads_finished = false;
while !writes_finished || !reads_finished {
let status = self.regs().read_isr();
let status = self.regs().read_interrupt_status();
if status.rx_not_empty() && !reads_finished {
if read_idx < read.len() {
read[read_idx] = self.inner.read_fifo_unchecked();
+16 -9
View File
@@ -87,15 +87,18 @@ pub struct InterruptStatus {
rx_full: bool,
#[bit(4, rw)]
rx_not_empty: bool,
/// Switches to 1 when the FIFO becomes full and then remains asserted until the FIFO falls
/// below the configured threshold level.
#[bit(3, rw)]
tx_full: bool,
/// TX FIFO level below configured threshold.
#[bit(2, rw)]
tx_not_full: bool,
tx_below_threshold: bool,
#[bit(1, rw)]
mode_fault: bool,
/// Receiver overflow interrupt.
#[bit(0, rw)]
rx_ovr: bool,
rx_overrun: bool,
}
#[bitbybit::bitfield(
@@ -114,8 +117,9 @@ pub struct InterruptControl {
rx_not_empty: bool,
#[bit(3, w)]
tx_full: bool,
/// Interrupt when TX FIFO level below configured threshold.
#[bit(2, w)]
tx_trig: bool,
tx_below_threshold: bool,
#[bit(1, w)]
mode_fault: bool,
/// Receiver overflow interrupt.
@@ -124,17 +128,20 @@ pub struct InterruptControl {
}
#[bitbybit::bitfield(u32, debug, defmt_bitfields(feature = "defmt"), forbid_overlaps)]
pub struct InterruptMask {
pub struct InterruptEnabled {
#[bit(6, r)]
tx_underflow: bool,
#[bit(5, r)]
rx_full: bool,
#[bit(4, r)]
rx_not_empty: bool,
/// Switches to 1 when the FIFO becomes full and then remains asserted until the FIFO falls
/// below the configured threshold level.
#[bit(3, r)]
tx_full: bool,
/// Interrupt when TX FIFO level below configured threshold.
#[bit(2, r)]
tx_trig: bool,
tx_below_threshold: bool,
#[bit(1, r)]
mode_fault: bool,
/// Receiver overflow interrupt.
@@ -208,16 +215,16 @@ pub struct DelayControl {
pub struct Registers {
cr: Config,
#[mmio(PureRead, Write)]
isr: InterruptStatus,
interrupt_status: InterruptStatus,
/// Interrupt Enable Register.
#[mmio(Write)]
ier: InterruptControl,
interrupt_enable: InterruptControl,
/// Interrupt Disable Register.
#[mmio(Write)]
idr: InterruptControl,
interupt_disable: InterruptControl,
/// Interrupt Mask Register.
#[mmio(PureRead)]
imr: InterruptMask,
enabled_interrupts: InterruptEnabled,
enable: u32,
delay_control: DelayControl,
#[mmio(Write)]