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);