Skip to content

Commit

Permalink
Add priority-limited locks (esp-rs#2684)
Browse files Browse the repository at this point in the history
* Add priority-limited locks

* Rename to RawMutex, only provide lock() publicly

* Explode implementation and move into the interrupt module
  • Loading branch information
bugadani authored Dec 16, 2024
1 parent 3a03dd8 commit bc0bedd
Show file tree
Hide file tree
Showing 9 changed files with 480 additions and 105 deletions.
10 changes: 9 additions & 1 deletion esp-hal-embassy/src/executor/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,15 @@ This will use software-interrupt 3 which isn't available for anything else to wa
// sections in Xtensa are implemented via increasing `PS.INTLEVEL`.
// The critical section ends here. Take care not add code after
// `waiti` if it needs to be inside the CS.
unsafe { core::arch::asm!("waiti 0") };
// Do not lower INTLEVEL below the current value.
match token & 0x0F {
0 => unsafe { core::arch::asm!("waiti 0") },
1 => unsafe { core::arch::asm!("waiti 1") },
2 => unsafe { core::arch::asm!("waiti 2") },
3 => unsafe { core::arch::asm!("waiti 3") },
4 => unsafe { core::arch::asm!("waiti 4") },
_ => unsafe { core::arch::asm!("waiti 5") },
}
}
// If this races and some waker sets the signal, we'll reset it, but still poll.
SIGNAL_WORK_THREAD_MODE[cpu].store(false, Ordering::Relaxed);
Expand Down
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Dropped GPIO futures stop listening for interrupts (#2625)
- UART driver's `StopBits` enum variants now correctly use UpperCamelCase (#2669)
- The `PeripheralInput` and `PeripheralOutput` traits are now sealed (#2690)
- `esp_hal::sync::Lock` has been renamed to RawMutex (#2684)

### Fixed

Expand Down
88 changes: 87 additions & 1 deletion esp-hal/src/interrupt/riscv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ pub enum CpuInterrupt {
}

/// Interrupt priority levels.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
Expand Down Expand Up @@ -167,6 +167,32 @@ impl Priority {
}
}

impl TryFrom<u8> for Priority {
type Error = Error;

fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Priority::None),
1 => Ok(Priority::Priority1),
2 => Ok(Priority::Priority2),
3 => Ok(Priority::Priority3),
4 => Ok(Priority::Priority4),
5 => Ok(Priority::Priority5),
6 => Ok(Priority::Priority6),
7 => Ok(Priority::Priority7),
8 => Ok(Priority::Priority8),
9 => Ok(Priority::Priority9),
10 => Ok(Priority::Priority10),
11 => Ok(Priority::Priority11),
12 => Ok(Priority::Priority12),
13 => Ok(Priority::Priority13),
14 => Ok(Priority::Priority14),
15 => Ok(Priority::Priority15),
_ => Err(Error::InvalidInterruptPriority),
}
}
}

/// The interrupts reserved by the HAL
#[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")]
pub static RESERVED_INTERRUPTS: &[usize] = PRIORITY_TO_INTERRUPT;
Expand Down Expand Up @@ -681,6 +707,34 @@ mod classic {
let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR;
intr.cpu_int_thresh().write(|w| w.bits(stored_prio));
}

/// Get the current run level (the level below which interrupts are masked).
pub(crate) fn current_runlevel() -> Priority {
let intr = unsafe { crate::peripherals::INTERRUPT_CORE0::steal() };
let prev_interrupt_priority = intr.cpu_int_thresh().read().bits().saturating_sub(1) as u8;

unwrap!(Priority::try_from(prev_interrupt_priority))
}

/// Changes the current run level (the level below which interrupts are
/// masked), and returns the previous run level.
///
/// # Safety
///
/// This function must only be used to raise the runlevel and to restore it
/// to a previous value. It must not be used to arbitrarily lower the
/// runlevel.
pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
let prev_interrupt_priority = current_runlevel();

// The CPU responds to interrupts `>= level`, but we want to also disable
// interrupts at `level` so we set the threshold to `level + 1`.
crate::peripherals::INTERRUPT_CORE0::steal()
.cpu_int_thresh()
.write(|w| w.bits(level as u32 + 1));

prev_interrupt_priority
}
}

#[cfg(plic)]
Expand Down Expand Up @@ -817,4 +871,36 @@ mod plic {
plic.mxint_thresh()
.write(|w| w.cpu_mxint_thresh().bits(stored_prio as u8));
}

/// Get the current run level (the level below which interrupts are masked).
pub(crate) fn current_runlevel() -> Priority {
let prev_interrupt_priority = unsafe { crate::peripherals::PLIC_MX::steal() }
.mxint_thresh()
.read()
.cpu_mxint_thresh()
.bits()
.saturating_sub(1);

unwrap!(Priority::try_from(prev_interrupt_priority))
}

/// Changes the current run level (the level below which interrupts are
/// masked), and returns the previous run level.
///
/// # Safety
///
/// This function must only be used to raise the runlevel and to restore it
/// to a previous value. It must not be used to arbitrarily lower the
/// runlevel.
pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
let prev_interrupt_priority = current_runlevel();

// The CPU responds to interrupts `>= level`, but we want to also disable
// interrupts at `level` so we set the threshold to `level + 1`.
crate::peripherals::PLIC_MX::steal()
.mxint_thresh()
.write(|w| w.cpu_mxint_thresh().bits(level as u8 + 1));

prev_interrupt_priority
}
}
48 changes: 47 additions & 1 deletion esp-hal/src/interrupt/xtensa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,13 +334,45 @@ unsafe fn core1_interrupt_peripheral() -> *const crate::peripherals::interrupt_c
crate::peripherals::INTERRUPT_CORE1::PTR
}

/// Get the current run level (the level below which interrupts are masked).
pub(crate) fn current_runlevel() -> Priority {
let ps: u32;
unsafe { core::arch::asm!("rsr.ps {0}", out(reg) ps) };

let prev_interrupt_priority = ps as u8 & 0x0F;

unwrap!(Priority::try_from(prev_interrupt_priority))
}

/// Changes the current run level (the level below which interrupts are
/// masked), and returns the previous run level.
///
/// # Safety
///
/// This function must only be used to raise the runlevel and to restore it
/// to a previous value. It must not be used to arbitrarily lower the
/// runlevel.
pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
let token: u32;
match level {
Priority::None => core::arch::asm!("rsil {0}, 0", out(reg) token),
Priority::Priority1 => core::arch::asm!("rsil {0}, 1", out(reg) token),
Priority::Priority2 => core::arch::asm!("rsil {0}, 2", out(reg) token),
Priority::Priority3 => core::arch::asm!("rsil {0}, 3", out(reg) token),
};

let prev_interrupt_priority = token as u8 & 0x0F;

unwrap!(Priority::try_from(prev_interrupt_priority))
}

mod vectored {
use procmacros::ram;

use super::*;

/// Interrupt priority levels.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Priority {
Expand All @@ -366,6 +398,20 @@ mod vectored {
}
}

impl TryFrom<u8> for Priority {
type Error = Error;

fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Priority::None),
1 => Ok(Priority::Priority1),
2 => Ok(Priority::Priority2),
3 => Ok(Priority::Priority3),
_ => Err(Error::InvalidInterrupt),
}
}
}

impl CpuInterrupt {
#[inline]
fn level(&self) -> Priority {
Expand Down
Loading

0 comments on commit bc0bedd

Please sign in to comment.