Skip to main content

embassy_rp/pio/
mod.rs

1//! PIO driver.
2use core::future::Future;
3use core::marker::PhantomData;
4use core::pin::Pin as FuturePin;
5use core::sync::atomic::{AtomicU8, AtomicU32, Ordering};
6use core::task::{Context, Poll};
7
8use embassy_hal_internal::{Peri, PeripheralType};
9use embassy_sync::waitqueue::AtomicWaker;
10use fixed::FixedU32;
11use fixed::types::extra::U8;
12use pio::{Program, SideSet, Wrap};
13
14use crate::dma::{self, Transfer, Word};
15use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate};
16use crate::interrupt::typelevel::{Binding, Handler, Interrupt};
17use crate::relocate::RelocatedProgram;
18use crate::{RegExt, pac, peripherals};
19
20mod instr;
21
22#[doc(inline)]
23pub use pio as program;
24
25/// Wakers for interrupts and FIFOs.
26pub struct Wakers([AtomicWaker; 12]);
27
28impl Wakers {
29    #[inline(always)]
30    fn fifo_in(&self) -> &[AtomicWaker] {
31        &self.0[0..4]
32    }
33    #[inline(always)]
34    fn fifo_out(&self) -> &[AtomicWaker] {
35        &self.0[4..8]
36    }
37    #[inline(always)]
38    fn irq(&self) -> &[AtomicWaker] {
39        &self.0[8..12]
40    }
41}
42
43/// FIFO config.
44#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
45#[cfg_attr(feature = "defmt", derive(defmt::Format))]
46#[repr(u8)]
47pub enum FifoJoin {
48    /// Both TX and RX fifo is enabled
49    #[default]
50    Duplex,
51    /// Rx fifo twice as deep. TX fifo disabled
52    RxOnly,
53    /// Tx fifo twice as deep. RX fifo disabled
54    TxOnly,
55    /// Enable random writes (`FJOIN_RX_PUT`) from the state machine (through ISR),
56    /// and random reads from the system (using [`StateMachine::get_rxf_entry`]).
57    #[cfg(feature = "_rp235x")]
58    RxAsStatus,
59    /// Enable random reads (`FJOIN_RX_GET`) from the state machine (through OSR),
60    /// and random writes from the system (using [`StateMachine::set_rxf_entry`]).
61    #[cfg(feature = "_rp235x")]
62    RxAsControl,
63    /// FJOIN_RX_PUT | FJOIN_RX_GET: RX can be used as a scratch register,
64    /// not accessible from the CPU
65    #[cfg(feature = "_rp235x")]
66    PioScratch,
67}
68
69/// Shift direction.
70#[allow(missing_docs)]
71#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
72#[cfg_attr(feature = "defmt", derive(defmt::Format))]
73#[repr(u8)]
74pub enum ShiftDirection {
75    #[default]
76    Right = 1,
77    Left = 0,
78}
79
80/// Pin direction.
81#[allow(missing_docs)]
82#[derive(Clone, Copy, PartialEq, Eq, Debug)]
83#[cfg_attr(feature = "defmt", derive(defmt::Format))]
84#[repr(u8)]
85pub enum Direction {
86    In = 0,
87    Out = 1,
88}
89
90/// Which fifo level to use in status check.
91#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
92#[cfg_attr(feature = "defmt", derive(defmt::Format))]
93#[repr(u8)]
94pub enum StatusSource {
95    #[default]
96    /// All-ones if TX FIFO level < N, otherwise all-zeroes.
97    TxFifoLevel = 0,
98    /// All-ones if RX FIFO level < N, otherwise all-zeroes.
99    RxFifoLevel = 1,
100    /// All-ones if the indexed IRQ flag is raised, otherwise all-zeroes
101    #[cfg(feature = "_rp235x")]
102    Irq = 2,
103}
104
105const RXNEMPTY_MASK: u32 = 1 << 0;
106const TXNFULL_MASK: u32 = 1 << 4;
107const SMIRQ_MASK: u32 = 1 << 8;
108
109/// Interrupt handler for PIO.
110pub struct InterruptHandler<PIO: Instance> {
111    _pio: PhantomData<PIO>,
112}
113
114impl<PIO: Instance> Handler<PIO::Interrupt> for InterruptHandler<PIO> {
115    unsafe fn on_interrupt() {
116        let ints = PIO::PIO.irqs(0).ints().read().0;
117        for bit in 0..12 {
118            if ints & (1 << bit) != 0 {
119                PIO::wakers().0[bit].wake();
120            }
121        }
122        PIO::PIO.irqs(0).inte().write_clear(|m| m.0 = ints);
123    }
124}
125
126/// Future that waits for TX-FIFO to become writable
127#[must_use = "futures do nothing unless you `.await` or poll them"]
128pub struct FifoOutFuture<'a, 'd, PIO: Instance, const SM: usize> {
129    sm_tx: &'a mut StateMachineTx<'d, PIO, SM>,
130    value: u32,
131}
132
133impl<'a, 'd, PIO: Instance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> {
134    /// Create a new future waiting for TX-FIFO to become writable.
135    pub fn new(sm: &'a mut StateMachineTx<'d, PIO, SM>, value: u32) -> Self {
136        FifoOutFuture { sm_tx: sm, value }
137    }
138}
139
140impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PIO, SM> {
141    type Output = ();
142    fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
143        //debug!("Poll {},{}", PIO::PIO_NO, SM);
144        let value = self.value;
145        if self.get_mut().sm_tx.try_push(value) {
146            Poll::Ready(())
147        } else {
148            PIO::wakers().fifo_out()[SM].register(cx.waker());
149            PIO::PIO.irqs(0).inte().write_set(|m| {
150                m.0 = TXNFULL_MASK << SM;
151            });
152            // debug!("Pending");
153            Poll::Pending
154        }
155    }
156}
157
158impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> {
159    fn drop(&mut self) {
160        PIO::PIO.irqs(0).inte().write_clear(|m| {
161            m.0 = TXNFULL_MASK << SM;
162        });
163    }
164}
165
166/// Future that waits for RX-FIFO to become readable.
167#[must_use = "futures do nothing unless you `.await` or poll them"]
168pub struct FifoInFuture<'a, 'd, PIO: Instance, const SM: usize> {
169    sm_rx: &'a mut StateMachineRx<'d, PIO, SM>,
170}
171
172impl<'a, 'd, PIO: Instance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> {
173    /// Create future that waits for RX-FIFO to become readable.
174    pub fn new(sm: &'a mut StateMachineRx<'d, PIO, SM>) -> Self {
175        FifoInFuture { sm_rx: sm }
176    }
177}
178
179impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO, SM> {
180    type Output = u32;
181    fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
182        //debug!("Poll {},{}", PIO::PIO_NO, SM);
183        if let Some(v) = self.sm_rx.try_pull() {
184            Poll::Ready(v)
185        } else {
186            PIO::wakers().fifo_in()[SM].register(cx.waker());
187            PIO::PIO.irqs(0).inte().write_set(|m| {
188                m.0 = RXNEMPTY_MASK << SM;
189            });
190            //debug!("Pending");
191            Poll::Pending
192        }
193    }
194}
195
196impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> {
197    fn drop(&mut self) {
198        PIO::PIO.irqs(0).inte().write_clear(|m| {
199            m.0 = RXNEMPTY_MASK << SM;
200        });
201    }
202}
203
204/// Future that waits for IRQ
205#[must_use = "futures do nothing unless you `.await` or poll them"]
206pub struct IrqFuture<'a, 'd, PIO: Instance> {
207    pio: PhantomData<&'a mut Irq<'d, PIO, 0>>,
208    irq_no: u8,
209}
210
211impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> {
212    type Output = ();
213    fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
214        //debug!("Poll {},{}", PIO::PIO_NO, SM);
215
216        // Check if IRQ flag is already set
217        if PIO::PIO.irq().read().0 & (1 << self.irq_no) != 0 {
218            PIO::PIO.irq().write(|m| m.0 = 1 << self.irq_no);
219            return Poll::Ready(());
220        }
221
222        PIO::wakers().irq()[self.irq_no as usize].register(cx.waker());
223        PIO::PIO.irqs(0).inte().write_set(|m| {
224            m.0 = SMIRQ_MASK << self.irq_no;
225        });
226        Poll::Pending
227    }
228}
229
230impl<'a, 'd, PIO: Instance> Drop for IrqFuture<'a, 'd, PIO> {
231    fn drop(&mut self) {
232        PIO::PIO.irqs(0).inte().write_clear(|m| {
233            m.0 = SMIRQ_MASK << self.irq_no;
234        });
235    }
236}
237
238/// Type representing a PIO pin.
239pub struct Pin<'l, PIO: Instance> {
240    pin: Peri<'l, AnyPin>,
241    pio: PhantomData<PIO>,
242}
243
244impl<'l, PIO: Instance> Pin<'l, PIO> {
245    /// Set the pin's drive strength.
246    #[inline]
247    pub fn set_drive_strength(&mut self, strength: Drive) {
248        self.pin.pad_ctrl().modify(|w| {
249            w.set_drive(match strength {
250                Drive::_2mA => pac::pads::vals::Drive::_2M_A,
251                Drive::_4mA => pac::pads::vals::Drive::_4M_A,
252                Drive::_8mA => pac::pads::vals::Drive::_8M_A,
253                Drive::_12mA => pac::pads::vals::Drive::_12M_A,
254            });
255        });
256    }
257
258    /// Set the pin's slew rate.
259    #[inline]
260    pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {
261        self.pin.pad_ctrl().modify(|w| {
262            w.set_slewfast(slew_rate == SlewRate::Fast);
263        });
264    }
265
266    /// Set the pin's pull.
267    #[inline]
268    pub fn set_pull(&mut self, pull: Pull) {
269        self.pin.pad_ctrl().modify(|w| {
270            w.set_pue(pull == Pull::Up);
271            w.set_pde(pull == Pull::Down);
272        });
273    }
274
275    /// Set the pin's schmitt trigger.
276    #[inline]
277    pub fn set_schmitt(&mut self, enable: bool) {
278        self.pin.pad_ctrl().modify(|w| {
279            w.set_schmitt(enable);
280        });
281    }
282
283    /// Configure the output logic inversion of this pin.
284    #[inline]
285    pub fn set_output_inversion(&mut self, invert: bool) {
286        self.pin.gpio().ctrl().modify(|w| {
287            w.set_outover(if invert {
288                pac::io::vals::Outover::INVERT
289            } else {
290                pac::io::vals::Outover::NORMAL
291            })
292        });
293    }
294
295    /// Configure the output enable inversion of this pin
296    #[inline]
297    pub fn set_output_enable_inversion(&mut self, invert: bool) {
298        self.pin.gpio().ctrl().modify(|w| {
299            w.set_oeover(if invert {
300                pac::io::vals::Oeover::INVERT
301            } else {
302                pac::io::vals::Oeover::NORMAL
303            })
304        })
305    }
306
307    /// Set the pin's input sync bypass.
308    pub fn set_input_sync_bypass(&mut self, bypass: bool) {
309        let mask = 1 << self.pin();
310        if bypass {
311            PIO::PIO.input_sync_bypass().write_set(|w| *w = mask);
312        } else {
313            PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask);
314        }
315    }
316
317    /// Get the underlying pin number.
318    pub fn pin(&self) -> u8 {
319        self.pin._pin()
320    }
321}
322
323/// Type representing a state machine RX FIFO.
324pub struct StateMachineRx<'d, PIO: Instance, const SM: usize> {
325    pio: PhantomData<&'d mut PIO>,
326}
327
328impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
329    /// Check if RX FIFO is empty.
330    pub fn empty(&self) -> bool {
331        PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0
332    }
333
334    /// Check if RX FIFO is full.
335    pub fn full(&self) -> bool {
336        PIO::PIO.fstat().read().rxfull() & (1u8 << SM) != 0
337    }
338
339    /// Check RX FIFO level.
340    pub fn level(&self) -> u8 {
341        (PIO::PIO.flevel().read().0 >> (SM * 8 + 4)) as u8 & 0x0f
342    }
343
344    /// Check if state machine has stalled on full RX FIFO.
345    pub fn stalled(&self) -> bool {
346        let fdebug = PIO::PIO.fdebug();
347        let ret = fdebug.read().rxstall() & (1 << SM) != 0;
348        if ret {
349            fdebug.write(|w| w.set_rxstall(1 << SM));
350        }
351        ret
352    }
353
354    /// Check if RX FIFO underflow (i.e. read-on-empty by the system) has occurred.
355    pub fn underflowed(&self) -> bool {
356        let fdebug = PIO::PIO.fdebug();
357        let ret = fdebug.read().rxunder() & (1 << SM) != 0;
358        if ret {
359            fdebug.write(|w| w.set_rxunder(1 << SM));
360        }
361        ret
362    }
363
364    /// Pull data from RX FIFO.
365    ///
366    /// This function doesn't check if there is data available to be read.
367    /// If the rx FIFO is empty, an undefined value is returned. If you only
368    /// want to pull if data is available, use `try_pull` instead.
369    pub fn pull(&mut self) -> u32 {
370        PIO::PIO.rxf(SM).read()
371    }
372
373    /// Attempt pulling data from RX FIFO.
374    pub fn try_pull(&mut self) -> Option<u32> {
375        if self.empty() {
376            return None;
377        }
378        Some(self.pull())
379    }
380
381    /// Wait for RX FIFO readable.
382    pub fn wait_pull<'a>(&'a mut self) -> FifoInFuture<'a, 'd, PIO, SM> {
383        FifoInFuture::new(self)
384    }
385
386    fn dreq() -> crate::pac::dma::vals::TreqSel {
387        crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8 + 4)
388    }
389
390    /// Prepare DMA transfer from RX FIFO.
391    pub fn dma_pull<'a, W: Word>(
392        &'a mut self,
393        ch: &'a mut dma::Channel<'_>,
394        data: &'a mut [W],
395        bswap: bool,
396    ) -> Transfer<'a> {
397        unsafe { ch.read(PIO::PIO.rxf(SM).as_ptr() as *const W, data, Self::dreq(), bswap) }
398    }
399
400    /// Prepare a repeated DMA transfer from RX FIFO.
401    pub fn dma_pull_discard<'a, W: Word>(&'a mut self, ch: &'a mut dma::Channel<'_>, len: usize) -> Transfer<'a> {
402        unsafe { ch.read_discard(PIO::PIO.rxf(SM).as_ptr(), len, Self::dreq()) }
403    }
404}
405
406/// Type representing a state machine TX FIFO.
407pub struct StateMachineTx<'d, PIO: Instance, const SM: usize> {
408    pio: PhantomData<&'d mut PIO>,
409}
410
411impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
412    /// Check if TX FIFO is empty.
413    pub fn empty(&self) -> bool {
414        PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0
415    }
416
417    /// Check if TX FIFO is full.
418    pub fn full(&self) -> bool {
419        PIO::PIO.fstat().read().txfull() & (1u8 << SM) != 0
420    }
421
422    /// Check TX FIFO level.
423    pub fn level(&self) -> u8 {
424        (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f
425    }
426
427    /// Check if state machine has stalled on empty TX FIFO.
428    pub fn stalled(&self) -> bool {
429        let fdebug = PIO::PIO.fdebug();
430        let ret = fdebug.read().txstall() & (1 << SM) != 0;
431        if ret {
432            fdebug.write(|w| w.set_txstall(1 << SM));
433        }
434        ret
435    }
436
437    /// Check if FIFO overflowed.
438    pub fn overflowed(&self) -> bool {
439        let fdebug = PIO::PIO.fdebug();
440        let ret = fdebug.read().txover() & (1 << SM) != 0;
441        if ret {
442            fdebug.write(|w| w.set_txover(1 << SM));
443        }
444        ret
445    }
446
447    /// Force push data to TX FIFO.
448    pub fn push(&mut self, v: u32) {
449        PIO::PIO.txf(SM).write_value(v);
450    }
451
452    /// Attempt to push data to TX FIFO.
453    pub fn try_push(&mut self, v: u32) -> bool {
454        if self.full() {
455            return false;
456        }
457        self.push(v);
458        true
459    }
460
461    /// Wait until FIFO is ready for writing.
462    pub fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, 'd, PIO, SM> {
463        FifoOutFuture::new(self, value)
464    }
465
466    fn dreq() -> crate::pac::dma::vals::TreqSel {
467        crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8)
468    }
469
470    /// Prepare a DMA transfer to TX FIFO.
471    pub fn dma_push<'a, W: Word>(
472        &'a mut self,
473        ch: &'a mut dma::Channel<'_>,
474        data: &'a [W],
475        bswap: bool,
476    ) -> Transfer<'a> {
477        unsafe { ch.write(data, PIO::PIO.txf(SM).as_ptr() as *mut W, Self::dreq(), bswap) }
478    }
479
480    /// Prepare a repeated DMA transfer to TX FIFO.
481    pub fn dma_push_zeros<'a, W: Word>(&'a mut self, ch: &'a mut dma::Channel<'_>, len: usize) -> Transfer<'a> {
482        unsafe { ch.write_zeros(len, PIO::PIO.txf(SM).as_ptr() as *mut W, Self::dreq()) }
483    }
484}
485
486/// A type representing a single PIO state machine.
487pub struct StateMachine<'d, PIO: Instance, const SM: usize> {
488    rx: StateMachineRx<'d, PIO, SM>,
489    tx: StateMachineTx<'d, PIO, SM>,
490}
491
492impl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> {
493    fn drop(&mut self) {
494        PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM));
495        on_pio_drop::<PIO>();
496    }
497}
498
499fn assert_consecutive<PIO: Instance>(pins: &[&Pin<PIO>]) {
500    for (p1, p2) in pins.iter().zip(pins.iter().skip(1)) {
501        // purposely does not allow wrap-around because we can't claim pins 30 and 31.
502        assert!(p1.pin() + 1 == p2.pin(), "pins must be consecutive");
503    }
504}
505
506/// PIO Execution config.
507#[derive(Clone, Copy, Default, Debug)]
508#[cfg_attr(feature = "defmt", derive(defmt::Format))]
509#[non_exhaustive]
510pub struct ExecConfig {
511    /// If true, the MSB of the Delay/Side-set instruction field is used as side-set enable, rather than a side-set data bit.
512    pub side_en: bool,
513    /// If true, side-set data is asserted to pin directions, instead of pin values.
514    pub side_pindir: bool,
515    /// Pin to trigger jump.
516    pub jmp_pin: u8,
517    /// After reaching this address, execution is wrapped to wrap_bottom.
518    pub wrap_top: u8,
519    /// After reaching wrap_top, execution is wrapped to this address.
520    pub wrap_bottom: u8,
521}
522
523/// PIO shift register config for input or output.
524#[derive(Clone, Copy, Default, Debug)]
525#[cfg_attr(feature = "defmt", derive(defmt::Format))]
526pub struct ShiftConfig {
527    /// Number of bits shifted out of OSR before autopull.
528    pub threshold: u8,
529    /// Shift direction.
530    pub direction: ShiftDirection,
531    /// For output: Pull automatically output shift register is emptied.
532    /// For input: Push automatically when the input shift register is filled.
533    pub auto_fill: bool,
534}
535
536/// PIO pin config.
537#[derive(Clone, Copy, Default, Debug)]
538#[cfg_attr(feature = "defmt", derive(defmt::Format))]
539pub struct PinConfig {
540    /// The number of MSBs of the Delay/Side-set instruction field which are used for side-set.
541    pub sideset_count: u8,
542    /// The number of pins asserted by a SET. In the range 0 to 5 inclusive.
543    pub set_count: u8,
544    /// The number of pins asserted by an OUT PINS, OUT PINDIRS or MOV PINS instruction. In the range 0 to 32 inclusive.
545    pub out_count: u8,
546    /// The pin which is mapped to the least-significant bit of a state machine's IN data bus.
547    pub in_base: u8,
548    /// The lowest-numbered pin that will be affected by a side-set operation.
549    pub sideset_base: u8,
550    /// The lowest-numbered pin that will be affected by a SET PINS or SET PINDIRS instruction.
551    pub set_base: u8,
552    /// The lowest-numbered pin that will be affected by an OUT PINS, OUT PINDIRS or MOV PINS instruction.
553    pub out_base: u8,
554}
555
556/// Comparison level or IRQ index for the MOV x, STATUS instruction.
557#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
558#[cfg_attr(feature = "defmt", derive(defmt::Format))]
559#[cfg(feature = "_rp235x")]
560pub enum StatusN {
561    /// IRQ flag in this PIO block
562    This(u8),
563    /// IRQ flag in the next lower PIO block
564    Lower(u8),
565    /// IRQ flag in the next higher PIO block
566    Higher(u8),
567}
568
569#[cfg(feature = "_rp235x")]
570impl Default for StatusN {
571    fn default() -> Self {
572        Self::This(0)
573    }
574}
575
576#[cfg(feature = "_rp235x")]
577impl Into<crate::pac::pio::vals::ExecctrlStatusN> for StatusN {
578    fn into(self) -> crate::pac::pio::vals::ExecctrlStatusN {
579        let x = match self {
580            StatusN::This(n) => n,
581            StatusN::Lower(n) => n + 0x08,
582            StatusN::Higher(n) => n + 0x10,
583        };
584
585        crate::pac::pio::vals::ExecctrlStatusN(x)
586    }
587}
588
589/// PIO config.
590#[derive(Clone, Copy, Debug)]
591pub struct Config<'d, PIO: Instance> {
592    /// Clock divisor register for state machines.
593    pub clock_divider: FixedU32<U8>,
594    /// Which data bit to use for inline OUT enable.
595    pub out_en_sel: u8,
596    /// Use a bit of OUT data as an auxiliary write enable When used in conjunction with OUT_STICKY.
597    pub inline_out_en: bool,
598    /// Continuously assert the most recent OUT/SET to the pins.
599    pub out_sticky: bool,
600    /// Which source to use for checking status.
601    pub status_sel: StatusSource,
602    /// Status comparison level.
603    #[cfg(feature = "rp2040")]
604    pub status_n: u8,
605    // This cfg probably shouldn't be required, but the SVD for the 2040 doesn't have the enum
606    #[cfg(feature = "_rp235x")]
607    /// Status comparison level.
608    pub status_n: StatusN,
609    exec: ExecConfig,
610    origin: Option<u8>,
611    /// Configure FIFO allocation.
612    pub fifo_join: FifoJoin,
613    /// Input shifting config.
614    pub shift_in: ShiftConfig,
615    /// Output shifting config.
616    pub shift_out: ShiftConfig,
617    // PINCTRL
618    pins: PinConfig,
619    in_count: u8,
620    _pio: PhantomData<&'d mut PIO>,
621}
622
623impl<'d, PIO: Instance> Default for Config<'d, PIO> {
624    fn default() -> Self {
625        Self {
626            clock_divider: 1u8.into(),
627            out_en_sel: Default::default(),
628            inline_out_en: Default::default(),
629            out_sticky: Default::default(),
630            status_sel: Default::default(),
631            status_n: Default::default(),
632            exec: Default::default(),
633            origin: Default::default(),
634            fifo_join: Default::default(),
635            shift_in: Default::default(),
636            shift_out: Default::default(),
637            pins: Default::default(),
638            in_count: Default::default(),
639            _pio: Default::default(),
640        }
641    }
642}
643
644impl<'d, PIO: Instance> Config<'d, PIO> {
645    /// Get execution configuration.
646    pub fn get_exec(&self) -> ExecConfig {
647        self.exec
648    }
649
650    /// Update execution configuration.
651    pub unsafe fn set_exec(&mut self, e: ExecConfig) {
652        self.exec = e;
653    }
654
655    /// Get pin configuration.
656    pub fn get_pins(&self) -> PinConfig {
657        self.pins
658    }
659
660    /// Update pin configuration.
661    pub unsafe fn set_pins(&mut self, p: PinConfig) {
662        self.pins = p;
663    }
664
665    /// Configures this state machine to use the given program, including jumping to the origin
666    /// of the program. The state machine is not started.
667    ///
668    /// `side_set` sets the range of pins affected by side-sets. The range must be consecutive.
669    /// Sideset pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be
670    /// effective.
671    pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) {
672        assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len());
673        assert_consecutive(side_set);
674        self.exec.side_en = prog.side_set.optional();
675        self.exec.side_pindir = prog.side_set.pindirs();
676        self.exec.wrap_bottom = prog.wrap.target;
677        self.exec.wrap_top = prog.wrap.source;
678        self.pins.sideset_count = prog.side_set.bits();
679        self.pins.sideset_base = side_set.first().map_or(0, |p| p.pin());
680        self.origin = Some(prog.origin);
681    }
682
683    /// Set pin used to signal jump.
684    pub fn set_jmp_pin(&mut self, pin: &Pin<'d, PIO>) {
685        self.exec.jmp_pin = pin.pin();
686    }
687
688    /// Sets the range of pins affected by SET instructions. The range must be consecutive.
689    /// Set pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be
690    /// effective.
691    pub fn set_set_pins(&mut self, pins: &[&Pin<'d, PIO>]) {
692        assert!(pins.len() <= 5);
693        assert_consecutive(pins);
694        self.pins.set_base = pins.first().map_or(0, |p| p.pin());
695        self.pins.set_count = pins.len() as u8;
696    }
697
698    /// Sets the range of pins affected by OUT instructions. The range must be consecutive.
699    /// Out pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be
700    /// effective.
701    pub fn set_out_pins(&mut self, pins: &[&Pin<'d, PIO>]) {
702        assert_consecutive(pins);
703        self.pins.out_base = pins.first().map_or(0, |p| p.pin());
704        self.pins.out_count = pins.len() as u8;
705    }
706
707    /// Sets the range of pins used by IN instructions. The range must be consecutive.
708    /// In pins must configured as inputs using [`StateMachine::set_pin_dirs`] to be
709    /// effective.
710    pub fn set_in_pins(&mut self, pins: &[&Pin<'d, PIO>]) {
711        assert_consecutive(pins);
712        self.pins.in_base = pins.first().map_or(0, |p| p.pin());
713        self.in_count = pins.len() as u8;
714    }
715}
716
717impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
718    /// Set the config for a given PIO state machine.
719    pub fn set_config(&mut self, config: &Config<'d, PIO>) {
720        // sm expects 0 for 65536, truncation makes that happen
721        assert!(config.clock_divider <= 65536, "clkdiv must be <= 65536");
722        assert!(config.clock_divider >= 1, "clkdiv must be >= 1");
723        assert!(config.out_en_sel < 32, "out_en_sel must be < 32");
724        //assert!(config.status_n < 32, "status_n must be < 32");
725        // sm expects 0 for 32, truncation makes that happen
726        assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32");
727        assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32");
728        let sm = Self::this_sm();
729        sm.clkdiv().write(|w| w.0 = config.clock_divider.to_bits() << 8);
730        sm.execctrl().write(|w| {
731            w.set_side_en(config.exec.side_en);
732            w.set_side_pindir(config.exec.side_pindir);
733            w.set_jmp_pin(config.exec.jmp_pin);
734            w.set_out_en_sel(config.out_en_sel);
735            w.set_inline_out_en(config.inline_out_en);
736            w.set_out_sticky(config.out_sticky);
737            w.set_wrap_top(config.exec.wrap_top);
738            w.set_wrap_bottom(config.exec.wrap_bottom);
739            #[cfg(feature = "_rp235x")]
740            w.set_status_sel(match config.status_sel {
741                StatusSource::TxFifoLevel => pac::pio::vals::ExecctrlStatusSel::TXLEVEL,
742                StatusSource::RxFifoLevel => pac::pio::vals::ExecctrlStatusSel::RXLEVEL,
743                StatusSource::Irq => pac::pio::vals::ExecctrlStatusSel::IRQ,
744            });
745            #[cfg(feature = "rp2040")]
746            w.set_status_sel(match config.status_sel {
747                StatusSource::TxFifoLevel => pac::pio::vals::SmExecctrlStatusSel::TXLEVEL,
748                StatusSource::RxFifoLevel => pac::pio::vals::SmExecctrlStatusSel::RXLEVEL,
749            });
750            w.set_status_n(config.status_n.into());
751        });
752        sm.shiftctrl().write(|w| {
753            w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly);
754            w.set_fjoin_tx(config.fifo_join == FifoJoin::TxOnly);
755            w.set_pull_thresh(config.shift_out.threshold);
756            w.set_push_thresh(config.shift_in.threshold);
757            w.set_out_shiftdir(config.shift_out.direction == ShiftDirection::Right);
758            w.set_in_shiftdir(config.shift_in.direction == ShiftDirection::Right);
759            w.set_autopull(config.shift_out.auto_fill);
760            w.set_autopush(config.shift_in.auto_fill);
761
762            #[cfg(feature = "_rp235x")]
763            {
764                w.set_fjoin_rx_get(
765                    config.fifo_join == FifoJoin::RxAsControl || config.fifo_join == FifoJoin::PioScratch,
766                );
767                w.set_fjoin_rx_put(
768                    config.fifo_join == FifoJoin::RxAsStatus || config.fifo_join == FifoJoin::PioScratch,
769                );
770                w.set_in_count(config.in_count);
771            }
772        });
773
774        #[cfg(feature = "rp2040")]
775        sm.pinctrl().write(|w| {
776            w.set_sideset_count(config.pins.sideset_count);
777            w.set_set_count(config.pins.set_count);
778            w.set_out_count(config.pins.out_count);
779            w.set_in_base(config.pins.in_base);
780            w.set_sideset_base(config.pins.sideset_base);
781            w.set_set_base(config.pins.set_base);
782            w.set_out_base(config.pins.out_base);
783        });
784
785        #[cfg(feature = "_rp235x")]
786        {
787            let mut low_ok = true;
788            let mut high_ok = true;
789
790            let in_pins = config.pins.in_base..config.pins.in_base + config.in_count;
791            let side_pins = config.pins.sideset_base..config.pins.sideset_base + config.pins.sideset_count;
792            let set_pins = config.pins.set_base..config.pins.set_base + config.pins.set_count;
793            let out_pins = config.pins.out_base..config.pins.out_base + config.pins.out_count;
794
795            for pin_range in [in_pins, side_pins, set_pins, out_pins] {
796                for pin in pin_range {
797                    low_ok &= pin < 32;
798                    high_ok &= pin >= 16;
799                }
800            }
801
802            if !low_ok && !high_ok {
803                panic!(
804                    "All pins must either be <32 or >=16, in:{:?}-{:?}, side:{:?}-{:?}, set:{:?}-{:?}, out:{:?}-{:?}",
805                    config.pins.in_base,
806                    config.pins.in_base + config.in_count - 1,
807                    config.pins.sideset_base,
808                    config.pins.sideset_base + config.pins.sideset_count - 1,
809                    config.pins.set_base,
810                    config.pins.set_base + config.pins.set_count - 1,
811                    config.pins.out_base,
812                    config.pins.out_base + config.pins.out_count - 1,
813                )
814            }
815            let shift = if low_ok { 0 } else { 16 };
816
817            sm.pinctrl().write(|w| {
818                w.set_sideset_count(config.pins.sideset_count);
819                w.set_set_count(config.pins.set_count);
820                w.set_out_count(config.pins.out_count);
821                w.set_in_base(config.pins.in_base.checked_sub(shift).unwrap_or_default());
822                w.set_sideset_base(config.pins.sideset_base.checked_sub(shift).unwrap_or_default());
823                w.set_set_base(config.pins.set_base.checked_sub(shift).unwrap_or_default());
824                w.set_out_base(config.pins.out_base.checked_sub(shift).unwrap_or_default());
825            });
826
827            PIO::PIO.gpiobase().write(|w| w.set_gpiobase(shift == 16));
828        }
829
830        if let Some(origin) = config.origin {
831            unsafe { self.exec_jmp(origin) }
832        }
833    }
834
835    /// Get pointer to rx fifo
836    pub fn rx_fifo_ptr(&self) -> *mut u32 {
837        PIO::PIO.rxf(SM).as_ptr()
838    }
839
840    /// Get pointer to tx fifo
841    pub fn tx_fifo_ptr(&self) -> *mut u32 {
842        PIO::PIO.txf(SM).as_ptr()
843    }
844
845    /// Get dma Treq of rx fifo
846    pub fn rx_treq(&self) -> crate::pac::dma::vals::TreqSel {
847        StateMachineRx::<PIO, SM>::dreq()
848    }
849
850    /// Get dma Treq of tx fifo
851    pub fn tx_treq(&self) -> crate::pac::dma::vals::TreqSel {
852        StateMachineTx::<PIO, SM>::dreq()
853    }
854
855    /// Read current instruction address for this state machine
856    pub fn get_addr(&self) -> u8 {
857        let addr = Self::this_sm().addr();
858        addr.read().addr()
859    }
860
861    /// Read TX FIFO threshold for this state machine.
862    pub fn get_tx_threshold(&self) -> u8 {
863        let shiftctrl = Self::this_sm().shiftctrl();
864        shiftctrl.read().pull_thresh()
865    }
866
867    /// Set/change the TX FIFO threshold for this state machine.
868    pub fn set_tx_threshold(&mut self, threshold: u8) {
869        assert!(threshold <= 31);
870        let shiftctrl = Self::this_sm().shiftctrl();
871        shiftctrl.modify(|w| {
872            w.set_pull_thresh(threshold);
873        });
874    }
875
876    /// Read TX FIFO threshold for this state machine.
877    pub fn get_rx_threshold(&self) -> u8 {
878        Self::this_sm().shiftctrl().read().push_thresh()
879    }
880
881    /// Set/change the RX FIFO threshold for this state machine.
882    pub fn set_rx_threshold(&mut self, threshold: u8) {
883        assert!(threshold <= 31);
884        let shiftctrl = Self::this_sm().shiftctrl();
885        shiftctrl.modify(|w| {
886            w.set_push_thresh(threshold);
887        });
888    }
889
890    /// Set/change both TX and RX FIFO thresholds for this state machine.
891    pub fn set_thresholds(&mut self, threshold: u8) {
892        assert!(threshold <= 31);
893        let shiftctrl = Self::this_sm().shiftctrl();
894        shiftctrl.modify(|w| {
895            w.set_push_thresh(threshold);
896            w.set_pull_thresh(threshold);
897        });
898    }
899
900    /// Set the clock divider for this state machine.
901    pub fn set_clock_divider(&mut self, clock_divider: FixedU32<U8>) {
902        let sm = Self::this_sm();
903        sm.clkdiv().write(|w| w.0 = clock_divider.to_bits() << 8);
904    }
905
906    #[inline(always)]
907    fn this_sm() -> crate::pac::pio::StateMachine {
908        PIO::PIO.sm(SM)
909    }
910
911    /// Restart this state machine.
912    pub fn restart(&mut self) {
913        let mask = 1u8 << SM;
914        PIO::PIO.ctrl().write_set(|w| w.set_sm_restart(mask));
915    }
916
917    /// Enable state machine.
918    pub fn set_enable(&mut self, enable: bool) {
919        let mask = 1u8 << SM;
920        if enable {
921            PIO::PIO.ctrl().write_set(|w| w.set_sm_enable(mask));
922        } else {
923            PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask));
924        }
925    }
926
927    /// Check if state machine is enabled.
928    pub fn is_enabled(&self) -> bool {
929        PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0
930    }
931
932    /// Restart a state machine's clock divider from an initial phase of 0.
933    pub fn clkdiv_restart(&mut self) {
934        let mask = 1u8 << SM;
935        PIO::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask));
936    }
937
938    fn with_paused(&mut self, f: impl FnOnce(&mut Self)) {
939        let enabled = self.is_enabled();
940        self.set_enable(false);
941        let pincfg = Self::this_sm().pinctrl().read();
942        let execcfg = Self::this_sm().execctrl().read();
943        Self::this_sm().execctrl().write_clear(|w| w.set_out_sticky(true));
944        f(self);
945        Self::this_sm().pinctrl().write_value(pincfg);
946        Self::this_sm().execctrl().write_value(execcfg);
947        self.set_enable(enabled);
948    }
949
950    #[cfg(feature = "rp2040")]
951    fn pin_base() -> u8 {
952        0
953    }
954
955    #[cfg(feature = "_rp235x")]
956    fn pin_base() -> u8 {
957        if PIO::PIO.gpiobase().read().gpiobase() { 16 } else { 0 }
958    }
959
960    /// Sets pin directions. This pauses the current state machine to run `SET` commands
961    /// and temporarily unsets the `OUT_STICKY` bit.
962    pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) {
963        self.with_paused(|sm| {
964            for pin in pins {
965                Self::this_sm().pinctrl().write(|w| {
966                    w.set_set_base(pin.pin() - Self::pin_base());
967                    w.set_set_count(1);
968                });
969                // SET PINDIRS, (dir)
970                unsafe { sm.exec_instr(0b111_00000_100_00000 | dir as u16) };
971            }
972        });
973    }
974
975    /// Sets pin output values. This pauses the current state machine to run
976    /// `SET` commands and temporarily unsets the `OUT_STICKY` bit.
977    pub fn set_pins(&mut self, level: Level, pins: &[&Pin<'d, PIO>]) {
978        self.with_paused(|sm| {
979            for pin in pins {
980                Self::this_sm().pinctrl().write(|w| {
981                    w.set_set_base(pin.pin() - Self::pin_base());
982                    w.set_set_count(1);
983                });
984                // SET PINS, (dir)
985                unsafe { sm.exec_instr(0b11100_000_000_00000 | level as u16) };
986            }
987        });
988    }
989
990    /// Flush FIFOs for state machine.
991    pub fn clear_fifos(&mut self) {
992        // Toggle FJOIN_RX to flush FIFOs
993        let shiftctrl = Self::this_sm().shiftctrl();
994        shiftctrl.modify(|w| {
995            w.set_fjoin_rx(!w.fjoin_rx());
996        });
997        shiftctrl.modify(|w| {
998            w.set_fjoin_rx(!w.fjoin_rx());
999        });
1000    }
1001
1002    /// Instruct state machine to execute a given instructions
1003    ///
1004    /// SAFETY: The state machine must be in a state where executing
1005    /// an arbitrary instruction does not crash it.
1006    pub unsafe fn exec_instr(&mut self, instr: u16) {
1007        Self::this_sm().instr().write(|w| w.set_instr(instr));
1008    }
1009
1010    /// Return a read handle for reading state machine outputs.
1011    pub fn rx(&mut self) -> &mut StateMachineRx<'d, PIO, SM> {
1012        &mut self.rx
1013    }
1014
1015    /// Return a handle for writing to inputs.
1016    pub fn tx(&mut self) -> &mut StateMachineTx<'d, PIO, SM> {
1017        &mut self.tx
1018    }
1019
1020    /// Return both read and write handles for the state machine.
1021    pub fn rx_tx(&mut self) -> (&mut StateMachineRx<'d, PIO, SM>, &mut StateMachineTx<'d, PIO, SM>) {
1022        (&mut self.rx, &mut self.tx)
1023    }
1024
1025    /// Return the contents of the nth entry of the RX FIFO
1026    /// (should be used only when the FIFO config is set to [`FifoJoin::RxAsStatus`])
1027    #[cfg(feature = "_rp235x")]
1028    pub fn get_rxf_entry(&self, n: usize) -> u32 {
1029        PIO::PIO.rxf_putget(SM).putget(n).read()
1030    }
1031
1032    /// Set the contents of the nth entry of the RX FIFO
1033    /// (should be used only when the FIFO config is set to [`FifoJoin::RxAsControl`])
1034    #[cfg(feature = "_rp235x")]
1035    pub fn set_rxf_entry(&self, n: usize, val: u32) {
1036        PIO::PIO.rxf_putget(SM).putget(n).write_value(val)
1037    }
1038}
1039
1040/// PIO handle.
1041pub struct Common<'d, PIO: Instance> {
1042    instructions_used: u32,
1043    pio: PhantomData<&'d mut PIO>,
1044}
1045
1046impl<'d, PIO: Instance> Drop for Common<'d, PIO> {
1047    fn drop(&mut self) {
1048        on_pio_drop::<PIO>();
1049    }
1050}
1051
1052/// Memory of PIO instance.
1053pub struct InstanceMemory<'d, PIO: Instance> {
1054    used_mask: u32,
1055    pio: PhantomData<&'d mut PIO>,
1056}
1057
1058/// A loaded PIO program.
1059pub struct LoadedProgram<'d, PIO: Instance> {
1060    /// Memory used by program.
1061    pub used_memory: InstanceMemory<'d, PIO>,
1062    /// Program origin for loading.
1063    pub origin: u8,
1064    /// Wrap controls what to do once program is done executing.
1065    pub wrap: Wrap,
1066    /// Data for 'side' set instruction parameters.
1067    pub side_set: SideSet,
1068}
1069
1070/// Errors loading a PIO program.
1071#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1072#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1073pub enum LoadError {
1074    /// Insufficient consecutive free instruction space to load program.
1075    InsufficientSpace,
1076    /// Loading the program would overwrite an instruction address already
1077    /// used by another program.
1078    AddressInUse(usize),
1079}
1080
1081impl<'d, PIO: Instance> Common<'d, PIO> {
1082    /// Load a PIO program. This will automatically relocate the program to
1083    /// an available chunk of free instruction memory if the program origin
1084    /// was not explicitly specified, otherwise it will attempt to load the
1085    /// program only at its origin.
1086    pub fn load_program<const SIZE: usize>(&mut self, prog: &Program<SIZE>) -> LoadedProgram<'d, PIO> {
1087        match self.try_load_program(prog) {
1088            Ok(r) => r,
1089            Err(e) => panic!("Failed to load PIO program: {:?}", e),
1090        }
1091    }
1092
1093    /// Load a PIO program. This will automatically relocate the program to
1094    /// an available chunk of free instruction memory if the program origin
1095    /// was not explicitly specified, otherwise it will attempt to load the
1096    /// program only at its origin.
1097    pub fn try_load_program<const SIZE: usize>(
1098        &mut self,
1099        prog: &Program<SIZE>,
1100    ) -> Result<LoadedProgram<'d, PIO>, LoadError> {
1101        match prog.origin {
1102            Some(origin) => self.try_load_program_at(prog, origin).map_err(LoadError::AddressInUse),
1103            None => {
1104                // naively search for free space, allowing wraparound since
1105                // PIO does support that. with only 32 instruction slots it
1106                // doesn't make much sense to do anything more fancy.
1107                let mut origin = 0;
1108                while origin < 32 {
1109                    match self.try_load_program_at(prog, origin as _) {
1110                        Ok(r) => return Ok(r),
1111                        Err(a) => origin = a + 1,
1112                    }
1113                }
1114                Err(LoadError::InsufficientSpace)
1115            }
1116        }
1117    }
1118
1119    fn try_load_program_at<const SIZE: usize>(
1120        &mut self,
1121        prog: &Program<SIZE>,
1122        origin: u8,
1123    ) -> Result<LoadedProgram<'d, PIO>, usize> {
1124        #[cfg(not(feature = "_rp235x"))]
1125        assert!(prog.version == pio::PioVersion::V0);
1126
1127        let prog = RelocatedProgram::new_with_origin(prog, origin);
1128        let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?;
1129        Ok(LoadedProgram {
1130            used_memory,
1131            origin: prog.origin(),
1132            wrap: prog.wrap(),
1133            side_set: prog.side_set(),
1134        })
1135    }
1136
1137    fn try_write_instr<I>(&mut self, start: usize, instrs: I) -> Result<InstanceMemory<'d, PIO>, usize>
1138    where
1139        I: Iterator<Item = u16>,
1140    {
1141        let mut used_mask = 0;
1142        for (i, instr) in instrs.enumerate() {
1143            // wrapping around the end of program memory is valid, let's make use of that.
1144            let addr = (i + start) % 32;
1145            let mask = 1 << addr;
1146            if (self.instructions_used | used_mask) & mask != 0 {
1147                return Err(addr);
1148            }
1149            PIO::PIO.instr_mem(addr).write(|w| {
1150                w.set_instr_mem(instr);
1151            });
1152            used_mask |= mask;
1153        }
1154        self.instructions_used |= used_mask;
1155        Ok(InstanceMemory {
1156            used_mask,
1157            pio: PhantomData,
1158        })
1159    }
1160
1161    /// Free instruction memory. This is always possible but unsafe if any
1162    /// state machine is still using this bit of memory.
1163    pub unsafe fn free_instr(&mut self, instrs: InstanceMemory<PIO>) {
1164        self.instructions_used &= !instrs.used_mask;
1165    }
1166
1167    /// Bypass flipflop synchronizer on GPIO inputs.
1168    pub fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) {
1169        // this can interfere with per-pin bypass functions. splitting the
1170        // modification is going to be fine since nothing that relies on
1171        // it can reasonably run before we finish.
1172        PIO::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass);
1173        PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass);
1174    }
1175
1176    /// Get bypass configuration.
1177    pub fn get_input_sync_bypass(&self) -> u32 {
1178        PIO::PIO.input_sync_bypass().read()
1179    }
1180
1181    /// Register a pin for PIO usage. Pins will be released from the PIO block
1182    /// (i.e., have their `FUNCSEL` reset to `NULL`) when the [`Common`] *and*
1183    /// all [`StateMachine`]s for this block have been dropped. **Other members
1184    /// of [`Pio`] do not keep pin registrations alive.**
1185    pub fn make_pio_pin(&mut self, pin: Peri<'d, impl PioPin + 'd>) -> Pin<'d, PIO> {
1186        // enable the outputs
1187        pin.pad_ctrl().write(|w| w.set_od(false));
1188        // especially important on the 235x, where IE defaults to 0
1189        pin.pad_ctrl().write(|w| w.set_ie(true));
1190
1191        pin.gpio().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _));
1192        pin.pad_ctrl().write(|w| {
1193            #[cfg(feature = "_rp235x")]
1194            w.set_iso(false);
1195            w.set_schmitt(true);
1196            w.set_slewfast(false);
1197            // TODO rp235x errata E9 recommends to not enable IE if we're not
1198            // going to use input. Maybe add an API for the user to enable/disable this?
1199            w.set_ie(true);
1200            w.set_od(false);
1201            w.set_pue(false);
1202            w.set_pde(false);
1203        });
1204        // we can be relaxed about this because we're &mut here and nothing is cached
1205        critical_section::with(|_| {
1206            let val = PIO::state().used_pins.load(Ordering::Relaxed);
1207            PIO::state()
1208                .used_pins
1209                .store(val | 1 << pin.pin_bank(), Ordering::Relaxed);
1210        });
1211
1212        Pin {
1213            pin: pin.into(),
1214            pio: PhantomData::default(),
1215        }
1216    }
1217}
1218
1219/// Represents multiple state machines in a single type.
1220pub struct PioBatch<'a, PIO: Instance> {
1221    clkdiv_restart: u8,
1222    sm_restart: u8,
1223    sm_enable_mask: u8,
1224    sm_enable: u8,
1225    _pio: PhantomData<&'a PIO>,
1226}
1227
1228impl<'a, PIO: Instance> PioBatch<'a, PIO> {
1229    /// Create nop PioBatch object
1230    pub fn new() -> Self {
1231        Self {
1232            clkdiv_restart: 0,
1233            sm_restart: 0,
1234            sm_enable_mask: 0,
1235            sm_enable: 0,
1236            _pio: PhantomData,
1237        }
1238    }
1239
1240    /// Restart a state machine's clock divider from an initial phase of 0.
1241    pub fn restart<const SM: usize>(&mut self, _sm: &mut StateMachine<'a, PIO, SM>) {
1242        self.clkdiv_restart |= 1 << SM;
1243    }
1244
1245    /// Enable a specific state machine.
1246    pub fn set_enable<const SM: usize>(&mut self, _sm: &mut StateMachine<'a, PIO, SM>, enable: bool) {
1247        self.sm_enable_mask |= 1 << SM;
1248        self.sm_enable |= (enable as u8) << SM;
1249    }
1250
1251    /// Apply changes to state machines in a batch.
1252    pub fn execute(&mut self) {
1253        PIO::PIO.ctrl().modify(|w| {
1254            w.set_clkdiv_restart(self.clkdiv_restart);
1255            w.set_sm_restart(self.sm_restart);
1256            w.set_sm_enable((w.sm_enable() & !self.sm_enable_mask) | self.sm_enable);
1257        });
1258    }
1259}
1260
1261/// Type representing a PIO interrupt.
1262pub struct Irq<'d, PIO: Instance, const N: usize> {
1263    pio: PhantomData<&'d mut PIO>,
1264}
1265
1266impl<'d, PIO: Instance, const N: usize> Irq<'d, PIO, N> {
1267    /// Wait for an IRQ to fire.
1268    pub fn wait<'a>(&'a mut self) -> IrqFuture<'a, 'd, PIO> {
1269        IrqFuture {
1270            pio: PhantomData,
1271            irq_no: N as u8,
1272        }
1273    }
1274}
1275
1276/// Interrupt flags for a PIO instance.
1277#[derive(Clone)]
1278pub struct IrqFlags<'d, PIO: Instance> {
1279    pio: PhantomData<&'d mut PIO>,
1280}
1281
1282impl<'d, PIO: Instance> IrqFlags<'d, PIO> {
1283    /// Check if interrupt fired.
1284    pub fn check(&self, irq_no: u8) -> bool {
1285        assert!(irq_no < 8);
1286        self.check_any(1 << irq_no)
1287    }
1288
1289    /// Check if any of the interrupts in the bitmap fired.
1290    pub fn check_any(&self, irqs: u8) -> bool {
1291        PIO::PIO.irq().read().irq() & irqs != 0
1292    }
1293
1294    /// Check if all interrupts have fired.
1295    pub fn check_all(&self, irqs: u8) -> bool {
1296        PIO::PIO.irq().read().irq() & irqs == irqs
1297    }
1298
1299    /// Clear interrupt for interrupt number.
1300    pub fn clear(&self, irq_no: usize) {
1301        assert!(irq_no < 8);
1302        self.clear_all(1 << irq_no);
1303    }
1304
1305    /// Clear all interrupts set in the bitmap.
1306    pub fn clear_all(&self, irqs: u8) {
1307        PIO::PIO.irq().write(|w| w.set_irq(irqs))
1308    }
1309
1310    /// Fire a given interrupt.
1311    pub fn set(&self, irq_no: usize) {
1312        assert!(irq_no < 8);
1313        self.set_all(1 << irq_no);
1314    }
1315
1316    /// Fire all interrupts.
1317    pub fn set_all(&self, irqs: u8) {
1318        PIO::PIO.irq_force().write(|w| w.set_irq_force(irqs))
1319    }
1320}
1321
1322/// An instance of the PIO driver.
1323pub struct Pio<'d, PIO: Instance> {
1324    /// PIO handle.
1325    pub common: Common<'d, PIO>,
1326    /// PIO IRQ flags.
1327    pub irq_flags: IrqFlags<'d, PIO>,
1328    /// IRQ0 configuration.
1329    pub irq0: Irq<'d, PIO, 0>,
1330    /// IRQ1 configuration.
1331    pub irq1: Irq<'d, PIO, 1>,
1332    /// IRQ2 configuration.
1333    pub irq2: Irq<'d, PIO, 2>,
1334    /// IRQ3 configuration.
1335    pub irq3: Irq<'d, PIO, 3>,
1336    /// State machine 0 handle.
1337    pub sm0: StateMachine<'d, PIO, 0>,
1338    /// State machine 1 handle.
1339    pub sm1: StateMachine<'d, PIO, 1>,
1340    /// State machine 2 handle.
1341    pub sm2: StateMachine<'d, PIO, 2>,
1342    /// State machine 3 handle.
1343    pub sm3: StateMachine<'d, PIO, 3>,
1344    _pio: PhantomData<&'d mut PIO>,
1345}
1346
1347impl<'d, PIO: Instance> Pio<'d, PIO> {
1348    /// Create a new instance of a PIO peripheral.
1349    pub fn new(_pio: Peri<'d, PIO>, _irq: impl Binding<PIO::Interrupt, InterruptHandler<PIO>>) -> Self {
1350        PIO::state().users.store(5, Ordering::Release);
1351        PIO::state().used_pins.store(0, Ordering::Release);
1352        PIO::Interrupt::unpend();
1353
1354        unsafe { PIO::Interrupt::enable() };
1355        Self {
1356            common: Common {
1357                instructions_used: 0,
1358                pio: PhantomData,
1359            },
1360            irq_flags: IrqFlags { pio: PhantomData },
1361            irq0: Irq { pio: PhantomData },
1362            irq1: Irq { pio: PhantomData },
1363            irq2: Irq { pio: PhantomData },
1364            irq3: Irq { pio: PhantomData },
1365            sm0: StateMachine {
1366                rx: StateMachineRx { pio: PhantomData },
1367                tx: StateMachineTx { pio: PhantomData },
1368            },
1369            sm1: StateMachine {
1370                rx: StateMachineRx { pio: PhantomData },
1371                tx: StateMachineTx { pio: PhantomData },
1372            },
1373            sm2: StateMachine {
1374                rx: StateMachineRx { pio: PhantomData },
1375                tx: StateMachineTx { pio: PhantomData },
1376            },
1377            sm3: StateMachine {
1378                rx: StateMachineRx { pio: PhantomData },
1379                tx: StateMachineTx { pio: PhantomData },
1380            },
1381            _pio: PhantomData,
1382        }
1383    }
1384}
1385
1386struct AtomicU64 {
1387    upper_32: AtomicU32,
1388    lower_32: AtomicU32,
1389}
1390
1391impl AtomicU64 {
1392    const fn new(val: u64) -> Self {
1393        let upper_32 = (val >> 32) as u32;
1394        let lower_32 = val as u32;
1395
1396        Self {
1397            upper_32: AtomicU32::new(upper_32),
1398            lower_32: AtomicU32::new(lower_32),
1399        }
1400    }
1401
1402    fn load(&self, order: Ordering) -> u64 {
1403        let (upper, lower) = critical_section::with(|_| (self.upper_32.load(order), self.lower_32.load(order)));
1404
1405        let upper = (upper as u64) << 32;
1406        let lower = lower as u64;
1407
1408        upper | lower
1409    }
1410
1411    fn store(&self, val: u64, order: Ordering) {
1412        let upper_32 = (val >> 32) as u32;
1413        let lower_32 = val as u32;
1414
1415        critical_section::with(|_| {
1416            self.upper_32.store(upper_32, order);
1417            self.lower_32.store(lower_32, order);
1418        });
1419    }
1420}
1421
1422/// Representation of the PIO state keeping a record of which pins are assigned to
1423/// each PIO.
1424// make_pio_pin notionally takes ownership of the pin it is given, but the wrapped pin
1425// cannot be treated as an owned resource since dropping it would have to deconfigure
1426// the pin, breaking running state machines in the process. pins are also shared
1427// between all state machines, which makes ownership even messier to track any
1428// other way.
1429pub struct State {
1430    users: AtomicU8,
1431    used_pins: AtomicU64,
1432}
1433
1434fn on_pio_drop<PIO: Instance>() {
1435    let state = PIO::state();
1436    let users_state = critical_section::with(|_| {
1437        let val = state.users.load(Ordering::Acquire);
1438        state.users.store(val - 1, Ordering::Release);
1439        val
1440    });
1441    if users_state == 1 {
1442        let used_pins = state.used_pins.load(Ordering::Relaxed);
1443        let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _;
1444        for i in 0..crate::gpio::BANK0_PIN_COUNT {
1445            if used_pins & (1 << i) != 0 {
1446                pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null));
1447            }
1448        }
1449    }
1450}
1451
1452trait SealedInstance {
1453    const PIO_NO: u8;
1454    const PIO: &'static crate::pac::pio::Pio;
1455    const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel;
1456
1457    #[inline]
1458    fn wakers() -> &'static Wakers {
1459        static WAKERS: Wakers = Wakers([const { AtomicWaker::new() }; 12]);
1460        &WAKERS
1461    }
1462
1463    #[inline]
1464    fn state() -> &'static State {
1465        static STATE: State = State {
1466            users: AtomicU8::new(0),
1467            used_pins: AtomicU64::new(0),
1468        };
1469
1470        &STATE
1471    }
1472}
1473
1474/// PIO instance.
1475#[allow(private_bounds)]
1476pub trait Instance: SealedInstance + PeripheralType + Sized + Unpin {
1477    /// Interrupt for this peripheral.
1478    type Interrupt: crate::interrupt::typelevel::Interrupt;
1479}
1480
1481macro_rules! impl_pio {
1482    ($name:ident, $pio:expr, $pac:ident, $funcsel:ident, $irq:ident) => {
1483        impl SealedInstance for peripherals::$name {
1484            const PIO_NO: u8 = $pio;
1485            const PIO: &'static pac::pio::Pio = &pac::$pac;
1486            const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel;
1487        }
1488        impl Instance for peripherals::$name {
1489            type Interrupt = crate::interrupt::typelevel::$irq;
1490        }
1491    };
1492}
1493
1494impl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0);
1495impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0);
1496#[cfg(feature = "_rp235x")]
1497impl_pio!(PIO2, 2, PIO2, PIO2_0, PIO2_IRQ_0);
1498
1499/// PIO pin.
1500pub trait PioPin: gpio::Pin {}
1501
1502macro_rules! impl_pio_pin {
1503    ($( $pin:ident, )*) => {
1504        $(
1505            impl PioPin for peripherals::$pin {}
1506        )*
1507    };
1508}
1509
1510impl_pio_pin! {
1511    PIN_0,
1512    PIN_1,
1513    PIN_2,
1514    PIN_3,
1515    PIN_4,
1516    PIN_5,
1517    PIN_6,
1518    PIN_7,
1519    PIN_8,
1520    PIN_9,
1521    PIN_10,
1522    PIN_11,
1523    PIN_12,
1524    PIN_13,
1525    PIN_14,
1526    PIN_15,
1527    PIN_16,
1528    PIN_17,
1529    PIN_18,
1530    PIN_19,
1531    PIN_20,
1532    PIN_21,
1533    PIN_22,
1534    PIN_23,
1535    PIN_24,
1536    PIN_25,
1537    PIN_26,
1538    PIN_27,
1539    PIN_28,
1540    PIN_29,
1541}
1542
1543#[cfg(feature = "rp235xb")]
1544impl_pio_pin! {
1545    PIN_30,
1546    PIN_31,
1547    PIN_32,
1548    PIN_33,
1549    PIN_34,
1550    PIN_35,
1551    PIN_36,
1552    PIN_37,
1553    PIN_38,
1554    PIN_39,
1555    PIN_40,
1556    PIN_41,
1557    PIN_42,
1558    PIN_43,
1559    PIN_44,
1560    PIN_45,
1561    PIN_46,
1562    PIN_47,
1563}