vorago_shared_hal/spi/
mod.rs

1use crate::FunctionSelect;
2use crate::gpio::{DynPinId, IoPeriphPin};
3use crate::{PeripheralSelect, enable_peripheral_clock, pins::AnyPin, sealed::Sealed, time::Hertz};
4use core::{convert::Infallible, fmt::Debug, marker::PhantomData};
5use embedded_hal::spi::{MODE_0, Mode};
6
7use regs::{ClockPrescaler, Data, FifoClear, WordSize};
8#[cfg(feature = "vor1x")]
9use va108xx as pac;
10#[cfg(feature = "vor4x")]
11use va416xx as pac;
12
13pub use regs::{Bank, HwChipSelectId};
14
15pub mod regs;
16
17pub fn configure_pin_as_hw_cs_pin<P: AnyPin + HwCsProvider>(_pin: P) -> HwChipSelectId {
18    IoPeriphPin::new(P::ID, P::FUN_SEL, None);
19    P::CS_ID
20}
21
22//==================================================================================================
23// Pins and traits.
24//==================================================================================================
25
26pub trait PinSck: AnyPin {
27    const SPI_ID: Bank;
28    const FUN_SEL: FunctionSelect;
29}
30
31pub trait PinMosi: AnyPin {
32    const SPI_ID: Bank;
33    const FUN_SEL: FunctionSelect;
34}
35
36pub trait PinMiso: AnyPin {
37    const SPI_ID: Bank;
38    const FUN_SEL: FunctionSelect;
39}
40
41pub trait HwCsProvider {
42    const PIN_ID: DynPinId;
43    const SPI_ID: Bank;
44    const FUN_SEL: FunctionSelect;
45    const CS_ID: HwChipSelectId;
46}
47
48#[macro_use]
49mod macros {
50    #[cfg(not(feature = "va41628"))]
51    macro_rules! hw_cs_multi_pin {
52    (
53        // name of the newtype wrapper struct
54        $name:ident,
55        // Pb0
56        $pin_id:ident,
57        // SpiId::B
58        $spi_id:path,
59        // FunSel::Sel1
60        $fun_sel:path,
61        // HwChipSelectId::Id2
62        $cs_id:path
63    ) => {
64            #[doc = concat!(
65                "Newtype wrapper to use [Pin] [`", stringify!($pin_id), "`] as a HW CS pin for [`", stringify!($spi_id), "`] with [`", stringify!($cs_id), "`]."
66            )]
67            pub struct $name(Pin<$pin_id>);
68
69            impl $name {
70                pub fn new(pin: Pin<$pin_id>) -> Self {
71                    Self(pin)
72                }
73            }
74
75            impl crate::sealed::Sealed for $name {}
76
77            impl HwCsProvider for $name {
78                const PIN_ID: DynPinId = <$pin_id as PinId>::ID;
79                const SPI_ID: Bank = $spi_id;
80                const FUN_SEL: FunctionSelect = $fun_sel;
81                const CS_ID: HwChipSelectId = $cs_id;
82            }
83        };
84    }
85
86    #[macro_export]
87    macro_rules! hw_cs_pins {
88        ($SpiId:path, $(($Px:ident, $FunSel:path, $HwCsIdent:path)$(,)?)+) => {
89            $(
90                impl HwCsProvider for Pin<$Px> {
91                    const PIN_ID: DynPinId = $Px::ID;
92                    const SPI_ID: Bank = $SpiId;
93                    const FUN_SEL: FunctionSelect = $FunSel;
94                    const CS_ID: HwChipSelectId = $HwCsIdent;
95                }
96            )+
97        };
98    }
99}
100
101#[cfg(feature = "vor1x")]
102pub mod pins_vor1x;
103#[cfg(feature = "vor4x")]
104pub mod pins_vor4x;
105
106//==================================================================================================
107// Defintions
108//==================================================================================================
109
110// FIFO has a depth of 16.
111const FILL_DEPTH: usize = 12;
112
113pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
114pub const BMSKIPDATA_MASK: u32 = 1 << 30;
115
116pub const DEFAULT_CLK_DIV: u16 = 2;
117
118/// Common trait implemented by all PAC peripheral access structures. The register block
119/// format is the same for all SPI blocks.
120pub trait SpiInstance: Sealed {
121    const ID: Bank;
122    const PERIPH_SEL: PeripheralSelect;
123}
124
125#[cfg(feature = "vor1x")]
126pub type Spi0 = pac::Spia;
127#[cfg(feature = "vor4x")]
128pub type Spi0 = pac::Spi0;
129
130impl SpiInstance for Spi0 {
131    const ID: Bank = Bank::Spi0;
132    const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0;
133}
134impl Sealed for Spi0 {}
135
136#[cfg(feature = "vor1x")]
137pub type Spi1 = pac::Spib;
138#[cfg(feature = "vor4x")]
139pub type Spi1 = pac::Spi1;
140
141impl SpiInstance for Spi1 {
142    const ID: Bank = Bank::Spi1;
143    const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1;
144}
145impl Sealed for Spi1 {}
146
147#[cfg(feature = "vor1x")]
148pub type Spi2 = pac::Spic;
149#[cfg(feature = "vor4x")]
150pub type Spi2 = pac::Spi2;
151
152impl SpiInstance for Spi2 {
153    const ID: Bank = Bank::Spi2;
154    const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2;
155}
156impl Sealed for Spi2 {}
157
158#[cfg(feature = "vor4x")]
159impl SpiInstance for pac::Spi3 {
160    const ID: Bank = Bank::Spi3;
161    const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi3;
162}
163#[cfg(feature = "vor4x")]
164impl Sealed for pac::Spi3 {}
165
166//==================================================================================================
167// Config
168//==================================================================================================
169
170pub trait TransferConfigProvider {
171    fn sod(&mut self, sod: bool);
172    fn blockmode(&mut self, blockmode: bool);
173    fn mode(&mut self, mode: Mode);
174    fn clk_cfg(&mut self, clk_cfg: SpiClockConfig);
175    fn hw_cs_id(&self) -> u8;
176}
177
178/// Type erased variant of the transfer configuration. This is required to avoid generics in
179/// the SPI constructor.
180#[derive(Copy, Clone, Debug)]
181#[cfg_attr(feature = "defmt", derive(defmt::Format))]
182pub struct TransferConfig {
183    pub clk_cfg: Option<SpiClockConfig>,
184    pub mode: Option<Mode>,
185    pub sod: bool,
186    /// If this is enabled, all data in the FIFO is transmitted in a single frame unless
187    /// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the
188    /// duration of multiple data words
189    pub blockmode: bool,
190    /// Only used when blockmode is used. The SCK will be stalled until an explicit stop bit
191    /// is set on a written word.
192    pub bmstall: bool,
193    pub hw_cs: Option<HwChipSelectId>,
194}
195
196impl TransferConfig {
197    pub fn new_with_hw_cs(
198        clk_cfg: Option<SpiClockConfig>,
199        mode: Option<Mode>,
200        blockmode: bool,
201        bmstall: bool,
202        sod: bool,
203        hw_cs_id: HwChipSelectId,
204    ) -> Self {
205        TransferConfig {
206            clk_cfg,
207            mode,
208            sod,
209            blockmode,
210            bmstall,
211            hw_cs: Some(hw_cs_id),
212        }
213    }
214}
215
216/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
217#[derive(Debug, Copy, Clone)]
218#[cfg_attr(feature = "defmt", derive(defmt::Format))]
219pub struct SpiConfig {
220    clk: SpiClockConfig,
221    // SPI mode configuration
222    pub init_mode: Mode,
223    /// If this is enabled, all data in the FIFO is transmitted in a single frame unless
224    /// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the
225    /// duration of multiple data words. Defaults to true.
226    pub blockmode: bool,
227    /// This enables the stalling of the SPI SCK if in blockmode and the FIFO is empty.
228    /// Currently enabled by default.
229    pub bmstall: bool,
230    /// Slave output disable. Useful if separate GPIO pins or decoders are used for CS control
231    pub slave_output_disable: bool,
232    /// Loopback mode. If you use this, don't connect MISO to MOSI, they will be tied internally
233    pub loopback_mode: bool,
234    /// Enable Master Delayer Capture Mode. See Programmers Guide p.92 for more details
235    pub master_delayer_capture: bool,
236}
237
238impl Default for SpiConfig {
239    fn default() -> Self {
240        Self {
241            init_mode: MODE_0,
242            blockmode: true,
243            bmstall: true,
244            // Default value is definitely valid.
245            clk: SpiClockConfig::from_div(DEFAULT_CLK_DIV).unwrap(),
246            slave_output_disable: Default::default(),
247            loopback_mode: Default::default(),
248            master_delayer_capture: Default::default(),
249        }
250    }
251}
252
253impl SpiConfig {
254    pub fn loopback(mut self, enable: bool) -> Self {
255        self.loopback_mode = enable;
256        self
257    }
258
259    pub fn blockmode(mut self, enable: bool) -> Self {
260        self.blockmode = enable;
261        self
262    }
263
264    pub fn bmstall(mut self, enable: bool) -> Self {
265        self.bmstall = enable;
266        self
267    }
268
269    pub fn mode(mut self, mode: Mode) -> Self {
270        self.init_mode = mode;
271        self
272    }
273
274    pub fn clk_cfg(mut self, clk_cfg: SpiClockConfig) -> Self {
275        self.clk = clk_cfg;
276        self
277    }
278
279    pub fn slave_output_disable(mut self, sod: bool) -> Self {
280        self.slave_output_disable = sod;
281        self
282    }
283}
284
285//==================================================================================================
286// Word Size
287//==================================================================================================
288
289/// Configuration trait for the Word Size
290/// used by the SPI peripheral
291pub trait SpiWord: Copy + Default + Into<u32> + TryFrom<u32> + 'static {
292    const MASK: u32;
293    const WORD_SIZE: regs::WordSize;
294    fn word_reg() -> u8;
295}
296
297impl SpiWord for u8 {
298    const MASK: u32 = 0xff;
299    const WORD_SIZE: regs::WordSize = regs::WordSize::EightBits;
300    fn word_reg() -> u8 {
301        0x07
302    }
303}
304
305impl SpiWord for u16 {
306    const MASK: u32 = 0xffff;
307    const WORD_SIZE: regs::WordSize = regs::WordSize::SixteenBits;
308    fn word_reg() -> u8 {
309        0x0f
310    }
311}
312
313//==================================================================================================
314// Spi
315//==================================================================================================
316
317/// Low level access trait for the SPI peripheral.
318pub trait SpiLowLevel {
319    /// Low level function to write a word to the SPI FIFO but also checks whether
320    /// there is actually data in the FIFO.
321    ///
322    /// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
323    fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible>;
324
325    /// Low level function to write a word to the SPI FIFO without checking whether
326    /// there FIFO is full.
327    ///
328    /// This does not necesarily mean there is a space in the FIFO available.
329    /// Use [Self::write_fifo] function to write a word into the FIFO reliably.
330    fn write_fifo_unchecked(&mut self, data: u32);
331
332    /// Low level function to read a word from the SPI FIFO. Must be preceeded by a
333    /// [Self::write_fifo] call.
334    ///
335    /// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
336    fn read_fifo(&mut self) -> nb::Result<u32, Infallible>;
337
338    /// Low level function to read a word from from the SPI FIFO.
339    ///
340    /// This does not necesarily mean there is a word in the FIFO available.
341    /// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb]
342    /// API.
343    /// You might also need to mask the value to ignore the BMSTART/BMSTOP bit.
344    fn read_fifo_unchecked(&mut self) -> u32;
345}
346
347#[inline(always)]
348pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
349    match mode {
350        embedded_hal::spi::MODE_0 => (false, false),
351        embedded_hal::spi::MODE_1 => (false, true),
352        embedded_hal::spi::MODE_2 => (true, false),
353        embedded_hal::spi::MODE_3 => (true, true),
354    }
355}
356
357#[derive(Debug, Copy, Clone, PartialEq, Eq)]
358#[cfg_attr(feature = "defmt", derive(defmt::Format))]
359pub struct SpiClockConfig {
360    prescale_val: u8,
361    scrdv: u8,
362}
363
364impl SpiClockConfig {
365    pub fn prescale_val(&self) -> u8 {
366        self.prescale_val
367    }
368    pub fn scrdv(&self) -> u8 {
369        self.scrdv
370    }
371}
372
373impl SpiClockConfig {
374    pub fn new(prescale_val: u8, scrdv: u8) -> Self {
375        Self {
376            prescale_val,
377            scrdv,
378        }
379    }
380
381    pub fn from_div(div: u16) -> Result<Self, SpiClockConfigError> {
382        spi_clk_config_from_div(div)
383    }
384
385    #[cfg(feature = "vor1x")]
386    pub fn from_clk(sys_clk: Hertz, spi_clk: Hertz) -> Option<Self> {
387        clk_div_for_target_clock(sys_clk, spi_clk).map(|div| spi_clk_config_from_div(div).unwrap())
388    }
389
390    #[cfg(feature = "vor4x")]
391    pub fn from_clks(clks: &crate::clock::Clocks, spi_clk: Hertz) -> Option<Self> {
392        Self::from_apb1_clk(clks.apb1(), spi_clk)
393    }
394
395    #[cfg(feature = "vor4x")]
396    pub fn from_apb1_clk(apb1_clk: Hertz, spi_clk: Hertz) -> Option<Self> {
397        clk_div_for_target_clock(apb1_clk, spi_clk).map(|div| spi_clk_config_from_div(div).unwrap())
398    }
399}
400
401#[derive(Debug, thiserror::Error)]
402#[cfg_attr(feature = "defmt", derive(defmt::Format))]
403pub enum SpiClockConfigError {
404    #[error("division by zero")]
405    DivIsZero,
406    #[error("divide value is not even")]
407    DivideValueNotEven,
408    #[error("scrdv value is too large")]
409    ScrdvValueTooLarge,
410}
411
412#[inline]
413pub fn spi_clk_config_from_div(mut div: u16) -> Result<SpiClockConfig, SpiClockConfigError> {
414    if div == 0 {
415        return Err(SpiClockConfigError::DivIsZero);
416    }
417    if div % 2 != 0 {
418        return Err(SpiClockConfigError::DivideValueNotEven);
419    }
420    let mut prescale_val = 0;
421
422    // find largest (even) prescale value that divides into div
423    for i in (2..=0xfe).rev().step_by(2) {
424        if div % i == 0 {
425            prescale_val = i;
426            break;
427        }
428    }
429
430    if prescale_val == 0 {
431        return Err(SpiClockConfigError::DivideValueNotEven);
432    }
433
434    div /= prescale_val;
435    if div > u8::MAX as u16 + 1 {
436        return Err(SpiClockConfigError::ScrdvValueTooLarge);
437    }
438    Ok(SpiClockConfig {
439        prescale_val: prescale_val as u8,
440        scrdv: (div - 1) as u8,
441    })
442}
443
444#[inline]
445pub fn clk_div_for_target_clock(sys_clk: Hertz, spi_clk: Hertz) -> Option<u16> {
446    if spi_clk > sys_clk {
447        return None;
448    }
449
450    // Step 1: Calculate raw divider.
451    let raw_div = sys_clk.raw() / spi_clk.raw();
452    let remainder = sys_clk.raw() % spi_clk.raw();
453
454    // Step 2: Round up if necessary.
455    let mut rounded_div = if remainder * 2 >= spi_clk.raw() {
456        raw_div + 1
457    } else {
458        raw_div
459    };
460
461    if rounded_div % 2 != 0 {
462        // Take slower clock conservatively.
463        rounded_div += 1;
464    }
465    if rounded_div > u16::MAX as u32 {
466        return None;
467    }
468    Some(rounded_div as u16)
469}
470
471#[derive(Debug, thiserror::Error)]
472#[error("peripheral or peripheral pin ID is not consistent")]
473pub struct SpiIdMissmatchError;
474
475/// SPI peripheral driver structure.
476pub struct Spi<Word = u8> {
477    id: Bank,
478    regs: regs::MmioSpi<'static>,
479    cfg: SpiConfig,
480    /// Fill word for read-only SPI transactions.
481    fill_word: Word,
482    blockmode: bool,
483    bmstall: bool,
484    word: PhantomData<Word>,
485}
486
487impl<Word: SpiWord> Spi<Word>
488where
489    <Word as TryFrom<u32>>::Error: core::fmt::Debug,
490{
491    /// Create a new SPI struct for using SPI with the fixed ROM SPI pins.
492    ///
493    /// ## Arguments
494    ///
495    /// * `spi` - SPI bus to use
496    /// * `spi_cfg` - Configuration specific to the SPI bus
497    pub fn new_for_rom<SpiI: SpiInstance>(
498        spi: SpiI,
499        spi_cfg: SpiConfig,
500    ) -> Result<Self, SpiIdMissmatchError> {
501        #[cfg(feature = "vor1x")]
502        if SpiI::ID != Bank::Spi2 {
503            return Err(SpiIdMissmatchError);
504        }
505        #[cfg(feature = "vor4x")]
506        if SpiI::ID != Bank::Spi3 {
507            return Err(SpiIdMissmatchError);
508        }
509        Ok(Self::new_generic(spi, spi_cfg))
510    }
511
512    /// Create a new SPI peripheral driver.
513    ///
514    /// ## Arguments
515    ///
516    /// * `spi` - SPI bus to use
517    /// * `pins` - Pins to be used for SPI transactions. These pins are consumed
518    ///   to ensure the pins can not be used for other purposes anymore
519    /// * `spi_cfg` - Configuration specific to the SPI bus
520    pub fn new<SpiI: SpiInstance, Sck: PinSck, Miso: PinMiso, Mosi: PinMosi>(
521        spi: SpiI,
522        _pins: (Sck, Miso, Mosi),
523        spi_cfg: SpiConfig,
524    ) -> Result<Self, SpiIdMissmatchError> {
525        if SpiI::ID != Sck::SPI_ID || SpiI::ID != Miso::SPI_ID || SpiI::ID != Mosi::SPI_ID {
526            return Err(SpiIdMissmatchError);
527        }
528        IoPeriphPin::new(Sck::ID, Sck::FUN_SEL, None);
529        IoPeriphPin::new(Miso::ID, Miso::FUN_SEL, None);
530        IoPeriphPin::new(Mosi::ID, Mosi::FUN_SEL, None);
531        Ok(Self::new_generic(spi, spi_cfg))
532    }
533
534    pub fn new_generic<SpiI: SpiInstance>(_spi: SpiI, spi_cfg: SpiConfig) -> Self {
535        enable_peripheral_clock(SpiI::PERIPH_SEL);
536        let mut regs = regs::Spi::new_mmio(SpiI::ID);
537        let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(spi_cfg.init_mode);
538        regs.write_ctrl0(
539            regs::Control0::builder()
540                .with_scrdv(spi_cfg.clk.scrdv)
541                .with_sph(cph_bit)
542                .with_spo(cpo_bit)
543                .with_word_size(Word::WORD_SIZE)
544                .build(),
545        );
546        regs.write_ctrl1(
547            regs::Control1::builder()
548                .with_mtxpause(false)
549                .with_mdlycap(spi_cfg.master_delayer_capture)
550                .with_bm_stall(spi_cfg.bmstall)
551                .with_bm_start(false)
552                .with_blockmode(spi_cfg.blockmode)
553                .with_ss(HwChipSelectId::Id0)
554                .with_sod(spi_cfg.slave_output_disable)
555                .with_slave_mode(false)
556                .with_enable(false)
557                .with_lbm(spi_cfg.loopback_mode)
558                .build(),
559        );
560        regs.write_clkprescale(ClockPrescaler::new(spi_cfg.clk.prescale_val));
561        regs.write_fifo_clear(
562            FifoClear::builder()
563                .with_tx_fifo(true)
564                .with_rx_fifo(true)
565                .build(),
566        );
567        // Enable the peripheral as the last step as recommended in the
568        // programmers guide
569        regs.modify_ctrl1(|mut value| {
570            value.set_enable(true);
571            value
572        });
573        Spi {
574            id: SpiI::ID,
575            regs: regs::Spi::new_mmio(SpiI::ID),
576            cfg: spi_cfg,
577            fill_word: Default::default(),
578            bmstall: spi_cfg.bmstall,
579            blockmode: spi_cfg.blockmode,
580            word: PhantomData,
581        }
582    }
583
584    #[inline]
585    pub fn cfg_clock(&mut self, cfg: SpiClockConfig) {
586        self.regs.modify_ctrl0(|mut value| {
587            value.set_scrdv(cfg.scrdv);
588            value
589        });
590        self.regs
591            .write_clkprescale(regs::ClockPrescaler::new(cfg.prescale_val));
592    }
593
594    pub fn set_fill_word(&mut self, fill_word: Word) {
595        self.fill_word = fill_word;
596    }
597
598    #[inline]
599    pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClockConfigError> {
600        let val = spi_clk_config_from_div(div)?;
601        self.cfg_clock(val);
602        Ok(())
603    }
604
605    #[inline]
606    pub fn cfg_mode(&mut self, mode: Mode) {
607        let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode);
608        self.regs.modify_ctrl0(|mut value| {
609            value.set_spo(cpo_bit);
610            value.set_sph(cph_bit);
611            value
612        });
613    }
614
615    #[inline]
616    pub fn fill_word(&self) -> Word {
617        self.fill_word
618    }
619
620    #[inline]
621    pub fn clear_tx_fifo(&mut self) {
622        self.regs.write_fifo_clear(
623            regs::FifoClear::builder()
624                .with_tx_fifo(true)
625                .with_rx_fifo(false)
626                .build(),
627        );
628    }
629
630    #[inline]
631    pub fn clear_rx_fifo(&mut self) {
632        self.regs.write_fifo_clear(
633            regs::FifoClear::builder()
634                .with_tx_fifo(false)
635                .with_rx_fifo(true)
636                .build(),
637        );
638    }
639
640    #[inline]
641    pub fn perid(&self) -> u32 {
642        self.regs.read_perid()
643    }
644
645    /// Configure the hardware chip select given a hardware chip select ID.
646    ///
647    /// The pin also needs to be configured to be used as a HW CS pin. This can be done
648    /// by using the [configure_pin_as_hw_cs_pin] function which also returns the
649    /// corresponding [HwChipSelectId].
650    #[inline]
651    pub fn cfg_hw_cs(&mut self, hw_cs: HwChipSelectId) {
652        self.regs.modify_ctrl1(|mut value| {
653            value.set_sod(false);
654            value.set_ss(hw_cs);
655            value
656        });
657    }
658
659    /// Disables the hardware chip select functionality. This can be used when performing
660    /// external chip select handling, for example with GPIO pins.
661    #[inline]
662    pub fn cfg_hw_cs_disable(&mut self) {
663        self.regs.modify_ctrl1(|mut value| {
664            value.set_sod(true);
665            value
666        });
667    }
668
669    /// Utility function to configure all relevant transfer parameters in one go.
670    /// This is useful if multiple devices with different clock and mode configurations
671    /// are connected to one bus.
672    pub fn cfg_transfer(&mut self, transfer_cfg: &TransferConfig) {
673        if let Some(trans_clk_div) = transfer_cfg.clk_cfg {
674            self.cfg_clock(trans_clk_div);
675        }
676        if let Some(mode) = transfer_cfg.mode {
677            self.cfg_mode(mode);
678        }
679        self.blockmode = transfer_cfg.blockmode;
680        self.regs.modify_ctrl1(|mut value| {
681            if transfer_cfg.sod {
682                value.set_sod(transfer_cfg.sod);
683            } else {
684                value.set_sod(false);
685                if let Some(hw_cs) = transfer_cfg.hw_cs {
686                    value.set_ss(hw_cs);
687                }
688            }
689            value.set_blockmode(transfer_cfg.blockmode);
690            value.set_bm_stall(transfer_cfg.bmstall);
691            value
692        });
693    }
694
695    fn flush_internal(&mut self) {
696        let mut status_reg = self.regs.read_status();
697        while !status_reg.tx_empty() || status_reg.rx_not_empty() || status_reg.busy() {
698            if status_reg.rx_not_empty() {
699                self.read_fifo_unchecked();
700            }
701            status_reg = self.regs.read_status();
702        }
703    }
704
705    fn transfer_preparation(&mut self, words: &[Word]) -> Result<(), Infallible> {
706        if words.is_empty() {
707            return Ok(());
708        }
709        self.flush_internal();
710        Ok(())
711    }
712
713    // The FIFO can hold a guaranteed amount of data, so we can pump it on transfer
714    // initialization. Returns the amount of written bytes.
715    fn initial_send_fifo_pumping_with_words(&mut self, words: &[Word]) -> usize {
716        //let reg_block = self.reg_block();
717        if self.blockmode {
718            self.regs.modify_ctrl1(|mut value| {
719                value.set_mtxpause(true);
720                value
721            });
722        }
723        // Fill the first half of the write FIFO
724        let mut current_write_idx = 0;
725        let smaller_idx = core::cmp::min(FILL_DEPTH, words.len());
726        for _ in 0..smaller_idx {
727            if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall {
728                self.write_fifo_unchecked(words[current_write_idx].into() | BMSTART_BMSTOP_MASK);
729            } else {
730                self.write_fifo_unchecked(words[current_write_idx].into());
731            }
732            current_write_idx += 1;
733        }
734        if self.blockmode {
735            self.regs.modify_ctrl1(|mut value| {
736                value.set_mtxpause(false);
737                value
738            });
739        }
740        current_write_idx
741    }
742
743    // The FIFO can hold a guaranteed amount of data, so we can pump it on transfer
744    // initialization.
745    fn initial_send_fifo_pumping_with_fill_words(&mut self, send_len: usize) -> usize {
746        if self.blockmode {
747            self.regs.modify_ctrl1(|mut value| {
748                value.set_mtxpause(true);
749                value
750            });
751        }
752        // Fill the first half of the write FIFO
753        let mut current_write_idx = 0;
754        let smaller_idx = core::cmp::min(FILL_DEPTH, send_len);
755        for _ in 0..smaller_idx {
756            if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall {
757                self.write_fifo_unchecked(self.fill_word.into() | BMSTART_BMSTOP_MASK);
758            } else {
759                self.write_fifo_unchecked(self.fill_word.into());
760            }
761            current_write_idx += 1;
762        }
763        if self.blockmode {
764            self.regs.modify_ctrl1(|mut value| {
765                value.set_mtxpause(false);
766                value
767            });
768        }
769        current_write_idx
770    }
771}
772
773impl<W: SpiWord> SpiLowLevel for Spi<W>
774where
775    <W as TryFrom<u32>>::Error: core::fmt::Debug,
776{
777    #[inline(always)]
778    fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible> {
779        if !self.regs.read_status().tx_not_full() {
780            return Err(nb::Error::WouldBlock);
781        }
782        self.write_fifo_unchecked(data);
783        Ok(())
784    }
785
786    #[inline(always)]
787    fn write_fifo_unchecked(&mut self, data: u32) {
788        self.regs.write_data(Data::new_with_raw_value(data));
789    }
790
791    #[inline(always)]
792    fn read_fifo(&mut self) -> nb::Result<u32, Infallible> {
793        if !self.regs.read_status().rx_not_empty() {
794            return Err(nb::Error::WouldBlock);
795        }
796        Ok(self.read_fifo_unchecked())
797    }
798
799    #[inline(always)]
800    fn read_fifo_unchecked(&mut self) -> u32 {
801        self.regs.read_data().raw_value()
802    }
803}
804
805impl<Word: SpiWord> embedded_hal::spi::ErrorType for Spi<Word> {
806    type Error = Infallible;
807}
808
809impl<Word: SpiWord> embedded_hal::spi::SpiBus<Word> for Spi<Word>
810where
811    <Word as TryFrom<u32>>::Error: core::fmt::Debug,
812{
813    fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
814        self.transfer_preparation(words)?;
815        let mut current_read_idx = 0;
816        let mut current_write_idx = self.initial_send_fifo_pumping_with_fill_words(words.len());
817        loop {
818            if current_read_idx < words.len() {
819                words[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK)
820                    .try_into()
821                    .unwrap();
822                current_read_idx += 1;
823            }
824            if current_write_idx < words.len() {
825                if current_write_idx == words.len() - 1 && self.bmstall {
826                    nb::block!(self.write_fifo(self.fill_word.into() | BMSTART_BMSTOP_MASK))?;
827                } else {
828                    nb::block!(self.write_fifo(self.fill_word.into()))?;
829                }
830                current_write_idx += 1;
831            }
832            if current_read_idx >= words.len() && current_write_idx >= words.len() {
833                break;
834            }
835        }
836        Ok(())
837    }
838
839    fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
840        self.transfer_preparation(words)?;
841        let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words);
842        while current_write_idx < words.len() {
843            if current_write_idx == words.len() - 1 && self.bmstall {
844                nb::block!(self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK))?;
845            } else {
846                nb::block!(self.write_fifo(words[current_write_idx].into()))?;
847            }
848            current_write_idx += 1;
849            // Ignore received words.
850            if self.regs.read_status().rx_not_empty() {
851                self.clear_rx_fifo();
852            }
853        }
854        Ok(())
855    }
856
857    fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
858        self.transfer_preparation(write)?;
859        let mut current_read_idx = 0;
860        let mut current_write_idx = self.initial_send_fifo_pumping_with_words(write);
861        let max_idx = core::cmp::max(read.len(), write.len());
862        while current_read_idx < read.len() || current_write_idx < write.len() {
863            if current_write_idx < max_idx {
864                if current_write_idx == write.len() - 1 && self.bmstall {
865                    nb::block!(
866                        self.write_fifo(write[current_write_idx].into() | BMSTART_BMSTOP_MASK)
867                    )?;
868                } else if current_write_idx < write.len() {
869                    nb::block!(self.write_fifo(write[current_write_idx].into()))?;
870                } else {
871                    nb::block!(self.write_fifo(0))?;
872                }
873                current_write_idx += 1;
874            }
875            if current_read_idx < max_idx {
876                if current_read_idx < read.len() {
877                    read[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK)
878                        .try_into()
879                        .unwrap();
880                } else {
881                    nb::block!(self.read_fifo()).unwrap();
882                }
883                current_read_idx += 1;
884            }
885        }
886
887        Ok(())
888    }
889
890    fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
891        self.transfer_preparation(words)?;
892        let mut current_read_idx = 0;
893        let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words);
894
895        while current_read_idx < words.len() || current_write_idx < words.len() {
896            if current_write_idx < words.len() {
897                if current_write_idx == words.len() - 1 && self.bmstall {
898                    nb::block!(
899                        self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK)
900                    )?;
901                } else {
902                    nb::block!(self.write_fifo(words[current_write_idx].into()))?;
903                }
904                current_write_idx += 1;
905            }
906            if current_read_idx < words.len() && current_read_idx < current_write_idx {
907                words[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK)
908                    .try_into()
909                    .unwrap();
910                current_read_idx += 1;
911            }
912        }
913        Ok(())
914    }
915
916    fn flush(&mut self) -> Result<(), Self::Error> {
917        self.flush_internal();
918        Ok(())
919    }
920}
921
922/// Changing the word size also requires a type conversion
923impl From<Spi<u8>> for Spi<u16> {
924    fn from(mut old_spi: Spi<u8>) -> Self {
925        old_spi.regs.modify_ctrl0(|mut value| {
926            value.set_word_size(WordSize::SixteenBits);
927            value
928        });
929        Spi {
930            id: old_spi.id,
931            regs: old_spi.regs,
932            cfg: old_spi.cfg,
933            blockmode: old_spi.blockmode,
934            fill_word: Default::default(),
935            bmstall: old_spi.bmstall,
936            word: PhantomData,
937        }
938    }
939}
940
941impl From<Spi<u16>> for Spi<u8> {
942    fn from(mut old_spi: Spi<u16>) -> Self {
943        old_spi.regs.modify_ctrl0(|mut value| {
944            value.set_word_size(WordSize::EightBits);
945            value
946        });
947        Spi {
948            id: old_spi.id,
949            regs: old_spi.regs,
950            cfg: old_spi.cfg,
951            blockmode: old_spi.blockmode,
952            fill_word: Default::default(),
953            bmstall: old_spi.bmstall,
954            word: PhantomData,
955        }
956    }
957}