Skip to content

Commit 9801539

Browse files
committed
Merge pull request #576 from sw/cs-msr
Optimize critical_section: use MSR instruction
1 parent 89cb6db commit 9801539

File tree

6 files changed

+60
-18
lines changed

6 files changed

+60
-18
lines changed

cortex-m/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ cm7-r0p1 = ["cm7"]
3838
inline-asm = []
3939
linker-plugin-lto = []
4040
std = []
41-
critical-section-single-core = ["critical-section/restore-state-bool"]
41+
critical-section-single-core = ["critical-section/restore-state-u32"]
4242

4343
[package.metadata.docs.rs]
4444
targets = [

cortex-m/src/critical_section.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,18 @@ mod single_core_critical_section {
1010

1111
unsafe impl Impl for SingleCoreCriticalSection {
1212
unsafe fn acquire() -> RawRestoreState {
13-
let was_active = primask::read().is_active();
13+
// Backup previous state of PRIMASK register. We access the entire register directly as a
14+
// u32 instead of using the primask::read() function to minimize the number of processor
15+
// cycles during which interrupts are disabled.
16+
let restore_state = primask::read_raw();
17+
// NOTE: Fence guarantees are provided by interrupt::disable(), which performs a `compiler_fence(SeqCst)`.
1418
interrupt::disable();
15-
was_active
19+
restore_state
1620
}
1721

18-
unsafe fn release(was_active: RawRestoreState) {
19-
// Only re-enable interrupts if they were enabled before the critical section.
20-
if was_active {
21-
interrupt::enable()
22-
}
22+
unsafe fn release(restore_state: RawRestoreState) {
23+
// NOTE: Fence guarantees are provided by primask::write_raw(), which performs a `compiler_fence(SeqCst)`.
24+
primask::write_raw(restore_state);
2325
}
2426
}
2527
}

cortex-m/src/interrupt.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,18 @@ pub fn free<F, R>(f: F) -> R
5656
where
5757
F: FnOnce(&CriticalSection) -> R,
5858
{
59-
let primask = crate::register::primask::read();
59+
// Backup previous state of PRIMASK register. We access the entire register directly as a
60+
// u32 instead of using the primask::read() function to minimize the number of processor
61+
// cycles during which interrupts are disabled.
62+
let primask = crate::register::primask::read_raw();
6063

6164
// disable interrupts
6265
disable();
6366

6467
let r = f(unsafe { &CriticalSection::new() });
6568

66-
// If the interrupts were active before our `disable` call, then re-enable
67-
// them. Otherwise, keep them disabled
68-
if primask.is_active() {
69-
unsafe { enable() }
69+
unsafe {
70+
crate::register::primask::write_raw(primask);
7071
}
7172

7273
r

cortex-m/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@
3535
//! or critical sections are managed as part of an RTOS. In these cases, you should use
3636
//! a target-specific implementation instead, typically provided by a HAL or RTOS crate.
3737
//!
38+
//! The critical section has been optimized to block interrupts for as few cycles as possible,
39+
//! but -- due to `critical-section` implementation details -- incurs branches in a normal build
40+
//! configuration. For minimal interrupt latency, you can achieve inlining by enabling
41+
//! [linker-plugin-based LTO](https://doc.rust-lang.org/rustc/linker-plugin-lto.html).
42+
//!
3843
//! ## `cm7-r0p1`
3944
//!
4045
//! This feature enables workarounds for errata found on Cortex-M7 chips with revision r0p1. Some

cortex-m/src/register/primask.rs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
//! Priority mask register
22
3+
#[cfg(cortex_m)]
4+
use core::arch::asm;
5+
#[cfg(cortex_m)]
6+
use core::sync::atomic::{compiler_fence, Ordering};
7+
38
/// All exceptions with configurable priority are ...
49
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
510
pub enum Primask {
@@ -23,13 +28,42 @@ impl Primask {
2328
}
2429
}
2530

26-
/// Reads the CPU register
31+
/// Reads the prioritizable interrupt mask
32+
#[cfg(cortex_m)]
2733
#[inline]
2834
pub fn read() -> Primask {
29-
let r: u32 = call_asm!(__primask_r() -> u32);
30-
if r & (1 << 0) == (1 << 0) {
35+
if read_raw() & (1 << 0) == (1 << 0) {
3136
Primask::Inactive
3237
} else {
3338
Primask::Active
3439
}
3540
}
41+
42+
/// Reads the entire PRIMASK register
43+
/// Note that bits [31:1] are reserved and UNK (Unknown)
44+
#[cfg(cortex_m)]
45+
#[inline]
46+
pub fn read_raw() -> u32 {
47+
let r: u32;
48+
unsafe { asm!("mrs {}, PRIMASK", out(reg) r, options(nomem, nostack, preserves_flags)) };
49+
r
50+
}
51+
52+
/// Writes the entire PRIMASK register
53+
/// Note that bits [31:1] are reserved and SBZP (Should-Be-Zero-or-Preserved)
54+
///
55+
/// # Safety
56+
///
57+
/// This method is unsafe as other unsafe code may rely on interrupts remaining disabled, for
58+
/// example during a critical section, and being able to safely re-enable them would lead to
59+
/// undefined behaviour. Do not call this function in a context where interrupts are expected to
60+
/// remain disabled -- for example, in the midst of a critical section or `interrupt::free()` call.
61+
#[cfg(cortex_m)]
62+
#[inline]
63+
pub unsafe fn write_raw(r: u32) {
64+
// Ensure no preceeding memory accesses are reordered to after interrupts are possibly enabled.
65+
compiler_fence(Ordering::SeqCst);
66+
unsafe { asm!("msr PRIMASK, {}", in(reg) r, options(nomem, nostack, preserves_flags)) };
67+
// Ensure no subsequent memory accesses are reordered to before interrupts are possibly disabled.
68+
compiler_fence(Ordering::SeqCst);
69+
}

testsuite/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ mod tests {
7777
#[test]
7878
fn interrupt_free_nesting() {
7979
EXCEPTION_FLAG.store(false, Ordering::SeqCst);
80-
cortex_m::interrupt::free(|_| {
81-
cortex_m::interrupt::free(|_| {
80+
cortex_m::interrupt::free(|| {
81+
cortex_m::interrupt::free(|| {
8282
cortex_m::peripheral::SCB::set_pendsv();
8383
assert!(!EXCEPTION_FLAG.load(Ordering::SeqCst));
8484
});

0 commit comments

Comments
 (0)