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
This commit is contained in:
Jan Niehusmann 2021-10-22 14:30:30 +02:00 committed by GitHub
parent 0eb65ee99b
commit bac6714f09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 26 deletions

View file

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

View file

@ -4,8 +4,10 @@ use super::{
InputOverride, Interrupt, InterruptOverride, OutputDriveStrength, OutputEnableOverride, InputOverride, Interrupt, InterruptOverride, OutputDriveStrength, OutputEnableOverride,
OutputOverride, OutputSlewRate, OutputOverride, OutputSlewRate,
}; };
use crate::atomic_register_access::{write_bitmask_clear, write_bitmask_set};
use crate::gpio::dynpin::{DynDisabled, DynFunction, DynInput, DynOutput, DynPinMode}; use crate::gpio::dynpin::{DynDisabled, DynFunction, DynInput, DynOutput, DynPinMode};
use crate::pac; use crate::pac;
use core::ptr::read_volatile;
//============================================================================== //==============================================================================
// ModeFields // ModeFields
@ -278,7 +280,7 @@ pub(super) unsafe trait RegisterInterface {
.as_ptr() .as_ptr()
.add(num / 8 + cpuid as usize * 12); .add(num / 8 + cpuid as usize * 12);
let bit_in_reg = num % 8 * 4 + interrupt as usize; 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() .as_ptr()
.add(num / 8 + cpuid as usize * 12); .add(num / 8 + cpuid as usize * 12);
let bit_in_reg = num % 8 * 4 + interrupt as usize; 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); .add(num / 8 + cpuid as usize * 12);
let bit_in_reg = num % 8 * 4 + interrupt as usize; let bit_in_reg = num % 8 * 4 + interrupt as usize;
if enabled { if enabled {
*reg |= 1 << bit_in_reg; write_bitmask_set(reg, 1 << bit_in_reg);
} else { } 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() .as_ptr()
.add(num / 8 + cpuid as usize * 12); .add(num / 8 + cpuid as usize * 12);
let bit_in_reg = num % 8 * 4 + interrupt as usize; 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); .add(num / 8 + cpuid as usize * 12);
let bit_in_reg = num % 8 * 4 + interrupt as usize; let bit_in_reg = num % 8 * 4 + interrupt as usize;
if forced { if forced {
*reg |= 1 << bit_in_reg; write_bitmask_set(reg, 1 << bit_in_reg);
} else { } else {
*reg &= !(1 << bit_in_reg); write_bitmask_clear(reg, 1 << bit_in_reg);
} }
} }
} }

View file

@ -14,6 +14,7 @@ pub use paste;
pub extern crate rp2040_pac as pac; pub extern crate rp2040_pac as pac;
pub mod adc; pub mod adc;
pub(crate) mod atomic_register_access;
pub mod clocks; pub mod clocks;
pub mod gpio; pub mod gpio;
pub mod i2c; pub mod i2c;

View file

@ -1,6 +1,9 @@
//! Programmable IO (PIO) //! Programmable IO (PIO)
/// See [Chapter 3](https://rptl.io/pico-datasheet) for more details. /// 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 pio::{Program, SideSet, Wrap};
use rp2040_pac::{PIO0, PIO1}; use rp2040_pac::{PIO0, PIO1};
@ -430,26 +433,16 @@ impl<SM: ValidStateMachine> UninitStateMachine<SM> {
} }
fn set_ctrl_bits(&mut self, bits: u32) { fn set_ctrl_bits(&mut self, bits: u32) {
const ATOMIC_SET_OFFSET: usize = 0x2000;
// Safety: We only use the atomic alias of the register. // Safety: We only use the atomic alias of the register.
unsafe { unsafe {
(*self.block) write_bitmask_set((*self.block).ctrl.as_ptr(), bits);
.ctrl
.as_ptr()
.add(ATOMIC_SET_OFFSET / 4)
.write_volatile(bits);
} }
} }
fn clear_ctrl_bits(&mut self, bits: u32) { fn clear_ctrl_bits(&mut self, bits: u32) {
const ATOMIC_CLEAR_OFFSET: usize = 0x3000;
// Safety: We only use the atomic alias of the register. // Safety: We only use the atomic alias of the register.
unsafe { unsafe {
(*self.block) write_bitmask_clear((*self.block).ctrl.as_ptr(), bits);
.ctrl
.as_ptr()
.add(ATOMIC_CLEAR_OFFSET / 4)
.write_volatile(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. // Restart the clocks of all state machines specified by the mask.
// Bits 11:8 of CTRL contain CLKDIV_RESTART. // Bits 11:8 of CTRL contain CLKDIV_RESTART.
let sm_mask = self.sm_mask << 8; let sm_mask = self.sm_mask << 8;
const ATOMIC_SET_OFFSET: usize = 0x2000;
// Safety: We only use the atomic alias of the register. // Safety: We only use the atomic alias of the register.
unsafe { unsafe {
(*self.sm.sm.block) write_bitmask_set((*self.sm.sm.block).ctrl.as_ptr(), sm_mask as u32);
.ctrl
.as_ptr()
.add(ATOMIC_SET_OFFSET / 4)
.write_volatile(sm_mask as u32);
} }
} }
} }