Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add implementation for critical-section 1.0 #447

Merged
merged 1 commit into from
Aug 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion .github/workflows/clippy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 2 additions & 2 deletions .github/workflows/on-target.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/rt-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -32,6 +32,7 @@ cm7 = []
cm7-r0p1 = ["cm7"]
linker-plugin-lto = []
std = []
critical-section-single-core = ["critical-section/restore-state-bool"]

[workspace]
members = [
Expand Down
31 changes: 17 additions & 14 deletions cortex-m-rt/ci/script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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=(
Expand Down Expand Up @@ -43,35 +46,35 @@ 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

case $TARGET in
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

;;
Expand Down
1 change: 1 addition & 0 deletions cortex-m-semihosting/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ no-semihosting = []

[dependencies]
cortex-m = { path = "..", version = ">= 0.5.8, < 0.8" }
critical-section = "1.0.0"
10 changes: 4 additions & 6 deletions cortex-m-semihosting/src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?);
}
Expand All @@ -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()?);
}
Expand All @@ -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()?);
}
Expand All @@ -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()?);
}
Expand Down
27 changes: 27 additions & 0 deletions src/critical_section.rs
Original file line number Diff line number Diff line change
@@ -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;
22 changes: 13 additions & 9 deletions src/interrupt.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Interrupts

pub use bare_metal::{CriticalSection, Mutex};
#[cfg(cortex_m)]
use core::arch::asm;
#[cfg(cortex_m)]
Expand All @@ -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() {
Expand All @@ -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() {
Expand All @@ -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
Expand All @@ -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");
}
7 changes: 4 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)))]
Expand Down
2 changes: 1 addition & 1 deletion src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`...
Expand Down
3 changes: 1 addition & 2 deletions src/peripheral/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
//!
//! - ARMv7-M Architecture Reference Manual (Issue E.b) - Chapter B3

use crate::interrupt;
use core::marker::PhantomData;
use core::ops;

Expand Down Expand Up @@ -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 {
Expand Down
5 changes: 2 additions & 3 deletions src/peripheral/sau.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions testsuite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 2 additions & 2 deletions testsuite/minitest/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
);
}
});
Expand Down
Loading