Skip to main content

vorago_shared_periphs/spi/
mod.rs

1use 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
24//==================================================================================================
25// Pins and traits.
26//==================================================================================================
27
28pub 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 of the newtype wrapper struct
56        $name:ident,
57        // Pb0
58        $pin_id:ident,
59        // SpiId::B
60        $spi_id:path,
61        // FunSel::Sel1
62        $fun_sel:path,
63        // HwChipSelectId::Id2
64        $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
108//==================================================================================================
109// Defintions
110//==================================================================================================
111
112// FIFO has a depth of 16.
113const 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
120/// Common trait implemented by all PAC peripheral access structures. The register block
121/// format is the same for all SPI blocks.
122pub 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
168//==================================================================================================
169// Config
170//==================================================================================================
171
172pub 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/// Type erased variant of the transfer configuration. This is required to avoid generics in
181/// the SPI constructor.
182#[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    /// If this is enabled, all data in the FIFO is transmitted in a single frame unless
189    /// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the
190    /// duration of multiple data words
191    pub blockmode: bool,
192    /// Only used when blockmode is used. The SCK will be stalled until an explicit stop bit
193    /// is set on a written word.
194    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/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
219#[derive(Debug, Copy, Clone)]
220#[cfg_attr(feature = "defmt", derive(defmt::Format))]
221pub struct SpiConfig {
222    clk: SpiClkConfig,
223    // SPI mode configuration
224    pub init_mode: Mode,
225    /// If this is enabled, all data in the FIFO is transmitted in a single frame unless
226    /// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the
227    /// duration of multiple data words. Defaults to true.
228    pub blockmode: bool,
229    /// This enables the stalling of the SPI SCK if in blockmode and the FIFO is empty.
230    /// Currently enabled by default.
231    pub bmstall: bool,
232    /// Slave output disable. Useful if separate GPIO pins or decoders are used for CS control
233    pub slave_output_disable: bool,
234    /// Loopback mode. If you use this, don't connect MISO to MOSI, they will be tied internally
235    pub loopback_mode: bool,
236    /// Enable Master Delayer Capture Mode. See Programmers Guide p.92 for more details
237    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            // Default value is definitely valid.
247            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
287//==================================================================================================
288// Word Size
289//==================================================================================================
290
291/// Configuration trait for the Word Size
292/// used by the SPI peripheral
293pub 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
315//==================================================================================================
316// Spi
317//==================================================================================================
318
319/// Low level access trait for the SPI peripheral.
320pub trait SpiLowLevel {
321    /// Low level function to write a word to the SPI FIFO but also checks whether
322    /// there is actually data in the FIFO.
323    ///
324    /// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
325    fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible>;
326
327    /// Low level function to write a word to the SPI FIFO without checking whether
328    /// there FIFO is full.
329    ///
330    /// This does not necesarily mean there is a space in the FIFO available.
331    /// Use [Self::write_fifo] function to write a word into the FIFO reliably.
332    fn write_fifo_unchecked(&mut self, data: u32);
333
334    /// Low level function to read a word from the SPI FIFO. Must be preceeded by a
335    /// [Self::write_fifo] call.
336    ///
337    /// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
338    fn read_fifo(&mut self) -> nb::Result<u32, Infallible>;
339
340    /// Low level function to read a word from from the SPI FIFO.
341    ///
342    /// This does not necesarily mean there is a word in the FIFO available.
343    /// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb]
344    /// API.
345    /// You might also need to mask the value to ignore the BMSTART/BMSTOP bit.
346    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    // find largest (even) prescale value that divides into div
425    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    // Step 1: Calculate raw divider.
453    let raw_div = sys_clk.raw() / spi_clk.raw();
454    let remainder = sys_clk.raw() % spi_clk.raw();
455
456    // Step 2: Round up if necessary.
457    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        // Take slower clock conservatively.
465        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
477/// SPI peripheral driver structure.
478pub struct Spi<Word = u8> {
479    id: Bank,
480    regs: regs::MmioSpi<'static>,
481    cfg: SpiConfig,
482    /// Fill word for read-only SPI transactions.
483    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    /// Create a new SPI struct for using SPI with the fixed ROM SPI pins.
494    ///
495    /// ## Arguments
496    ///
497    /// * `spi` - SPI bus to use
498    /// * `spi_cfg` - Configuration specific to the SPI bus
499    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    /// Create a new SPI peripheral driver.
515    ///
516    /// ## Arguments
517    ///
518    /// * `spi` - SPI bus to use
519    /// * `pins` - Pins to be used for SPI transactions. These pins are consumed
520    ///   to ensure the pins can not be used for other purposes anymore
521    /// * `spi_cfg` - Configuration specific to the SPI bus
522    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        // Enable the peripheral as the last step as recommended in the
570        // programmers guide
571        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    /// Configure the hardware chip select given a hardware chip select ID.
648    ///
649    /// The pin also needs to be configured to be used as a HW CS pin. This can be done
650    /// by using the [configure_pin_as_hw_cs_pin] function which also returns the
651    /// corresponding [HwChipSelectId].
652    #[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    /// Disables the hardware chip select functionality. This can be used when performing
662    /// external chip select handling, for example with GPIO pins.
663    #[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    /// Utility function to configure all relevant transfer parameters in one go.
672    /// This is useful if multiple devices with different clock and mode configurations
673    /// are connected to one bus.
674    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    // The FIFO can hold a guaranteed amount of data, so we can pump it on transfer
716    // initialization. Returns the amount of written bytes.
717    fn initial_send_fifo_pumping_with_words(&mut self, words: &[Word]) -> usize {
718        //let reg_block = self.reg_block();
719        if self.blockmode {
720            self.regs.modify_ctrl1(|mut value| {
721                value.set_mtxpause(true);
722                value
723            });
724        }
725        // Fill the first half of the write FIFO
726        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    // The FIFO can hold a guaranteed amount of data, so we can pump it on transfer
746    // initialization.
747    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        // Fill the first half of the write FIFO
755        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            // Ignore received words.
852            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
924/// Changing the word size also requires a type conversion
925impl 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}