1use core::cell::RefCell;
2
3use arbitrary_int::u5;
4use critical_section::Mutex;
5use embassy_sync::waitqueue::AtomicWaker;
6use portable_atomic::AtomicBool;
7use raw_slice::{RawBufSlice, RawBufSliceMut};
8
9use crate::{
10 shared::{FifoClear, TriggerLevel},
11 spi::{
12 FIFO_DEPTH,
13 regs::{Data, InterruptClear, InterruptControl},
14 },
15};
16
17#[cfg(feature = "vor1x")]
18pub const NUM_SPIS: usize = 3;
19#[cfg(feature = "vor4x")]
20pub const NUM_SPIS: usize = 4;
21
22static WAKERS: [AtomicWaker; NUM_SPIS] = [const { AtomicWaker::new() }; NUM_SPIS];
23static TRANSFER_CONTEXTS: [Mutex<RefCell<TransferContext>>; NUM_SPIS] =
24 [const { Mutex::new(RefCell::new(TransferContext::new())) }; NUM_SPIS];
25static DONE: [AtomicBool; NUM_SPIS] = [const { AtomicBool::new(false) }; NUM_SPIS];
28
29#[derive(Debug, Clone, Copy, thiserror::Error)]
30#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31#[error("SPI RX FIFO overrun")]
32pub struct RxOverrunError;
33
34impl embedded_hal_async::spi::Error for RxOverrunError {
35 fn kind(&self) -> embedded_hal::spi::ErrorKind {
36 embedded_hal::spi::ErrorKind::Overrun
37 }
38}
39
40pub fn on_interrupt(peripheral: super::Bank) {
46 let mut spi = unsafe { peripheral.steal_regs() };
47 let index = peripheral as usize;
48 let enabled_irqs = spi.read_interrupt_control();
49 let interrupt_status = spi.read_interrupt_status();
50 spi.write_interrupt_clear(InterruptClear::ALL);
51 spi.write_interrupt_control(InterruptControl::DISABLE_ALL);
53 if enabled_irqs.raw_value() == 0 {
55 reset_trigger_levels(&mut spi);
56 spi.write_fifo_clear(FifoClear::ALL);
57 return;
58 }
59 if interrupt_status.rx_overrun() {
60 return handle_rx_overrun(&mut spi, index);
62 }
63 let mut context = critical_section::with(|cs| {
64 let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
65 *context_ref.borrow()
66 });
67 if context.transfer_type.is_none() {
69 return;
70 }
71 let transfer_type = context.transfer_type.unwrap();
72 match transfer_type {
73 TransferType::Read => on_interrupt_read(index, &mut context, &mut spi, enabled_irqs),
74 TransferType::Write => on_interrupt_write(index, &mut context, &mut spi, enabled_irqs),
75 TransferType::Transfer => {
76 on_interrupt_transfer(index, &mut context, &mut spi, enabled_irqs)
77 }
78 TransferType::TransferInPlace => {
79 on_interrupt_transfer_in_place(index, &mut context, &mut spi, enabled_irqs)
80 }
81 };
82}
83
84fn handle_rx_overrun(spi: &mut super::regs::MmioSpi<'static>, idx: usize) {
85 critical_section::with(|cs| {
86 let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
87 context_ref.borrow_mut().rx_overrun = true;
88 });
89 reset_trigger_levels(spi);
91 spi.write_fifo_clear(FifoClear::ALL);
92 DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
94 WAKERS[idx].wake();
95}
96
97fn on_interrupt_read(
98 idx: usize,
99 context: &mut TransferContext,
100 spi: &mut super::regs::MmioSpi<'static>,
101 enabled_irqs: InterruptControl,
102) {
103 let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
104 let transfer_len = read_slice.len();
105
106 while spi.read_status().rx_not_empty() {
108 let data = spi.read_data();
109 if context.rx_progress < transfer_len {
110 read_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
111 context.rx_progress += 1;
112 }
113 }
114
115 while context.tx_progress < read_slice.len() && spi.read_status().tx_not_full() {
117 spi.write_data(Data::new_with_raw_value(0));
118 context.tx_progress += 1;
119 }
120
121 isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
122}
123
124fn on_interrupt_write(
125 idx: usize,
126 context: &mut TransferContext,
127 spi: &mut super::regs::MmioSpi<'static>,
128 enabled_irqs: InterruptControl,
129) {
130 let write_slice = unsafe { context.tx_slice.get().unwrap() };
131 let transfer_len = write_slice.len();
132
133 while spi.read_status().rx_not_empty() {
135 spi.read_data();
136 if context.rx_progress < transfer_len {
137 context.rx_progress += 1;
138 }
139 }
140
141 while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
143 spi.write_data(Data::new_with_raw_value(
144 write_slice[context.tx_progress] as u32,
145 ));
146 context.tx_progress += 1;
147 }
148
149 isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
150}
151
152fn on_interrupt_transfer(
153 idx: usize,
154 context: &mut TransferContext,
155 spi: &mut super::regs::MmioSpi<'static>,
156 enabled_irqs: InterruptControl,
157) {
158 let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
159 let read_len = read_slice.len();
160 let write_slice = unsafe { context.tx_slice.get().unwrap() };
161 let write_len = write_slice.len();
162 let transfer_len = core::cmp::max(read_len, write_len);
163
164 while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
166 spi.write_data(Data::new_with_raw_value(
167 write_slice.get(context.tx_progress).copied().unwrap_or(0) as u32,
168 ));
169 context.tx_progress += 1;
171 }
172
173 while spi.read_status().rx_not_empty() {
175 let data = spi.read_data();
176 if context.rx_progress < read_len {
177 read_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
178 }
179 context.rx_progress += 1;
181 }
182
183 isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
184}
185
186fn on_interrupt_transfer_in_place(
187 idx: usize,
188 context: &mut TransferContext,
189 spi: &mut super::regs::MmioSpi<'static>,
190 enabled_irqs: InterruptControl,
191) {
192 let transfer_slice = unsafe { context.rx_slice.get_mut().unwrap() };
193 let transfer_len = transfer_slice.len();
194 while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
196 spi.write_data(Data::new_with_raw_value(
197 transfer_slice[context.tx_progress] as u32,
198 ));
199 context.tx_progress += 1;
200 }
201 while spi.read_status().rx_not_empty() {
203 let data = spi.read_data();
204 if context.rx_progress < transfer_len {
205 transfer_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
206 context.rx_progress += 1;
207 }
208 }
209
210 isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
211}
212
213fn isr_finish_handler(
216 idx: usize,
217 spi: &mut super::regs::MmioSpi<'static>,
218 context: &mut TransferContext,
219 transfer_len: usize,
220 enabled: InterruptControl,
221) {
222 if context.rx_progress == context.tx_progress && context.rx_progress == transfer_len {
224 finish_transfer(spi, idx, context);
225 return;
226 }
227 critical_section::with(|cs| {
230 let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
231 *context_ref.borrow_mut() = *context;
232 });
233 unfinished_transfer(spi, transfer_len, context, enabled);
234}
235
236fn finish_transfer(
237 spi: &mut super::regs::MmioSpi<'static>,
238 idx: usize,
239 context: &mut TransferContext,
240) {
241 critical_section::with(|cs| {
243 let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
244 *context_ref.borrow_mut() = *context;
245 });
246 reset_trigger_levels(spi);
248 spi.write_fifo_clear(FifoClear::ALL);
249 DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
251 WAKERS[idx].wake();
252}
253
254#[inline]
255fn unfinished_transfer(
256 spi: &mut super::regs::MmioSpi<'static>,
257 transfer_len: usize,
258 context: &TransferContext,
259 enabled_irqs: InterruptControl,
260) {
261 let new_trig_level = core::cmp::min(8, transfer_len - context.rx_progress);
264 spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(new_trig_level as u8)));
265
266 let enable_tx = !(enabled_irqs.tx() && context.tx_progress == transfer_len);
271
272 spi.write_interrupt_control(
274 InterruptControl::builder()
275 .with_tx(enable_tx)
276 .with_rx(true)
277 .with_rx_timeout(true)
278 .with_rx_overrun(true)
279 .build(),
280 );
281}
282
283#[inline]
284fn reset_trigger_levels(spi: &mut super::regs::MmioSpi<'static>) {
285 spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(0x08)));
286 spi.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0x00)));
287}
288
289#[derive(Debug, Clone, Copy)]
290#[cfg_attr(feature = "defmt", derive(defmt::Format))]
291pub enum TransferType {
292 Read,
293 Write,
294 Transfer,
295 TransferInPlace,
296}
297
298#[derive(Default, Debug, Copy, Clone)]
299pub struct TransferContext {
300 transfer_type: Option<TransferType>,
301 tx_progress: usize,
302 rx_progress: usize,
303 tx_slice: RawBufSlice,
304 rx_slice: RawBufSliceMut,
305 rx_overrun: bool,
306}
307
308#[allow(clippy::new_without_default)]
309impl TransferContext {
310 pub const fn new() -> Self {
311 Self {
312 transfer_type: None,
313 tx_progress: 0,
314 rx_progress: 0,
315 tx_slice: RawBufSlice::new_nulled(),
316 rx_slice: RawBufSliceMut::new_nulled(),
317 rx_overrun: false,
318 }
319 }
320}
321
322pub struct SpiFuture<'spi> {
323 bank: super::Bank,
324 spi: &'spi mut super::Spi<u8>,
325 finished_regularly: core::cell::Cell<bool>,
326}
327
328impl<'spi> SpiFuture<'spi> {
329 fn new_for_read(spi: &'spi mut super::Spi<u8>, bank: super::Bank, words: &mut [u8]) -> Self {
330 if words.is_empty() {
331 panic!("words length unexpectedly 0");
332 }
333 Self::generic_init_transfer(spi, bank);
334
335 let write_index = core::cmp::min(super::FIFO_DEPTH, words.len());
336 (0..write_index).for_each(|_| {
338 spi.regs.write_data(Data::new_with_raw_value(0));
339 });
340
341 Self::set_triggers(spi, write_index, words.len());
342
343 critical_section::with(|cs| {
344 let context_ref = TRANSFER_CONTEXTS[bank as usize].borrow(cs);
345 let mut context = context_ref.borrow_mut();
346 context.transfer_type = Some(TransferType::Read);
347 unsafe {
348 context.rx_slice.set(words);
349 }
350 context.tx_slice.set_null();
351 context.tx_progress = write_index;
352 context.rx_progress = 0;
353 spi.regs.write_interrupt_clear(InterruptClear::ALL);
354 spi.regs.write_interrupt_control(
355 InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH),
356 );
357 spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
358 });
359 Self {
360 bank,
361 spi,
362 finished_regularly: core::cell::Cell::new(false),
363 }
364 }
365
366 fn new_for_write(spi: &'spi mut super::Spi<u8>, bank: super::Bank, words: &[u8]) -> Self {
367 if words.is_empty() {
368 panic!("words length unexpectedly 0");
369 }
370 let index = bank as usize;
371 let write_index = Self::generic_init_transfer_write_transfer_in_place(spi, bank, words);
372 critical_section::with(|cs| {
373 let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
374 let mut context = context_ref.borrow_mut();
375 context.transfer_type = Some(TransferType::Write);
376 unsafe {
377 context.tx_slice.set(words);
378 }
379 context.rx_slice.set_null();
380 context.tx_progress = write_index;
381 context.rx_progress = 0;
382 spi.regs.write_interrupt_clear(InterruptClear::ALL);
383 spi.regs.write_interrupt_control(
384 InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH),
385 );
386 spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
387 });
388 Self {
389 bank,
390 spi,
391 finished_regularly: core::cell::Cell::new(false),
392 }
393 }
394
395 fn new_for_transfer(
396 spi: &'spi mut super::Spi<u8>,
397 bank: super::Bank,
398 read: &mut [u8],
399 write: &[u8],
400 ) -> Self {
401 if read.is_empty() || write.is_empty() {
402 panic!("read or write buffer unexpectedly empty");
403 }
404 let index = bank as usize;
405 let full_write_len = core::cmp::max(read.len(), write.len());
406 let fifo_prefill = core::cmp::min(super::FIFO_DEPTH, full_write_len);
407
408 Self::generic_init_transfer(spi, bank);
409
410 for write_index in 0..fifo_prefill {
411 let value = write.get(write_index).copied().unwrap_or(0);
412 spi.regs.write_data(Data::new_with_raw_value(value as u32));
413 }
414
415 Self::set_triggers(spi, fifo_prefill, full_write_len);
416
417 critical_section::with(|cs| {
418 let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
419 let mut context = context_ref.borrow_mut();
420 context.transfer_type = Some(TransferType::Transfer);
421 unsafe {
422 context.tx_slice.set(write);
423 context.rx_slice.set(read);
424 }
425 context.tx_progress = fifo_prefill;
426 context.rx_progress = 0;
427 spi.regs.write_interrupt_clear(InterruptClear::ALL);
428 spi.regs.write_interrupt_control(
429 InterruptControl::ENABLE_ALL.with_tx(fifo_prefill > FIFO_DEPTH),
430 );
431 spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
432 });
433 Self {
434 bank,
435 spi,
436 finished_regularly: core::cell::Cell::new(false),
437 }
438 }
439
440 fn new_for_transfer_in_place(
441 spi: &'spi mut super::Spi<u8>,
442 bank: super::Bank,
443 words: &mut [u8],
444 ) -> Self {
445 if words.is_empty() {
446 panic!("read and write buffer unexpectedly empty");
447 }
448 let write_idx = Self::generic_init_transfer_write_transfer_in_place(spi, bank, words);
449 critical_section::with(|cs| {
450 let context_ref = TRANSFER_CONTEXTS[bank as usize].borrow(cs);
451 let mut context = context_ref.borrow_mut();
452 context.transfer_type = Some(TransferType::TransferInPlace);
453 unsafe {
454 context.rx_slice.set(words);
455 }
456 context.tx_slice.set_null();
457 context.tx_progress = write_idx;
458 context.rx_progress = 0;
459 spi.regs.write_interrupt_clear(InterruptClear::ALL);
460 spi.regs.write_interrupt_control(
461 InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH),
462 );
463 spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
464 });
465 Self {
466 bank,
467 spi,
468 finished_regularly: core::cell::Cell::new(false),
469 }
470 }
471
472 fn generic_init_transfer(spi: &mut super::Spi<u8>, bank: super::Bank) {
473 let idx = bank as usize;
474 DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
475 spi.regs
476 .write_interrupt_control(InterruptControl::DISABLE_ALL);
477 spi.regs.write_fifo_clear(FifoClear::ALL);
478 spi.regs.modify_ctrl1(|v| v.with_mtxpause(true));
479 }
480
481 fn generic_init_transfer_write_transfer_in_place(
483 spi: &mut super::Spi<u8>,
484 bank: super::Bank,
485 write: &[u8],
486 ) -> usize {
487 Self::generic_init_transfer(spi, bank);
488
489 let write_idx = core::cmp::min(super::FIFO_DEPTH, write.len());
490 (0..write_idx).for_each(|idx| {
491 spi.regs
492 .write_data(Data::new_with_raw_value(write[idx] as u32));
493 });
494
495 Self::set_triggers(spi, write_idx, write.len());
496 write_idx
497 }
498
499 fn set_triggers(spi: &mut super::Spi<u8>, fifo_prefill: usize, write_len: usize) {
500 spi.regs
501 .write_rx_fifo_trigger(TriggerLevel::new(u5::new(core::cmp::min(
502 fifo_prefill,
503 FIFO_DEPTH / 2,
504 ) as u8)));
505 if write_len > super::FIFO_DEPTH {
509 spi.regs
510 .write_tx_fifo_trigger(TriggerLevel::new(u5::new(8)));
511 } else {
512 spi.regs
513 .write_tx_fifo_trigger(TriggerLevel::new(u5::new(0)));
514 }
515 }
516}
517
518impl<'spi> Future for SpiFuture<'spi> {
519 type Output = Result<(), RxOverrunError>;
520
521 fn poll(
522 self: core::pin::Pin<&mut Self>,
523 cx: &mut core::task::Context<'_>,
524 ) -> core::task::Poll<Self::Output> {
525 WAKERS[self.bank as usize].register(cx.waker());
526 if DONE[self.bank as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
527 let rx_overrun = critical_section::with(|cs| {
528 let mut ctx = TRANSFER_CONTEXTS[self.bank as usize]
529 .borrow(cs)
530 .borrow_mut();
531 let overrun = ctx.rx_overrun;
532 *ctx = TransferContext::default();
533 overrun
534 });
535 self.finished_regularly.set(true);
536 if rx_overrun {
537 return core::task::Poll::Ready(Err(RxOverrunError));
538 }
539 return core::task::Poll::Ready(Ok(()));
540 }
541 core::task::Poll::Pending
542 }
543}
544
545impl<'spi> Drop for SpiFuture<'spi> {
546 fn drop(&mut self) {
547 if !self.finished_regularly.get() {
548 self.spi.regs.write_interrupt_clear(InterruptClear::ALL);
551 self.spi
552 .regs
553 .write_interrupt_control(InterruptControl::DISABLE_ALL);
554 self.spi.regs.write_fifo_clear(FifoClear::ALL);
555 }
556 }
557}
558
559pub struct SpiAsync(pub super::Spi<u8>);
564
565impl SpiAsync {
566 pub fn new(
567 mut spi: super::Spi<u8>,
568 #[cfg(feature = "vor1x")] opt_irq_cfg: Option<crate::InterruptConfig>,
569 ) -> Self {
570 #[cfg(feature = "vor1x")]
571 if let Some(irq_cfg) = opt_irq_cfg {
572 spi.regs
573 .write_interrupt_control(InterruptControl::DISABLE_ALL);
574 spi.regs.write_interrupt_clear(InterruptClear::ALL);
575 if irq_cfg.route {
576 crate::enable_peripheral_clock(crate::PeripheralSelect::Irqsel);
577 unsafe { va108xx::Irqsel::steal() }
578 .spi(spi.id as usize)
579 .write(|w| unsafe { w.bits(irq_cfg.id as u32) });
580 }
581 if irq_cfg.enable_in_nvic {
582 unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
584 }
585 }
586 spi.regs
588 .modify_ctrl1(|v| v.with_bm_stall(false).with_blockmode(false));
589 Self(spi)
590 }
591
592 fn read(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
593 if words.is_empty() {
594 return None;
595 }
596 let id = self.0.id;
597 Some(SpiFuture::new_for_read(&mut self.0, id, words))
598 }
599
600 fn write(&mut self, words: &[u8]) -> Option<SpiFuture<'_>> {
601 if words.is_empty() {
602 return None;
603 }
604 let id = self.0.id;
605 Some(SpiFuture::new_for_write(&mut self.0, id, words))
606 }
607
608 fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Option<SpiFuture<'_>> {
609 if read.is_empty() || write.is_empty() {
610 return None;
611 }
612 let id = self.0.id;
613 Some(SpiFuture::new_for_transfer(&mut self.0, id, read, write))
614 }
615
616 fn transfer_in_place(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
617 if words.is_empty() {
618 return None;
619 }
620 let id = self.0.id;
621 Some(SpiFuture::new_for_transfer_in_place(&mut self.0, id, words))
622 }
623}
624
625impl embedded_hal_async::spi::ErrorType for SpiAsync {
626 type Error = RxOverrunError;
627}
628
629impl embedded_hal_async::spi::SpiBus for SpiAsync {
630 async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
631 if words.is_empty() {
632 return Ok(());
633 }
634 self.read(words).unwrap().await
635 }
636
637 async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
638 if words.is_empty() {
639 return Ok(());
640 }
641 self.write(words).unwrap().await
642 }
643
644 async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
645 if read.is_empty() && write.is_empty() {
646 return Ok(());
647 }
648 self.transfer(read, write).unwrap().await
649 }
650
651 async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
652 if words.is_empty() {
653 return Ok(());
654 }
655 self.transfer_in_place(words).unwrap().await
656 }
657
658 async fn flush(&mut self) -> Result<(), Self::Error> {
659 Ok(())
660 }
661}