From bac6714f09f887cbfc168590291c03f580bc107a Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Fri, 22 Oct 2021 14:30:30 +0200 Subject: [PATCH] Use volatile register access for gpio interrupts (#170) * Use volatile register access for gpio interrupts * Provide functions for atomic register updates * Use atomic register access functions in pio.rs * Improve doc comments * Limit visibility of atomic register access functions Also remove write_xor for now, as it's not yet used anywhere --- rp2040-hal/src/atomic_register_access.rs | 40 ++++++++++++++++++++++++ rp2040-hal/src/gpio/reg.rs | 16 +++++----- rp2040-hal/src/lib.rs | 1 + rp2040-hal/src/pio.rs | 26 +++++---------- 4 files changed, 57 insertions(+), 26 deletions(-) create mode 100644 rp2040-hal/src/atomic_register_access.rs diff --git a/rp2040-hal/src/atomic_register_access.rs b/rp2040-hal/src/atomic_register_access.rs new file mode 100644 index 0000000..b196ac6 --- /dev/null +++ b/rp2040-hal/src/atomic_register_access.rs @@ -0,0 +1,40 @@ +//! Provide atomic access to peripheral registers +//! +//! This feature is not available for all peripherals. +//! See [section 2.1.2 of the RP2040 datasheet][section_2_1_2] for details. +//! +//! [section_2_1_2]: https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#atomic-rwtype + +use core::ptr::write_volatile; + +/// Perform atomic bitmask set operation on register +/// +/// See [section 2.1.2 of the RP2040 datasheet][section_2_1_2] for details. +/// +/// [section_2_1_2]: https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#atomic-rwtype +/// +/// # Safety +/// +/// In addition to the requirements of [core::ptr::write_volatile], +/// `register` must point to a register providing atomic aliases. +#[inline] +pub(crate) unsafe fn write_bitmask_set(register: *mut u32, bits: u32) { + let alias = (register as usize + 0x2000) as *mut u32; + write_volatile(alias, bits); +} + +/// Perform atomic bitmask clear operation on register +/// +/// See [section 2.1.2 of the RP2040 datasheet][section_2_1_2] for details. +/// +/// [section_2_1_2]: https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf#atomic-rwtype +/// +/// # Safety +/// +/// In addition to the requirements of [core::ptr::write_volatile], +/// `register` must point to a register providing atomic aliases. +#[inline] +pub(crate) unsafe fn write_bitmask_clear(register: *mut u32, bits: u32) { + let alias = (register as usize + 0x3000) as *mut u32; + write_volatile(alias, bits); +} diff --git a/rp2040-hal/src/gpio/reg.rs b/rp2040-hal/src/gpio/reg.rs index 6a5ac78..5058a4a 100644 --- a/rp2040-hal/src/gpio/reg.rs +++ b/rp2040-hal/src/gpio/reg.rs @@ -4,8 +4,10 @@ use super::{ InputOverride, Interrupt, InterruptOverride, OutputDriveStrength, OutputEnableOverride, OutputOverride, OutputSlewRate, }; +use crate::atomic_register_access::{write_bitmask_clear, write_bitmask_set}; use crate::gpio::dynpin::{DynDisabled, DynFunction, DynInput, DynOutput, DynPinMode}; use crate::pac; +use core::ptr::read_volatile; //============================================================================== // ModeFields @@ -278,7 +280,7 @@ pub(super) unsafe trait RegisterInterface { .as_ptr() .add(num / 8 + cpuid as usize * 12); let bit_in_reg = num % 8 * 4 + interrupt as usize; - (*reg & (1 << bit_in_reg)) != 0 + (read_volatile(reg) & (1 << bit_in_reg)) != 0 } } @@ -293,7 +295,7 @@ pub(super) unsafe trait RegisterInterface { .as_ptr() .add(num / 8 + cpuid as usize * 12); let bit_in_reg = num % 8 * 4 + interrupt as usize; - (*reg & (1 << bit_in_reg)) != 0 + (read_volatile(reg) & (1 << bit_in_reg)) != 0 } } @@ -309,9 +311,9 @@ pub(super) unsafe trait RegisterInterface { .add(num / 8 + cpuid as usize * 12); let bit_in_reg = num % 8 * 4 + interrupt as usize; if enabled { - *reg |= 1 << bit_in_reg; + write_bitmask_set(reg, 1 << bit_in_reg); } else { - *reg &= !(1 << bit_in_reg); + write_bitmask_clear(reg, 1 << bit_in_reg); } } } @@ -327,7 +329,7 @@ pub(super) unsafe trait RegisterInterface { .as_ptr() .add(num / 8 + cpuid as usize * 12); let bit_in_reg = num % 8 * 4 + interrupt as usize; - (*reg & (1 << bit_in_reg)) != 0 + (read_volatile(reg) & (1 << bit_in_reg)) != 0 } } @@ -343,9 +345,9 @@ pub(super) unsafe trait RegisterInterface { .add(num / 8 + cpuid as usize * 12); let bit_in_reg = num % 8 * 4 + interrupt as usize; if forced { - *reg |= 1 << bit_in_reg; + write_bitmask_set(reg, 1 << bit_in_reg); } else { - *reg &= !(1 << bit_in_reg); + write_bitmask_clear(reg, 1 << bit_in_reg); } } } diff --git a/rp2040-hal/src/lib.rs b/rp2040-hal/src/lib.rs index 5810aea..8651af9 100644 --- a/rp2040-hal/src/lib.rs +++ b/rp2040-hal/src/lib.rs @@ -14,6 +14,7 @@ pub use paste; pub extern crate rp2040_pac as pac; pub mod adc; +pub(crate) mod atomic_register_access; pub mod clocks; pub mod gpio; pub mod i2c; diff --git a/rp2040-hal/src/pio.rs b/rp2040-hal/src/pio.rs index 0db9b9f..1ca2a09 100644 --- a/rp2040-hal/src/pio.rs +++ b/rp2040-hal/src/pio.rs @@ -1,6 +1,9 @@ //! Programmable IO (PIO) /// See [Chapter 3](https://rptl.io/pico-datasheet) for more details. -use crate::resets::SubsystemReset; +use crate::{ + atomic_register_access::{write_bitmask_clear, write_bitmask_set}, + resets::SubsystemReset, +}; use pio::{Program, SideSet, Wrap}; use rp2040_pac::{PIO0, PIO1}; @@ -430,26 +433,16 @@ impl UninitStateMachine { } fn set_ctrl_bits(&mut self, bits: u32) { - const ATOMIC_SET_OFFSET: usize = 0x2000; // Safety: We only use the atomic alias of the register. unsafe { - (*self.block) - .ctrl - .as_ptr() - .add(ATOMIC_SET_OFFSET / 4) - .write_volatile(bits); + write_bitmask_set((*self.block).ctrl.as_ptr(), bits); } } fn clear_ctrl_bits(&mut self, bits: u32) { - const ATOMIC_CLEAR_OFFSET: usize = 0x3000; // Safety: We only use the atomic alias of the register. unsafe { - (*self.block) - .ctrl - .as_ptr() - .add(ATOMIC_CLEAR_OFFSET / 4) - .write_volatile(bits); + write_bitmask_clear((*self.block).ctrl.as_ptr(), bits); } } @@ -638,14 +631,9 @@ impl<'sm, SM: ValidStateMachine> Drop for Synchronize<'sm, SM> { // Restart the clocks of all state machines specified by the mask. // Bits 11:8 of CTRL contain CLKDIV_RESTART. let sm_mask = self.sm_mask << 8; - const ATOMIC_SET_OFFSET: usize = 0x2000; // Safety: We only use the atomic alias of the register. unsafe { - (*self.sm.sm.block) - .ctrl - .as_ptr() - .add(ATOMIC_SET_OFFSET / 4) - .write_volatile(sm_mask as u32); + write_bitmask_set((*self.sm.sm.block).ctrl.as_ptr(), sm_mask as u32); } } }