vorago_shared_periphs/spi/
mod.rs1use crate::FunSel;
2use crate::gpio::{IoPeriphPin, PinId};
3use crate::{
4 PeripheralSelect, enable_peripheral_clock, pins::PinMarker, sealed::Sealed, time::Hertz,
5};
6use core::{convert::Infallible, fmt::Debug, marker::PhantomData};
7use embedded_hal::spi::{MODE_0, Mode};
8
9use regs::{ClkPrescaler, Data, FifoClear, WordSize};
10#[cfg(feature = "vor1x")]
11use va108xx as pac;
12#[cfg(feature = "vor4x")]
13use va416xx as pac;
14
15pub use regs::{Bank, HwChipSelectId};
16
17pub mod regs;
18
19pub fn configure_pin_as_hw_cs_pin<P: PinMarker + HwCsProvider>(_pin: P) -> HwChipSelectId {
20 IoPeriphPin::new(P::ID, P::FUN_SEL, None);
21 P::CS_ID
22}
23
24pub trait PinSck: PinMarker {
29 const SPI_ID: Bank;
30 const FUN_SEL: FunSel;
31}
32
33pub trait PinMosi: PinMarker {
34 const SPI_ID: Bank;
35 const FUN_SEL: FunSel;
36}
37
38pub trait PinMiso: PinMarker {
39 const SPI_ID: Bank;
40 const FUN_SEL: FunSel;
41}
42
43pub trait HwCsProvider {
44 const PIN_ID: PinId;
45 const SPI_ID: Bank;
46 const FUN_SEL: FunSel;
47 const CS_ID: HwChipSelectId;
48}
49
50#[macro_use]
51mod macros {
52 #[cfg(not(feature = "va41628"))]
53 macro_rules! hw_cs_multi_pin {
54 (
55 $name:ident,
57 $pin_id:ident,
59 $spi_id:path,
61 $fun_sel:path,
63 $cs_id:path
65 ) => {
66 #[doc = concat!(
67 "Newtype wrapper to use [Pin] [`", stringify!($pin_id), "`] as a HW CS pin for [`", stringify!($spi_id), "`] with [`", stringify!($cs_id), "`]."
68 )]
69 pub struct $name(Pin<$pin_id>);
70
71 impl $name {
72 pub fn new(pin: Pin<$pin_id>) -> Self {
73 Self(pin)
74 }
75 }
76
77 impl crate::sealed::Sealed for $name {}
78
79 impl HwCsProvider for $name {
80 const PIN_ID: PinId = <$pin_id as PinIdProvider>::ID;
81 const SPI_ID: Bank = $spi_id;
82 const FUN_SEL: FunSel = $fun_sel;
83 const CS_ID: HwChipSelectId = $cs_id;
84 }
85 };
86 }
87
88 #[macro_export]
89 macro_rules! hw_cs_pins {
90 ($SpiId:path, $(($Px:ident, $FunSel:path, $HwCsIdent:path)$(,)?)+) => {
91 $(
92 impl HwCsProvider for Pin<$Px> {
93 const PIN_ID: PinId = $Px::ID;
94 const SPI_ID: Bank = $SpiId;
95 const FUN_SEL: FunSel = $FunSel;
96 const CS_ID: HwChipSelectId = $HwCsIdent;
97 }
98 )+
99 };
100 }
101}
102
103#[cfg(feature = "vor1x")]
104pub mod pins_vor1x;
105#[cfg(feature = "vor4x")]
106pub mod pins_vor4x;
107
108const FILL_DEPTH: usize = 12;
114
115pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
116pub const BMSKIPDATA_MASK: u32 = 1 << 30;
117
118pub const DEFAULT_CLK_DIV: u16 = 2;
119
120pub trait SpiMarker: Sealed {
123 const ID: Bank;
124 const PERIPH_SEL: PeripheralSelect;
125}
126
127#[cfg(feature = "vor1x")]
128pub type Spi0 = pac::Spia;
129#[cfg(feature = "vor4x")]
130pub type Spi0 = pac::Spi0;
131
132impl SpiMarker for Spi0 {
133 const ID: Bank = Bank::Spi0;
134 const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0;
135}
136impl Sealed for Spi0 {}
137
138#[cfg(feature = "vor1x")]
139pub type Spi1 = pac::Spib;
140#[cfg(feature = "vor4x")]
141pub type Spi1 = pac::Spi1;
142
143impl SpiMarker for Spi1 {
144 const ID: Bank = Bank::Spi1;
145 const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1;
146}
147impl Sealed for Spi1 {}
148
149#[cfg(feature = "vor1x")]
150pub type Spi2 = pac::Spic;
151#[cfg(feature = "vor4x")]
152pub type Spi2 = pac::Spi2;
153
154impl SpiMarker for Spi2 {
155 const ID: Bank = Bank::Spi2;
156 const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2;
157}
158impl Sealed for Spi2 {}
159
160#[cfg(feature = "vor4x")]
161impl SpiMarker for pac::Spi3 {
162 const ID: Bank = Bank::Spi3;
163 const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi3;
164}
165#[cfg(feature = "vor4x")]
166impl Sealed for pac::Spi3 {}
167
168pub trait TransferConfigProvider {
173 fn sod(&mut self, sod: bool);
174 fn blockmode(&mut self, blockmode: bool);
175 fn mode(&mut self, mode: Mode);
176 fn clk_cfg(&mut self, clk_cfg: SpiClkConfig);
177 fn hw_cs_id(&self) -> u8;
178}
179
180#[derive(Copy, Clone, Debug)]
183#[cfg_attr(feature = "defmt", derive(defmt::Format))]
184pub struct TransferConfig {
185 pub clk_cfg: Option<SpiClkConfig>,
186 pub mode: Option<Mode>,
187 pub sod: bool,
188 pub blockmode: bool,
192 pub bmstall: bool,
195 pub hw_cs: Option<HwChipSelectId>,
196}
197
198impl TransferConfig {
199 pub fn new_with_hw_cs(
200 clk_cfg: Option<SpiClkConfig>,
201 mode: Option<Mode>,
202 blockmode: bool,
203 bmstall: bool,
204 sod: bool,
205 hw_cs_id: HwChipSelectId,
206 ) -> Self {
207 TransferConfig {
208 clk_cfg,
209 mode,
210 sod,
211 blockmode,
212 bmstall,
213 hw_cs: Some(hw_cs_id),
214 }
215 }
216}
217
218#[derive(Debug, Copy, Clone)]
220#[cfg_attr(feature = "defmt", derive(defmt::Format))]
221pub struct SpiConfig {
222 clk: SpiClkConfig,
223 pub init_mode: Mode,
225 pub blockmode: bool,
229 pub bmstall: bool,
232 pub slave_output_disable: bool,
234 pub loopback_mode: bool,
236 pub master_delayer_capture: bool,
238}
239
240impl Default for SpiConfig {
241 fn default() -> Self {
242 Self {
243 init_mode: MODE_0,
244 blockmode: true,
245 bmstall: true,
246 clk: SpiClkConfig::from_div(DEFAULT_CLK_DIV).unwrap(),
248 slave_output_disable: Default::default(),
249 loopback_mode: Default::default(),
250 master_delayer_capture: Default::default(),
251 }
252 }
253}
254
255impl SpiConfig {
256 pub fn loopback(mut self, enable: bool) -> Self {
257 self.loopback_mode = enable;
258 self
259 }
260
261 pub fn blockmode(mut self, enable: bool) -> Self {
262 self.blockmode = enable;
263 self
264 }
265
266 pub fn bmstall(mut self, enable: bool) -> Self {
267 self.bmstall = enable;
268 self
269 }
270
271 pub fn mode(mut self, mode: Mode) -> Self {
272 self.init_mode = mode;
273 self
274 }
275
276 pub fn clk_cfg(mut self, clk_cfg: SpiClkConfig) -> Self {
277 self.clk = clk_cfg;
278 self
279 }
280
281 pub fn slave_output_disable(mut self, sod: bool) -> Self {
282 self.slave_output_disable = sod;
283 self
284 }
285}
286
287pub trait WordProvider: Copy + Default + Into<u32> + TryFrom<u32> + 'static {
294 const MASK: u32;
295 const WORD_SIZE: regs::WordSize;
296 fn word_reg() -> u8;
297}
298
299impl WordProvider for u8 {
300 const MASK: u32 = 0xff;
301 const WORD_SIZE: regs::WordSize = regs::WordSize::EightBits;
302 fn word_reg() -> u8 {
303 0x07
304 }
305}
306
307impl WordProvider for u16 {
308 const MASK: u32 = 0xffff;
309 const WORD_SIZE: regs::WordSize = regs::WordSize::SixteenBits;
310 fn word_reg() -> u8 {
311 0x0f
312 }
313}
314
315pub trait SpiLowLevel {
321 fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible>;
326
327 fn write_fifo_unchecked(&mut self, data: u32);
333
334 fn read_fifo(&mut self) -> nb::Result<u32, Infallible>;
339
340 fn read_fifo_unchecked(&mut self) -> u32;
347}
348
349#[inline(always)]
350pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
351 match mode {
352 embedded_hal::spi::MODE_0 => (false, false),
353 embedded_hal::spi::MODE_1 => (false, true),
354 embedded_hal::spi::MODE_2 => (true, false),
355 embedded_hal::spi::MODE_3 => (true, true),
356 }
357}
358
359#[derive(Debug, Copy, Clone, PartialEq, Eq)]
360#[cfg_attr(feature = "defmt", derive(defmt::Format))]
361pub struct SpiClkConfig {
362 prescale_val: u8,
363 scrdv: u8,
364}
365
366impl SpiClkConfig {
367 pub fn prescale_val(&self) -> u8 {
368 self.prescale_val
369 }
370 pub fn scrdv(&self) -> u8 {
371 self.scrdv
372 }
373}
374
375impl SpiClkConfig {
376 pub fn new(prescale_val: u8, scrdv: u8) -> Self {
377 Self {
378 prescale_val,
379 scrdv,
380 }
381 }
382
383 pub fn from_div(div: u16) -> Result<Self, SpiClkConfigError> {
384 spi_clk_config_from_div(div)
385 }
386
387 #[cfg(feature = "vor1x")]
388 pub fn from_clk(sys_clk: Hertz, spi_clk: Hertz) -> Option<Self> {
389 clk_div_for_target_clock(sys_clk, spi_clk).map(|div| spi_clk_config_from_div(div).unwrap())
390 }
391
392 #[cfg(feature = "vor4x")]
393 pub fn from_clks(clks: &crate::clock::Clocks, spi_clk: Hertz) -> Option<Self> {
394 Self::from_apb1_clk(clks.apb1(), spi_clk)
395 }
396
397 #[cfg(feature = "vor4x")]
398 pub fn from_apb1_clk(apb1_clk: Hertz, spi_clk: Hertz) -> Option<Self> {
399 clk_div_for_target_clock(apb1_clk, spi_clk).map(|div| spi_clk_config_from_div(div).unwrap())
400 }
401}
402
403#[derive(Debug, thiserror::Error)]
404#[cfg_attr(feature = "defmt", derive(defmt::Format))]
405pub enum SpiClkConfigError {
406 #[error("division by zero")]
407 DivIsZero,
408 #[error("divide value is not even")]
409 DivideValueNotEven,
410 #[error("scrdv value is too large")]
411 ScrdvValueTooLarge,
412}
413
414#[inline]
415pub fn spi_clk_config_from_div(mut div: u16) -> Result<SpiClkConfig, SpiClkConfigError> {
416 if div == 0 {
417 return Err(SpiClkConfigError::DivIsZero);
418 }
419 if div % 2 != 0 {
420 return Err(SpiClkConfigError::DivideValueNotEven);
421 }
422 let mut prescale_val = 0;
423
424 for i in (2..=0xfe).rev().step_by(2) {
426 if div % i == 0 {
427 prescale_val = i;
428 break;
429 }
430 }
431
432 if prescale_val == 0 {
433 return Err(SpiClkConfigError::DivideValueNotEven);
434 }
435
436 div /= prescale_val;
437 if div > u8::MAX as u16 + 1 {
438 return Err(SpiClkConfigError::ScrdvValueTooLarge);
439 }
440 Ok(SpiClkConfig {
441 prescale_val: prescale_val as u8,
442 scrdv: (div - 1) as u8,
443 })
444}
445
446#[inline]
447pub fn clk_div_for_target_clock(sys_clk: Hertz, spi_clk: Hertz) -> Option<u16> {
448 if spi_clk > sys_clk {
449 return None;
450 }
451
452 let raw_div = sys_clk.raw() / spi_clk.raw();
454 let remainder = sys_clk.raw() % spi_clk.raw();
455
456 let mut rounded_div = if remainder * 2 >= spi_clk.raw() {
458 raw_div + 1
459 } else {
460 raw_div
461 };
462
463 if rounded_div % 2 != 0 {
464 rounded_div += 1;
466 }
467 if rounded_div > u16::MAX as u32 {
468 return None;
469 }
470 Some(rounded_div as u16)
471}
472
473#[derive(Debug, thiserror::Error)]
474#[error("peripheral or peripheral pin ID is not consistent")]
475pub struct SpiIdMissmatchError;
476
477pub struct Spi<Word = u8> {
479 id: Bank,
480 regs: regs::MmioSpi<'static>,
481 cfg: SpiConfig,
482 fill_word: Word,
484 blockmode: bool,
485 bmstall: bool,
486 word: PhantomData<Word>,
487}
488
489impl<Word: WordProvider> Spi<Word>
490where
491 <Word as TryFrom<u32>>::Error: core::fmt::Debug,
492{
493 pub fn new_for_rom<SpiI: SpiMarker>(
500 spi: SpiI,
501 spi_cfg: SpiConfig,
502 ) -> Result<Self, SpiIdMissmatchError> {
503 #[cfg(feature = "vor1x")]
504 if SpiI::ID != Bank::Spi2 {
505 return Err(SpiIdMissmatchError);
506 }
507 #[cfg(feature = "vor4x")]
508 if SpiI::ID != Bank::Spi3 {
509 return Err(SpiIdMissmatchError);
510 }
511 Ok(Self::new_generic(spi, spi_cfg))
512 }
513
514 pub fn new<SpiI: SpiMarker, Sck: PinSck, Miso: PinMiso, Mosi: PinMosi>(
523 spi: SpiI,
524 _pins: (Sck, Miso, Mosi),
525 spi_cfg: SpiConfig,
526 ) -> Result<Self, SpiIdMissmatchError> {
527 if SpiI::ID != Sck::SPI_ID || SpiI::ID != Miso::SPI_ID || SpiI::ID != Mosi::SPI_ID {
528 return Err(SpiIdMissmatchError);
529 }
530 IoPeriphPin::new(Sck::ID, Sck::FUN_SEL, None);
531 IoPeriphPin::new(Miso::ID, Miso::FUN_SEL, None);
532 IoPeriphPin::new(Mosi::ID, Mosi::FUN_SEL, None);
533 Ok(Self::new_generic(spi, spi_cfg))
534 }
535
536 pub fn new_generic<SpiI: SpiMarker>(_spi: SpiI, spi_cfg: SpiConfig) -> Self {
537 enable_peripheral_clock(SpiI::PERIPH_SEL);
538 let mut regs = regs::Spi::new_mmio(SpiI::ID);
539 let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(spi_cfg.init_mode);
540 regs.write_ctrl0(
541 regs::Control0::builder()
542 .with_scrdv(spi_cfg.clk.scrdv)
543 .with_sph(cph_bit)
544 .with_spo(cpo_bit)
545 .with_word_size(Word::WORD_SIZE)
546 .build(),
547 );
548 regs.write_ctrl1(
549 regs::Control1::builder()
550 .with_mtxpause(false)
551 .with_mdlycap(spi_cfg.master_delayer_capture)
552 .with_bm_stall(spi_cfg.bmstall)
553 .with_bm_start(false)
554 .with_blockmode(spi_cfg.blockmode)
555 .with_ss(HwChipSelectId::Id0)
556 .with_sod(spi_cfg.slave_output_disable)
557 .with_slave_mode(false)
558 .with_enable(false)
559 .with_lbm(spi_cfg.loopback_mode)
560 .build(),
561 );
562 regs.write_clkprescale(ClkPrescaler::new(spi_cfg.clk.prescale_val));
563 regs.write_fifo_clear(
564 FifoClear::builder()
565 .with_tx_fifo(true)
566 .with_rx_fifo(true)
567 .build(),
568 );
569 regs.modify_ctrl1(|mut value| {
572 value.set_enable(true);
573 value
574 });
575 Spi {
576 id: SpiI::ID,
577 regs: regs::Spi::new_mmio(SpiI::ID),
578 cfg: spi_cfg,
579 fill_word: Default::default(),
580 bmstall: spi_cfg.bmstall,
581 blockmode: spi_cfg.blockmode,
582 word: PhantomData,
583 }
584 }
585
586 #[inline]
587 pub fn cfg_clock(&mut self, cfg: SpiClkConfig) {
588 self.regs.modify_ctrl0(|mut value| {
589 value.set_scrdv(cfg.scrdv);
590 value
591 });
592 self.regs
593 .write_clkprescale(regs::ClkPrescaler::new(cfg.prescale_val));
594 }
595
596 pub fn set_fill_word(&mut self, fill_word: Word) {
597 self.fill_word = fill_word;
598 }
599
600 #[inline]
601 pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClkConfigError> {
602 let val = spi_clk_config_from_div(div)?;
603 self.cfg_clock(val);
604 Ok(())
605 }
606
607 #[inline]
608 pub fn cfg_mode(&mut self, mode: Mode) {
609 let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode);
610 self.regs.modify_ctrl0(|mut value| {
611 value.set_spo(cpo_bit);
612 value.set_sph(cph_bit);
613 value
614 });
615 }
616
617 #[inline]
618 pub fn fill_word(&self) -> Word {
619 self.fill_word
620 }
621
622 #[inline]
623 pub fn clear_tx_fifo(&mut self) {
624 self.regs.write_fifo_clear(
625 regs::FifoClear::builder()
626 .with_tx_fifo(true)
627 .with_rx_fifo(false)
628 .build(),
629 );
630 }
631
632 #[inline]
633 pub fn clear_rx_fifo(&mut self) {
634 self.regs.write_fifo_clear(
635 regs::FifoClear::builder()
636 .with_tx_fifo(false)
637 .with_rx_fifo(true)
638 .build(),
639 );
640 }
641
642 #[inline]
643 pub fn perid(&self) -> u32 {
644 self.regs.read_perid()
645 }
646
647 #[inline]
653 pub fn cfg_hw_cs(&mut self, hw_cs: HwChipSelectId) {
654 self.regs.modify_ctrl1(|mut value| {
655 value.set_sod(false);
656 value.set_ss(hw_cs);
657 value
658 });
659 }
660
661 #[inline]
664 pub fn cfg_hw_cs_disable(&mut self) {
665 self.regs.modify_ctrl1(|mut value| {
666 value.set_sod(true);
667 value
668 });
669 }
670
671 pub fn cfg_transfer(&mut self, transfer_cfg: &TransferConfig) {
675 if let Some(trans_clk_div) = transfer_cfg.clk_cfg {
676 self.cfg_clock(trans_clk_div);
677 }
678 if let Some(mode) = transfer_cfg.mode {
679 self.cfg_mode(mode);
680 }
681 self.blockmode = transfer_cfg.blockmode;
682 self.regs.modify_ctrl1(|mut value| {
683 if transfer_cfg.sod {
684 value.set_sod(transfer_cfg.sod);
685 } else {
686 value.set_sod(false);
687 if let Some(hw_cs) = transfer_cfg.hw_cs {
688 value.set_ss(hw_cs);
689 }
690 }
691 value.set_blockmode(transfer_cfg.blockmode);
692 value.set_bm_stall(transfer_cfg.bmstall);
693 value
694 });
695 }
696
697 fn flush_internal(&mut self) {
698 let mut status_reg = self.regs.read_status();
699 while !status_reg.tx_empty() || status_reg.rx_not_empty() || status_reg.busy() {
700 if status_reg.rx_not_empty() {
701 self.read_fifo_unchecked();
702 }
703 status_reg = self.regs.read_status();
704 }
705 }
706
707 fn transfer_preparation(&mut self, words: &[Word]) -> Result<(), Infallible> {
708 if words.is_empty() {
709 return Ok(());
710 }
711 self.flush_internal();
712 Ok(())
713 }
714
715 fn initial_send_fifo_pumping_with_words(&mut self, words: &[Word]) -> usize {
718 if self.blockmode {
720 self.regs.modify_ctrl1(|mut value| {
721 value.set_mtxpause(true);
722 value
723 });
724 }
725 let mut current_write_idx = 0;
727 let smaller_idx = core::cmp::min(FILL_DEPTH, words.len());
728 for _ in 0..smaller_idx {
729 if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall {
730 self.write_fifo_unchecked(words[current_write_idx].into() | BMSTART_BMSTOP_MASK);
731 } else {
732 self.write_fifo_unchecked(words[current_write_idx].into());
733 }
734 current_write_idx += 1;
735 }
736 if self.blockmode {
737 self.regs.modify_ctrl1(|mut value| {
738 value.set_mtxpause(false);
739 value
740 });
741 }
742 current_write_idx
743 }
744
745 fn initial_send_fifo_pumping_with_fill_words(&mut self, send_len: usize) -> usize {
748 if self.blockmode {
749 self.regs.modify_ctrl1(|mut value| {
750 value.set_mtxpause(true);
751 value
752 });
753 }
754 let mut current_write_idx = 0;
756 let smaller_idx = core::cmp::min(FILL_DEPTH, send_len);
757 for _ in 0..smaller_idx {
758 if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall {
759 self.write_fifo_unchecked(self.fill_word.into() | BMSTART_BMSTOP_MASK);
760 } else {
761 self.write_fifo_unchecked(self.fill_word.into());
762 }
763 current_write_idx += 1;
764 }
765 if self.blockmode {
766 self.regs.modify_ctrl1(|mut value| {
767 value.set_mtxpause(false);
768 value
769 });
770 }
771 current_write_idx
772 }
773}
774
775impl<Word: WordProvider> SpiLowLevel for Spi<Word>
776where
777 <Word as TryFrom<u32>>::Error: core::fmt::Debug,
778{
779 #[inline(always)]
780 fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible> {
781 if !self.regs.read_status().tx_not_full() {
782 return Err(nb::Error::WouldBlock);
783 }
784 self.write_fifo_unchecked(data);
785 Ok(())
786 }
787
788 #[inline(always)]
789 fn write_fifo_unchecked(&mut self, data: u32) {
790 self.regs.write_data(Data::new_with_raw_value(data));
791 }
792
793 #[inline(always)]
794 fn read_fifo(&mut self) -> nb::Result<u32, Infallible> {
795 if !self.regs.read_status().rx_not_empty() {
796 return Err(nb::Error::WouldBlock);
797 }
798 Ok(self.read_fifo_unchecked())
799 }
800
801 #[inline(always)]
802 fn read_fifo_unchecked(&mut self) -> u32 {
803 self.regs.read_data().raw_value()
804 }
805}
806
807impl<Word: WordProvider> embedded_hal::spi::ErrorType for Spi<Word> {
808 type Error = Infallible;
809}
810
811impl<Word: WordProvider> embedded_hal::spi::SpiBus<Word> for Spi<Word>
812where
813 <Word as TryFrom<u32>>::Error: core::fmt::Debug,
814{
815 fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
816 self.transfer_preparation(words)?;
817 let mut current_read_idx = 0;
818 let mut current_write_idx = self.initial_send_fifo_pumping_with_fill_words(words.len());
819 loop {
820 if current_read_idx < words.len() {
821 words[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK)
822 .try_into()
823 .unwrap();
824 current_read_idx += 1;
825 }
826 if current_write_idx < words.len() {
827 if current_write_idx == words.len() - 1 && self.bmstall {
828 nb::block!(self.write_fifo(self.fill_word.into() | BMSTART_BMSTOP_MASK))?;
829 } else {
830 nb::block!(self.write_fifo(self.fill_word.into()))?;
831 }
832 current_write_idx += 1;
833 }
834 if current_read_idx >= words.len() && current_write_idx >= words.len() {
835 break;
836 }
837 }
838 Ok(())
839 }
840
841 fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
842 self.transfer_preparation(words)?;
843 let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words);
844 while current_write_idx < words.len() {
845 if current_write_idx == words.len() - 1 && self.bmstall {
846 nb::block!(self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK))?;
847 } else {
848 nb::block!(self.write_fifo(words[current_write_idx].into()))?;
849 }
850 current_write_idx += 1;
851 if self.regs.read_status().rx_not_empty() {
853 self.clear_rx_fifo();
854 }
855 }
856 Ok(())
857 }
858
859 fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
860 self.transfer_preparation(write)?;
861 let mut current_read_idx = 0;
862 let mut current_write_idx = self.initial_send_fifo_pumping_with_words(write);
863 let max_idx = core::cmp::max(read.len(), write.len());
864 while current_read_idx < read.len() || current_write_idx < write.len() {
865 if current_write_idx < max_idx {
866 if current_write_idx == write.len() - 1 && self.bmstall {
867 nb::block!(
868 self.write_fifo(write[current_write_idx].into() | BMSTART_BMSTOP_MASK)
869 )?;
870 } else if current_write_idx < write.len() {
871 nb::block!(self.write_fifo(write[current_write_idx].into()))?;
872 } else {
873 nb::block!(self.write_fifo(0))?;
874 }
875 current_write_idx += 1;
876 }
877 if current_read_idx < max_idx {
878 if current_read_idx < read.len() {
879 read[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK)
880 .try_into()
881 .unwrap();
882 } else {
883 nb::block!(self.read_fifo()).unwrap();
884 }
885 current_read_idx += 1;
886 }
887 }
888
889 Ok(())
890 }
891
892 fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
893 self.transfer_preparation(words)?;
894 let mut current_read_idx = 0;
895 let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words);
896
897 while current_read_idx < words.len() || current_write_idx < words.len() {
898 if current_write_idx < words.len() {
899 if current_write_idx == words.len() - 1 && self.bmstall {
900 nb::block!(
901 self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK)
902 )?;
903 } else {
904 nb::block!(self.write_fifo(words[current_write_idx].into()))?;
905 }
906 current_write_idx += 1;
907 }
908 if current_read_idx < words.len() && current_read_idx < current_write_idx {
909 words[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK)
910 .try_into()
911 .unwrap();
912 current_read_idx += 1;
913 }
914 }
915 Ok(())
916 }
917
918 fn flush(&mut self) -> Result<(), Self::Error> {
919 self.flush_internal();
920 Ok(())
921 }
922}
923
924impl From<Spi<u8>> for Spi<u16> {
926 fn from(mut old_spi: Spi<u8>) -> Self {
927 old_spi.regs.modify_ctrl0(|mut value| {
928 value.set_word_size(WordSize::SixteenBits);
929 value
930 });
931 Spi {
932 id: old_spi.id,
933 regs: old_spi.regs,
934 cfg: old_spi.cfg,
935 blockmode: old_spi.blockmode,
936 fill_word: Default::default(),
937 bmstall: old_spi.bmstall,
938 word: PhantomData,
939 }
940 }
941}
942
943impl From<Spi<u16>> for Spi<u8> {
944 fn from(mut old_spi: Spi<u16>) -> Self {
945 old_spi.regs.modify_ctrl0(|mut value| {
946 value.set_word_size(WordSize::EightBits);
947 value
948 });
949 Spi {
950 id: old_spi.id,
951 regs: old_spi.regs,
952 cfg: old_spi.cfg,
953 blockmode: old_spi.blockmode,
954 fill_word: Default::default(),
955 bmstall: old_spi.bmstall,
956 word: PhantomData,
957 }
958 }
959}