aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Dario Nieuwenhuis <dirbaio@dirbaio.net> 2022-08-11 01:49:33 +0200
committerGravatar Dario Nieuwenhuis <dirbaio@dirbaio.net> 2022-08-11 23:31:08 +0200
commit3a15a6b4b320fa328e8ab99c31f81536960dd280 (patch)
tree4c3ef55e91988845873479460e438afea445c919
parent4e908625204a1e95dd3fd5bdcd8d66d6bc11c3bc (diff)
downloadcortex-m-3a15a6b4b320fa328e8ab99c31f81536960dd280.tar.gz
cortex-m-3a15a6b4b320fa328e8ab99c31f81536960dd280.tar.zst
cortex-m-3a15a6b4b320fa328e8ab99c31f81536960dd280.zip
Add implementation for critical-section 1.0
Co-Authored-By: Markus Reiter <me@reitermark.us>
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--.github/workflows/clippy.yml2
-rw-r--r--.github/workflows/on-target.yml4
-rw-r--r--.github/workflows/rt-ci.yml14
-rw-r--r--CHANGELOG.md2
-rw-r--r--Cargo.toml3
-rwxr-xr-xcortex-m-rt/ci/script.sh31
-rw-r--r--cortex-m-semihosting/Cargo.toml1
-rw-r--r--cortex-m-semihosting/src/export.rs10
-rw-r--r--src/critical_section.rs27
-rw-r--r--src/interrupt.rs22
-rw-r--r--src/lib.rs7
-rw-r--r--src/macros.rs2
-rw-r--r--src/peripheral/mod.rs3
-rw-r--r--src/peripheral/sau.rs5
-rw-r--r--testsuite/Cargo.toml1
-rw-r--r--testsuite/minitest/macros/src/lib.rs4
-rw-r--r--xtask/tests/ci.rs7
18 files changed, 95 insertions, 52 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index aa88a56..0d9b2b4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,6 +29,6 @@ jobs:
toolchain: ${{ matrix.rust }}
override: true
- name: Run tests
- run: cargo test --all --exclude cortex-m-rt --exclude testsuite
+ run: cargo test --all --exclude cortex-m-rt --exclude testsuite --features cortex-m/critical-section-single-core
# FIXME: test on macOS and Windows
diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml
index 75c61dc..ecfd0b9 100644
--- a/.github/workflows/clippy.yml
+++ b/.github/workflows/clippy.yml
@@ -23,4 +23,4 @@ jobs:
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
- args: --all
+ args: --all --features cortex-m/critical-section-single-core
diff --git a/.github/workflows/on-target.yml b/.github/workflows/on-target.yml
index 437c0ed..20121ee 100644
--- a/.github/workflows/on-target.yml
+++ b/.github/workflows/on-target.yml
@@ -22,7 +22,7 @@ jobs:
- name: Build testsuite
env:
RUSTFLAGS: -C link-arg=-Tlink.x -D warnings
- run: cargo build -p testsuite --target thumbv7m-none-eabi --features testsuite/semihosting
+ run: cargo build -p testsuite --target thumbv7m-none-eabi --features semihosting,cortex-m/critical-section-single-core
- name: Install QEMU
run: sudo apt-get update && sudo apt-get install qemu qemu-system-arm
- name: Run testsuite
@@ -51,7 +51,7 @@ jobs:
- name: Build testsuite
env:
RUSTFLAGS: -C link-arg=-Tlink.x -D warnings
- run: cargo build -p testsuite --target thumbv6m-none-eabi --features testsuite/rtt
+ run: cargo build -p testsuite --target thumbv6m-none-eabi --features rtt,cortex-m/critical-section-single-core
- name: Upload testsuite binaries
uses: actions/upload-artifact@v3
with:
diff --git a/.github/workflows/rt-ci.yml b/.github/workflows/rt-ci.yml
index c3efb0c..d46e48a 100644
--- a/.github/workflows/rt-ci.yml
+++ b/.github/workflows/rt-ci.yml
@@ -69,18 +69,18 @@ jobs:
- name: Install all Rust targets
run: rustup target install thumbv6m-none-eabi thumbv7m-none-eabi thumbv7em-none-eabi thumbv7em-none-eabihf thumbv8m.base-none-eabi thumbv8m.main-none-eabi thumbv8m.main-none-eabihf
- name: Build examples for thumbv6m-none-eabi
- run: cargo build --target=thumbv6m-none-eabi --examples
+ run: cargo build --target=thumbv6m-none-eabi --features cortex-m/critical-section-single-core --examples
- name: Build examples for thumbv7m-none-eabi
- run: cargo build --target=thumbv7m-none-eabi --examples
+ run: cargo build --target=thumbv7m-none-eabi --features cortex-m/critical-section-single-core --examples
- name: Build examples for thumbv7em-none-eabi
- run: cargo build --target=thumbv7em-none-eabi --examples
+ run: cargo build --target=thumbv7em-none-eabi --features cortex-m/critical-section-single-core --examples
- name: Build examples for thumbv7em-none-eabihf
- run: cargo build --target=thumbv7em-none-eabihf --examples
+ run: cargo build --target=thumbv7em-none-eabihf --features cortex-m/critical-section-single-core --examples
- name: Build examples for thumbv8m.base-none-eabi
- run: cargo build --target=thumbv8m.base-none-eabi --examples
+ run: cargo build --target=thumbv8m.base-none-eabi --features cortex-m/critical-section-single-core --examples
- name: Build examples for thumbv8m.main-none-eabi
- run: cargo build --target=thumbv8m.main-none-eabi --examples
+ run: cargo build --target=thumbv8m.main-none-eabi --features cortex-m/critical-section-single-core --examples
- name: Build examples for thumbv8m.main-none-eabihf
- run: cargo build --target=thumbv8m.main-none-eabihf --examples
+ run: cargo build --target=thumbv8m.main-none-eabihf --features cortex-m/critical-section-single-core --examples
- name: Build crate for host OS
run: cargo build
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2349607..ebcd2c4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,9 +17,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- TPIU: add `swo_supports` for checking what SWO configurations the target supports. (#381)
- Add `std` and `serde` crate features for improved host-side ITM decode functionality when working with the downstream `itm`, `cargo-rtic-scope` crates (#363, #366).
- Added the ability to name the statics generated by `singleton!()` for better debuggability (#364, #380).
+- Added `critical-section-single-core` feature which provides an implementation for the `critical_section` crate for single-core systems, based on disabling all interrupts. (#447)
### Fixed
- Fixed `singleton!()` statics sometimes ending up in `.data` instead of `.bss` (#364, #380).
+- `interrupt::free` no longer hands out a `CriticalSection` token because it is unsound on multi-core. Use `critical_section::with` instead. (#447)
### Changed
- Inline assembly is now always used, requiring Rust 1.59.
diff --git a/Cargo.toml b/Cargo.toml
index b4f23c0..f6ac580 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,7 +17,7 @@ rust-version = "1.59"
links = "cortex-m" # prevent multiple versions of this crate to be linked together
[dependencies]
-bare-metal = "1"
+critical-section = "1.0.0"
volatile-register = "0.2.0"
bitfield = "0.13.2"
embedded-hal = "0.2.4"
@@ -32,6 +32,7 @@ cm7 = []
cm7-r0p1 = ["cm7"]
linker-plugin-lto = []
std = []
+critical-section-single-core = ["critical-section/restore-state-bool"]
[workspace]
members = [
diff --git a/cortex-m-rt/ci/script.sh b/cortex-m-rt/ci/script.sh
index 4683566..2941e48 100755
--- a/cortex-m-rt/ci/script.sh
+++ b/cortex-m-rt/ci/script.sh
@@ -7,10 +7,13 @@ main() {
cargo check --target "$TARGET" --features device
+ # A `critical_section` implementation is always needed.
+ needed_features=cortex-m/critical-section-single-core
+
if [ "$TARGET" = x86_64-unknown-linux-gnu ] && [ "$TRAVIS_RUST_VERSION" = stable ]; then
( cd macros && cargo check && cargo test )
- cargo test --features device --test compiletest
+ cargo test --features "device,${needed_features}" --test compiletest
fi
local examples=(
@@ -43,25 +46,25 @@ main() {
if [ "$TARGET" != x86_64-unknown-linux-gnu ]; then
# Only test on stable and nightly, not MSRV.
if [ "$TRAVIS_RUST_VERSION" = stable ] || [ "$TRAVIS_RUST_VERSION" = nightly ]; then
- RUSTDOCFLAGS="-Cpanic=abort" cargo test --doc
+ RUSTDOCFLAGS="-Cpanic=abort" cargo test --features "${needed_features}" --doc
fi
for linker in "${linkers[@]}"; do
for ex in "${examples[@]}"; do
- cargo rustc --target "$TARGET" --example "$ex" -- $linker
- cargo rustc --target "$TARGET" --example "$ex" --release -- $linker
+ cargo rustc --target "$TARGET" --example "$ex" --features "${needed_features}" -- $linker
+ cargo rustc --target "$TARGET" --example "$ex" --features "${needed_features}" --release -- $linker
done
for ex in "${fail_examples[@]}"; do
- ! cargo rustc --target "$TARGET" --example "$ex" -- $linker
- ! cargo rustc --target "$TARGET" --example "$ex" --release -- $linker
+ ! cargo rustc --target "$TARGET" --example "$ex" --features "${needed_features}" -- $linker
+ ! cargo rustc --target "$TARGET" --example "$ex" --features "${needed_features}" --release -- $linker
done
- cargo rustc --target "$TARGET" --example device --features device -- $linker
- cargo rustc --target "$TARGET" --example device --features device --release -- $linker
+ cargo rustc --target "$TARGET" --example device --features "device,${needed_features}" -- $linker
+ cargo rustc --target "$TARGET" --example device --features "device,${needed_features}" --release -- $linker
- cargo rustc --target "$TARGET" --example minimal --features set-sp -- $linker
- cargo rustc --target "$TARGET" --example minimal --features set-sp --release -- $linker
- cargo rustc --target "$TARGET" --example minimal --features set-vtor -- $linker
- cargo rustc --target "$TARGET" --example minimal --features set-vtor --release -- $linker
+ cargo rustc --target "$TARGET" --example minimal --features "set-sp,${needed_features}" -- $linker
+ cargo rustc --target "$TARGET" --example minimal --features "set-sp,${needed_features}" --release -- $linker
+ cargo rustc --target "$TARGET" --example minimal --features "set-vtor,${needed_features}" -- $linker
+ cargo rustc --target "$TARGET" --example minimal --features "set-vtor,${needed_features}" --release -- $linker
done
fi
@@ -69,9 +72,9 @@ main() {
thumbv6m-none-eabi|thumbv7m-none-eabi)
for linker in "${linkers[@]}"; do
env RUSTFLAGS="$linker -C link-arg=-Tlink.x" cargo run \
- --target "$TARGET" --example qemu | grep "x = 42"
+ --target "$TARGET" --features "${needed_features}" --example qemu | grep "x = 42"
env RUSTFLAGS="$linker -C link-arg=-Tlink.x" cargo run \
- --target "$TARGET" --example qemu --release | grep "x = 42"
+ --target "$TARGET" --features "${needed_features}" --example qemu --release | grep "x = 42"
done
;;
diff --git a/cortex-m-semihosting/Cargo.toml b/cortex-m-semihosting/Cargo.toml
index 5afe0ac..ac0afa5 100644
--- a/cortex-m-semihosting/Cargo.toml
+++ b/cortex-m-semihosting/Cargo.toml
@@ -21,3 +21,4 @@ no-semihosting = []
[dependencies]
cortex-m = { path = "..", version = ">= 0.5.8, < 0.8" }
+critical-section = "1.0.0"
diff --git a/cortex-m-semihosting/src/export.rs b/cortex-m-semihosting/src/export.rs
index 0bbd09f..46e70e7 100644
--- a/cortex-m-semihosting/src/export.rs
+++ b/cortex-m-semihosting/src/export.rs
@@ -2,14 +2,12 @@
use core::fmt::{self, Write};
-use cortex_m::interrupt;
-
use crate::hio::{self, HostStream};
static mut HSTDOUT: Option<HostStream> = None;
pub fn hstdout_str(s: &str) {
- let _result = interrupt::free(|_| unsafe {
+ let _result = critical_section::with(|_| unsafe {
if HSTDOUT.is_none() {
HSTDOUT = Some(hio::hstdout()?);
}
@@ -19,7 +17,7 @@ pub fn hstdout_str(s: &str) {
}
pub fn hstdout_fmt(args: fmt::Arguments) {
- let _result = interrupt::free(|_| unsafe {
+ let _result = critical_section::with(|_| unsafe {
if HSTDOUT.is_none() {
HSTDOUT = Some(hio::hstdout()?);
}
@@ -31,7 +29,7 @@ pub fn hstdout_fmt(args: fmt::Arguments) {
static mut HSTDERR: Option<HostStream> = None;
pub fn hstderr_str(s: &str) {
- let _result = interrupt::free(|_| unsafe {
+ let _result = critical_section::with(|_| unsafe {
if HSTDERR.is_none() {
HSTDERR = Some(hio::hstderr()?);
}
@@ -41,7 +39,7 @@ pub fn hstderr_str(s: &str) {
}
pub fn hstderr_fmt(args: fmt::Arguments) {
- let _result = interrupt::free(|_| unsafe {
+ let _result = critical_section::with(|_| unsafe {
if HSTDERR.is_none() {
HSTDERR = Some(hio::hstderr()?);
}
diff --git a/src/critical_section.rs b/src/critical_section.rs
new file mode 100644
index 0000000..688058d
--- /dev/null
+++ b/src/critical_section.rs
@@ -0,0 +1,27 @@
+#[cfg(all(cortex_m, feature = "critical-section-single-core"))]
+mod single_core_critical_section {
+ use critical_section::{set_impl, Impl, RawRestoreState};
+
+ use crate::interrupt;
+ use crate::register::primask;
+
+ struct SingleCoreCriticalSection;
+ set_impl!(SingleCoreCriticalSection);
+
+ unsafe impl Impl for SingleCoreCriticalSection {
+ unsafe fn acquire() -> RawRestoreState {
+ let was_active = primask::read().is_active();
+ interrupt::disable();
+ was_active
+ }
+
+ unsafe fn release(was_active: RawRestoreState) {
+ // Only re-enable interrupts if they were enabled before the critical section.
+ if was_active {
+ interrupt::enable()
+ }
+ }
+ }
+}
+
+pub use critical_section::with;
diff --git a/src/interrupt.rs b/src/interrupt.rs
index 72450c4..f6ce990 100644
--- a/src/interrupt.rs
+++ b/src/interrupt.rs
@@ -1,6 +1,5 @@
//! Interrupts
-pub use bare_metal::{CriticalSection, Mutex};
#[cfg(cortex_m)]
use core::arch::asm;
#[cfg(cortex_m)]
@@ -27,7 +26,7 @@ pub unsafe trait InterruptNumber: Copy {
fn number(self) -> u16;
}
-/// Disables all interrupts
+/// Disables all interrupts in the current core.
#[cfg(cortex_m)]
#[inline]
pub fn disable() {
@@ -39,11 +38,11 @@ pub fn disable() {
compiler_fence(Ordering::SeqCst);
}
-/// Enables all the interrupts
+/// Enables all the interrupts in the current core.
///
/// # Safety
///
-/// - Do not call this function inside an `interrupt::free` critical section
+/// - Do not call this function inside a critical section.
#[cfg(cortex_m)]
#[inline]
pub unsafe fn enable() {
@@ -53,21 +52,26 @@ pub unsafe fn enable() {
asm!("cpsie i", options(nomem, nostack, preserves_flags));
}
-/// Execute closure `f` in an interrupt-free context.
+/// Execute closure `f` with interrupts disabled in the current core.
///
-/// This as also known as a "critical section".
+/// This method does not synchronise multiple cores and may disable required
+/// interrupts on some platforms; see the `critical-section` crate for a cross-platform
+/// way to enter a critical section which provides a `CriticalSection` token.
+///
+/// This crate provides an implementation for `critical-section` suitable for single-core systems,
+/// based on disabling all interrupts. It can be enabled with the `critical-section-single-core` feature.
#[cfg(cortex_m)]
#[inline]
pub fn free<F, R>(f: F) -> R
where
- F: FnOnce(&CriticalSection) -> R,
+ F: FnOnce() -> R,
{
let primask = crate::register::primask::read();
// disable interrupts
disable();
- let r = f(unsafe { &CriticalSection::new() });
+ let r = f();
// If the interrupts were active before our `disable` call, then re-enable
// them. Otherwise, keep them disabled
@@ -85,7 +89,7 @@ where
#[inline]
pub fn free<F, R>(_: F) -> R
where
- F: FnOnce(&CriticalSection) -> R,
+ F: FnOnce() -> R,
{
panic!("cortex_m::interrupt::free() is only functional on cortex-m platforms");
}
diff --git a/src/lib.rs b/src/lib.rs
index e430dd8..97fd97a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -43,15 +43,16 @@
// Don't warn about feature(asm) being stable on Rust >= 1.59.0
#![allow(stable_features)]
-extern crate bare_metal;
-extern crate volatile_register;
-
#[macro_use]
mod macros;
pub mod asm;
#[cfg(armv8m)]
pub mod cmse;
+// This is only public so the `singleton` macro does not require depending on
+// the `critical-section` crate separately.
+#[doc(hidden)]
+pub mod critical_section;
pub mod delay;
pub mod interrupt;
#[cfg(all(not(armv6m), not(armv8m_base)))]
diff --git a/src/macros.rs b/src/macros.rs
index 512c932..21bf78b 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -62,7 +62,7 @@ macro_rules! iprintln {
#[macro_export]
macro_rules! singleton {
($name:ident: $ty:ty = $expr:expr) => {
- $crate::interrupt::free(|_| {
+ $crate::critical_section::with(|_| {
// this is a tuple of a MaybeUninit and a bool because using an Option here is
// problematic: Due to niche-optimization, an Option could end up producing a non-zero
// initializer value which would move the entire static from `.bss` into `.data`...
diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs
index c316886..bf18151 100644
--- a/src/peripheral/mod.rs
+++ b/src/peripheral/mod.rs
@@ -57,7 +57,6 @@
//!
//! - ARMv7-M Architecture Reference Manual (Issue E.b) - Chapter B3
-use crate::interrupt;
use core::marker::PhantomData;
use core::ops;
@@ -164,7 +163,7 @@ impl Peripherals {
/// Returns all the core peripherals *once*
#[inline]
pub fn take() -> Option<Self> {
- interrupt::free(|_| {
+ critical_section::with(|_| {
if unsafe { TAKEN } {
None
} else {
diff --git a/src/peripheral/sau.rs b/src/peripheral/sau.rs
index da91aca..6b8477f 100644
--- a/src/peripheral/sau.rs
+++ b/src/peripheral/sau.rs
@@ -7,7 +7,6 @@
//!
//! For reference please check the section B8.3 of the Armv8-M Architecture Reference Manual.
-use crate::interrupt;
use crate::peripheral::SAU;
use bitfield::bitfield;
use volatile_register::{RO, RW};
@@ -162,7 +161,7 @@ impl SAU {
/// This function is executed under a critical section to prevent having inconsistent results.
#[inline]
pub fn set_region(&mut self, region_number: u8, region: SauRegion) -> Result<(), SauError> {
- interrupt::free(|_| {
+ critical_section::with(|_| {
let base_address = region.base_address;
let limit_address = region.limit_address;
let attribute = region.attribute;
@@ -215,7 +214,7 @@ impl SAU {
/// This function is executed under a critical section to prevent having inconsistent results.
#[inline]
pub fn get_region(&mut self, region_number: u8) -> Result<SauRegion, SauError> {
- interrupt::free(|_| {
+ critical_section::with(|_| {
if region_number >= self.region_numbers() {
Err(SauError::RegionNumberTooBig)
} else {
diff --git a/testsuite/Cargo.toml b/testsuite/Cargo.toml
index 17f1562..be3e43d 100644
--- a/testsuite/Cargo.toml
+++ b/testsuite/Cargo.toml
@@ -13,6 +13,7 @@ semihosting = ["cortex-m-semihosting", "minitest/semihosting"]
cortex-m-rt.path = "../cortex-m-rt"
cortex-m.path = ".."
minitest.path = "minitest"
+critical-section = "1.0.0"
[dependencies.rtt-target]
version = "0.3.1"
diff --git a/testsuite/minitest/macros/src/lib.rs b/testsuite/minitest/macros/src/lib.rs
index 6570502..e8a1087 100644
--- a/testsuite/minitest/macros/src/lib.rs
+++ b/testsuite/minitest/macros/src/lib.rs
@@ -215,8 +215,8 @@ fn tests_impl(args: TokenStream, input: TokenStream) -> parse::Result<TokenStrea
unsafe {
::rtt_target::set_print_channel_cs(
channels.up.0,
- &((|arg, f| cortex_m::interrupt::free(|_| f(arg)))
- as rtt_target::CriticalSectionFunc),
+ &((|arg, f| ::critical_section::with(|_| f(arg)))
+ as ::rtt_target::CriticalSectionFunc),
);
}
});
diff --git a/xtask/tests/ci.rs b/xtask/tests/ci.rs
index 603491c..1dc4754 100644
--- a/xtask/tests/ci.rs
+++ b/xtask/tests/ci.rs
@@ -32,6 +32,13 @@ fn build(package: &str, target: &str, features: &[&str]) {
cargo.args(&["--features", *feat]);
}
+ // A `critical_section` implementation is always needed.
+ if package == "cortex-m" {
+ cargo.args(&["--features", "critical-section-single-core"]);
+ } else {
+ cargo.args(&["--features", "cortex-m/critical-section-single-core"]);
+ }
+
// Cargo features don't work right when invoked from the workspace root, so change to the
// package's directory when necessary.
if package != "cortex-m" {