Skip to main content

rp235x_hal/
sio.rs

1//! Single Cycle Input and Output (SIO)
2//!
3//! To be able to partition parts of the SIO block to other modules:
4//!
5//! ```no_run
6//! use rp235x_hal::{self as hal, gpio::Pins, sio::Sio};
7//!
8//! let mut peripherals = hal::pac::Peripherals::take().unwrap();
9//! let sio = Sio::new(peripherals.SIO);
10//! ```
11//!
12//! And then for example
13//!
14//! ```no_run
15//! # use rp235x_hal::{self as hal, gpio::Pins, sio::Sio};
16//! # let mut peripherals = hal::pac::Peripherals::take().unwrap();
17//! # let sio = Sio::new(peripherals.SIO);
18//! let pins = Pins::new(
19//!     peripherals.IO_BANK0,
20//!     peripherals.PADS_BANK0,
21//!     sio.gpio_bank0,
22//!     &mut peripherals.RESETS,
23//! );
24//! ```
25
26use crate::typelevel::Sealed;
27
28use super::*;
29use core::convert::Infallible;
30
31/// Id of the core.
32#[repr(u8)]
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35pub enum CoreId {
36    #[allow(missing_docs)]
37    Core0 = 0,
38    #[allow(missing_docs)]
39    Core1 = 1,
40}
41
42/// Marker struct for ownership of SIO gpio bank0
43#[derive(Debug)]
44pub struct SioGpioBank0 {
45    _private: (),
46}
47
48/// Marker struct for ownership of SIO FIFO
49#[derive(Debug)]
50pub struct SioFifo {
51    _private: (),
52}
53
54/// Marker struct for ownership of SIO gpio qspi
55#[derive(Debug)]
56pub struct SioGpioQspi {
57    _private: (),
58}
59
60/// Marker struct for ownership of SIO Machine Timer
61pub struct MachineTimer {
62    _private: (),
63}
64
65/// Struct containing ownership markers for managing ownership of the SIO registers.
66pub struct Sio {
67    _sio: pac::SIO,
68    /// GPIO Bank 0 registers
69    pub gpio_bank0: SioGpioBank0,
70    /// GPIO QSPI registers
71    pub gpio_qspi: SioGpioQspi,
72    /// Inter-core FIFO
73    pub fifo: SioFifo,
74    /// Interpolator 0
75    pub interp0: Interp0,
76    /// Interpolator 1
77    pub interp1: Interp1,
78    /// RISC-V Machine Timer
79    pub machine_timer: MachineTimer,
80}
81
82impl Sio {
83    /// Create `Sio` from the PAC.
84    pub fn new(sio: pac::SIO) -> Self {
85        Self {
86            _sio: sio,
87            gpio_bank0: SioGpioBank0 { _private: () },
88            gpio_qspi: SioGpioQspi { _private: () },
89            fifo: SioFifo { _private: () },
90            interp0: Interp0 {
91                lane0: Interp0Lane0 { _private: () },
92                lane1: Interp0Lane1 { _private: () },
93            },
94            interp1: Interp1 {
95                lane0: Interp1Lane0 { _private: () },
96                lane1: Interp1Lane1 { _private: () },
97            },
98            machine_timer: MachineTimer { _private: () },
99        }
100    }
101
102    /// Reads the whole bank0 at once.
103    pub fn read_bank0() -> u32 {
104        unsafe { (*pac::SIO::PTR).gpio_in().read().bits() }
105    }
106
107    /// Returns whether we are running on Core 0 (`0`) or Core 1 (`1`).
108    pub fn core() -> CoreId {
109        // Safety: it is always safe to read this read-only register
110        match unsafe { (*pac::SIO::ptr()).cpuid().read().bits() as u8 } {
111            0 => CoreId::Core0,
112            1 => CoreId::Core1,
113            _ => unreachable!("This MCU only has 2 cores."),
114        }
115    }
116}
117
118impl SioFifo {
119    /// Check if the inter-core FIFO has valid data for reading.
120    ///
121    /// Returning `true` means there is valid data, `false` means it is empty
122    /// and you must not read from it.
123    pub fn is_read_ready(&mut self) -> bool {
124        let sio = unsafe { &(*pac::SIO::ptr()) };
125        sio.fifo_st().read().vld().bit_is_set()
126    }
127
128    /// Check if the inter-core FIFO is ready to receive data.
129    ///
130    /// Returning `true` means there is room, `false` means it is full and you
131    /// must not write to it.
132    pub fn is_write_ready(&mut self) -> bool {
133        let sio = unsafe { &(*pac::SIO::ptr()) };
134        sio.fifo_st().read().rdy().bit_is_set()
135    }
136
137    /// Return the FIFO status, as an integer.
138    pub fn status(&self) -> u32 {
139        let sio = unsafe { &(*pac::SIO::ptr()) };
140        sio.fifo_st().read().bits()
141    }
142
143    /// Write to the inter-core FIFO.
144    ///
145    /// You must ensure the FIFO has space by calling `is_write_ready`
146    pub fn write(&mut self, value: u32) {
147        let sio = unsafe { &(*pac::SIO::ptr()) };
148        sio.fifo_wr().write(|w| unsafe { w.bits(value) });
149        // Fire off an event to the other core.
150        // This is required as the other core may be `wfe` (waiting for event)
151        crate::arch::sev();
152    }
153
154    /// Read from the inter-core FIFO.
155    ///
156    /// Will return `Some(data)`, or `None` if the FIFO is empty.
157    pub fn read(&mut self) -> Option<u32> {
158        if self.is_read_ready() {
159            let sio = unsafe { &(*pac::SIO::ptr()) };
160            Some(sio.fifo_rd().read().bits())
161        } else {
162            None
163        }
164    }
165
166    /// Read from the FIFO until it is empty, throwing the contents away.
167    pub fn drain(&mut self) {
168        while self.read().is_some() {
169            // Retry until FIFO empty
170        }
171    }
172
173    /// Push to the FIFO, spinning if there's no space.
174    pub fn write_blocking(&mut self, value: u32) {
175        // We busy-wait for the FIFO to have some space
176        while !self.is_write_ready() {
177            crate::arch::nop();
178        }
179
180        // Write the value to the FIFO - the other core will now be able to
181        // pop it off its end of the FIFO.
182        self.write(value);
183
184        // Fire off an event to the other core
185        crate::arch::sev();
186    }
187
188    /// Pop from the FIFO, spinning if there's currently no data.
189    pub fn read_blocking(&mut self) -> u32 {
190        // Keep trying until FIFO has data
191        loop {
192            // Have we got something?
193            if let Some(data) = self.read() {
194                // Yes, return it right away
195                return data;
196            } else {
197                // No, so sleep the CPU. We expect the sending core to `sev`
198                // on write.
199                crate::arch::wfe();
200            }
201        }
202    }
203}
204
205impl MachineTimer {
206    /// Read the SIO's Machine Timer
207    pub fn read(&self) -> u64 {
208        let sio = unsafe { &(*pac::SIO::ptr()) };
209        loop {
210            let mtimeh = sio.mtimeh().read().mtimeh().bits();
211            let mtime = sio.mtime().read().mtime().bits();
212            let mtimeh2 = sio.mtimeh().read().mtimeh().bits();
213            if mtimeh == mtimeh2 {
214                return u64::from(mtimeh) << 32 | u64::from(mtime);
215            }
216        }
217    }
218
219    /// Set whether the timer is enabled
220    pub fn set_enabled(&mut self, enabled: bool) {
221        let sio = unsafe { &(*pac::SIO::ptr()) };
222        sio.mtime_ctrl().write(|w| w.en().variant(enabled));
223    }
224
225    /// Set the speed the clock runs at
226    pub fn set_fullspeed(&mut self, fullspeed: bool) {
227        let sio = unsafe { &(*pac::SIO::ptr()) };
228        sio.mtime_ctrl().write(|w| w.fullspeed().variant(fullspeed));
229    }
230}
231
232/// This type is just used to limit us to Spinlocks `0..=31`
233pub trait SpinlockValid: Sealed {}
234
235/// Hardware based spinlock.
236///
237/// You can claim this lock by calling either [`claim`], [`try_claim`] or
238/// [`claim_async`]. These spin-locks are hardware backed, so if you lock
239/// e.g. `Spinlock<6>`, then any other part of your application using
240/// `Spinlock<6>` will contend for the same lock, without them needing to
241/// share a reference or otherwise communicate with each other.
242///
243/// When the obtained spinlock goes out of scope, it is automatically unlocked.
244///
245///
246/// ```no_run
247/// use rp235x_hal::sio::Spinlock0;
248/// static mut SOME_GLOBAL_VAR: u32 = 0;
249///
250/// /// This function is safe to call from two different cores, but is not safe
251/// /// to call from an interrupt routine!
252/// fn update_global_var() {
253///     // Do not say `let _ = ` here - it will immediately unlock!
254///     let _lock = Spinlock0::claim();
255///     // Do your thing here that Core 0 and Core 1 might want to do at the
256///     // same time, like update this global variable:
257///     unsafe { SOME_GLOBAL_VAR += 1 };
258///     // The lock is dropped here.
259/// }
260/// ```
261///
262/// **Warning**: These spinlocks are not re-entrant, meaning that the
263///   following code will cause a deadlock:
264///
265/// ```no_run
266/// use rp235x_hal::sio::Spinlock0;
267/// let lock_1 = Spinlock0::claim();
268/// let lock_2 = Spinlock0::claim(); // deadlock here
269/// ```
270///
271/// **Note:** The `critical-section` implementation uses Spinlock 31.
272///
273/// [`claim`]: #method.claim
274/// [`try_claim`]: #method.try_claim
275/// [`claim_async`]: #method.claim_asyncs
276pub struct Spinlock<const N: usize>(core::marker::PhantomData<()>)
277where
278    Spinlock<N>: SpinlockValid;
279
280impl<const N: usize> Spinlock<N>
281where
282    Spinlock<N>: SpinlockValid,
283{
284    /// Try to claim the spinlock. Will return `Some(Self)` if the lock is obtained, and `None` if the lock is
285    /// already in use somewhere else.
286    pub fn try_claim() -> Option<Self> {
287        // Safety: We're only reading from this register
288        let sio = unsafe { &*pac::SIO::ptr() };
289        let lock = sio.spinlock(N).read().bits();
290        if lock > 0 {
291            Some(Self(core::marker::PhantomData))
292        } else {
293            None
294        }
295    }
296
297    /// Claim the spinlock, will block the current thread until the lock is available.
298    ///
299    /// Note that calling this multiple times in a row will cause a deadlock
300    pub fn claim() -> Self {
301        loop {
302            if let Some(result) = Self::try_claim() {
303                break result;
304            }
305        }
306    }
307
308    /// Try to claim the spinlock. Will return `WouldBlock` until the spinlock is available.
309    pub fn claim_async() -> nb::Result<Self, Infallible> {
310        Self::try_claim().ok_or(nb::Error::WouldBlock)
311    }
312
313    /// Clear a locked spin-lock.
314    ///
315    /// # Safety
316    ///
317    /// Only call this function if you hold the spin-lock.
318    pub unsafe fn release() {
319        let sio = &*pac::SIO::ptr();
320        // Write (any value): release the lock
321        sio.spinlock(N).write_with_zero(|b| b.bits(1));
322    }
323}
324
325impl<const N: usize> Drop for Spinlock<N>
326where
327    Spinlock<N>: SpinlockValid,
328{
329    fn drop(&mut self) {
330        // This is safe because we own the object, and hence hold the lock.
331        unsafe { Self::release() }
332    }
333}
334
335macro_rules! spinlock {
336    ($first:expr, $($rest:tt),+) => {
337        spinlock!($first);
338        spinlock!($($rest),+);
339    };
340    ($id:expr) => {
341        $crate::paste::paste! {
342            /// Spinlock number $id
343            pub type [<Spinlock $id>] = Spinlock<$id>;
344            impl SpinlockValid for Spinlock<$id> {}
345            impl Sealed for Spinlock<$id> {}
346        }
347    };
348}
349spinlock!(
350    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
351    26, 27, 28, 29, 30
352);
353
354/// Spinlock number 31 - used by critical section implementation
355#[cfg(feature = "critical-section-impl")]
356pub(crate) type Spinlock31 = Spinlock<31>;
357
358/// Spinlock number 31 - only public if critical-section-impl is not enabled
359#[cfg(not(feature = "critical-section-impl"))]
360pub type Spinlock31 = Spinlock<31>;
361
362impl SpinlockValid for Spinlock<31> {}
363impl Sealed for Spinlock<31> {}
364
365/// Returns the current state of the spinlocks. Each index corresponds to the associated spinlock, e.g. if index `5` is set to `true`, it means that [`Spinlock5`] is currently locked.
366///
367/// Note that spinlocks can be claimed or released at any point, so this function cannot guarantee the spinlock is actually available right after calling this function. This function is mainly intended for debugging.
368pub fn spinlock_state() -> [bool; 32] {
369    // Safety: we're only reading from a register
370    let sio = unsafe { &*pac::SIO::ptr() };
371    // A bitmap containing the state of all 32 spinlocks (1=locked).
372    let register = sio.spinlock_st().read().bits();
373    let mut result = [false; 32];
374    #[allow(clippy::needless_range_loop)]
375    for i in 0..32 {
376        result[i] = (register & (1 << i)) > 0;
377    }
378    result
379}
380
381/// Free all spinlocks, regardless of their current status
382///
383/// rp235x does not release all spinlocks on reset.
384/// The C SDK clears these all during entry, and so do we if you call hal::entry!
385/// But if someone is using the default cortex-m entry they risk hitting deadlocks so provide *something* to help out
386///
387/// # Safety
388/// Where possible, you should use the hal::entry macro attribute on main instead of this.
389/// You should call this as soon as possible after reset - preferably as the first entry in fn main(), before *ANY* use of spinlocks, atomics, or critical_section
390pub unsafe fn spinlock_reset() {
391    // Using raw pointers to avoid taking peripherals accidently at startup
392    const SIO_BASE: u32 = 0xd0000000;
393    const SPINLOCK0_PTR: *mut u32 = (SIO_BASE + 0x100) as *mut u32;
394    const SPINLOCK_COUNT: usize = 32;
395    for i in 0..SPINLOCK_COUNT {
396        SPINLOCK0_PTR.wrapping_add(i).write_volatile(1);
397    }
398}
399
400/// Configuration struct for one lane of the interpolator
401pub struct LaneCtrl {
402    /// Bit 22 - Only present on INTERP1 on each core. If CLAMP mode is enabled:  
403    /// - LANE0 result is shifted and masked ACCUM0, clamped by a lower bound of  
404    ///   BASE0 and an upper bound of BASE1.  
405    /// - Signedness of these comparisons is determined by LANE0_CTRL_SIGNED
406    pub clamp: bool,
407    /// Bit 21 - Only present on INTERP0 on each core. If BLEND mode is enabled:
408    ///
409    /// - LANE1 result is a linear interpolation between BASE0 and BASE1, controlled
410    ///   by the 8 LSBs of lane 1 shift and mask value (a fractional number between
411    ///   0 and 255/256ths)
412    /// - LANE0 result does not have BASE0 added (yields only
413    ///   the 8 LSBs of lane 1 shift+mask value)
414    /// - FULL result does not have lane 1 shift+mask value added (BASE2 + lane 0 shift+mask)
415    ///
416    /// LANE1 SIGNED flag controls whether the interpolation is signed or unsigned.
417    pub blend: bool,
418    /// Bits 19:20 - ORed into bits 29:28 of the lane result presented to the processor on the bus.  
419    /// No effect on the internal 32-bit datapath. Handy for using a lane to generate sequence  
420    /// of pointers into flash or SRAM.
421    pub force_msb: u8,
422    /// Bit 18 - If 1, mask + shift is bypassed for LANE0 result. This does not affect FULL result.
423    pub add_raw: bool,
424    /// Bit 17 - If 1, feed the opposite lane's result into this lane's accumulator on POP.
425    pub cross_result: bool,
426    /// Bit 16 - If 1, feed the opposite lane's accumulator into this lane's shift + mask hardware.  
427    /// Takes effect even if ADD_RAW is set (the CROSS_INPUT mux is before the shift+mask bypass)
428    pub cross_input: bool,
429    /// Bit 15 - If SIGNED is set, the shifted and masked accumulator value is sign-extended to 32 bits  
430    /// before adding to BASE0, and LANE0 PEEK/POP appear extended to 32 bits when read by processor.
431    pub signed: bool,
432    /// Bits 10:14 - The most-significant bit allowed to pass by the mask (inclusive)  
433    /// Setting MSB < LSB may cause chip to turn inside-out
434    pub mask_msb: u8,
435    /// Bits 5:9 - The least-significant bit allowed to pass by the mask (inclusive)
436    pub mask_lsb: u8,
437    /// Bits 0:4 - Logical right-shift applied to accumulator before masking
438    pub shift: u8,
439}
440
441impl Default for LaneCtrl {
442    fn default() -> Self {
443        Self::new()
444    }
445}
446
447impl LaneCtrl {
448    /// Default configuration. Normal operation, unsigned, mask keeps all bits, no shift.
449    pub const fn new() -> Self {
450        Self {
451            clamp: false,
452            blend: false,
453            force_msb: 0,
454            add_raw: false,
455            cross_result: false,
456            cross_input: false,
457            signed: false,
458            mask_msb: 31,
459            mask_lsb: 0,
460            shift: 0,
461        }
462    }
463
464    /// encode the configuration to be loaded in the ctrl register of one lane of an interpolator
465    pub const fn encode(&self) -> u32 {
466        assert!(!(self.blend && self.clamp));
467        assert!(self.force_msb < 0b100);
468        assert!(self.mask_msb < 0b100000);
469        assert!(self.mask_lsb < 0b100000);
470        assert!(self.mask_msb >= self.mask_lsb);
471        assert!(self.shift < 0b100000);
472        ((self.clamp as u32) << 22)
473            | ((self.blend as u32) << 21)
474            | ((self.force_msb as u32) << 19)
475            | ((self.add_raw as u32) << 18)
476            | ((self.cross_result as u32) << 17)
477            | ((self.cross_input as u32) << 16)
478            | ((self.signed as u32) << 15)
479            | ((self.mask_msb as u32) << 10)
480            | ((self.mask_lsb as u32) << 5)
481            | (self.shift as u32)
482    }
483}
484
485///Trait representing the functionality of a single lane of an interpolator.
486pub trait Lane: Sealed {
487    ///Read the lane result, and simultaneously write lane results to both accumulators.
488    fn pop(&mut self) -> u32;
489    ///Read the lane result without altering any internal state
490    fn peek(&self) -> u32;
491    ///Write a value to the accumulator
492    fn set_accum(&mut self, v: u32);
493    ///Read the value from the accumulator
494    fn get_accum(&self) -> u32;
495    ///Write a value to the base register
496    fn set_base(&mut self, v: u32);
497    ///Read the value from the base register
498    fn get_base(&self) -> u32;
499    ///Write to the control register
500    fn set_ctrl(&mut self, v: u32);
501    ///Read from the control register
502    fn get_ctrl(&self) -> u32;
503    ///Add the value to the accumulator register
504    fn add_accum(&mut self, v: u32);
505    ///Read the raw shift and mask value (BASE register not added)
506    fn read_raw(&self) -> u32;
507}
508
509///Trait representing the functionality of an interpolator.
510/// ```no_run
511/// use rp235x_hal::{
512///     self as hal,
513///     sio::{Lane, LaneCtrl, Sio},
514/// };
515/// let mut peripherals = hal::pac::Peripherals::take().unwrap();
516/// let mut sio = Sio::new(peripherals.SIO);
517///
518/// // by having the configuration const, the validity is checked during compilation.
519/// const config: u32 = LaneCtrl {
520///     mask_msb: 4, // Most significant bit of the mask is bit 4
521///     // By default the least significant bit is bit 0
522///     // this will keep only the 5 least significant bits.
523///     // this is equivalent to %32
524///     ..LaneCtrl::new()
525/// }
526/// .encode();
527/// sio.interp0.get_lane0().set_ctrl(config);
528/// sio.interp0.get_lane0().set_accum(0);
529/// sio.interp0.get_lane0().set_base(1); // will increment the value by 1 on each call to pop
530///
531/// sio.interp0.get_lane0().peek(); // returns 1
532/// sio.interp0.get_lane0().pop(); // returns 1
533/// sio.interp0.get_lane0().pop(); // returns 2
534/// sio.interp0.get_lane0().pop(); // returns 3
535/// ```
536pub trait Interp: Sealed {
537    ///Read the interpolator result (Result 2 in the datasheet), and simultaneously write lane results to both accumulators.
538    fn pop(&mut self) -> u32;
539    ///Read the interpolator result (Result 2 in the datasheet) without altering any internal state
540    fn peek(&self) -> u32;
541    ///Write to the interpolator Base register (Base2 in the datasheet)
542    fn set_base(&mut self, v: u32);
543    ///Read the interpolator Base register (Base2 in the datasheet)
544    fn get_base(&self) -> u32;
545    ///Write the lower 16 bits to BASE0 and the upper bits to BASE1 simultaneously. Each half is sign-extended to 32 bits if that lane's SIGNED flag is set
546    fn set_base_1and0(&mut self, v: u32);
547}
548
549macro_rules! interpolators {
550    (
551        $($interp:ident : ( $( [ $lane:ident,$lane_id:expr ] ),+ ) ),+
552    ) => {
553        $crate::paste::paste! {
554
555
556                $(
557                    $(
558                        #[doc = "The lane " $lane_id " of " $interp]
559                        pub struct [<$interp $lane>]{
560                            _private: (),
561                        }
562                        impl Lane for [<$interp $lane>]{
563                            fn pop(&mut self) ->u32{
564                                let sio = unsafe { &*pac::SIO::ptr() };
565                                sio.[<$interp:lower _pop_ $lane:lower>]().read().bits()
566                            }
567                            fn peek(&self) ->u32{
568                                let sio = unsafe { &*pac::SIO::ptr() };
569                                sio.[<$interp:lower _peek_ $lane:lower>]().read().bits()
570                            }
571                            fn set_accum(&mut self,v:u32){
572                                let sio = unsafe { &*pac::SIO::ptr() };
573                                sio.[<$interp:lower _accum $lane_id>]().write(|w| unsafe { w.bits(v) });
574                            }
575                            fn get_accum(&self)->u32{
576                                let sio = unsafe { &*pac::SIO::ptr() };
577                                sio.[<$interp:lower _accum $lane_id>]().read().bits()
578                            }
579                            fn set_base(&mut self, v:u32){
580                                let sio = unsafe { &*pac::SIO::ptr() };
581                                sio.[<$interp:lower _base $lane_id>]().write(|w| unsafe { w.bits(v) });
582                            }
583                            fn get_base(&self)->u32{
584                                let sio = unsafe { &*pac::SIO::ptr() };
585                                sio.[<$interp:lower _base $lane_id>]().read().bits()
586                            }
587                            fn set_ctrl(&mut self, v:u32){
588                                let sio = unsafe { &*pac::SIO::ptr() };
589                                sio.[<$interp:lower _ctrl_lane $lane_id>]().write(|w| unsafe { w.bits(v) });
590                            }
591                            fn get_ctrl(&self)->u32{
592                                let sio = unsafe { &*pac::SIO::ptr() };
593                                sio.[<$interp:lower _ctrl_lane $lane_id>]().read().bits()
594                            }
595                            fn add_accum(&mut self, v:u32){
596                                let sio = unsafe { &*pac::SIO::ptr() };
597                                sio.[<$interp:lower _accum $lane_id _add>]().write(|w| unsafe { w.bits(v) });
598                            }
599                            fn read_raw(&self)->u32{
600                                let sio = unsafe { &*pac::SIO::ptr() };
601                                sio.[<$interp:lower _accum $lane_id _add>]().read().bits()
602                            }
603                        }
604                        impl Sealed for [<$interp $lane>] {}
605                    )+
606                    #[doc = "Interpolator " $interp]
607                    pub struct $interp {
608                        $(
609                            [<$lane:lower>]: [<$interp $lane>],
610                        )+
611                    }
612                    impl $interp{
613                        $(
614                            /// Lane accessor function
615                            pub fn [<get_ $lane:lower>](&mut self)->&mut [<$interp $lane>]{
616                                &mut self.[<$lane:lower>]
617                            }
618                        )+
619                    }
620                    impl Interp for $interp{
621                        fn pop(&mut self) ->u32{
622                            let sio = unsafe { &*pac::SIO::ptr() };
623                            sio.[<$interp:lower _pop_full>]().read().bits()
624                        }
625                        fn peek(&self) ->u32{
626                            let sio = unsafe { &*pac::SIO::ptr() };
627                            sio.[<$interp:lower _peek_full>]().read().bits()
628                        }
629                        fn set_base(&mut self, v:u32){
630                            let sio = unsafe { &*pac::SIO::ptr() };
631                            sio.[<$interp:lower _base2>]().write(|w| unsafe { w.bits(v)});
632                        }
633                        fn get_base(&self)->u32{
634                            let sio = unsafe { &*pac::SIO::ptr() };
635                            sio.[<$interp:lower _base2>]().read().bits()
636                        }
637                        fn set_base_1and0(&mut self, v:u32){
638                            let sio = unsafe { &*pac::SIO::ptr() };
639                            sio.[<$interp:lower _base_1and0>]().write(|w| unsafe { w.bits(v)});
640                        }
641                    }
642                    impl Sealed for $interp {}
643                )+
644            }
645        }
646    }
647
648interpolators!(
649    Interp0 : ([Lane0,0],[Lane1,1]),
650    Interp1 : ([Lane0,0],[Lane1,1])
651);