Skip to main content

embassy_rp/
lib.rs

1#![no_std]
2#![allow(async_fn_in_trait)]
3#![allow(unsafe_op_in_unsafe_fn)]
4#![allow(unused_unsafe)]
5#![doc = include_str!("../README.md")]
6#![warn(missing_docs)]
7
8//! ## Feature flags
9#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
10
11// This mod MUST go first, so that the others see its macros.
12pub(crate) mod fmt;
13
14#[cfg(feature = "binary-info")]
15pub use rp_binary_info as binary_info;
16
17#[cfg(feature = "critical-section-impl")]
18mod critical_section_impl;
19
20#[cfg(feature = "rp2040")]
21mod intrinsics;
22
23pub mod adc;
24#[cfg(feature = "_rp235x")]
25pub mod aon_timer;
26#[cfg(feature = "_rp235x")]
27pub mod block;
28#[cfg(feature = "rp2040")]
29pub mod bootsel;
30pub mod clocks;
31pub(crate) mod datetime;
32pub mod dma;
33#[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))]
34pub mod executor;
35pub mod flash;
36#[cfg(feature = "rp2040")]
37mod float;
38pub mod gpio;
39pub mod i2c;
40pub mod i2c_slave;
41pub mod multicore;
42#[cfg(feature = "_rp235x")]
43pub mod otp;
44pub mod pio_programs;
45#[cfg(feature = "_rp235x")]
46pub mod psram;
47pub mod pwm;
48#[cfg(feature = "_rp235x")]
49pub mod qmi_cs1;
50mod reset;
51pub mod rom_data;
52#[cfg(feature = "rp2040")]
53pub mod rtc;
54pub mod spi;
55mod spinlock;
56pub mod spinlock_mutex;
57#[cfg(feature = "time-driver")]
58pub mod time_driver;
59#[cfg(feature = "_rp235x")]
60pub mod trng;
61pub mod uart;
62pub mod usb;
63pub mod watchdog;
64
65// PIO
66pub mod pio;
67pub(crate) mod relocate;
68
69// Reexports
70pub use embassy_hal_internal::{Peri, PeripheralType};
71#[cfg(feature = "unstable-pac")]
72pub use rp_pac as pac;
73#[cfg(not(feature = "unstable-pac"))]
74pub(crate) use rp_pac as pac;
75
76#[cfg(feature = "rt")]
77pub use crate::pac::NVIC_PRIO_BITS;
78
79#[cfg(feature = "rp2040")]
80embassy_hal_internal::interrupt_mod!(
81    TIMER_IRQ_0,
82    TIMER_IRQ_1,
83    TIMER_IRQ_2,
84    TIMER_IRQ_3,
85    PWM_IRQ_WRAP,
86    USBCTRL_IRQ,
87    XIP_IRQ,
88    PIO0_IRQ_0,
89    PIO0_IRQ_1,
90    PIO1_IRQ_0,
91    PIO1_IRQ_1,
92    DMA_IRQ_0,
93    DMA_IRQ_1,
94    IO_IRQ_BANK0,
95    IO_IRQ_QSPI,
96    SIO_IRQ_PROC0,
97    SIO_IRQ_PROC1,
98    CLOCKS_IRQ,
99    SPI0_IRQ,
100    SPI1_IRQ,
101    UART0_IRQ,
102    UART1_IRQ,
103    ADC_IRQ_FIFO,
104    I2C0_IRQ,
105    I2C1_IRQ,
106    RTC_IRQ,
107    SWI_IRQ_0,
108    SWI_IRQ_1,
109    SWI_IRQ_2,
110    SWI_IRQ_3,
111    SWI_IRQ_4,
112    SWI_IRQ_5,
113);
114
115#[cfg(feature = "_rp235x")]
116embassy_hal_internal::interrupt_mod!(
117    TIMER0_IRQ_0,
118    TIMER0_IRQ_1,
119    TIMER0_IRQ_2,
120    TIMER0_IRQ_3,
121    TIMER1_IRQ_0,
122    TIMER1_IRQ_1,
123    TIMER1_IRQ_2,
124    TIMER1_IRQ_3,
125    PWM_IRQ_WRAP_0,
126    PWM_IRQ_WRAP_1,
127    DMA_IRQ_0,
128    DMA_IRQ_1,
129    USBCTRL_IRQ,
130    PIO0_IRQ_0,
131    PIO0_IRQ_1,
132    PIO1_IRQ_0,
133    PIO1_IRQ_1,
134    PIO2_IRQ_0,
135    PIO2_IRQ_1,
136    IO_IRQ_BANK0,
137    IO_IRQ_BANK0_NS,
138    IO_IRQ_QSPI,
139    IO_IRQ_QSPI_NS,
140    SIO_IRQ_FIFO,
141    SIO_IRQ_BELL,
142    SIO_IRQ_FIFO_NS,
143    SIO_IRQ_BELL_NS,
144    CLOCKS_IRQ,
145    SPI0_IRQ,
146    SPI1_IRQ,
147    UART0_IRQ,
148    UART1_IRQ,
149    ADC_IRQ_FIFO,
150    I2C0_IRQ,
151    I2C1_IRQ,
152    TRNG_IRQ,
153    PLL_SYS_IRQ,
154    PLL_USB_IRQ,
155    POWMAN_IRQ_POW,
156    POWMAN_IRQ_TIMER,
157    SWI_IRQ_0,
158    SWI_IRQ_1,
159    SWI_IRQ_2,
160    SWI_IRQ_3,
161    SWI_IRQ_4,
162    SWI_IRQ_5,
163);
164
165/// Macro to bind interrupts to handlers.
166///
167/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
168/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
169/// prove at compile-time that the right interrupts have been bound.
170///
171/// Example of how to bind one interrupt:
172///
173/// ```rust,ignore
174/// use embassy_rp::{bind_interrupts, usb, peripherals};
175///
176/// bind_interrupts!(
177///     /// Binds the USB Interrupts.
178///     struct Irqs {
179///         USBCTRL_IRQ => usb::InterruptHandler<peripherals::USB>;
180///     }
181/// );
182/// ```
183///
184// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
185#[macro_export]
186macro_rules! bind_interrupts {
187    ($(#[$attr:meta])* $vis:vis struct $name:ident {
188        $(
189            $(#[cfg($cond_irq:meta)])?
190            $irq:ident => $(
191                $(#[cfg($cond_handler:meta)])?
192                $handler:ty
193            ),*;
194        )*
195    }) => {
196        #[derive(Copy, Clone)]
197        $(#[$attr])*
198        $vis struct $name;
199
200        $(
201            #[allow(non_snake_case)]
202            #[unsafe(no_mangle)]
203            $(#[cfg($cond_irq)])?
204            unsafe extern "C" fn $irq() {
205                unsafe {
206                    $(
207                        $(#[cfg($cond_handler)])?
208                        <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
209
210                    )*
211                }
212            }
213
214            $(#[cfg($cond_irq)])?
215            $crate::bind_interrupts!(@inner
216                $(
217                    $(#[cfg($cond_handler)])?
218                    unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
219                )*
220            );
221        )*
222    };
223    (@inner $($t:tt)*) => {
224        $($t)*
225    }
226}
227
228#[cfg(feature = "rp2040")]
229embassy_hal_internal::peripherals! {
230    PIN_0,
231    PIN_1,
232    PIN_2,
233    PIN_3,
234    PIN_4,
235    PIN_5,
236    PIN_6,
237    PIN_7,
238    PIN_8,
239    PIN_9,
240    PIN_10,
241    PIN_11,
242    PIN_12,
243    PIN_13,
244    PIN_14,
245    PIN_15,
246    PIN_16,
247    PIN_17,
248    PIN_18,
249    PIN_19,
250    PIN_20,
251    PIN_21,
252    PIN_22,
253    PIN_23,
254    PIN_24,
255    PIN_25,
256    PIN_26,
257    PIN_27,
258    PIN_28,
259    PIN_29,
260    PIN_QSPI_SCLK,
261    PIN_QSPI_SS,
262    PIN_QSPI_SD0,
263    PIN_QSPI_SD1,
264    PIN_QSPI_SD2,
265    PIN_QSPI_SD3,
266
267    UART0,
268    UART1,
269
270    SPI0,
271    SPI1,
272
273    I2C0,
274    I2C1,
275
276    DMA_CH0,
277    DMA_CH1,
278    DMA_CH2,
279    DMA_CH3,
280    DMA_CH4,
281    DMA_CH5,
282    DMA_CH6,
283    DMA_CH7,
284    DMA_CH8,
285    DMA_CH9,
286    DMA_CH10,
287    DMA_CH11,
288
289    PWM_SLICE0,
290    PWM_SLICE1,
291    PWM_SLICE2,
292    PWM_SLICE3,
293    PWM_SLICE4,
294    PWM_SLICE5,
295    PWM_SLICE6,
296    PWM_SLICE7,
297
298    USB,
299
300    RTC,
301
302    FLASH,
303
304    ADC,
305    ADC_TEMP_SENSOR,
306
307    CORE1,
308
309    PIO0,
310    PIO1,
311
312    WATCHDOG,
313    BOOTSEL,
314}
315
316#[cfg(feature = "_rp235x")]
317embassy_hal_internal::peripherals! {
318    PIN_0,
319    PIN_1,
320    PIN_2,
321    PIN_3,
322    PIN_4,
323    PIN_5,
324    PIN_6,
325    PIN_7,
326    PIN_8,
327    PIN_9,
328    PIN_10,
329    PIN_11,
330    PIN_12,
331    PIN_13,
332    PIN_14,
333    PIN_15,
334    PIN_16,
335    PIN_17,
336    PIN_18,
337    PIN_19,
338    PIN_20,
339    PIN_21,
340    PIN_22,
341    PIN_23,
342    PIN_24,
343    PIN_25,
344    PIN_26,
345    PIN_27,
346    PIN_28,
347    PIN_29,
348    #[cfg(feature = "rp235xb")]
349    PIN_30,
350    #[cfg(feature = "rp235xb")]
351    PIN_31,
352    #[cfg(feature = "rp235xb")]
353    PIN_32,
354    #[cfg(feature = "rp235xb")]
355    PIN_33,
356    #[cfg(feature = "rp235xb")]
357    PIN_34,
358    #[cfg(feature = "rp235xb")]
359    PIN_35,
360    #[cfg(feature = "rp235xb")]
361    PIN_36,
362    #[cfg(feature = "rp235xb")]
363    PIN_37,
364    #[cfg(feature = "rp235xb")]
365    PIN_38,
366    #[cfg(feature = "rp235xb")]
367    PIN_39,
368    #[cfg(feature = "rp235xb")]
369    PIN_40,
370    #[cfg(feature = "rp235xb")]
371    PIN_41,
372    #[cfg(feature = "rp235xb")]
373    PIN_42,
374    #[cfg(feature = "rp235xb")]
375    PIN_43,
376    #[cfg(feature = "rp235xb")]
377    PIN_44,
378    #[cfg(feature = "rp235xb")]
379    PIN_45,
380    #[cfg(feature = "rp235xb")]
381    PIN_46,
382    #[cfg(feature = "rp235xb")]
383    PIN_47,
384    PIN_QSPI_SCLK,
385    PIN_QSPI_SS,
386    PIN_QSPI_SD0,
387    PIN_QSPI_SD1,
388    PIN_QSPI_SD2,
389    PIN_QSPI_SD3,
390
391    UART0,
392    UART1,
393
394    SPI0,
395    SPI1,
396
397    QMI_CS1,
398
399    I2C0,
400    I2C1,
401
402    DMA_CH0,
403    DMA_CH1,
404    DMA_CH2,
405    DMA_CH3,
406    DMA_CH4,
407    DMA_CH5,
408    DMA_CH6,
409    DMA_CH7,
410    DMA_CH8,
411    DMA_CH9,
412    DMA_CH10,
413    DMA_CH11,
414    DMA_CH12,
415    DMA_CH13,
416    DMA_CH14,
417    DMA_CH15,
418
419    PWM_SLICE0,
420    PWM_SLICE1,
421    PWM_SLICE2,
422    PWM_SLICE3,
423    PWM_SLICE4,
424    PWM_SLICE5,
425    PWM_SLICE6,
426    PWM_SLICE7,
427    PWM_SLICE8,
428    PWM_SLICE9,
429    PWM_SLICE10,
430    PWM_SLICE11,
431
432    USB,
433
434    RTC,
435
436    FLASH,
437
438    ADC,
439    ADC_TEMP_SENSOR,
440
441    CORE1,
442
443    PIO0,
444    PIO1,
445    PIO2,
446
447    WATCHDOG,
448    BOOTSEL,
449
450    POWMAN,
451    TRNG
452}
453
454#[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))]
455macro_rules! select_bootloader {
456    ( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => {
457        $(
458            #[cfg(feature = $feature)]
459            #[unsafe(link_section = ".boot2")]
460            #[used]
461            static BOOT2: [u8; 256] = rp2040_boot2::$loader;
462        )*
463
464        #[cfg(not(any( $( feature = $feature),* )))]
465        #[unsafe(link_section = ".boot2")]
466        #[used]
467        static BOOT2: [u8; 256] = rp2040_boot2::$default;
468    }
469}
470
471#[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))]
472select_bootloader! {
473    "boot2-at25sf128a" => BOOT_LOADER_AT25SF128A,
474    "boot2-gd25q64cs" => BOOT_LOADER_GD25Q64CS,
475    "boot2-generic-03h" => BOOT_LOADER_GENERIC_03H,
476    "boot2-is25lp080" => BOOT_LOADER_IS25LP080,
477    "boot2-ram-memcpy" => BOOT_LOADER_RAM_MEMCPY,
478    "boot2-w25q080" => BOOT_LOADER_W25Q080,
479    "boot2-w25x10cl" => BOOT_LOADER_W25X10CL,
480    default => BOOT_LOADER_W25Q080
481}
482
483#[cfg(all(not(feature = "imagedef-none"), feature = "_rp235x"))]
484macro_rules! select_imagedef {
485    ( $( $feature:literal => $imagedef:ident, )+ default => $default:ident ) => {
486        $(
487            #[cfg(feature = $feature)]
488            #[unsafe(link_section = ".start_block")]
489            #[used]
490            static IMAGE_DEF: crate::block::ImageDef = crate::block::ImageDef::$imagedef();
491        )*
492
493        #[cfg(not(any( $( feature = $feature),* )))]
494        #[unsafe(link_section = ".start_block")]
495        #[used]
496        static IMAGE_DEF: crate::block::ImageDef = crate::block::ImageDef::$default();
497    }
498}
499
500#[cfg(all(not(feature = "imagedef-none"), feature = "_rp235x"))]
501select_imagedef! {
502    "imagedef-secure-exe" => secure_exe,
503    "imagedef-nonsecure-exe" => non_secure_exe,
504    default => secure_exe
505}
506
507/// Installs a stack guard for the CORE0 stack in MPU region 0.
508/// Will fail if the MPU is already configured. This function requires
509/// a `_stack_end` symbol to be defined by the linker script, and expects
510/// `_stack_end` to be located at the lowest address (largest depth) of
511/// the stack.
512///
513/// This method can *only* set up stack guards on the currently
514/// executing core. Stack guards for CORE1 are set up automatically,
515/// only CORE0 should ever use this.
516///
517/// # Usage
518///
519/// ```no_run
520/// use embassy_rp::install_core0_stack_guard;
521/// use embassy_executor::{Executor, Spawner};
522///
523/// #[embassy_executor::main]
524/// async fn main(_spawner: Spawner) {
525///     // set up by the linker as follows:
526///     //
527///     //     MEMORY {
528///     //       STACK0: ORIGIN = 0x20040000, LENGTH = 4K
529///     //     }
530///     //
531///     //     _stack_end = ORIGIN(STACK0);
532///     //     _stack_start = _stack_end + LENGTH(STACK0);
533///     //
534///     install_core0_stack_guard().expect("MPU already configured");
535///     let p = embassy_rp::init(Default::default());
536///
537///     // ...
538/// }
539/// ```
540pub fn install_core0_stack_guard() -> Result<(), ()> {
541    unsafe extern "C" {
542        static mut _stack_end: usize;
543    }
544    unsafe { install_stack_guard(core::ptr::addr_of_mut!(_stack_end)) }
545}
546
547#[cfg(all(feature = "rp2040", not(feature = "_test")))]
548#[inline(always)]
549unsafe fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
550    let core = unsafe { cortex_m::Peripherals::steal() };
551
552    // Fail if MPU is already configured
553    if core.MPU.ctrl.read() != 0 {
554        return Err(());
555    }
556
557    // The minimum we can protect is 32 bytes on a 32 byte boundary, so round up which will
558    // just shorten the valid stack range a tad.
559    let addr = (stack_bottom as u32 + 31) & !31;
560    // Mask is 1 bit per 32 bytes of the 256 byte range... clear the bit for the segment we want
561    let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7));
562    unsafe {
563        core.MPU.ctrl.write(5); // enable mpu with background default map
564        core.MPU.rbar.write((addr & !0xff) | (1 << 4)); // set address and update RNR
565        core.MPU.rasr.write(
566            1 // enable region
567               | (0x7 << 1) // size 2^(7 + 1) = 256
568               | (subregion_select << 8)
569               | 0x10000000, // XN = disable instruction fetch; no other bits means no permissions
570        );
571    }
572    Ok(())
573}
574
575#[cfg(all(feature = "_rp235x", not(feature = "_test")))]
576#[inline(always)]
577unsafe fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> {
578    // The RP2350 arm cores are cortex-m33 and can use the MSPLIM register to guard the end of stack.
579    // We'll need to do something else for the riscv cores.
580    cortex_m::register::msplim::write(stack_bottom.addr() as u32);
581
582    Ok(())
583}
584
585// This is to hack around cortex_m defaulting to ARMv7 when building tests,
586// so the compile fails when we try to use ARMv8 peripherals.
587#[cfg(feature = "_test")]
588#[inline(always)]
589unsafe fn install_stack_guard(_stack_bottom: *mut usize) -> Result<(), ()> {
590    Ok(())
591}
592
593/// HAL configuration for RP.
594pub mod config {
595    use crate::clocks::ClockConfig;
596
597    /// HAL configuration passed when initializing.
598    #[non_exhaustive]
599    pub struct Config {
600        /// Clock configuration.
601        pub clocks: ClockConfig,
602    }
603
604    impl Default for Config {
605        fn default() -> Self {
606            Self {
607                clocks: ClockConfig::crystal(12_000_000),
608            }
609        }
610    }
611
612    impl Config {
613        /// Create a new configuration with the provided clock config.
614        pub fn new(clocks: ClockConfig) -> Self {
615            Self { clocks }
616        }
617    }
618}
619
620/// Initialize the `embassy-rp` HAL with the provided configuration.
621///
622/// This returns the peripheral singletons that can be used for creating drivers.
623///
624/// This should only be called once at startup, otherwise it panics.
625pub fn init(config: config::Config) -> Peripherals {
626    // Do this first, so that it panics if user is calling `init` a second time
627    // before doing anything important.
628    let peripherals = Peripherals::take();
629
630    unsafe {
631        clocks::init(config.clocks);
632        #[cfg(feature = "time-driver")]
633        time_driver::init();
634        dma::init();
635        gpio::init();
636    }
637
638    peripherals
639}
640
641#[cfg(feature = "rt")]
642#[cortex_m_rt::pre_init]
643unsafe fn pre_init() {
644    // SIO does not get reset when core0 is reset with either `scb::sys_reset()` or with SWD.
645    // Since we're using SIO spinlock 31 for the critical-section impl, this causes random
646    // hangs if we reset in the middle of a CS, because the next boot sees the spinlock
647    // as locked and waits forever.
648    //
649    // See https://github.com/embassy-rs/embassy/issues/1736
650    // and https://github.com/rp-rs/rp-hal/issues/292
651    // and https://matrix.to/#/!vhKMWjizPZBgKeknOo:matrix.org/$VfOkQgyf1PjmaXZbtycFzrCje1RorAXd8BQFHTl4d5M
652    //
653    // According to Raspberry Pi, this is considered Working As Intended, and not an errata,
654    // even though this behavior is different from every other ARM chip (sys_reset usually resets
655    // the *system* as its name implies, not just the current core).
656    //
657    // To fix this, reset SIO on boot. We must do this in pre_init because it's unsound to do it
658    // in `embassy_rp::init`, since the user could've acquired a CS by then. pre_init is guaranteed
659    // to run before any user code.
660    //
661    // A similar thing could happen with PROC1. It is unclear whether it's possible for PROC1
662    // to stay unreset through a PROC0 reset, so we reset it anyway just in case.
663    //
664    // Important info from PSM logic (from Luke Wren in above Matrix thread)
665    //
666    //     The logic is, each PSM stage is reset if either of the following is true:
667    //     - The previous stage is in reset and FRCE_ON is false
668    //     - FRCE_OFF is true
669    //
670    // The PSM order is SIO -> PROC0 -> PROC1.
671    // So, we have to force-on PROC0 to prevent it from getting reset when resetting SIO.
672    #[cfg(feature = "rp2040")]
673    {
674        pac::PSM.frce_on().write_and_wait(|w| {
675            w.set_proc0(true);
676        });
677        // Then reset SIO and PROC1.
678        pac::PSM.frce_off().write_and_wait(|w| {
679            w.set_sio(true);
680            w.set_proc1(true);
681        });
682        // clear force_off first, force_on second. The other way around would reset PROC0.
683        pac::PSM.frce_off().write_and_wait(|_| {});
684        pac::PSM.frce_on().write_and_wait(|_| {});
685    }
686
687    #[cfg(feature = "_rp235x")]
688    {
689        // on RP235x, datasheet says "The FRCE_ON register is a development feature that does nothing in production devices."
690        // No idea why they removed it. Removing it means we can't use PSM to reset SIO, because it comes before
691        // PROC0, so we'd need FRCE_ON to prevent resetting ourselves.
692        //
693        // So we just unlock the spinlock manually.
694        pac::SIO.spinlock(31).write_value(1);
695
696        // We can still use PSM to reset PROC1 since it comes after PROC0 in the state machine.
697        pac::PSM.frce_off().write_and_wait(|w| w.set_proc1(true));
698        pac::PSM.frce_off().write_and_wait(|_| {});
699
700        // Make atomics work between cores.
701        enable_actlr_extexclall();
702    }
703}
704
705/// Set the EXTEXCLALL bit in ACTLR.
706///
707/// The default MPU memory map marks all memory as non-shareable, so atomics don't
708/// synchronize memory accesses between cores at all. This bit forces all memory to be
709/// considered shareable regardless of what the MPU says.
710///
711/// TODO: does this interfere somehow if the user wants to use a custom MPU configuration?
712/// maybe we need to add a way to disable this?
713///
714/// This must be done FOR EACH CORE.
715#[cfg(feature = "_rp235x")]
716unsafe fn enable_actlr_extexclall() {
717    (&*cortex_m::peripheral::ICB::PTR).actlr.modify(|w| w | (1 << 29));
718}
719
720/// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes.
721#[allow(unused)]
722trait RegExt<T: Copy> {
723    #[allow(unused)]
724    fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
725    fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
726    fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
727    fn write_and_wait<R>(&self, f: impl FnOnce(&mut T) -> R) -> R
728    where
729        T: PartialEq;
730}
731
732impl<T: Default + Copy, A: pac::common::Write> RegExt<T> for pac::common::Reg<T, A> {
733    fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
734        let mut val = Default::default();
735        let res = f(&mut val);
736        unsafe {
737            let ptr = (self.as_ptr() as *mut u8).add(0x1000) as *mut T;
738            ptr.write_volatile(val);
739        }
740        res
741    }
742
743    fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
744        let mut val = Default::default();
745        let res = f(&mut val);
746        unsafe {
747            let ptr = (self.as_ptr() as *mut u8).add(0x2000) as *mut T;
748            ptr.write_volatile(val);
749        }
750        res
751    }
752
753    fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
754        let mut val = Default::default();
755        let res = f(&mut val);
756        unsafe {
757            let ptr = (self.as_ptr() as *mut u8).add(0x3000) as *mut T;
758            ptr.write_volatile(val);
759        }
760        res
761    }
762
763    fn write_and_wait<R>(&self, f: impl FnOnce(&mut T) -> R) -> R
764    where
765        T: PartialEq,
766    {
767        let mut val = Default::default();
768        let res = f(&mut val);
769        unsafe {
770            self.as_ptr().write_volatile(val);
771            while self.as_ptr().read_volatile() != val {}
772        }
773        res
774    }
775}