aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/bors.toml6
-rw-r--r--.github/workflows/ci.yml6
-rw-r--r--.github/workflows/clippy.yml6
-rw-r--r--.github/workflows/cron.yml10
-rw-r--r--.github/workflows/on-target.yml83
-rw-r--r--.github/workflows/rt-ci.yml21
-rw-r--r--.github/workflows/rustfmt.yml2
-rw-r--r--CHANGELOG.md6
-rw-r--r--Cargo.toml14
-rw-r--r--README.md2
-rw-r--r--asm-toolchain1
-rw-r--r--asm/inline.rs448
-rw-r--r--asm/lib.rs143
-rw-r--r--bin/thumbv6m-none-eabi-lto.abin11196 -> 0 bytes
-rw-r--r--bin/thumbv6m-none-eabi.abin14576 -> 0 bytes
-rw-r--r--bin/thumbv7em-none-eabi-lto.abin15280 -> 0 bytes
-rw-r--r--bin/thumbv7em-none-eabi.abin19336 -> 0 bytes
-rw-r--r--bin/thumbv7em-none-eabihf-lto.abin16104 -> 0 bytes
-rw-r--r--bin/thumbv7em-none-eabihf.abin20480 -> 0 bytes
-rw-r--r--bin/thumbv7m-none-eabi-lto.abin14244 -> 0 bytes
-rw-r--r--bin/thumbv7m-none-eabi.abin18068 -> 0 bytes
-rw-r--r--bin/thumbv8m.base-none-eabi-lto.abin14280 -> 0 bytes
-rw-r--r--bin/thumbv8m.base-none-eabi.abin18680 -> 0 bytes
-rw-r--r--bin/thumbv8m.main-none-eabi-lto.abin18672 -> 0 bytes
-rw-r--r--bin/thumbv8m.main-none-eabi.abin24408 -> 0 bytes
-rw-r--r--bin/thumbv8m.main-none-eabihf-lto.abin19508 -> 0 bytes
-rw-r--r--bin/thumbv8m.main-none-eabihf.abin25524 -> 0 bytes
-rw-r--r--build.rs24
-rw-r--r--cortex-m-rt/CHANGELOG.md14
-rw-r--r--cortex-m-rt/Cargo.toml7
-rw-r--r--cortex-m-rt/README.md2
-rw-r--r--cortex-m-rt/asm.S113
-rwxr-xr-xcortex-m-rt/assemble.sh33
-rw-r--r--cortex-m-rt/bin/thumbv6m-none-eabi.abin2738 -> 0 bytes
-rw-r--r--cortex-m-rt/bin/thumbv7em-none-eabi.abin2746 -> 0 bytes
-rw-r--r--cortex-m-rt/bin/thumbv7em-none-eabihf.abin2778 -> 0 bytes
-rw-r--r--cortex-m-rt/bin/thumbv7m-none-eabi.abin2746 -> 0 bytes
-rw-r--r--cortex-m-rt/bin/thumbv8m.base-none-eabi.abin2742 -> 0 bytes
-rw-r--r--cortex-m-rt/bin/thumbv8m.main-none-eabi.abin2750 -> 0 bytes
-rw-r--r--cortex-m-rt/bin/thumbv8m.main-none-eabihf.abin2782 -> 0 bytes
-rw-r--r--cortex-m-rt/build.rs15
-rwxr-xr-xcortex-m-rt/check-blobs.sh21
-rwxr-xr-xcortex-m-rt/ci/script.sh32
-rw-r--r--cortex-m-rt/examples/qemu.rs30
-rw-r--r--cortex-m-rt/link.x.in8
-rw-r--r--cortex-m-rt/macros/Cargo.toml3
-rw-r--r--cortex-m-rt/src/lib.rs213
-rw-r--r--cortex-m-rt/tests/compile-fail/non-static-resource.rs4
-rw-r--r--cortex-m-semihosting/CHANGELOG.md8
-rw-r--r--cortex-m-semihosting/Cargo.toml7
-rw-r--r--cortex-m-semihosting/README.md2
l---------cortex-m-semihosting/bin1
-rw-r--r--cortex-m-semihosting/build.rs16
-rw-r--r--cortex-m-semihosting/src/export.rs10
-rw-r--r--cortex-m-semihosting/src/lib.rs33
-rw-r--r--panic-semihosting/CHANGELOG.md8
-rw-r--r--panic-semihosting/Cargo.toml7
-rw-r--r--panic-semihosting/README.md2
-rw-r--r--panic-semihosting/src/lib.rs8
-rw-r--r--src/asm.rs167
-rw-r--r--src/call_asm.rs24
-rw-r--r--src/cmse.rs6
-rw-r--r--src/critical_section.rs22
-rw-r--r--src/interrupt.rs51
-rw-r--r--src/lib.rs58
-rw-r--r--src/macros.rs7
-rw-r--r--src/peripheral/dwt.rs12
-rw-r--r--src/peripheral/mod.rs4
-rw-r--r--src/peripheral/sau.rs5
-rw-r--r--src/peripheral/tpiu.rs1
-rw-r--r--src/prelude.rs3
-rw-r--r--src/register/apsr.rs9
-rw-r--r--src/register/basepri.rs24
-rw-r--r--src/register/basepri_max.rs23
-rw-r--r--src/register/control.rs23
-rw-r--r--src/register/faultmask.rs7
-rw-r--r--src/register/fpscr.rs7
-rw-r--r--src/register/lr.rs15
-rw-r--r--src/register/mod.rs5
-rw-r--r--src/register/msp.rs21
-rw-r--r--src/register/msplim.rs8
-rw-r--r--src/register/pc.rs15
-rw-r--r--src/register/primask.rs7
-rw-r--r--src/register/psp.rs13
-rw-r--r--src/register/psplim.rs8
-rw-r--r--testsuite/.cargo/config.toml6
-rw-r--r--testsuite/Cargo.toml24
-rw-r--r--testsuite/README.md69
-rw-r--r--testsuite/build.rs18
-rw-r--r--testsuite/minitest/Cargo.toml23
-rw-r--r--testsuite/minitest/README.md7
-rw-r--r--testsuite/minitest/macros/Cargo.toml18
-rw-r--r--testsuite/minitest/macros/src/lib.rs331
-rw-r--r--testsuite/minitest/src/export.rs13
-rw-r--r--testsuite/minitest/src/lib.rs70
-rw-r--r--testsuite/src/main.rs54
-rw-r--r--xtask/src/lib.rs195
-rw-r--r--xtask/src/main.rs6
-rw-r--r--xtask/tests/ci.rs23
99 files changed, 1418 insertions, 1353 deletions
diff --git a/.github/bors.toml b/.github/bors.toml
index 4402e95..17cef85 100644
--- a/.github/bors.toml
+++ b/.github/bors.toml
@@ -3,11 +3,13 @@ delete_merged_branches = true
required_approvals = 1
status = [
"ci-linux (stable)",
- "ci-linux (1.42.0)",
+ "ci-linux (1.59.0)",
"rt-ci-linux (stable)",
- "rt-ci-linux (1.42.0)",
+ "rt-ci-linux (1.59.0)",
"rt-ci-other-os (macOS-latest)",
"rt-ci-other-os (windows-latest)",
+ "hil-qemu",
+ "hil-compile-rtt",
"rustfmt",
"clippy",
]
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8caebd0..0d9b2b4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,19 +16,19 @@ jobs:
include:
# Test MSRV
- - rust: 1.42.0
+ - rust: 1.59.0
# Test nightly but don't fail
- rust: nightly
experimental: true
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- name: Run tests
- run: cargo test --all --exclude cortex-m-rt
+ 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 5a76037..ecfd0b9 100644
--- a/.github/workflows/clippy.yml
+++ b/.github/workflows/clippy.yml
@@ -8,11 +8,11 @@ jobs:
clippy:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
if: github.event_name == 'pull_request_target'
with:
ref: refs/pull/${{ github.event.number }}/head
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
if: github.event_name != 'pull_request_target'
- uses: actions-rs/toolchain@v1
with:
@@ -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/cron.yml b/.github/workflows/cron.yml
index 24b547d..866143a 100644
--- a/.github/workflows/cron.yml
+++ b/.github/workflows/cron.yml
@@ -9,15 +9,15 @@ jobs:
ci-linux:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- name: Run tests
- run: cargo test --all --exclude cortex-m-rt
- - uses: imjohnbo/issue-bot@v2
+ run: cargo test --all --exclude cortex-m-rt --exclude testsuite
+ - uses: imjohnbo/issue-bot@v3
if: failure()
with:
title: CI Failure
@@ -36,7 +36,7 @@ jobs:
run:
working-directory: cortex-m-rt
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
@@ -62,7 +62,7 @@ jobs:
run: TARGET=thumbv8m.main-none-eabi TRAVIS_RUST_VERSION=stable bash ci/script.sh
- name: Run CI script for thumbv8m.main-none-eabihf under stable
run: TARGET=thumbv8m.main-none-eabihf TRAVIS_RUST_VERSION=stable bash ci/script.sh
- - uses: imjohnbo/issue-bot@v2
+ - uses: imjohnbo/issue-bot@v3
if: failure()
with:
title: CI Failure
diff --git a/.github/workflows/on-target.yml b/.github/workflows/on-target.yml
new file mode 100644
index 0000000..20121ee
--- /dev/null
+++ b/.github/workflows/on-target.yml
@@ -0,0 +1,83 @@
+on:
+ push:
+ branches: [ staging, trying, master ]
+ pull_request:
+ # allows manual triggering
+ workflow_dispatch:
+
+name: cortex-m on-target tests
+
+jobs:
+
+ hil-qemu:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: stable
+ override: true
+ target: thumbv7m-none-eabi
+ - name: Build testsuite
+ env:
+ RUSTFLAGS: -C link-arg=-Tlink.x -D warnings
+ 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
+ run: |
+ qemu-system-arm \
+ -cpu cortex-m3 \
+ -machine lm3s6965evb \
+ -nographic \
+ -semihosting-config enable=on,target=native \
+ -kernel target/thumbv7m-none-eabi/debug/testsuite
+
+ hil-compile-rtt:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: stable
+ override: true
+ target: thumbv6m-none-eabi
+ - name: Modify linkerfile
+ run: |
+ sed -i 's/FLASH : ORIGIN = 0x00000000, LENGTH = 256K/FLASH : ORIGIN = 0x8000000, LENGTH = 128K/g' memory.x
+ sed -i 's/RAM : ORIGIN = 0x20000000, LENGTH = 64K/RAM : ORIGIN = 0x20000000, LENGTH = 16K/g' memory.x
+ - name: Build testsuite
+ env:
+ RUSTFLAGS: -C link-arg=-Tlink.x -D warnings
+ 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:
+ name: testsuite-bin
+ if-no-files-found: error
+ retention-days: 1
+ path: target/thumbv6m-none-eabi/debug/testsuite
+
+ hil-stm32:
+ runs-on: self-hosted
+ needs:
+ - hil-compile-rtt
+ steps:
+ - uses: actions/checkout@v3
+ - name: Display probe-run version
+ run: probe-run --version
+ - name: List probes
+ run: probe-run --list-probes
+ - uses: actions/download-artifact@v3
+ with:
+ name: testsuite-bin
+ path: testsuite-bin
+ - name: Run on-target tests
+ timeout-minutes: 5
+ run: |
+ probe-run \
+ --chip STM32F070RBTx \
+ --connect-under-reset \
+ testsuite-bin/testsuite
diff --git a/.github/workflows/rt-ci.yml b/.github/workflows/rt-ci.yml
index 8b95612..d46e48a 100644
--- a/.github/workflows/rt-ci.yml
+++ b/.github/workflows/rt-ci.yml
@@ -11,8 +11,7 @@ jobs:
continue-on-error: ${{ matrix.experimental || false }}
strategy:
matrix:
- # All generated code should be running on stable now
- rust: [nightly, stable, 1.42.0]
+ rust: [nightly, stable, 1.59.0]
include:
# Nightly is only for reference and allowed to fail
@@ -22,7 +21,7 @@ jobs:
run:
working-directory: cortex-m-rt
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
@@ -61,7 +60,7 @@ jobs:
run:
working-directory: cortex-m-rt
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
@@ -70,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/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml
index bd5997c..c29106e 100644
--- a/.github/workflows/rustfmt.yml
+++ b/.github/workflows/rustfmt.yml
@@ -10,7 +10,7 @@ jobs:
name: rustfmt
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 421dce7..ebcd2c4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,12 +17,18 @@ 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.
### Removed
- removed all peripherals `ptr()` functions in favor of the associated constant `PTR` (#385).
+- removed `inline-asm` feature which is now always enabled
## [v0.7.4] - 2021-12-31
diff --git a/Cargo.toml b/Cargo.toml
index 8527a89..f6ac580 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,11 +12,12 @@ name = "cortex-m"
readme = "README.md"
repository = "https://github.com/rust-embedded/cortex-m"
version = "0.7.4"
-edition = "2018"
+edition = "2021"
+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"
@@ -29,17 +30,20 @@ optional = true
[features]
cm7 = []
cm7-r0p1 = ["cm7"]
-inline-asm = []
linker-plugin-lto = []
std = []
+critical-section-single-core = ["critical-section/restore-state-bool"]
[workspace]
members = [
- "xtask",
"cortex-m-rt",
"cortex-m-semihosting",
+ "panic-itm",
"panic-semihosting",
- "panic-itm"
+ "testsuite",
+ "testsuite/minitest",
+ "testsuite/minitest/macros",
+ "xtask",
]
[package.metadata.docs.rs]
diff --git a/README.md b/README.md
index df9fd52..a045765 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ This project is developed and maintained by the [Cortex-M team][team].
## Minimum Supported Rust Version (MSRV)
-This crate is guaranteed to compile on stable Rust 1.42 and up. It might compile with older versions but that may change in any new patch release.
+This crate is guaranteed to compile on stable Rust 1.59 and up. It might compile with older versions but that may change in any new patch release.
## License
diff --git a/asm-toolchain b/asm-toolchain
deleted file mode 100644
index cc5dbb2..0000000
--- a/asm-toolchain
+++ /dev/null
@@ -1 +0,0 @@
-nightly-2021-12-16
diff --git a/asm/inline.rs b/asm/inline.rs
deleted file mode 100644
index bbc04d2..0000000
--- a/asm/inline.rs
+++ /dev/null
@@ -1,448 +0,0 @@
-//! Inline assembly implementing the routines exposed in `cortex_m::asm`.
-//!
-//! If the `inline-asm` feature is enabled, these functions will be directly called by the
-//! `cortex-m` wrappers. Otherwise, `cortex-m` links against them via prebuilt archives.
-//!
-//! All of these functions should be blanket-`unsafe`. `cortex-m` provides safe wrappers where
-//! applicable.
-
-use core::arch::asm;
-use core::sync::atomic::{compiler_fence, Ordering};
-
-#[inline(always)]
-pub unsafe fn __bkpt() {
- asm!("bkpt", options(nomem, nostack, preserves_flags));
-}
-
-#[inline(always)]
-pub unsafe fn __control_r() -> u32 {
- let r;
- asm!("mrs {}, CONTROL", out(reg) r, options(nomem, nostack, preserves_flags));
- r
-}
-
-#[inline(always)]
-pub unsafe fn __control_w(w: u32) {
- // ISB is required after writing to CONTROL,
- // per ARM architectural requirements (see Application Note 321).
- asm!(
- "msr CONTROL, {}",
- "isb",
- in(reg) w,
- options(nomem, nostack, preserves_flags),
- );
-
- // Ensure memory accesses are not reordered around the CONTROL update.
- compiler_fence(Ordering::SeqCst);
-}
-
-#[inline(always)]
-pub unsafe fn __cpsid() {
- asm!("cpsid i", options(nomem, nostack, preserves_flags));
-
- // Ensure no subsequent memory accesses are reordered to before interrupts are disabled.
- compiler_fence(Ordering::SeqCst);
-}
-
-#[inline(always)]
-pub unsafe fn __cpsie() {
- // Ensure no preceeding memory accesses are reordered to after interrupts are enabled.
- compiler_fence(Ordering::SeqCst);
-
- asm!("cpsie i", options(nomem, nostack, preserves_flags));
-}
-
-#[inline(always)]
-pub unsafe fn __delay(cyc: u32) {
- // The loop will normally take 3 to 4 CPU cycles per iteration, but superscalar cores
- // (eg. Cortex-M7) can potentially do it in 2, so we use that as the lower bound, since delaying
- // for more cycles is okay.
- // Add 1 to prevent an integer underflow which would cause a long freeze
- let real_cyc = 1 + cyc / 2;
- asm!(
- // Use local labels to avoid R_ARM_THM_JUMP8 relocations which fail on thumbv6m.
- "1:",
- "subs {}, #1",
- "bne 1b",
- inout(reg) real_cyc => _,
- options(nomem, nostack),
- );
-}
-
-#[inline(always)]
-pub unsafe fn __dmb() {
- compiler_fence(Ordering::SeqCst);
- asm!("dmb", options(nomem, nostack, preserves_flags));
- compiler_fence(Ordering::SeqCst);
-}
-
-#[inline(always)]
-pub unsafe fn __dsb() {
- compiler_fence(Ordering::SeqCst);
- asm!("dsb", options(nomem, nostack, preserves_flags));
- compiler_fence(Ordering::SeqCst);
-}
-
-#[inline(always)]
-pub unsafe fn __isb() {
- compiler_fence(Ordering::SeqCst);
- asm!("isb", options(nomem, nostack, preserves_flags));
- compiler_fence(Ordering::SeqCst);
-}
-
-#[inline(always)]
-pub unsafe fn __msp_r() -> u32 {
- let r;
- asm!("mrs {}, MSP", out(reg) r, options(nomem, nostack, preserves_flags));
- r
-}
-
-#[inline(always)]
-pub unsafe fn __msp_w(val: u32) {
- // Technically is writing to the stack pointer "not pushing any data to the stack"?
- // In any event, if we don't set `nostack` here, this method is useless as the new
- // stack value is immediately mutated by returning. Really this is just not a good
- // method and its higher-level use is marked as deprecated in cortex-m.
- asm!("msr MSP, {}", in(reg) val, options(nomem, nostack, preserves_flags));
-}
-
-// NOTE: No FFI shim, this requires inline asm.
-#[inline(always)]
-pub unsafe fn __apsr_r() -> u32 {
- let r;
- asm!("mrs {}, APSR", out(reg) r, options(nomem, nostack, preserves_flags));
- r
-}
-
-#[inline(always)]
-pub unsafe fn __nop() {
- // NOTE: This is a `pure` asm block, but applying that option allows the compiler to eliminate
- // the nop entirely (or to collapse multiple subsequent ones). Since the user probably wants N
- // nops when they call `nop` N times, let's not add that option.
- asm!("nop", options(nomem, nostack, preserves_flags));
-}
-
-// NOTE: No FFI shim, this requires inline asm.
-#[inline(always)]
-pub unsafe fn __pc_r() -> u32 {
- let r;
- asm!("mov {}, pc", out(reg) r, options(nomem, nostack, preserves_flags));
- r
-}
-
-// NOTE: No FFI shim, this requires inline asm.
-#[inline(always)]
-pub unsafe fn __pc_w(val: u32) {
- asm!("mov pc, {}", in(reg) val, options(nomem, nostack, preserves_flags));
-}
-
-// NOTE: No FFI shim, this requires inline asm.
-#[inline(always)]
-pub unsafe fn __lr_r() -> u32 {
- let r;
- asm!("mov {}, lr", out(reg) r, options(nomem, nostack, preserves_flags));
- r
-}
-
-// NOTE: No FFI shim, this requires inline asm.
-#[inline(always)]
-pub unsafe fn __lr_w(val: u32) {
- asm!("mov lr, {}", in(reg) val, options(nomem, nostack, preserves_flags));
-}
-
-#[inline(always)]
-pub unsafe fn __primask_r() -> u32 {
- let r;
- asm!("mrs {}, PRIMASK", out(reg) r, options(nomem, nostack, preserves_flags));
- r
-}
-
-#[inline(always)]
-pub unsafe fn __psp_r() -> u32 {
- let r;
- asm!("mrs {}, PSP", out(reg) r, options(nomem, nostack, preserves_flags));
- r
-}
-
-#[inline(always)]
-pub unsafe fn __psp_w(val: u32) {
- // See comment on __msp_w. Unlike MSP, there are legitimate use-cases for modifying PSP
- // if MSP is currently being used as the stack pointer.
- asm!("msr PSP, {}", in(reg) val, options(nomem, nostack, preserves_flags));
-}
-
-#[inline(always)]
-pub unsafe fn __sev() {
- asm!("sev", options(nomem, nostack, preserves_flags));
-}
-
-#[inline(always)]
-pub unsafe fn __udf() -> ! {
- asm!("udf #0", options(noreturn, nomem, nostack, preserves_flags));
-}
-
-#[inline(always)]
-pub unsafe fn __wfe() {
- asm!("wfe", options(nomem, nostack, preserves_flags));
-}
-
-#[inline(always)]
-pub unsafe fn __wfi() {
- asm!("wfi", options(nomem, nostack, preserves_flags));
-}
-
-/// Semihosting syscall.
-#[inline(always)]
-pub unsafe fn __sh_syscall(mut nr: u32, arg: u32) -> u32 {
- asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nomem, nostack, preserves_flags));
- nr
-}
-
-/// Set CONTROL.SPSEL to 0, write `msp` to MSP, branch to `rv`.
-#[inline(always)]
-pub unsafe fn __bootstrap(msp: u32, rv: u32) -> ! {
- asm!(
- "mrs {tmp}, CONTROL",
- "bics {tmp}, {spsel}",
- "msr CONTROL, {tmp}",
- "isb",
- "msr MSP, {msp}",
- "bx {rv}",
- // `out(reg) _` is not permitted in a `noreturn` asm! call,
- // so instead use `in(reg) 0` and don't restore it afterwards.
- tmp = in(reg) 0,
- spsel = in(reg) 2,
- msp = in(reg) msp,
- rv = in(reg) rv,
- options(noreturn, nomem, nostack),
- );
-}
-
-// v7m *AND* v8m.main, but *NOT* v8m.base
-#[cfg(any(armv7m, armv8m_main))]
-pub use self::v7m::*;
-#[cfg(any(armv7m, armv8m_main))]
-mod v7m {
- use core::arch::asm;
- use core::sync::atomic::{compiler_fence, Ordering};
-
- #[inline(always)]
- pub unsafe fn __basepri_max(val: u8) {
- asm!("msr BASEPRI_MAX, {}", in(reg) val, options(nomem, nostack, preserves_flags));
- }
-
- #[inline(always)]
- pub unsafe fn __basepri_r() -> u8 {
- let r;
- asm!("mrs {}, BASEPRI", out(reg) r, options(nomem, nostack, preserves_flags));
- r
- }
-
- #[inline(always)]
- pub unsafe fn __basepri_w(val: u8) {
- asm!("msr BASEPRI, {}", in(reg) val, options(nomem, nostack, preserves_flags));
- }
-
- #[inline(always)]
- pub unsafe fn __faultmask_r() -> u32 {
- let r;
- asm!("mrs {}, FAULTMASK", out(reg) r, options(nomem, nostack, preserves_flags));
- r
- }
-
- #[inline(always)]
- pub unsafe fn __enable_icache() {
- asm!(
- "ldr {0}, =0xE000ED14", // CCR
- "mrs {2}, PRIMASK", // save critical nesting info
- "cpsid i", // mask interrupts
- "ldr {1}, [{0}]", // read CCR
- "orr.w {1}, {1}, #(1 << 17)", // Set bit 17, IC
- "str {1}, [{0}]", // write it back
- "dsb", // ensure store completes
- "isb", // synchronize pipeline
- "msr PRIMASK, {2}", // unnest critical section
- out(reg) _,
- out(reg) _,
- out(reg) _,
- options(nostack),
- );
- compiler_fence(Ordering::SeqCst);
- }
-
- #[inline(always)]
- pub unsafe fn __enable_dcache() {
- asm!(
- "ldr {0}, =0xE000ED14", // CCR
- "mrs {2}, PRIMASK", // save critical nesting info
- "cpsid i", // mask interrupts
- "ldr {1}, [{0}]", // read CCR
- "orr.w {1}, {1}, #(1 << 16)", // Set bit 16, DC
- "str {1}, [{0}]", // write it back
- "dsb", // ensure store completes
- "isb", // synchronize pipeline
- "msr PRIMASK, {2}", // unnest critical section
- out(reg) _,
- out(reg) _,
- out(reg) _,
- options(nostack),
- );
- compiler_fence(Ordering::SeqCst);
- }
-}
-
-#[cfg(armv7em)]
-pub use self::v7em::*;
-#[cfg(armv7em)]
-mod v7em {
- use core::arch::asm;
-
- #[inline(always)]
- pub unsafe fn __basepri_max_cm7_r0p1(val: u8) {
- asm!(
- "mrs {1}, PRIMASK",
- "cpsid i",
- "tst.w {1}, #1",
- "msr BASEPRI_MAX, {0}",
- "it ne",
- "bxne lr",
- "cpsie i",
- in(reg) val,
- out(reg) _,
- options(nomem, nostack, preserves_flags),
- );
- }
-
- #[inline(always)]
- pub unsafe fn __basepri_w_cm7_r0p1(val: u8) {
- asm!(
- "mrs {1}, PRIMASK",
- "cpsid i",
- "tst.w {1}, #1",
- "msr BASEPRI, {0}",
- "it ne",
- "bxne lr",
- "cpsie i",
- in(reg) val,
- out(reg) _,
- options(nomem, nostack, preserves_flags),
- );
- }
-}
-
-#[cfg(armv8m)]
-pub use self::v8m::*;
-/// Baseline and Mainline.
-#[cfg(armv8m)]
-mod v8m {
- use core::arch::asm;
-
- #[inline(always)]
- pub unsafe fn __tt(mut target: u32) -> u32 {
- asm!(
- "tt {target}, {target}",
- target = inout(reg) target,
- options(nomem, nostack, preserves_flags),
- );
- target
- }
-
- #[inline(always)]
- pub unsafe fn __ttt(mut target: u32) -> u32 {
- asm!(
- "ttt {target}, {target}",
- target = inout(reg) target,
- options(nomem, nostack, preserves_flags),
- );
- target
- }
-
- #[inline(always)]
- pub unsafe fn __tta(mut target: u32) -> u32 {
- asm!(
- "tta {target}, {target}",
- target = inout(reg) target,
- options(nomem, nostack, preserves_flags),
- );
- target
- }
-
- #[inline(always)]
- pub unsafe fn __ttat(mut target: u32) -> u32 {
- asm!(
- "ttat {target}, {target}",
- target = inout(reg) target,
- options(nomem, nostack, preserves_flags),
- );
- target
- }
-
- #[inline(always)]
- pub unsafe fn __msp_ns_r() -> u32 {
- let r;
- asm!("mrs {}, MSP_NS", out(reg) r, options(nomem, nostack, preserves_flags));
- r
- }
-
- #[inline(always)]
- pub unsafe fn __msp_ns_w(val: u32) {
- asm!("msr MSP_NS, {}", in(reg) val, options(nomem, nostack, preserves_flags));
- }
-
- #[inline(always)]
- pub unsafe fn __bxns(val: u32) {
- asm!("BXNS {}", in(reg) val, options(nomem, nostack, preserves_flags));
- }
-}
-
-#[cfg(armv8m_main)]
-pub use self::v8m_main::*;
-/// Mainline only.
-#[cfg(armv8m_main)]
-mod v8m_main {
- use core::arch::asm;
-
- #[inline(always)]
- pub unsafe fn __msplim_r() -> u32 {
- let r;
- asm!("mrs {}, MSPLIM", out(reg) r, options(nomem, nostack, preserves_flags));
- r
- }
-
- #[inline(always)]
- pub unsafe fn __msplim_w(val: u32) {
- asm!("msr MSPLIM, {}", in(reg) val, options(nomem, nostack, preserves_flags));
- }
-
- #[inline(always)]
- pub unsafe fn __psplim_r() -> u32 {
- let r;
- asm!("mrs {}, PSPLIM", out(reg) r, options(nomem, nostack, preserves_flags));
- r
- }
-
- #[inline(always)]
- pub unsafe fn __psplim_w(val: u32) {
- asm!("msr PSPLIM, {}", in(reg) val, options(nomem, nostack, preserves_flags));
- }
-}
-
-#[cfg(has_fpu)]
-pub use self::fpu::*;
-/// All targets with FPU.
-#[cfg(has_fpu)]
-mod fpu {
- use core::arch::asm;
-
- #[inline(always)]
- pub unsafe fn __fpscr_r() -> u32 {
- let r;
- asm!("vmrs {}, fpscr", out(reg) r, options(nomem, nostack, preserves_flags));
- r
- }
-
- #[inline(always)]
- pub unsafe fn __fpscr_w(val: u32) {
- asm!("vmsr fpscr, {}", in(reg) val, options(nomem, nostack));
- }
-}
diff --git a/asm/lib.rs b/asm/lib.rs
deleted file mode 100644
index 48f3dc2..0000000
--- a/asm/lib.rs
+++ /dev/null
@@ -1,143 +0,0 @@
-//! FFI shim around the inline assembly in `inline.rs`.
-//!
-//! We use this file to precompile some assembly stubs into the static libraries you can find in
-//! `bin`. Apps using the `cortex-m` crate then link against those static libraries and don't need
-//! to build this file themselves.
-//!
-//! Nowadays the assembly stubs are no longer actual assembly files, but actually just this small
-//! Rust crate that uses unstable inline assembly, coupled with the `xtask` tool to invoke rustc
-//! and build the files.
-//!
-//! Precompiling this to a static lib allows users to call assembly routines from stable Rust, but
-//! also perform [linker plugin LTO] with the precompiled artifacts to completely inline the
-//! assembly routines into their code, which brings the "outline assembly" on par with "real" inline
-//! assembly.
-//!
-//! For developers and contributors to `cortex-m`, this setup means that they don't have to install
-//! any binutils, assembler, or C compiler to hack on the crate. All they need is to run `cargo
-//! xtask assemble` to rebuild the archives from this file.
-//!
-//! Cool, right?
-//!
-//! # Rust version management
-//!
-//! Since inline assembly is still unstable, and we want to ensure that the created blobs are
-//! up-to-date in CI, we have to pin the nightly version we use for this. The nightly toolchain is
-//! stored in `asm-toolchain`.
-//!
-//! The `cargo xtask` automation will automatically install the `asm-toolchain` as well as all
-//! Cortex-M targets needed to generate the blobs.
-//!
-//! [linker plugin LTO]: https://doc.rust-lang.org/stable/rustc/linker-plugin-lto.html
-
-#![feature(asm)]
-#![no_std]
-#![crate_type = "staticlib"]
-#![deny(warnings)]
-// Don't warn about feature(asm) being stable on Rust >= 1.59.0
-#![allow(stable_features)]
-
-mod inline;
-
-macro_rules! shims {
- (
- $( fn $name:ident( $($arg:ident: $argty:ty),* ) $(-> $ret:ty)?; )+
- ) => {
- $(
- #[no_mangle]
- pub unsafe extern "C" fn $name(
- $($arg: $argty),*
- ) $(-> $ret)? {
- crate::inline::$name($($arg),*)
- }
- )+
- };
-}
-
-shims! {
- fn __bkpt();
- fn __control_r() -> u32;
- fn __control_w(w: u32);
- fn __cpsid();
- fn __cpsie();
- fn __delay(cyc: u32);
- fn __dmb();
- fn __dsb();
- fn __isb();
- fn __msp_r() -> u32;
- fn __msp_w(val: u32);
- fn __nop();
- fn __primask_r() -> u32;
- fn __psp_r() -> u32;
- fn __psp_w(val: u32);
- fn __sev();
- fn __udf() -> !;
- fn __wfe();
- fn __wfi();
- fn __sh_syscall(nr: u32, arg: u32) -> u32;
- fn __bootstrap(msp: u32, rv: u32) -> !;
-}
-
-// v7m *AND* v8m.main, but *NOT* v8m.base
-#[cfg(any(armv7m, armv8m_main))]
-shims! {
- fn __basepri_max(val: u8);
- fn __basepri_r() -> u8;
- fn __basepri_w(val: u8);
- fn __faultmask_r() -> u32;
- fn __enable_icache();
- fn __enable_dcache();
-}
-
-#[cfg(armv7em)]
-shims! {
- fn __basepri_max_cm7_r0p1(val: u8);
- fn __basepri_w_cm7_r0p1(val: u8);
-}
-
-// Baseline and Mainline.
-#[cfg(armv8m)]
-shims! {
- fn __tt(target: u32) -> u32;
- fn __ttt(target: u32) -> u32;
- fn __tta(target: u32) -> u32;
- fn __ttat(target: u32) -> u32;
- fn __msp_ns_r() -> u32;
- fn __msp_ns_w(val: u32);
- fn __bxns(val: u32);
-}
-
-// Mainline only.
-#[cfg(armv8m_main)]
-shims! {
- fn __msplim_r() -> u32;
- fn __msplim_w(val: u32);
- fn __psplim_r() -> u32;
- fn __psplim_w(val: u32);
-}
-
-// All targets with FPU.
-#[cfg(has_fpu)]
-shims! {
- fn __fpscr_r() -> u32;
- fn __fpscr_w(val: u32);
-}
-
-/// We *must* define a panic handler here, even though nothing here should ever be able to panic.
-///
-/// We prove that nothing will ever panic by calling a function that doesn't exist. If the panic
-/// handler gets linked in, this causes a linker error. We always build this file with optimizations
-/// enabled, but even without them the panic handler should never be linked in.
-#[panic_handler]
-#[link_section = ".text.asm_panic_handler"]
-fn panic(_: &core::panic::PanicInfo) -> ! {
- extern "C" {
- #[link_name = "cortex-m internal error: panic handler not optimized out, please file an \
- issue at https://github.com/rust-embedded/cortex-m"]
- fn __cortex_m_should_not_panic() -> !;
- }
-
- unsafe {
- __cortex_m_should_not_panic();
- }
-}
diff --git a/bin/thumbv6m-none-eabi-lto.a b/bin/thumbv6m-none-eabi-lto.a
deleted file mode 100644
index a203d7a..0000000
--- a/bin/thumbv6m-none-eabi-lto.a
+++ /dev/null
Binary files differ
diff --git a/bin/thumbv6m-none-eabi.a b/bin/thumbv6m-none-eabi.a
deleted file mode 100644
index 9640a69..0000000
--- a/bin/thumbv6m-none-eabi.a
+++ /dev/null
Binary files differ
diff --git a/bin/thumbv7em-none-eabi-lto.a b/bin/thumbv7em-none-eabi-lto.a
deleted file mode 100644
index b34ac64..0000000
--- a/bin/thumbv7em-none-eabi-lto.a
+++ /dev/null
Binary files differ
diff --git a/bin/thumbv7em-none-eabi.a b/bin/thumbv7em-none-eabi.a
deleted file mode 100644
index 88acbdd..0000000
--- a/bin/thumbv7em-none-eabi.a
+++ /dev/null
Binary files differ
diff --git a/bin/thumbv7em-none-eabihf-lto.a b/bin/thumbv7em-none-eabihf-lto.a
deleted file mode 100644
index 6de94bb..0000000
--- a/bin/thumbv7em-none-eabihf-lto.a
+++ /dev/null
Binary files differ
diff --git a/bin/thumbv7em-none-eabihf.a b/bin/thumbv7em-none-eabihf.a
deleted file mode 100644
index cf91a7a..0000000
--- a/bin/thumbv7em-none-eabihf.a
+++ /dev/null
Binary files differ
diff --git a/bin/thumbv7m-none-eabi-lto.a b/bin/thumbv7m-none-eabi-lto.a
deleted file mode 100644
index 7f677a9..0000000
--- a/bin/thumbv7m-none-eabi-lto.a
+++ /dev/null
Binary files differ
diff --git a/bin/thumbv7m-none-eabi.a b/bin/thumbv7m-none-eabi.a
deleted file mode 100644
index ff4bf21..0000000
--- a/bin/thumbv7m-none-eabi.a
+++ /dev/null
Binary files differ
diff --git a/bin/thumbv8m.base-none-eabi-lto.a b/bin/thumbv8m.base-none-eabi-lto.a
deleted file mode 100644
index f62acaf..0000000
--- a/bin/thumbv8m.base-none-eabi-lto.a
+++ /dev/null
Binary files differ
diff --git a/bin/thumbv8m.base-none-eabi.a b/bin/thumbv8m.base-none-eabi.a
deleted file mode 100644
index c0cc96c..0000000
--- a/bin/thumbv8m.base-none-eabi.a
+++ /dev/null
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabi-lto.a b/bin/thumbv8m.main-none-eabi-lto.a
deleted file mode 100644
index 1a51515..0000000
--- a/bin/thumbv8m.main-none-eabi-lto.a
+++ /dev/null
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabi.a b/bin/thumbv8m.main-none-eabi.a
deleted file mode 100644
index d017a15..0000000
--- a/bin/thumbv8m.main-none-eabi.a
+++ /dev/null
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabihf-lto.a b/bin/thumbv8m.main-none-eabihf-lto.a
deleted file mode 100644
index fd3dc92..0000000
--- a/bin/thumbv8m.main-none-eabihf-lto.a
+++ /dev/null
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabihf.a b/bin/thumbv8m.main-none-eabihf.a
deleted file mode 100644
index 223ff1d..0000000
--- a/bin/thumbv8m.main-none-eabihf.a
+++ /dev/null
Binary files differ
diff --git a/build.rs b/build.rs
index 23ceeba..f81072b 100644
--- a/build.rs
+++ b/build.rs
@@ -1,33 +1,13 @@
-use std::path::PathBuf;
-use std::{env, fs};
+use std::env;
fn main() {
let target = env::var("TARGET").unwrap();
let host_triple = env::var("HOST").unwrap();
- let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
- let name = env::var("CARGO_PKG_NAME").unwrap();
if host_triple == target {
println!("cargo:rustc-cfg=native");
}
- if target.starts_with("thumb") {
- let suffix = if env::var_os("CARGO_FEATURE_LINKER_PLUGIN_LTO").is_some() {
- "-lto"
- } else {
- ""
- };
-
- fs::copy(
- format!("bin/{}{}.a", target, suffix),
- out_dir.join(format!("lib{}.a", name)),
- )
- .unwrap();
-
- println!("cargo:rustc-link-lib=static={}", name);
- println!("cargo:rustc-link-search={}", out_dir.display());
- }
-
if target.starts_with("thumbv6m-") {
println!("cargo:rustc-cfg=cortex_m");
println!("cargo:rustc-cfg=armv6m");
@@ -37,7 +17,7 @@ fn main() {
} else if target.starts_with("thumbv7em-") {
println!("cargo:rustc-cfg=cortex_m");
println!("cargo:rustc-cfg=armv7m");
- println!("cargo:rustc-cfg=armv7em"); // (not currently used)
+ println!("cargo:rustc-cfg=armv7em");
} else if target.starts_with("thumbv8m.base") {
println!("cargo:rustc-cfg=cortex_m");
println!("cargo:rustc-cfg=armv8m");
diff --git a/cortex-m-rt/CHANGELOG.md b/cortex-m-rt/CHANGELOG.md
index c66b5c0..0ee0510 100644
--- a/cortex-m-rt/CHANGELOG.md
+++ b/cortex-m-rt/CHANGELOG.md
@@ -7,11 +7,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
+## [v0.7.2]
+
+- MSRV is now Rust 1.59.
+- Moved precompiled assembly blobs to `global_asm!`, requiring Rust 1.59.
+- Add new `set_vtor` and `set-sp` features to conditionally set the VTOR and SP
+ registers at device reset ([#423]).
+- Allow (unstable) `naked` attribute on interrupt handlers and `pre_init`.
+
## Fixes
- Fix `cortex_m_rt::exception` macro no longer being usable fully-qualified ([#414])
+- Fix veneer limit position in linker script ([#434]).
[#414]: https://github.com/rust-embedded/cortex-m/issues/414
+[#423]: https://github.com/rust-embedded/cortex-m/issues/423
+[#434]: https://github.com/rust-embedded/cortex-m/issues/434
## Notes
@@ -591,7 +602,8 @@ section size addr
Initial release
-[Unreleased]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.7.1...HEAD
+[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/c-m-rt-v0.7.2...HEAD
+[v0.7.2]: https://github.com/rust-embedded/cortex-m/compare/c-m-rt-v0.7.1...c-m-rt-v0.7.2
[v0.7.1]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.7.0...v0.7.1
[v0.7.0]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.11...v0.7.0
[v0.6.15]: https://github.com/rust-embedded/cortex-m-rt/compare/v0.6.14...v0.6.15
diff --git a/cortex-m-rt/Cargo.toml b/cortex-m-rt/Cargo.toml
index 5289057..e6ea8c8 100644
--- a/cortex-m-rt/Cargo.toml
+++ b/cortex-m-rt/Cargo.toml
@@ -12,10 +12,11 @@ license = "MIT OR Apache-2.0"
name = "cortex-m-rt"
readme = "README.md"
repository = "https://github.com/rust-embedded/cortex-m"
-version = "0.7.1"
+version = "0.7.2"
autoexamples = true
links = "cortex-m-rt" # Prevent multiple versions of cortex-m-rt being linked
-edition = "2018"
+edition = "2021"
+rust-version = "1.59"
[dependencies]
cortex-m-rt-macros = { path = "macros", version = "=0.7.0" }
@@ -42,6 +43,8 @@ required-features = ["device"]
[features]
device = []
+set-sp = []
+set-vtor = []
[package.metadata.docs.rs]
features = ["device"]
diff --git a/cortex-m-rt/README.md b/cortex-m-rt/README.md
index 34b0f17..b62dbb5 100644
--- a/cortex-m-rt/README.md
+++ b/cortex-m-rt/README.md
@@ -11,7 +11,7 @@ This project is developed and maintained by the [Cortex-M team][team].
# Minimum Supported Rust Version (MSRV)
-This crate is guaranteed to compile on stable Rust 1.42.0 and up. It *might*
+This crate is guaranteed to compile on stable Rust 1.59.0 and up. It *might*
compile with older versions but that may change in any new patch release.
# License
diff --git a/cortex-m-rt/asm.S b/cortex-m-rt/asm.S
deleted file mode 100644
index 0d078b3..0000000
--- a/cortex-m-rt/asm.S
+++ /dev/null
@@ -1,113 +0,0 @@
- .cfi_sections .debug_frame
-
- # Notes for function attributes:
- # .type and .thumb_func are _both_ required, otherwise the Thumb mode bit
- # will not be set and an invalid vector table is generated.
- # LLD requires that section flags are set explicitly.
-
- .section .HardFaultTrampoline, "ax"
- .global HardFaultTrampoline
- .type HardFaultTrampoline,%function
- .thumb_func
- .cfi_startproc
- # HardFault exceptions are bounced through this trampoline which grabs the
- # stack pointer at the time of the exception and passes it to the user's
- # HardFault handler in r0.
-HardFaultTrampoline:
- # Depending on the stack mode in EXC_RETURN, fetch stack pointer from
- # PSP or MSP.
- mov r0, lr
- mov r1, #4
- tst r0, r1
- bne 0f
- mrs r0, MSP
- b HardFault
-0:
- mrs r0, PSP
- b HardFault
- .cfi_endproc
- .size HardFaultTrampoline, . - HardFaultTrampoline
-
- .section .Reset, "ax"
- .global Reset
- .type Reset,%function
- .thumb_func
- .cfi_startproc
- # Main entry point after reset. This jumps to the user __pre_init function,
- # which cannot be called from Rust code without invoking UB, then
- # initialises RAM. If the target has an FPU, it is enabled. Finally, jumps
- # to the user main function.
-Reset:
- # ARMv6-M does not initialise LR, but many tools expect it to be 0xFFFF_FFFF
- # when reaching the first call frame, so we set it at startup.
- # ARMv7-M and above initialise LR to 0xFFFF_FFFF at reset.
- ldr r4,=0xffffffff
- mov lr,r4
-
- # Run user pre-init code, which must be executed immediately after startup,
- # before the potentially time-consuming memory initialisation takes place.
- # Example use cases include disabling default watchdogs or enabling RAM.
- bl __pre_init
-
- # Restore LR after calling __pre_init (r4 is preserved by subroutines).
- mov lr,r4
-
- # Initialise .bss memory. `__sbss` and `__ebss` come from the linker script.
- ldr r0,=__sbss
- ldr r1,=__ebss
- mov r2,#0
-0:
- cmp r1, r0
- beq 1f
- stm r0!, {r2}
- b 0b
-1:
-
- # Initialise .data memory. `__sdata`, `__sidata`, and `__edata` come from the
- # linker script. Copy from r2 into r0 until r0 reaches r1.
- ldr r0,=__sdata
- ldr r1,=__edata
- ldr r2,=__sidata
-2:
- cmp r1, r0
- beq 3f
- # load 1 word from r2 to r3, inc r2
- ldm r2!, {r3}
- # store 1 word from r3 to r0, inc r0
- stm r0!, {r3}
- b 2b
-3:
-
-#ifdef HAS_FPU
- # Conditionally enable the FPU.
- # Address of SCB.CPACR.
- ldr r0, =0xE000ED88
- # Enable access to CP10 and CP11 from both privileged and unprivileged mode.
- ldr r1, =(0b1111 << 20)
- # RMW.
- ldr r2, [r0]
- orr r2, r2, r1
- str r2, [r0]
- # Barrier is required on some processors.
- dsb
- isb
-#endif
-
-4:
- # Preserve `lr` and emit debuginfo that lets external tools restore it.
- # This fixes unwinding past the `Reset` handler.
- # See https://sourceware.org/binutils/docs/as/CFI-directives.html for an
- # explanation of the directives.
-.cfi_def_cfa sp, 0
- push {lr}
-.cfi_offset lr, 0
-
- # Jump to user main function. We use bl for the extended range, but the
- # user main function may not return.
- bl main
-
- # Trap on return.
- udf
-
- .cfi_endproc
- .size Reset, . - Reset
diff --git a/cortex-m-rt/assemble.sh b/cortex-m-rt/assemble.sh
deleted file mode 100755
index 9b1f15c..0000000
--- a/cortex-m-rt/assemble.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env bash
-
-set -euxo pipefail
-
-# cflags taken from cc 1.0.22
-
-crate=cortex-m-rt
-
-# remove existing blobs because otherwise this will append object files to the old blobs
-rm -f bin/*.a
-
-arm-none-eabi-gcc -g -c -march=armv6s-m asm.S -o bin/$crate.o
-ar crs bin/thumbv6m-none-eabi.a bin/$crate.o
-
-arm-none-eabi-gcc -g -c -march=armv7-m asm.S -o bin/$crate.o
-ar crs bin/thumbv7m-none-eabi.a bin/$crate.o
-
-arm-none-eabi-gcc -g -c -march=armv7e-m asm.S -o bin/$crate.o
-ar crs bin/thumbv7em-none-eabi.a bin/$crate.o
-
-arm-none-eabi-gcc -g -c -march=armv7e-m asm.S -DHAS_FPU -o bin/$crate.o
-ar crs bin/thumbv7em-none-eabihf.a bin/$crate.o
-
-arm-none-eabi-gcc -g -c -march=armv8-m.base asm.S -o bin/$crate.o
-ar crs bin/thumbv8m.base-none-eabi.a bin/$crate.o
-
-arm-none-eabi-gcc -g -c -march=armv8-m.main asm.S -o bin/$crate.o
-ar crs bin/thumbv8m.main-none-eabi.a bin/$crate.o
-
-arm-none-eabi-gcc -g -c -march=armv8-m.main -DHAS_FPU asm.S -o bin/$crate.o
-ar crs bin/thumbv8m.main-none-eabihf.a bin/$crate.o
-
-rm bin/$crate.o
diff --git a/cortex-m-rt/bin/thumbv6m-none-eabi.a b/cortex-m-rt/bin/thumbv6m-none-eabi.a
deleted file mode 100644
index c145cc6..0000000
--- a/cortex-m-rt/bin/thumbv6m-none-eabi.a
+++ /dev/null
Binary files differ
diff --git a/cortex-m-rt/bin/thumbv7em-none-eabi.a b/cortex-m-rt/bin/thumbv7em-none-eabi.a
deleted file mode 100644
index 2d6b6a1..0000000
--- a/cortex-m-rt/bin/thumbv7em-none-eabi.a
+++ /dev/null
Binary files differ
diff --git a/cortex-m-rt/bin/thumbv7em-none-eabihf.a b/cortex-m-rt/bin/thumbv7em-none-eabihf.a
deleted file mode 100644
index aa765ea..0000000
--- a/cortex-m-rt/bin/thumbv7em-none-eabihf.a
+++ /dev/null
Binary files differ
diff --git a/cortex-m-rt/bin/thumbv7m-none-eabi.a b/cortex-m-rt/bin/thumbv7m-none-eabi.a
deleted file mode 100644
index 3d1783c..0000000
--- a/cortex-m-rt/bin/thumbv7m-none-eabi.a
+++ /dev/null
Binary files differ
diff --git a/cortex-m-rt/bin/thumbv8m.base-none-eabi.a b/cortex-m-rt/bin/thumbv8m.base-none-eabi.a
deleted file mode 100644
index a9fb434..0000000
--- a/cortex-m-rt/bin/thumbv8m.base-none-eabi.a
+++ /dev/null
Binary files differ
diff --git a/cortex-m-rt/bin/thumbv8m.main-none-eabi.a b/cortex-m-rt/bin/thumbv8m.main-none-eabi.a
deleted file mode 100644
index 40a5c51..0000000
--- a/cortex-m-rt/bin/thumbv8m.main-none-eabi.a
+++ /dev/null
Binary files differ
diff --git a/cortex-m-rt/bin/thumbv8m.main-none-eabihf.a b/cortex-m-rt/bin/thumbv8m.main-none-eabihf.a
deleted file mode 100644
index 6c523af..0000000
--- a/cortex-m-rt/bin/thumbv8m.main-none-eabihf.a
+++ /dev/null
Binary files differ
diff --git a/cortex-m-rt/build.rs b/cortex-m-rt/build.rs
index 96a8560..2b65cdf 100644
--- a/cortex-m-rt/build.rs
+++ b/cortex-m-rt/build.rs
@@ -1,4 +1,4 @@
-use std::fs::{self, File};
+use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::{env, ffi::OsStr};
@@ -16,15 +16,6 @@ fn main() {
.map_or(target.clone(), |stem| stem.to_str().unwrap().to_string());
}
- let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
-
- if target.starts_with("thumbv") {
- let lib_path = format!("bin/{}.a", target);
- fs::copy(&lib_path, out_dir.join("libcortex-m-rt.a")).unwrap();
- println!("cargo:rustc-link-lib=static=cortex-m-rt");
- println!("cargo:rerun-if-changed={}", lib_path);
- }
-
// Put the linker script somewhere the linker can find it
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
let link_x = include_bytes!("link.x.in");
@@ -69,6 +60,10 @@ INCLUDE device.x"#
240
};
+ if target.ends_with("-eabihf") {
+ println!("cargo:rustc-cfg=has_fpu");
+ }
+
// checking the size of the interrupts portion of the vector table is sub-architecture dependent
writeln!(
f,
diff --git a/cortex-m-rt/check-blobs.sh b/cortex-m-rt/check-blobs.sh
deleted file mode 100755
index 166b4a4..0000000
--- a/cortex-m-rt/check-blobs.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env bash
-
-# Checks that the blobs are up to date with the committed assembly files
-
-set -euxo pipefail
-
-for lib in bin/*.a; do
- filename=$(basename "$lib")
- arm-none-eabi-objdump -Cd "$lib" > "bin/${filename%.a}.before"
-done
-
-./assemble.sh
-
-for lib in bin/*.a; do
- filename=$(basename "$lib")
- arm-none-eabi-objdump -Cd "$lib" > "bin/${filename%.a}.after"
-done
-
-for cksum in bin/*.after; do
- diff -u "$cksum" "${cksum%.after}.before"
-done
diff --git a/cortex-m-rt/ci/script.sh b/cortex-m-rt/ci/script.sh
index 08ff863..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,20 +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,${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
@@ -64,17 +72,13 @@ 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
;;
esac
-
- if [ "$TARGET" = x86_64-unknown-linux-gnu ]; then
- ./check-blobs.sh
- fi
}
main
diff --git a/cortex-m-rt/examples/qemu.rs b/cortex-m-rt/examples/qemu.rs
index e903404..a8ffd20 100644
--- a/cortex-m-rt/examples/qemu.rs
+++ b/cortex-m-rt/examples/qemu.rs
@@ -1,28 +1,24 @@
-// #![feature(stdsimd)]
#![no_main]
#![no_std]
-extern crate cortex_m;
-extern crate cortex_m_rt as rt;
-extern crate cortex_m_semihosting as semihosting;
+use core::fmt::Write;
-extern crate panic_halt;
-
-use cortex_m::asm;
-use rt::entry;
-
-#[entry]
+#[cortex_m_rt::entry]
fn main() -> ! {
- use core::fmt::Write;
let x = 42;
loop {
- asm::nop();
-
- // write something through semihosting interface
- let mut hstdout = semihosting::hio::hstdout().unwrap();
+ let mut hstdout = cortex_m_semihosting::hio::hstdout().unwrap();
write!(hstdout, "x = {}\n", x).unwrap();
- // exit from qemu
- semihosting::debug::exit(semihosting::debug::EXIT_SUCCESS);
+ cortex_m_semihosting::debug::exit(cortex_m_semihosting::debug::EXIT_SUCCESS);
+ }
+}
+
+// Define a panic handler that uses semihosting to exit immediately,
+// so that any panics cause qemu to quit instead of hang.
+#[panic_handler]
+fn panic(_: &core::panic::PanicInfo) -> ! {
+ loop {
+ cortex_m_semihosting::debug::exit(cortex_m_semihosting::debug::EXIT_FAILURE);
}
}
diff --git a/cortex-m-rt/link.x.in b/cortex-m-rt/link.x.in
index 92004b7..4461646 100644
--- a/cortex-m-rt/link.x.in
+++ b/cortex-m-rt/link.x.in
@@ -66,6 +66,8 @@ SECTIONS
/* ### Vector table */
.vector_table ORIGIN(FLASH) :
{
+ __vector_table = .;
+
/* Initial Stack Pointer (SP) value */
LONG(_stack_start);
@@ -142,8 +144,12 @@ SECTIONS
__veneer_base = .;
*(.gnu.sgstubs*)
. = ALIGN(32);
- __veneer_limit = .;
} > FLASH
+ /* Place `__veneer_limit` outside the `.gnu.sgstubs` section because veneers are
+ * always inserted last in the section, which would otherwise be _after_ the `__veneer_limit` symbol.
+ */
+ . = ALIGN(32);
+ __veneer_limit = .;
/* ### .bss */
.bss (NOLOAD) : ALIGN(4)
diff --git a/cortex-m-rt/macros/Cargo.toml b/cortex-m-rt/macros/Cargo.toml
index c73ebc1..e95cc7d 100644
--- a/cortex-m-rt/macros/Cargo.toml
+++ b/cortex-m-rt/macros/Cargo.toml
@@ -8,7 +8,8 @@ license = "MIT OR Apache-2.0"
name = "cortex-m-rt-macros"
repository = "https://github.com/rust-embedded/cortex-m"
version = "0.7.0"
-edition = "2018"
+edition = "2021"
+rust-version = "1.59"
[lib]
proc-macro = true
diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs
index 752d3d7..6e6bf7e 100644
--- a/cortex-m-rt/src/lib.rs
+++ b/cortex-m-rt/src/lib.rs
@@ -34,14 +34,14 @@
//!
//! This crate expects the user, or some other crate, to provide the memory layout of the target
//! device via a linker script named `memory.x`. This section covers the contents of `memory.x`
-//! The `memory.x` file is used by during linking by the `link.x` script provided by this crate.
+//! The `memory.x` file is used during linking by the `link.x` script provided by this crate.
//!
//! ### `MEMORY`
//!
//! The linker script must specify the memory available in the device as, at least, two `MEMORY`
//! regions: one named `FLASH` and one named `RAM`. The `.text` and `.rodata` sections of the
//! program will be placed in the `FLASH` region, whereas the `.bss` and `.data` sections, as well
-//! as the heap,will be placed in the `RAM` region.
+//! as the heap, will be placed in the `RAM` region.
//!
//! ```text
//! /* Linker script for the STM32F103C8T6 */
@@ -158,6 +158,19 @@
//! conjunction with crates generated using `svd2rust`. Those *device crates* will populate the
//! missing part of the vector table when their `"rt"` feature is enabled.
//!
+//! ## `set-sp`
+//!
+//! If this feature is enabled, the stack pointer (SP) is initialised in the reset handler to the
+//! `_stack_start` value from the linker script. This is not usually required, but some debuggers
+//! do not initialise SP when performing a soft reset, which can lead to stack corruption.
+//!
+//! ## `set-vtor`
+//!
+//! If this feature is enabled, the vector table offset register (VTOR) is initialised in the reset
+//! handler to the start of the vector table defined in the linker script. This is not usually
+//! required, but some bootloaders do not set VTOR before jumping to application code, leading to
+//! your main function executing but interrupt handlers not being used.
+//!
//! # Inspection
//!
//! This section covers how to inspect a binary that builds on top of `cortex-m-rt`.
@@ -309,14 +322,10 @@
//!
//! We want to provide a default handler for all the interrupts while still letting the user
//! individually override each interrupt handler. In C projects, this is usually accomplished using
-//! weak aliases declared in external assembly files. In Rust, we could achieve something similar
-//! using `global_asm!`, but that's an unstable feature.
-//!
-//! A solution that doesn't require `global_asm!` or external assembly files is to use the `PROVIDE`
-//! command in a linker script to create the weak aliases. This is the approach that `cortex-m-rt`
-//! uses; when the `"device"` feature is enabled `cortex-m-rt`'s linker script (`link.x`) depends on
-//! a linker script named `device.x`. The crate that provides `__INTERRUPTS` must also provide this
-//! file.
+//! weak aliases declared in external assembly files. We use a similar solution via the `PROVIDE`
+//! command in the linker script: when the `"device"` feature is enabled, `cortex-m-rt`'s linker
+//! script (`link.x`) includes a linker script named `device.x`, which must be provided by
+//! whichever crate provides `__INTERRUPTS`.
//!
//! For our running example the `device.x` linker script looks like this:
//!
@@ -330,8 +339,8 @@
//! that the core exceptions use unless overridden.
//!
//! Because this linker script is provided by a dependency of the final application the dependency
-//! must contain build script that puts `device.x` somewhere the linker can find. An example of such
-//! build script is shown below:
+//! must contain a build script that puts `device.x` somewhere the linker can find. An example of
+//! such build script is shown below:
//!
//! ```ignore
//! use std::env;
@@ -418,7 +427,7 @@
//!
//! # Minimum Supported Rust Version (MSRV)
//!
-//! The MSRV of this release is Rust 1.42.0.
+//! The MSRV of this release is Rust 1.59.0.
// # Developer notes
//
@@ -430,16 +439,152 @@
extern crate cortex_m_rt_macros as macros;
+#[cfg(cortex_m)]
+use core::arch::global_asm;
use core::fmt;
-use core::sync::atomic::{self, Ordering};
+
+// HardFault exceptions are bounced through this trampoline which grabs the stack pointer at
+// the time of the exception and passes it to the user's HardFault handler in r0.
+// Depending on the stack mode in EXC_RETURN, fetches stack from either MSP or PSP.
+#[cfg(cortex_m)]
+global_asm!(
+ ".cfi_sections .debug_frame
+ .section .HardFaultTrampoline, \"ax\"
+ .global HardFaultTrampline
+ .type HardFaultTrampline,%function
+ .thumb_func
+ .cfi_startproc
+ HardFaultTrampoline:",
+ "mov r0, lr
+ movs r1, #4
+ tst r0, r1
+ bne 0f
+ mrs r0, MSP
+ b HardFault
+ 0:
+ mrs r0, PSP
+ b HardFault",
+ ".cfi_endproc
+ .size HardFaultTrampoline, . - HardFaultTrampoline",
+);
+
+/// Parse cfg attributes inside a global_asm call.
+#[cfg(cortex_m)]
+macro_rules! cfg_global_asm {
+ {@inner, [$($x:tt)*], } => {
+ global_asm!{$($x)*}
+ };
+ (@inner, [$($x:tt)*], #[cfg($meta:meta)] $asm:literal, $($rest:tt)*) => {
+ #[cfg($meta)]
+ cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*}
+ #[cfg(not($meta))]
+ cfg_global_asm!{@inner, [$($x)*], $($rest)*}
+ };
+ {@inner, [$($x:tt)*], $asm:literal, $($rest:tt)*} => {
+ cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*}
+ };
+ {$($asms:tt)*} => {
+ cfg_global_asm!{@inner, [], $($asms)*}
+ };
+}
+
+// This reset vector is the initial entry point after a system reset.
+// Calls an optional user-provided __pre_init and then initialises RAM.
+// If the target has an FPU, it is enabled.
+// Finally jumps to the user main function.
+#[cfg(cortex_m)]
+cfg_global_asm! {
+ ".cfi_sections .debug_frame
+ .section .Reset, \"ax\"
+ .global Reset
+ .type Reset,%function
+ .thumb_func",
+ ".cfi_startproc
+ Reset:",
+
+ // Ensure LR is loaded with 0xFFFF_FFFF at startup to help debuggers find the first call frame.
+ // On ARMv6-M LR is not initialised at all, while other platforms should initialise it.
+ "movs r4, #0
+ mvns r4, r4
+ mov lr, r4",
+
+ // If enabled, initialise the SP. This is normally initialised by the CPU itself or by a
+ // bootloader, but some debuggers fail to set it when resetting the target, leading to
+ // stack corruptions.
+ #[cfg(feature = "set-sp")]
+ "ldr r0, =_stack_start
+ msr msp, r0",
+
+ // If enabled, initialise VTOR to the start of the vector table. This is normally initialised
+ // by a bootloader when the non-reset value is required, but some bootloaders do not set it,
+ // leading to frustrating issues where everything seems to work but interrupts are never
+ // handled. The VTOR register is optional on ARMv6-M, but when not present is RAZ,WI and
+ // therefore safe to write to.
+ #[cfg(feature = "set-vtor")]
+ "ldr r0, =0xe000ed08
+ ldr r1, =__vector_table
+ str r1, [r0]",
+
+ // Run user pre-init code which must be executed immediately after startup, before the
+ // potentially time-consuming memory initialisation takes place.
+ // Example use cases include disabling default watchdogs or enabling RAM.
+ // Reload LR after returning from pre-init (r4 is preserved by subroutines).
+ "bl __pre_init
+ mov lr, r4",
+
+ // Initialise .bss memory. `__sbss` and `__ebss` come from the linker script.
+ "ldr r0, =__sbss
+ ldr r1, =__ebss
+ movs r2, #0
+ 0:
+ cmp r1, r0
+ beq 1f
+ stm r0!, {{r2}}
+ b 0b
+ 1:",
+
+ // Initialise .data memory. `__sdata`, `__sidata`, and `__edata` come from the linker script.
+ "ldr r0, =__sdata
+ ldr r1, =__edata
+ ldr r2, =__sidata
+ 2:
+ cmp r1, r0
+ beq 3f
+ ldm r2!, {{r3}}
+ stm r0!, {{r3}}
+ b 2b
+ 3:",
+
+ // Potentially enable an FPU.
+ // SCB.CPACR is 0xE000_ED88.
+ // We enable access to CP10 and CP11 from priviliged and unprivileged mode.
+ #[cfg(has_fpu)]
+ "ldr r0, =0xE000ED88
+ ldr r1, =(0b1111 << 20)
+ ldr r2, [r0]
+ orr r2, r2, r1
+ str r2, [r0]
+ dsb
+ isb",
+
+ // Push `lr` to the stack for debuggers, to prevent them unwinding past Reset.
+ // See https://sourceware.org/binutils/docs/as/CFI-directives.html.
+ ".cfi_def_cfa sp, 0
+ push {{lr}}
+ .cfi_offset lr, 0",
+
+ // Jump to user main function.
+ // `bl` is used for the extended range, but the user main function should not return,
+ // so trap on any unexpected return.
+ "bl main
+ udf #0",
+
+ ".cfi_endproc
+ .size Reset, . - Reset",
+}
/// Attribute to declare an interrupt (AKA device-specific exception) handler
///
-/// **IMPORTANT**: If you are using Rust 1.30 this attribute must be used on reachable items (i.e.
-/// there must be no private modules between the item and the root of the crate); if the item is in
-/// the root of the crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31
-/// and newer releases.
-///
/// **NOTE**: This attribute is exposed by `cortex-m-rt` only when the `device` feature is enabled.
/// However, that export is not meant to be used directly -- using it will result in a compilation
/// error. You should instead use the device crate (usually generated using `svd2rust`) re-export of
@@ -506,11 +651,6 @@ pub use macros::interrupt;
/// Attribute to declare the entry point of the program
///
-/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you
-/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
-/// private modules between the item and the root of the crate); if the item is in the root of the
-/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer releases.
-///
/// The specified function will be called by the reset handler *after* RAM has been initialized. In
/// the case of the `thumbv7em-none-eabihf` target the FPU will also be enabled before the function
/// is called.
@@ -565,11 +705,6 @@ pub use macros::entry;
/// Attribute to declare an exception handler
///
-/// **IMPORTANT**: If you are using Rust 1.30 this attribute must be used on reachable items (i.e.
-/// there must be no private modules between the item and the root of the crate); if the item is in
-/// the root of the crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31
-/// and newer releases.
-///
/// # Syntax
///
/// ```
@@ -681,11 +816,7 @@ pub use macros::exception;
/// Attribute to mark which function will be called at the beginning of the reset handler.
///
-/// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph. Also, if you
-/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
-/// private modules between the item and the root of the crate); if the item is in the root of the
-/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer
-/// releases.
+/// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph.
///
/// The function must have the signature of `unsafe fn()`.
///
@@ -920,21 +1051,15 @@ pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
#[cfg_attr(cortex_m, link_section = ".HardFault.default")]
#[no_mangle]
pub unsafe extern "C" fn HardFault_(ef: &ExceptionFrame) -> ! {
- loop {
- // add some side effect to prevent this from turning into a UDF instruction
- // see rust-lang/rust#28728 for details
- atomic::compiler_fence(Ordering::SeqCst);
- }
+ #[allow(clippy::empty_loop)]
+ loop {}
}
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn DefaultHandler_() -> ! {
- loop {
- // add some side effect to prevent this from turning into a UDF instruction
- // see rust-lang/rust#28728 for details
- atomic::compiler_fence(Ordering::SeqCst);
- }
+ #[allow(clippy::empty_loop)]
+ loop {}
}
#[doc(hidden)]
diff --git a/cortex-m-rt/tests/compile-fail/non-static-resource.rs b/cortex-m-rt/tests/compile-fail/non-static-resource.rs
index a603728..95f314b 100644
--- a/cortex-m-rt/tests/compile-fail/non-static-resource.rs
+++ b/cortex-m-rt/tests/compile-fail/non-static-resource.rs
@@ -21,7 +21,7 @@ fn SVCall() {
static mut STAT: u8 = 0;
let _stat: &'static mut u8 = STAT;
- //~^ ERROR lifetime of reference outlives lifetime of borrowed content
+ //~^ ERROR lifetime may not live long enough
}
#[interrupt]
@@ -29,7 +29,7 @@ fn UART0() {
static mut STAT: u8 = 0;
let _stat: &'static mut u8 = STAT;
- //~^ ERROR lifetime of reference outlives lifetime of borrowed content
+ //~^ ERROR lifetime may not live long enough
}
#[entry]
diff --git a/cortex-m-semihosting/CHANGELOG.md b/cortex-m-semihosting/CHANGELOG.md
index 0a942cf..76f0694 100644
--- a/cortex-m-semihosting/CHANGELOG.md
+++ b/cortex-m-semihosting/CHANGELOG.md
@@ -5,6 +5,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
+## [v0.5.0] - 2022-03-01
+
+- Always use inline-asm, requiring Rust 1.59.
+- Removed inline-asm feature.
+
## [v0.4.1] - 2020-10-20
0.4.1 was yanked because the pre-built binaries contain conflicting symbols
@@ -141,7 +146,8 @@ change.
- Initial release
-[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.4.1...HEAD
+[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.5.0...HEAD
+[v0.5.0]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.4.1...c-m-sh-v0.5.0
[v0.4.1]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.4.0...c-m-sh-v0.4.1
[v0.4.0]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.3.5...c-m-sh-v0.4.0
[v0.3.7]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.6...v0.3.7
diff --git a/cortex-m-semihosting/Cargo.toml b/cortex-m-semihosting/Cargo.toml
index 5894029..ac0afa5 100644
--- a/cortex-m-semihosting/Cargo.toml
+++ b/cortex-m-semihosting/Cargo.toml
@@ -11,13 +11,14 @@ license = "MIT OR Apache-2.0"
name = "cortex-m-semihosting"
readme = "README.md"
repository = "https://github.com/rust-embedded/cortex-m"
-version = "0.4.1"
-edition = "2018"
+version = "0.5.0"
+edition = "2021"
+rust-version = "1.59"
[features]
-inline-asm = []
jlink-quirks = []
no-semihosting = []
[dependencies]
cortex-m = { path = "..", version = ">= 0.5.8, < 0.8" }
+critical-section = "1.0.0"
diff --git a/cortex-m-semihosting/README.md b/cortex-m-semihosting/README.md
index bfbfb44..6036d4e 100644
--- a/cortex-m-semihosting/README.md
+++ b/cortex-m-semihosting/README.md
@@ -11,7 +11,7 @@ This project is developed and maintained by the [Cortex-M team][team].
# Minimum Supported Rust Version (MSRV)
-This crate is guaranteed to compile on stable Rust 1.33.0 and up. It *might*
+This crate is guaranteed to compile on stable Rust 1.59.0 and up. It *might*
compile with older versions but that may change in any new patch release.
## License
diff --git a/cortex-m-semihosting/bin b/cortex-m-semihosting/bin
deleted file mode 120000
index 19f285a..0000000
--- a/cortex-m-semihosting/bin
+++ /dev/null
@@ -1 +0,0 @@
-../bin \ No newline at end of file
diff --git a/cortex-m-semihosting/build.rs b/cortex-m-semihosting/build.rs
index 315035e..ed0d069 100644
--- a/cortex-m-semihosting/build.rs
+++ b/cortex-m-semihosting/build.rs
@@ -1,23 +1,9 @@
-use std::path::PathBuf;
-use std::{env, fs};
+use std::env;
fn main() {
let target = env::var("TARGET").unwrap();
- let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
- let name = env::var("CARGO_PKG_NAME").unwrap();
if target.starts_with("thumbv") {
- if env::var_os("CARGO_FEATURE_INLINE_ASM").is_none() {
- fs::copy(
- format!("bin/{}.a", target),
- out_dir.join(format!("lib{}.a", name)),
- )
- .unwrap();
-
- println!("cargo:rustc-link-lib=static={}", name);
- println!("cargo:rustc-link-search={}", out_dir.display());
- }
-
println!("cargo:rustc-cfg=thumb");
}
}
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/cortex-m-semihosting/src/lib.rs b/cortex-m-semihosting/src/lib.rs
index 3bc23ea..8306307 100644
--- a/cortex-m-semihosting/src/lib.rs
+++ b/cortex-m-semihosting/src/lib.rs
@@ -151,14 +151,6 @@
//!
//! # Optional features
//!
-//! ## `inline-asm`
-//!
-//! When this feature is enabled semihosting is implemented using inline assembly and
-//! compiling this crate requires nightly.
-//!
-//! When this feature is disabled semihosting is implemented using FFI calls into an external
-//! assembly file and compiling this crate works on stable and beta.
-//!
//! ## `jlink-quirks`
//!
//! When this feature is enabled, return values above `0xfffffff0` from semihosting operation
@@ -191,11 +183,6 @@ pub mod export;
pub mod hio;
pub mod nr;
-#[cfg(all(thumb, not(feature = "inline-asm")))]
-extern "C" {
- fn __sh_syscall(nr: usize, arg: usize) -> usize;
-}
-
/// Performs a semihosting operation, takes a pointer to an argument block
#[inline(always)]
pub unsafe fn syscall<T>(nr: usize, arg: &T) -> usize {
@@ -206,24 +193,16 @@ pub unsafe fn syscall<T>(nr: usize, arg: &T) -> usize {
#[inline(always)]
pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize {
match () {
- #[cfg(all(thumb, not(feature = "inline-asm"), not(feature = "no-semihosting")))]
- () => __sh_syscall(_nr, _arg),
-
- #[cfg(all(thumb, feature = "inline-asm", not(feature = "no-semihosting")))]
+ #[cfg(all(thumb, not(feature = "no-semihosting")))]
() => {
- let mut nr = _nr;
- core::arch::asm!(
- "bkpt #0xab",
- inout("r0") nr,
- in("r1") _arg,
- options(nomem, nostack, preserves_flags)
- );
- nr
+ use core::arch::asm;
+ let mut nr = _nr as u32;
+ let arg = _arg as u32;
+ asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nostack, preserves_flags));
+ nr as usize
}
-
#[cfg(all(thumb, feature = "no-semihosting"))]
() => 0,
-
#[cfg(not(thumb))]
() => unimplemented!(),
}
diff --git a/panic-semihosting/CHANGELOG.md b/panic-semihosting/CHANGELOG.md
index 95c3890..1fa6de3 100644
--- a/panic-semihosting/CHANGELOG.md
+++ b/panic-semihosting/CHANGELOG.md
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
+## [v0.6.0] - 2022-03-01
+
+- Always use inline-asm, requiring Rust 1.59.
+- Remove inline-asm feature.
+
## [v0.5.6] - 2020-11-14
- Fix update to docs.rs to build for an embedded target
@@ -69,7 +74,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
Initial release
-[Unreleased]: https://github.com/rust-embedded/panic-semihosting/compare/p-sh-v0.5.6...HEAD
+[Unreleased]: https://github.com/rust-embedded/panic-semihosting/compare/p-sh-v0.6.0...HEAD
+[v0.6.0]: https://github.com/rust-embedded/cortex-m/compare/p-sh-v0.5.6...p-sh-v0.6.0
[v0.5.6]: https://github.com/rust-embedded/cortex-m/compare/p-sh-v0.5.5...p-sh-v0.5.6
[v0.5.5]: https://github.com/rust-embedded/cortex-m/compare/p-sh-v0.5.4...p-sh-v0.5.5
[v0.5.4]: https://github.com/rust-embedded/cortex-m/compare/p-sh-v0.5.3...p-sh-v0.5.4
diff --git a/panic-semihosting/Cargo.toml b/panic-semihosting/Cargo.toml
index 46a3d2f..f096614 100644
--- a/panic-semihosting/Cargo.toml
+++ b/panic-semihosting/Cargo.toml
@@ -10,15 +10,16 @@ keywords = ["panic-handler", "panic-impl", "panic", "semihosting"]
license = "MIT OR Apache-2.0"
name = "panic-semihosting"
repository = "https://github.com/rust-embedded/cortex-m"
-version = "0.5.6"
+version = "0.6.0"
+rust-version = "1.59"
+edition = "2021"
[dependencies]
cortex-m = { path = "..", version = ">= 0.5.6, < 0.8" }
-cortex-m-semihosting = { path = "../cortex-m-semihosting", version = ">= 0.3, < 0.5" }
+cortex-m-semihosting = { path = "../cortex-m-semihosting", version = ">= 0.5.0, < 0.6" }
[features]
exit = []
-inline-asm = ["cortex-m-semihosting/inline-asm", "cortex-m/inline-asm"]
jlink-quirks = ["cortex-m-semihosting/jlink-quirks"]
[package.metadata.docs.rs]
diff --git a/panic-semihosting/README.md b/panic-semihosting/README.md
index baacf1a..f8057d3 100644
--- a/panic-semihosting/README.md
+++ b/panic-semihosting/README.md
@@ -8,7 +8,7 @@ This project is developed and maintained by the [Cortex-M team][team].
## Minimum Supported Rust Version (MSRV)
-This crate is guaranteed to compile on stable Rust 1.32.0 and up. It *might*
+This crate is guaranteed to compile on stable Rust 1.59.0 and up. It *might*
compile with older versions but that may change in any new patch release.
## License
diff --git a/panic-semihosting/src/lib.rs b/panic-semihosting/src/lib.rs
index 1db7b72..1d7379e 100644
--- a/panic-semihosting/src/lib.rs
+++ b/panic-semihosting/src/lib.rs
@@ -47,14 +47,6 @@
//!
//! We discourage using this feature when the program will run on hardware as the exit call can
//! leave the hardware debugger in an inconsistent state.
-//!
-//! ## `inline-asm`
-//!
-//! When this feature is enabled semihosting is implemented using inline assembly (`asm!`) and
-//! compiling this crate requires nightly.
-//!
-//! When this feature is disabled semihosting is implemented using FFI calls into an external
-//! assembly file and compiling this crate works on stable and beta.
#![cfg(all(target_arch = "arm", target_os = "none"))]
#![deny(missing_docs)]
diff --git a/src/asm.rs b/src/asm.rs
index 4dc1ab0..3a3485a 100644
--- a/src/asm.rs
+++ b/src/asm.rs
@@ -1,18 +1,17 @@
//! Miscellaneous assembly instructions
-// When inline assembly is enabled, pull in the assembly routines here. `call_asm!` will invoke
-// these routines.
-#[cfg(feature = "inline-asm")]
-#[path = "../asm/inline.rs"]
-pub(crate) mod inline;
+#[cfg(cortex_m)]
+use core::arch::asm;
+use core::sync::atomic::{compiler_fence, Ordering};
/// Puts the processor in Debug state. Debuggers can pick this up as a "breakpoint".
///
/// **NOTE** calling `bkpt` when the processor is not connected to a debugger will cause an
/// exception.
+#[cfg(cortex_m)]
#[inline(always)]
pub fn bkpt() {
- call_asm!(__bkpt());
+ unsafe { asm!("bkpt", options(nomem, nostack, preserves_flags)) };
}
/// Blocks the program for *at least* `cycles` CPU cycles.
@@ -24,50 +23,80 @@ pub fn bkpt() {
/// and the execution time may vary with other factors. This delay is mainly useful for simple
/// timer-less initialization of peripherals if and only if accurate timing is not essential. In
/// any other case please use a more accurate method to produce a delay.
+#[cfg(cortex_m)]
#[inline]
pub fn delay(cycles: u32) {
- call_asm!(__delay(cycles: u32));
+ // The loop will normally take 3 to 4 CPU cycles per iteration, but superscalar cores
+ // (eg. Cortex-M7) can potentially do it in 2, so we use that as the lower bound, since delaying
+ // for more cycles is okay.
+ // Add 1 to prevent an integer underflow which would cause a long freeze
+ let real_cycles = 1 + cycles / 2;
+ unsafe {
+ asm!(
+ // Use local labels to avoid R_ARM_THM_JUMP8 relocations which fail on thumbv6m.
+ "1:",
+ "subs {}, #1",
+ "bne 1b",
+ inout(reg) real_cycles => _,
+ options(nomem, nostack),
+ )
+ };
}
/// A no-operation. Useful to prevent delay loops from being optimized away.
-#[inline]
+#[inline(always)]
pub fn nop() {
- call_asm!(__nop());
+ // NOTE: This is a `pure` asm block, but applying that option allows the compiler to eliminate
+ // the nop entirely (or to collapse multiple subsequent ones). Since the user probably wants N
+ // nops when they call `nop` N times, let's not add that option.
+ #[cfg(cortex_m)]
+ unsafe {
+ asm!("nop", options(nomem, nostack, preserves_flags))
+ };
}
/// Generate an Undefined Instruction exception.
///
/// Can be used as a stable alternative to `core::intrinsics::abort`.
-#[inline]
+#[cfg(cortex_m)]
+#[inline(always)]
pub fn udf() -> ! {
- call_asm!(__udf() -> !)
+ unsafe { asm!("udf #0", options(noreturn, nomem, nostack, preserves_flags)) };
}
/// Wait For Event
-#[inline]
+#[cfg(cortex_m)]
+#[inline(always)]
pub fn wfe() {
- call_asm!(__wfe())
+ unsafe { asm!("wfe", options(nomem, nostack, preserves_flags)) };
}
/// Wait For Interrupt
-#[inline]
+#[cfg(cortex_m)]
+#[inline(always)]
pub fn wfi() {
- call_asm!(__wfi())
+ unsafe { asm!("wfi", options(nomem, nostack, preserves_flags)) };
}
/// Send Event
-#[inline]
+#[cfg(cortex_m)]
+#[inline(always)]
pub fn sev() {
- call_asm!(__sev())
+ unsafe { asm!("sev", options(nomem, nostack, preserves_flags)) };
}
/// Instruction Synchronization Barrier
///
/// Flushes the pipeline in the processor, so that all instructions following the `ISB` are fetched
/// from cache or memory, after the instruction has been completed.
-#[inline]
+#[inline(always)]
pub fn isb() {
- call_asm!(__isb())
+ compiler_fence(Ordering::SeqCst);
+ #[cfg(cortex_m)]
+ unsafe {
+ asm!("isb", options(nomem, nostack, preserves_flags))
+ };
+ compiler_fence(Ordering::SeqCst);
}
/// Data Synchronization Barrier
@@ -77,9 +106,14 @@ pub fn isb() {
///
/// * any explicit memory access made before this instruction is complete
/// * all cache and branch predictor maintenance operations before this instruction complete
-#[inline]
+#[inline(always)]
pub fn dsb() {
- call_asm!(__dsb())
+ compiler_fence(Ordering::SeqCst);
+ #[cfg(cortex_m)]
+ unsafe {
+ asm!("dsb", options(nomem, nostack, preserves_flags))
+ };
+ compiler_fence(Ordering::SeqCst);
}
/// Data Memory Barrier
@@ -87,9 +121,14 @@ pub fn dsb() {
/// Ensures that all explicit memory accesses that appear in program order before the `DMB`
/// instruction are observed before any explicit memory accesses that appear in program order
/// after the `DMB` instruction.
-#[inline]
+#[inline(always)]
pub fn dmb() {
- call_asm!(__dmb())
+ compiler_fence(Ordering::SeqCst);
+ #[cfg(cortex_m)]
+ unsafe {
+ asm!("dmb", options(nomem, nostack, preserves_flags))
+ };
+ compiler_fence(Ordering::SeqCst);
}
/// Test Target
@@ -97,13 +136,20 @@ pub fn dmb() {
/// Queries the Security state and access permissions of a memory location.
/// Returns a Test Target Response Payload (cf section D1.2.215 of
/// Armv8-M Architecture Reference Manual).
-#[inline]
+#[inline(always)]
#[cfg(armv8m)]
// The __tt function does not dereference the pointer received.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn tt(addr: *mut u32) -> u32 {
- let addr = addr as u32;
- call_asm!(__tt(addr: u32) -> u32)
+ let mut target = addr as u32;
+ unsafe {
+ asm!(
+ "tt {target}, {target}",
+ target = inout(reg) target,
+ options(nomem, nostack, preserves_flags),
+ )
+ };
+ target
}
/// Test Target Unprivileged
@@ -112,13 +158,20 @@ pub fn tt(addr: *mut u32) -> u32 {
/// access to that location.
/// Returns a Test Target Response Payload (cf section D1.2.215 of
/// Armv8-M Architecture Reference Manual).
-#[inline]
+#[inline(always)]
#[cfg(armv8m)]
// The __ttt function does not dereference the pointer received.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn ttt(addr: *mut u32) -> u32 {
- let addr = addr as u32;
- call_asm!(__ttt(addr: u32) -> u32)
+ let mut target = addr as u32;
+ unsafe {
+ asm!(
+ "ttt {target}, {target}",
+ target = inout(reg) target,
+ options(nomem, nostack, preserves_flags),
+ )
+ };
+ target
}
/// Test Target Alternate Domain
@@ -128,13 +181,20 @@ pub fn ttt(addr: *mut u32) -> u32 {
/// undefined if used from Non-Secure state.
/// Returns a Test Target Response Payload (cf section D1.2.215 of
/// Armv8-M Architecture Reference Manual).
-#[inline]
+#[inline(always)]
#[cfg(armv8m)]
// The __tta function does not dereference the pointer received.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn tta(addr: *mut u32) -> u32 {
- let addr = addr as u32;
- call_asm!(__tta(addr: u32) -> u32)
+ let mut target = addr as u32;
+ unsafe {
+ asm!(
+ "tta {target}, {target}",
+ target = inout(reg) target,
+ options(nomem, nostack, preserves_flags),
+ )
+ };
+ target
}
/// Test Target Alternate Domain Unprivileged
@@ -144,31 +204,40 @@ pub fn tta(addr: *mut u32) -> u32 {
/// state and is undefined if used from Non-Secure state.
/// Returns a Test Target Response Payload (cf section D1.2.215 of
/// Armv8-M Architecture Reference Manual).
-#[inline]
+#[inline(always)]
#[cfg(armv8m)]
// The __ttat function does not dereference the pointer received.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn ttat(addr: *mut u32) -> u32 {
- let addr = addr as u32;
- call_asm!(__ttat(addr: u32) -> u32)
+ let mut target = addr as u32;
+ unsafe {
+ asm!(
+ "ttat {target}, {target}",
+ target = inout(reg) target,
+ options(nomem, nostack, preserves_flags),
+ )
+ };
+ target
}
/// Branch and Exchange Non-secure
///
/// See section C2.4.26 of Armv8-M Architecture Reference Manual for details.
/// Undefined if executed in Non-Secure state.
-#[inline]
+#[inline(always)]
#[cfg(armv8m)]
pub unsafe fn bx_ns(addr: u32) {
- call_asm!(__bxns(addr: u32));
+ asm!("bxns {}", in(reg) addr, options(nomem, nostack, preserves_flags));
}
/// Semihosting syscall.
///
/// This method is used by cortex-m-semihosting to provide semihosting syscalls.
-#[inline]
-pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 {
- call_asm!(__sh_syscall(nr: u32, arg: u32) -> u32)
+#[cfg(cortex_m)]
+#[inline(always)]
+pub unsafe fn semihosting_syscall(mut nr: u32, arg: u32) -> u32 {
+ asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nostack, preserves_flags));
+ nr
}
/// Bootstrap.
@@ -181,12 +250,27 @@ pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 {
///
/// `msp` and `rv` must point to valid stack memory and executable code,
/// respectively.
+#[cfg(cortex_m)]
#[inline]
pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! {
// Ensure thumb mode is set.
let rv = (rv as u32) | 1;
let msp = msp as u32;
- call_asm!(__bootstrap(msp: u32, rv: u32) -> !);
+ asm!(
+ "mrs {tmp}, CONTROL",
+ "bics {tmp}, {spsel}",
+ "msr CONTROL, {tmp}",
+ "isb",
+ "msr MSP, {msp}",
+ "bx {rv}",
+ // `out(reg) _` is not permitted in a `noreturn` asm! call,
+ // so instead use `in(reg) 0` and don't restore it afterwards.
+ tmp = in(reg) 0,
+ spsel = in(reg) 2,
+ msp = in(reg) msp,
+ rv = in(reg) rv,
+ options(noreturn, nomem, nostack),
+ );
}
/// Bootload.
@@ -201,6 +285,7 @@ pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! {
/// The provided `vector_table` must point to a valid vector
/// table, with a valid stack pointer as the first word and
/// a valid reset vector as the second word.
+#[cfg(cortex_m)]
#[inline]
pub unsafe fn bootload(vector_table: *const u32) -> ! {
let msp = core::ptr::read_volatile(vector_table);
diff --git a/src/call_asm.rs b/src/call_asm.rs
deleted file mode 100644
index 295277f..0000000
--- a/src/call_asm.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-/// An internal macro to invoke an assembly routine.
-///
-/// Depending on whether the unstable `inline-asm` feature is enabled, this will either call into
-/// the inline assembly implementation directly, or through the FFI shim (see `asm/lib.rs`).
-macro_rules! call_asm {
- ( $func:ident ( $($args:ident: $tys:ty),* ) $(-> $ret:ty)? ) => {{
- #[allow(unused_unsafe)]
- unsafe {
- match () {
- #[cfg(feature = "inline-asm")]
- () => crate::asm::inline::$func($($args),*),
-
- #[cfg(not(feature = "inline-asm"))]
- () => {
- extern "C" {
- fn $func($($args: $tys),*) $(-> $ret)?;
- }
-
- $func($($args),*)
- },
- }
- }
- }};
-}
diff --git a/src/cmse.rs b/src/cmse.rs
index 36d7447..7826bb8 100644
--- a/src/cmse.rs
+++ b/src/cmse.rs
@@ -174,9 +174,9 @@ impl TestTarget {
/// * the TT instruction was executed from an unprivileged mode and the A flag was not specified.
#[inline]
pub fn mpu_region(self) -> Option<u8> {
- if self.tt_resp.srvalid() {
- // Cast is safe as SREGION field is defined on 8 bits.
- Some(self.tt_resp.sregion() as u8)
+ if self.tt_resp.mrvalid() {
+ // Cast is safe as MREGION field is defined on 8 bits.
+ Some(self.tt_resp.mregion() as u8)
} else {
None
}
diff --git a/src/critical_section.rs b/src/critical_section.rs
new file mode 100644
index 0000000..e3d57d1
--- /dev/null
+++ b/src/critical_section.rs
@@ -0,0 +1,22 @@
+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()
+ }
+ }
+}
diff --git a/src/interrupt.rs b/src/interrupt.rs
index 68719ec..f6ce990 100644
--- a/src/interrupt.rs
+++ b/src/interrupt.rs
@@ -1,6 +1,9 @@
//! Interrupts
-pub use bare_metal::{CriticalSection, Mutex};
+#[cfg(cortex_m)]
+use core::arch::asm;
+#[cfg(cortex_m)]
+use core::sync::atomic::{compiler_fence, Ordering};
/// Trait for enums of external interrupt numbers.
///
@@ -23,36 +26,52 @@ 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() {
- call_asm!(__cpsid());
+ unsafe {
+ asm!("cpsid i", options(nomem, nostack, preserves_flags));
+ }
+
+ // Ensure no subsequent memory accesses are reordered to before interrupts are disabled.
+ 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() {
- call_asm!(__cpsie());
+ // Ensure no preceeding memory accesses are reordered to after interrupts are enabled.
+ compiler_fence(Ordering::SeqCst);
+
+ 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
@@ -62,3 +81,15 @@ where
r
}
+
+// Make a `free()` function available to allow checking dependencies without specifying a target,
+// but that will panic at runtime if executed.
+#[doc(hidden)]
+#[cfg(not(cortex_m))]
+#[inline]
+pub fn free<F, R>(_: F) -> R
+where
+ 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 dd46fd7..7c1599c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -9,20 +9,15 @@
//!
//! # Optional features
//!
-//! ## `inline-asm`
+//! ## `critical-section-single-core`
//!
-//! When this feature is enabled the implementation of all the functions inside the `asm` and
-//! `register` modules use inline assembly (`asm!`) instead of external assembly (FFI into separate
-//! assembly files pre-compiled using `arm-none-eabi-gcc`). The advantages of enabling `inline-asm`
-//! are:
+//! This feature enables a [`critical-section`](https://github.com/rust-embedded/critical-section)
+//! implementation suitable for single-core targets, based on disabling interrupts globally.
//!
-//! - Reduced overhead. FFI eliminates the possibility of inlining so all operations include a
-//! function call overhead when `inline-asm` is not enabled.
-//!
-//! - Some of the `register` API only becomes available only when `inline-asm` is enabled. Check the
-//! API docs for details.
-//!
-//! The disadvantage is that `inline-asm` requires a nightly toolchain.
+//! It is **unsound** to enable it on multi-core targets or for code running in unprivileged mode,
+//! and may cause functional problems in systems where some interrupts must be not be disabled
+//! or critical sections are managed as part of an RTOS. In these cases, you should use
+//! a target-specific implementation instead, typically provided by a HAL or RTOS crate.
//!
//! ## `cm7-r0p1`
//!
@@ -30,32 +25,11 @@
//! functions in this crate only work correctly on those chips if this Cargo feature is enabled
//! (the functions are documented accordingly).
//!
-//! ## `linker-plugin-lto`
-//!
-//! This feature links against prebuilt assembly blobs that are compatible with [Linker-Plugin LTO].
-//! This allows inlining assembly routines into the caller, even without the `inline-asm` feature,
-//! and works on stable Rust (but note the drawbacks below!).
-//!
-//! If you want to use this feature, you need to be aware of a few things:
-//!
-//! - You need to make sure that `-Clinker-plugin-lto` is passed to rustc. Please refer to the
-//! [Linker-Plugin LTO] documentation for details.
-//!
-//! - You have to use a Rust version whose LLVM version is compatible with the toolchain in
-//! `asm-toolchain`.
-//!
-//! - Due to a [Rust bug][rust-lang/rust#75940] in compiler versions **before 1.49**, this option
-//! does not work with optimization levels `s` and `z`.
-//!
-//! [Linker-Plugin LTO]: https://doc.rust-lang.org/stable/rustc/linker-plugin-lto.html
-//! [rust-lang/rust#75940]: https://github.com/rust-lang/rust/issues/75940
-//!
//! # Minimum Supported Rust Version (MSRV)
//!
-//! This crate is guaranteed to compile on stable Rust 1.42 and up. It *might*
+//! This crate is guaranteed to compile on stable Rust 1.59 and up. It *might*
//! compile with older versions but that may change in any new patch release.
-#![cfg_attr(feature = "inline-asm", feature(asm))]
#![deny(missing_docs)]
#![no_std]
#![allow(clippy::identity_op)]
@@ -79,11 +53,6 @@
// 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 call_asm;
#[macro_use]
mod macros;
@@ -95,7 +64,16 @@ pub mod interrupt;
#[cfg(all(not(armv6m), not(armv8m_base)))]
pub mod itm;
pub mod peripheral;
-pub mod prelude;
pub mod register;
pub use crate::peripheral::Peripherals;
+
+#[cfg(all(cortex_m, feature = "critical-section-single-core"))]
+mod critical_section;
+
+/// Used to reexport items for use in macros. Do not use directly.
+/// Not covered by semver guarantees.
+#[doc(hidden)]
+pub mod _export {
+ pub use critical_section;
+}
diff --git a/src/macros.rs b/src/macros.rs
index 512c932..2cf4f89 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -31,7 +31,10 @@ macro_rules! iprintln {
/// at most once in the whole lifetime of the program.
///
/// # Notes
-/// This macro is unsound on multi core systems.
+///
+/// This macro requires a `critical-section` implementation to be set. For most single core systems,
+/// you can enable the `critical-section-single-core` feature for this crate. For other systems, you
+/// have to provide one from elsewhere, typically your chip's HAL crate.
///
/// For debuggability, you can set an explicit name for a singleton. This name only shows up the
/// the debugger and is not referencable from other code. See example below.
@@ -62,7 +65,7 @@ macro_rules! iprintln {
#[macro_export]
macro_rules! singleton {
($name:ident: $ty:ty = $expr:expr) => {
- $crate::interrupt::free(|_| {
+ $crate::_export::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/dwt.rs b/src/peripheral/dwt.rs
index c5f7bc9..72575d3 100644
--- a/src/peripheral/dwt.rs
+++ b/src/peripheral/dwt.rs
@@ -155,6 +155,18 @@ impl DWT {
}
}
+ /// Disables the cycle counter
+ #[cfg(not(armv6m))]
+ #[inline]
+ pub fn disable_cycle_counter(&mut self) {
+ unsafe {
+ self.ctrl.modify(|mut r| {
+ r.set_cyccntena(false);
+ r
+ });
+ }
+ }
+
/// Returns `true` if the cycle counter is enabled
#[cfg(not(armv6m))]
#[inline]
diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs
index af922b1..bf18151 100644
--- a/src/peripheral/mod.rs
+++ b/src/peripheral/mod.rs
@@ -60,8 +60,6 @@
use core::marker::PhantomData;
use core::ops;
-use crate::interrupt;
-
#[cfg(cm7)]
pub mod ac;
#[cfg(not(armv6m))]
@@ -165,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/src/peripheral/tpiu.rs b/src/peripheral/tpiu.rs
index 0762495..14dd35c 100644
--- a/src/peripheral/tpiu.rs
+++ b/src/peripheral/tpiu.rs
@@ -118,7 +118,6 @@ impl TPIU {
/// [`trace_output_protocol`](Self::set_trace_output_protocol).
#[inline]
pub fn trace_output_protocol(&self) -> Option<TraceProtocol> {
- use core::convert::TryInto;
self.sppr.read().txmode().try_into().ok()
}
diff --git a/src/prelude.rs b/src/prelude.rs
deleted file mode 100644
index bc47cc0..0000000
--- a/src/prelude.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-//! Prelude
-
-pub use embedded_hal::prelude::*;
diff --git a/src/register/apsr.rs b/src/register/apsr.rs
index e83435c..edb8737 100644
--- a/src/register/apsr.rs
+++ b/src/register/apsr.rs
@@ -1,5 +1,8 @@
//! Application Program Status Register
+#[cfg(cortex_m)]
+use core::arch::asm;
+
/// Application Program Status Register
#[derive(Clone, Copy, Debug)]
pub struct Apsr {
@@ -45,10 +48,10 @@ impl Apsr {
}
/// Reads the CPU register
-///
-/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
+#[cfg(cortex_m)]
#[inline]
pub fn read() -> Apsr {
- let bits: u32 = call_asm!(__apsr_r() -> u32);
+ let bits;
+ unsafe { asm!("mrs {}, APSR", out(reg) bits, options(nomem, nostack, preserves_flags)) };
Apsr { bits }
}
diff --git a/src/register/basepri.rs b/src/register/basepri.rs
index 07084cd..cffb379 100644
--- a/src/register/basepri.rs
+++ b/src/register/basepri.rs
@@ -1,24 +1,42 @@
//! Base Priority Mask Register
+#[cfg(cortex_m)]
+use core::arch::asm;
+
/// Reads the CPU register
+#[cfg(cortex_m)]
#[inline]
pub fn read() -> u8 {
- call_asm!(__basepri_r() -> u8)
+ let r;
+ unsafe { asm!("mrs {}, BASEPRI", out(reg) r, options(nomem, nostack, preserves_flags)) };
+ r
}
/// Writes to the CPU register
///
/// **IMPORTANT** If you are using a Cortex-M7 device with revision r0p1 you MUST enable the
/// `cm7-r0p1` Cargo feature or this function WILL misbehave.
+#[cfg(cortex_m)]
#[inline]
pub unsafe fn write(basepri: u8) {
#[cfg(feature = "cm7-r0p1")]
{
- call_asm!(__basepri_w_cm7_r0p1(basepri: u8));
+ asm!(
+ "mrs {1}, PRIMASK",
+ "cpsid i",
+ "tst.w {1}, #1",
+ "msr BASEPRI, {0}",
+ "it ne",
+ "bxne lr",
+ "cpsie i",
+ in(reg) basepri,
+ out(reg) _,
+ options(nomem, nostack, preserves_flags),
+ );
}
#[cfg(not(feature = "cm7-r0p1"))]
{
- call_asm!(__basepri_w(basepri: u8));
+ asm!("msr BASEPRI, {}", in(reg) basepri, options(nomem, nostack, preserves_flags));
}
}
diff --git a/src/register/basepri_max.rs b/src/register/basepri_max.rs
index cea3838..2881c4f 100644
--- a/src/register/basepri_max.rs
+++ b/src/register/basepri_max.rs
@@ -1,5 +1,8 @@
//! Base Priority Mask Register (conditional write)
+#[cfg(cortex_m)]
+use core::arch::asm;
+
/// Writes to BASEPRI *if*
///
/// - `basepri != 0` AND `basepri::read() == 0`, OR
@@ -7,15 +10,31 @@
///
/// **IMPORTANT** If you are using a Cortex-M7 device with revision r0p1 you MUST enable the
/// `cm7-r0p1` Cargo feature or this function WILL misbehave.
+#[cfg(cortex_m)]
#[inline]
pub fn write(basepri: u8) {
#[cfg(feature = "cm7-r0p1")]
{
- call_asm!(__basepri_max_cm7_r0p1(basepri: u8));
+ unsafe {
+ asm!(
+ "mrs {1}, PRIMASK",
+ "cpsid i",
+ "tst.w {1}, #1",
+ "msr BASEPRI_MAX, {0}",
+ "it ne",
+ "bxne lr",
+ "cpsie i",
+ in(reg) basepri,
+ out(reg) _,
+ options(nomem, nostack, preserves_flags),
+ );
+ }
}
#[cfg(not(feature = "cm7-r0p1"))]
{
- call_asm!(__basepri_max(basepri: u8));
+ unsafe {
+ asm!("msr BASEPRI_MAX, {}", in(reg) basepri, options(nomem, nostack, preserves_flags));
+ }
}
}
diff --git a/src/register/control.rs b/src/register/control.rs
index a991625..d781913 100644
--- a/src/register/control.rs
+++ b/src/register/control.rs
@@ -1,5 +1,10 @@
//! Control register
+#[cfg(cortex_m)]
+use core::arch::asm;
+#[cfg(cortex_m)]
+use core::sync::atomic::{compiler_fence, Ordering};
+
/// Control register
#[derive(Clone, Copy, Debug)]
pub struct Control {
@@ -150,15 +155,29 @@ impl Fpca {
}
/// Reads the CPU register
+#[cfg(cortex_m)]
#[inline]
pub fn read() -> Control {
- let bits: u32 = call_asm!(__control_r() -> u32);
+ let bits;
+ unsafe { asm!("mrs {}, CONTROL", out(reg) bits, options(nomem, nostack, preserves_flags)) };
Control { bits }
}
/// Writes to the CPU register.
+#[cfg(cortex_m)]
#[inline]
pub unsafe fn write(control: Control) {
let control = control.bits();
- call_asm!(__control_w(control: u32));
+
+ // ISB is required after writing to CONTROL,
+ // per ARM architectural requirements (see Application Note 321).
+ asm!(
+ "msr CONTROL, {}",
+ "isb",
+ in(reg) control,
+ options(nomem, nostack, preserves_flags),
+ );
+
+ // Ensure memory accesses are not reordered around the CONTROL update.
+ compiler_fence(Ordering::SeqCst);
}
diff --git a/src/register/faultmask.rs b/src/register/faultmask.rs
index e57fa28..1d32709 100644
--- a/src/register/faultmask.rs
+++ b/src/register/faultmask.rs
@@ -1,5 +1,8 @@
//! Fault Mask Register
+#[cfg(cortex_m)]
+use core::arch::asm;
+
/// All exceptions are ...
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Faultmask {
@@ -24,9 +27,11 @@ impl Faultmask {
}
/// Reads the CPU register
+#[cfg(cortex_m)]
#[inline]
pub fn read() -> Faultmask {
- let r: u32 = call_asm!(__faultmask_r() -> u32);
+ let r: u32;
+ unsafe { asm!("mrs {}, FAULTMASK", out(reg) r, options(nomem, nostack, preserves_flags)) };
if r & (1 << 0) == (1 << 0) {
Faultmask::Inactive
} else {
diff --git a/src/register/fpscr.rs b/src/register/fpscr.rs
index 68692c7..bffed6c 100644
--- a/src/register/fpscr.rs
+++ b/src/register/fpscr.rs
@@ -1,5 +1,7 @@
//! Floating-point Status Control Register
+use core::arch::asm;
+
/// Floating-point Status Control Register
#[derive(Clone, Copy, Debug)]
pub struct Fpscr {
@@ -293,7 +295,8 @@ impl RMode {
/// Read the FPSCR register
#[inline]
pub fn read() -> Fpscr {
- let r: u32 = call_asm!(__fpscr_r() -> u32);
+ let r;
+ unsafe { asm!("vmrs {}, fpscr", out(reg) r, options(nomem, nostack, preserves_flags)) };
Fpscr::from_bits(r)
}
@@ -301,5 +304,5 @@ pub fn read() -> Fpscr {
#[inline]
pub unsafe fn write(fpscr: Fpscr) {
let fpscr = fpscr.bits();
- call_asm!(__fpscr_w(fpscr: u32));
+ asm!("vmsr fpscr, {}", in(reg) fpscr, options(nomem, nostack));
}
diff --git a/src/register/lr.rs b/src/register/lr.rs
index 1aa546c..02708ae 100644
--- a/src/register/lr.rs
+++ b/src/register/lr.rs
@@ -1,17 +1,20 @@
//! Link register
+#[cfg(cortex_m)]
+use core::arch::asm;
+
/// Reads the CPU register
-///
-/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
+#[cfg(cortex_m)]
#[inline]
pub fn read() -> u32 {
- call_asm!(__lr_r() -> u32)
+ let r;
+ unsafe { asm!("mov {}, lr", out(reg) r, options(nomem, nostack, preserves_flags)) };
+ r
}
/// Writes `bits` to the CPU register
-///
-/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
+#[cfg(cortex_m)]
#[inline]
pub unsafe fn write(bits: u32) {
- call_asm!(__lr_w(bits: u32));
+ asm!("mov lr, {}", in(reg) bits, options(nomem, nostack, preserves_flags));
}
diff --git a/src/register/mod.rs b/src/register/mod.rs
index 48d157a..aee7d21 100644
--- a/src/register/mod.rs
+++ b/src/register/mod.rs
@@ -56,13 +56,8 @@ pub mod msplim;
#[cfg(armv8m_main)]
pub mod psplim;
-// Accessing these registers requires inline assembly because their contents are tied to the current
-// stack frame
-#[cfg(feature = "inline-asm")]
pub mod apsr;
-#[cfg(feature = "inline-asm")]
pub mod lr;
-#[cfg(feature = "inline-asm")]
pub mod pc;
diff --git a/src/register/msp.rs b/src/register/msp.rs
index bccc2ae..22ce7d9 100644
--- a/src/register/msp.rs
+++ b/src/register/msp.rs
@@ -1,16 +1,27 @@
//! Main Stack Pointer
+#[cfg(cortex_m)]
+use core::arch::asm;
+
/// Reads the CPU register
+#[cfg(cortex_m)]
#[inline]
pub fn read() -> u32 {
- call_asm!(__msp_r() -> u32)
+ let r;
+ unsafe { asm!("mrs {}, MSP", out(reg) r, options(nomem, nostack, preserves_flags)) };
+ r
}
/// Writes `bits` to the CPU register
+#[cfg(cortex_m)]
#[inline]
#[deprecated = "calling this function invokes Undefined Behavior, consider asm::bootstrap as an alternative"]
pub unsafe fn write(bits: u32) {
- call_asm!(__msp_w(bits: u32));
+ // Technically is writing to the stack pointer "not pushing any data to the stack"?
+ // In any event, if we don't set `nostack` here, this method is useless as the new
+ // stack value is immediately mutated by returning. Really this is just not a good
+ // method and its use is marked as deprecated.
+ asm!("msr MSP, {}", in(reg) bits, options(nomem, nostack, preserves_flags));
}
/// Reads the Non-Secure CPU register from Secure state.
@@ -19,7 +30,9 @@ pub unsafe fn write(bits: u32) {
#[cfg(armv8m)]
#[inline]
pub fn read_ns() -> u32 {
- call_asm!(__msp_ns_r() -> u32)
+ let r;
+ unsafe { asm!("mrs {}, MSP_NS", out(reg) r, options(nomem, nostack, preserves_flags)) };
+ r
}
/// Writes `bits` to the Non-Secure CPU register from Secure state.
@@ -28,5 +41,5 @@ pub fn read_ns() -> u32 {
#[cfg(armv8m)]
#[inline]
pub unsafe fn write_ns(bits: u32) {
- call_asm!(__msp_ns_w(bits: u32));
+ asm!("msr MSP_NS, {}", in(reg) bits, options(nomem, nostack, preserves_flags));
}
diff --git a/src/register/msplim.rs b/src/register/msplim.rs
index ac6f9ed..7b45b33 100644
--- a/src/register/msplim.rs
+++ b/src/register/msplim.rs
@@ -1,13 +1,17 @@
//! Main Stack Pointer Limit Register
+use core::arch::asm;
+
/// Reads the CPU register
#[inline]
pub fn read() -> u32 {
- call_asm!(__msplim_r() -> u32)
+ let r;
+ unsafe { asm!("mrs {}, MSPLIM", out(reg) r, options(nomem, nostack, preserves_flags)) };
+ r
}
/// Writes `bits` to the CPU register
#[inline]
pub unsafe fn write(bits: u32) {
- call_asm!(__msplim_w(bits: u32))
+ asm!("msr MSPLIM, {}", in(reg) bits, options(nomem, nostack, preserves_flags));
}
diff --git a/src/register/pc.rs b/src/register/pc.rs
index 0b33629..3460664 100644
--- a/src/register/pc.rs
+++ b/src/register/pc.rs
@@ -1,17 +1,20 @@
//! Program counter
+#[cfg(cortex_m)]
+use core::arch::asm;
+
/// Reads the CPU register
-///
-/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
+#[cfg(cortex_m)]
#[inline]
pub fn read() -> u32 {
- call_asm!(__pc_r() -> u32)
+ let r;
+ unsafe { asm!("mov {}, pc", out(reg) r, options(nomem, nostack, preserves_flags)) };
+ r
}
/// Writes `bits` to the CPU register
-///
-/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature.
+#[cfg(cortex_m)]
#[inline]
pub unsafe fn write(bits: u32) {
- call_asm!(__pc_w(bits: u32));
+ asm!("mov pc, {}", in(reg) bits, options(nomem, nostack, preserves_flags));
}
diff --git a/src/register/primask.rs b/src/register/primask.rs
index 842ca49..e95276f 100644
--- a/src/register/primask.rs
+++ b/src/register/primask.rs
@@ -1,5 +1,8 @@
//! Priority mask register
+#[cfg(cortex_m)]
+use core::arch::asm;
+
/// All exceptions with configurable priority are ...
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Primask {
@@ -24,9 +27,11 @@ impl Primask {
}
/// Reads the CPU register
+#[cfg(cortex_m)]
#[inline]
pub fn read() -> Primask {
- let r: u32 = call_asm!(__primask_r() -> u32);
+ let r: u32;
+ unsafe { asm!("mrs {}, PRIMASK", out(reg) r, options(nomem, nostack, preserves_flags)) };
if r & (1 << 0) == (1 << 0) {
Primask::Inactive
} else {
diff --git a/src/register/psp.rs b/src/register/psp.rs
index 0bca22c..c8f53b9 100644
--- a/src/register/psp.rs
+++ b/src/register/psp.rs
@@ -1,13 +1,22 @@
//! Process Stack Pointer
+#[cfg(cortex_m)]
+use core::arch::asm;
+
/// Reads the CPU register
+#[cfg(cortex_m)]
#[inline]
pub fn read() -> u32 {
- call_asm!(__psp_r() -> u32)
+ let r;
+ unsafe { asm!("mrs {}, PSP", out(reg) r, options(nomem, nostack, preserves_flags)) };
+ r
}
/// Writes `bits` to the CPU register
+#[cfg(cortex_m)]
#[inline]
pub unsafe fn write(bits: u32) {
- call_asm!(__psp_w(bits: u32))
+ // See comment on msp_w. Unlike MSP, there are legitimate use-cases for modifying PSP
+ // if MSP is currently being used as the stack pointer.
+ asm!("msr PSP, {}", in(reg) bits, options(nomem, nostack, preserves_flags));
}
diff --git a/src/register/psplim.rs b/src/register/psplim.rs
index 8ee1e94..832f9c6 100644
--- a/src/register/psplim.rs
+++ b/src/register/psplim.rs
@@ -1,13 +1,17 @@
//! Process Stack Pointer Limit Register
+use core::arch::asm;
+
/// Reads the CPU register
#[inline]
pub fn read() -> u32 {
- call_asm!(__psplim_r() -> u32)
+ let r;
+ unsafe { asm!("mrs {}, PSPLIM", out(reg) r, options(nomem, nostack, preserves_flags)) };
+ r
}
/// Writes `bits` to the CPU register
#[inline]
pub unsafe fn write(bits: u32) {
- call_asm!(__psplim_w(bits: u32))
+ asm!("msr PSPLIM, {}", in(reg) bits, options(nomem, nostack, preserves_flags));
}
diff --git a/testsuite/.cargo/config.toml b/testsuite/.cargo/config.toml
new file mode 100644
index 0000000..cce98a9
--- /dev/null
+++ b/testsuite/.cargo/config.toml
@@ -0,0 +1,6 @@
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+rustflags = ["-C", "link-arg=-Tlink.x"]
+runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
+
+[build]
+target = "thumbv7m-none-eabi"
diff --git a/testsuite/Cargo.toml b/testsuite/Cargo.toml
new file mode 100644
index 0000000..be3e43d
--- /dev/null
+++ b/testsuite/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+authors = ["The Cortex-M Team <cortex-m@teams.rust-embedded.org>"]
+name = "testsuite"
+publish = false
+edition = "2018"
+version = "0.1.0"
+
+[features]
+rtt = ["rtt-target", "minitest/rtt"]
+semihosting = ["cortex-m-semihosting", "minitest/semihosting"]
+
+[dependencies]
+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"
+optional = true
+
+[dependencies.cortex-m-semihosting]
+path = "../cortex-m-semihosting"
+optional = true
diff --git a/testsuite/README.md b/testsuite/README.md
new file mode 100644
index 0000000..c11d850
--- /dev/null
+++ b/testsuite/README.md
@@ -0,0 +1,69 @@
+# Testsuite
+
+This workspace contains tests that run on physical and simulated Cortex-M CPUs.
+
+## Building
+
+Exactly one of these features are required:
+
+* `semihosting` Use semihosting for logging, this is used for QEMU.
+* `rtt` Use RTT for logging, this is used with physical cortex-m CPUs.
+
+Assuming you are at the root of the repository you can build like this:
+
+```console
+$ cd testsuite
+$ cargo build --features semihosting
+ Compiling testsuite v0.1.0 (cortex-m/testsuite)
+ Finished dev [unoptimized + debuginfo] target(s) in 0.08
+```
+
+## Running with QEMU
+
+The runner is already configured for QEMU in `testsuite/.cargo/config.toml`.
+Use the `semihosting` feature for logging, QEMU does not have native support for RTT.
+
+For more information on QEMU reference the QEMU section in [The Embedded Rust Book].
+
+```console
+$ cd testsuite
+$ cargo run --features semihosting
+ Finished dev [unoptimized + debuginfo] target(s) in 0.01s
+ Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel /cortex-m/target/thumbv7m-none-eabi/debug/testsuite`
+Timer with period zero, disabling
+Hello world!
+(1/1) running `double_take`...
+all tests passed!
+```
+
+## Running with Physical Hardware
+
+No implementation-specific features are tested right now; any physical `thumbv7m` target should work.
+
+Tests are executed with [probe-run](https://github.com/knurling-rs/probe-run).
+
+* Update `memory.x` in the root of the repository to match your target memory layout.
+* Change the `probe-run` chip argument to match your chip, supported chips can be found with `probe-run --list-chips`
+* Change the target to match your CPU
+
+```console
+$ sed -i 's/FLASH : ORIGIN = 0x00000000, LENGTH = 256K/FLASH : ORIGIN = 0x8000000, LENGTH = 256K/g' memory.x
+$ cd testsuite
+$ cargo build --target thumbv7em-none-eabi --features rtt
+ Compiling minitest v0.1.0 (/cortex-m/testsuite/minitest)
+ Compiling testsuite v0.1.0 (/cortex-m/testsuite)
+ Finished dev [unoptimized + debuginfo] target(s) in 0.16s
+$ probe-run --chip STM32WLE5JCIx --connect-under-reset ../target/thumbv7em-none-eabi/debug/testsuite
+(HOST) INFO flashing program (19 pages / 19.00 KiB)
+(HOST) INFO success!
+────────────────────────────────────────────────────────────────────────────────
+Hello world!
+(1/2) running `double_take`...
+(2/2) running `cycle_count`...
+all tests passed!
+────────────────────────────────────────────────────────────────────────────────
+(HOST) INFO device halted without error
+```
+
+[The Embedded Rust Book]: https://docs.rust-embedded.org/book/start/qemu.html
+[probe-run]: https://github.com/knurling-rs/probe-run
diff --git a/testsuite/build.rs b/testsuite/build.rs
new file mode 100644
index 0000000..c0662b9
--- /dev/null
+++ b/testsuite/build.rs
@@ -0,0 +1,18 @@
+fn main() {
+ let target = std::env::var("TARGET").unwrap();
+
+ if target.starts_with("thumbv6m-") {
+ println!("cargo:rustc-cfg=armv6m");
+ } else if target.starts_with("thumbv7m-") {
+ println!("cargo:rustc-cfg=armv7m");
+ } else if target.starts_with("thumbv7em-") {
+ println!("cargo:rustc-cfg=armv7m");
+ println!("cargo:rustc-cfg=armv7em"); // (not currently used)
+ } else if target.starts_with("thumbv8m.base") {
+ println!("cargo:rustc-cfg=armv8m");
+ println!("cargo:rustc-cfg=armv8m_base");
+ } else if target.starts_with("thumbv8m.main") {
+ println!("cargo:rustc-cfg=armv8m");
+ println!("cargo:rustc-cfg=armv8m_main");
+ }
+}
diff --git a/testsuite/minitest/Cargo.toml b/testsuite/minitest/Cargo.toml
new file mode 100644
index 0000000..bf2c2eb
--- /dev/null
+++ b/testsuite/minitest/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+authors = ["The Cortex-M Team <cortex-m@teams.rust-embedded.org>"]
+name = "minitest"
+publish = false
+edition = "2018"
+version = "0.1.0"
+
+[features]
+semihosting = ["cortex-m-semihosting", "minitest-macros/semihosting"]
+rtt = ["rtt-target", "minitest-macros/rtt"]
+
+[dependencies]
+cortex-m.path = "../.."
+cortex-m-rt.path = "../../cortex-m-rt"
+minitest-macros.path = "macros"
+
+[dependencies.rtt-target]
+version = "0.3.1"
+optional = true
+
+[dependencies.cortex-m-semihosting]
+path = "../../cortex-m-semihosting"
+optional = true
diff --git a/testsuite/minitest/README.md b/testsuite/minitest/README.md
new file mode 100644
index 0000000..0a456a8
--- /dev/null
+++ b/testsuite/minitest/README.md
@@ -0,0 +1,7 @@
+# mini-test
+
+This is an embedded test framework forked from knurling's excellent [`defmt-test`] crate.
+
+This even more minimal than [`defmt-test`] to allow for for testing of this crate without dependency cycles.
+
+[`defmt-test`]: https://crates.io/crates/defmt-test/
diff --git a/testsuite/minitest/macros/Cargo.toml b/testsuite/minitest/macros/Cargo.toml
new file mode 100644
index 0000000..077e316
--- /dev/null
+++ b/testsuite/minitest/macros/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+authors = ["The Cortex-M Team <cortex-m@teams.rust-embedded.org>"]
+name = "minitest-macros"
+publish = false
+edition = "2018"
+version = "0.1.0"
+
+[lib]
+proc-macro = true
+
+[features]
+semihosting = []
+rtt = []
+
+[dependencies]
+proc-macro2 = "1.0.29"
+quote = "1.0.10"
+syn = { version = "1.0.80", features = ["extra-traits", "full"] }
diff --git a/testsuite/minitest/macros/src/lib.rs b/testsuite/minitest/macros/src/lib.rs
new file mode 100644
index 0000000..e8a1087
--- /dev/null
+++ b/testsuite/minitest/macros/src/lib.rs
@@ -0,0 +1,331 @@
+extern crate proc_macro;
+
+use proc_macro::TokenStream;
+use proc_macro2::Span;
+use quote::{format_ident, quote, quote_spanned};
+use syn::{parse, spanned::Spanned, Attribute, Item, ItemFn, ItemMod, ReturnType, Type};
+
+#[proc_macro_attribute]
+pub fn tests(args: TokenStream, input: TokenStream) -> TokenStream {
+ match tests_impl(args, input) {
+ Ok(ts) => ts,
+ Err(e) => e.to_compile_error().into(),
+ }
+}
+
+fn tests_impl(args: TokenStream, input: TokenStream) -> parse::Result<TokenStream> {
+ if !args.is_empty() {
+ return Err(parse::Error::new(
+ Span::call_site(),
+ "`#[test]` attribute takes no arguments",
+ ));
+ }
+
+ let module: ItemMod = syn::parse(input)?;
+
+ let items = if let Some(content) = module.content {
+ content.1
+ } else {
+ return Err(parse::Error::new(
+ module.span(),
+ "module must be inline (e.g. `mod foo {}`)",
+ ));
+ };
+
+ let mut init = None;
+ let mut tests = vec![];
+ let mut untouched_tokens = vec![];
+ for item in items {
+ match item {
+ Item::Fn(mut f) => {
+ let mut test_kind = None;
+ let mut should_error = false;
+
+ f.attrs.retain(|attr| {
+ if attr.path.is_ident("init") {
+ test_kind = Some(Attr::Init);
+ false
+ } else if attr.path.is_ident("test") {
+ test_kind = Some(Attr::Test);
+ false
+ } else if attr.path.is_ident("should_error") {
+ should_error = true;
+ false
+ } else {
+ true
+ }
+ });
+
+ let attr = match test_kind {
+ Some(it) => it,
+ None => {
+ return Err(parse::Error::new(
+ f.span(),
+ "function requires `#[init]` or `#[test]` attribute",
+ ));
+ }
+ };
+
+ match attr {
+ Attr::Init => {
+ if init.is_some() {
+ return Err(parse::Error::new(
+ f.sig.ident.span(),
+ "only a single `#[init]` function can be defined",
+ ));
+ }
+
+ if should_error {
+ return Err(parse::Error::new(
+ f.sig.ident.span(),
+ "`#[should_error]` is not allowed on the `#[init]` function",
+ ));
+ }
+
+ if check_fn_sig(&f.sig).is_err() || !f.sig.inputs.is_empty() {
+ return Err(parse::Error::new(
+ f.sig.ident.span(),
+ "`#[init]` function must have signature `fn() [-> Type]` (the return type is optional)",
+ ));
+ }
+
+ let state = match &f.sig.output {
+ ReturnType::Default => None,
+ ReturnType::Type(.., ty) => Some(ty.clone()),
+ };
+
+ init = Some(Init { func: f, state });
+ }
+
+ Attr::Test => {
+ if check_fn_sig(&f.sig).is_err() || f.sig.inputs.len() > 1 {
+ return Err(parse::Error::new(
+ f.sig.ident.span(),
+ "`#[test]` function must have signature `fn([&mut Type])` (parameter is optional)",
+ ));
+ }
+
+ let input = if f.sig.inputs.len() == 1 {
+ let arg = &f.sig.inputs[0];
+
+ // NOTE we cannot check the argument type matches `init.state` at this
+ // point
+ if let Some(ty) = get_mutable_reference_type(arg).cloned() {
+ Some(Input { ty })
+ } else {
+ // was not `&mut T`
+ return Err(parse::Error::new(
+ arg.span(),
+ "parameter must be a mutable reference (`&mut $Type`)",
+ ));
+ }
+ } else {
+ None
+ };
+
+ tests.push(Test {
+ cfgs: extract_cfgs(&f.attrs),
+ func: f,
+ input,
+ should_error,
+ })
+ }
+ }
+ }
+
+ _ => {
+ untouched_tokens.push(item);
+ }
+ }
+ }
+
+ let krate = format_ident!("minitest");
+ let ident = module.ident;
+ let mut state_ty = None;
+ let (init_fn, init_expr) = if let Some(init) = init {
+ let init_func = &init.func;
+ let init_ident = &init.func.sig.ident;
+ state_ty = init.state;
+
+ (
+ Some(quote!(#init_func)),
+ Some(quote!(#[allow(dead_code)] let mut state = #init_ident();)),
+ )
+ } else {
+ (None, None)
+ };
+
+ let mut unit_test_calls = vec![];
+ for test in &tests {
+ let should_error = test.should_error;
+ let ident = &test.func.sig.ident;
+ let span = test.func.sig.ident.span();
+ let call = if let Some(input) = test.input.as_ref() {
+ if let Some(state) = &state_ty {
+ if input.ty != **state {
+ return Err(parse::Error::new(
+ input.ty.span(),
+ "this type must match `#[init]`s return type",
+ ));
+ }
+ } else {
+ return Err(parse::Error::new(
+ span,
+ "no state was initialized by `#[init]`; signature must be `fn()`",
+ ));
+ }
+
+ quote!(#ident(&mut state))
+ } else {
+ quote!(#ident())
+ };
+ unit_test_calls.push(quote!(
+ #krate::export::check_outcome(#call, #should_error);
+ ));
+ }
+
+ let test_functions = tests.iter().map(|test| &test.func);
+ let test_cfgs = tests.iter().map(|test| &test.cfgs);
+ let declare_test_count = {
+ let test_cfgs = test_cfgs.clone();
+ quote!(
+ // We can't evaluate `#[cfg]`s in the macro, but this works too.
+ const __MINITEST_COUNT: usize = {
+ let mut counter = 0;
+ #(
+ #(#test_cfgs)*
+ { counter += 1; }
+ )*
+ counter
+ };
+ )
+ };
+
+ #[cfg(feature = "rtt")]
+ let init_logging = quote!({
+ let channels = ::rtt_target::rtt_init! {
+ up: {
+ 0: {
+ size: 256
+ mode: BlockIfFull
+ name: "minitest"
+ }
+ }
+ };
+ unsafe {
+ ::rtt_target::set_print_channel_cs(
+ channels.up.0,
+ &((|arg, f| ::critical_section::with(|_| f(arg)))
+ as ::rtt_target::CriticalSectionFunc),
+ );
+ }
+ });
+
+ #[cfg(not(feature = "rtt"))]
+ let init_logging = quote!({});
+
+ let unit_test_progress = tests
+ .iter()
+ .map(|test| {
+ let message = format!("({{}}/{{}}) running `{}`...", test.func.sig.ident);
+ quote_spanned! {
+ test.func.sig.ident.span() => #krate::log!(#message, __minitest_number, __MINITEST_COUNT);
+ }
+ })
+ .collect::<Vec<_>>();
+ Ok(quote!(mod #ident {
+ #(#untouched_tokens)*
+ #[cortex_m_rt::entry]
+ fn __minitest_entry() -> ! {
+ #init_logging
+ #declare_test_count
+ #init_expr
+
+ let mut __minitest_number: usize = 1;
+ #(
+ #(#test_cfgs)*
+ {
+ #unit_test_progress
+ #unit_test_calls
+ __minitest_number += 1;
+ }
+ )*
+
+ #krate::log!("all tests passed!");
+ #krate::exit()
+ }
+
+ #init_fn
+
+ #(
+ #test_functions
+ )*
+ })
+ .into())
+}
+
+#[derive(Clone, Copy)]
+enum Attr {
+ Init,
+ Test,
+}
+
+struct Init {
+ func: ItemFn,
+ state: Option<Box<Type>>,
+}
+
+struct Test {
+ func: ItemFn,
+ cfgs: Vec<Attribute>,
+ input: Option<Input>,
+ should_error: bool,
+}
+
+struct Input {
+ ty: Type,
+}
+
+// NOTE doesn't check the parameters or the return type
+fn check_fn_sig(sig: &syn::Signature) -> Result<(), ()> {
+ if sig.constness.is_none()
+ && sig.asyncness.is_none()
+ && sig.unsafety.is_none()
+ && sig.abi.is_none()
+ && sig.generics.params.is_empty()
+ && sig.generics.where_clause.is_none()
+ && sig.variadic.is_none()
+ {
+ Ok(())
+ } else {
+ Err(())
+ }
+}
+
+fn get_mutable_reference_type(arg: &syn::FnArg) -> Option<&Type> {
+ if let syn::FnArg::Typed(pat) = arg {
+ if let syn::Type::Reference(refty) = &*pat.ty {
+ if refty.mutability.is_some() {
+ Some(&refty.elem)
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+}
+
+fn extract_cfgs(attrs: &[Attribute]) -> Vec<Attribute> {
+ let mut cfgs = vec![];
+
+ for attr in attrs {
+ if attr.path.is_ident("cfg") {
+ cfgs.push(attr.clone());
+ }
+ }
+
+ cfgs
+}
diff --git a/testsuite/minitest/src/export.rs b/testsuite/minitest/src/export.rs
new file mode 100644
index 0000000..4b04fda
--- /dev/null
+++ b/testsuite/minitest/src/export.rs
@@ -0,0 +1,13 @@
+use crate::TestOutcome;
+use cortex_m_rt as _;
+
+pub fn check_outcome<T: TestOutcome>(outcome: T, should_error: bool) {
+ if outcome.is_success() == should_error {
+ let note: &str = if should_error {
+ "`#[should_error]` "
+ } else {
+ ""
+ };
+ panic!("{}test failed with outcome: {:?}", note, outcome);
+ }
+}
diff --git a/testsuite/minitest/src/lib.rs b/testsuite/minitest/src/lib.rs
new file mode 100644
index 0000000..d98fb64
--- /dev/null
+++ b/testsuite/minitest/src/lib.rs
@@ -0,0 +1,70 @@
+#![no_std]
+
+use core::fmt::Debug;
+pub use minitest_macros::tests;
+
+/// Private implementation details used by the proc macro.
+#[doc(hidden)]
+pub mod export;
+
+mod sealed {
+ pub trait Sealed {}
+ impl Sealed for () {}
+ impl<T, E> Sealed for Result<T, E> {}
+}
+
+/// Indicates whether a test succeeded or failed.
+///
+/// This is comparable to the `Termination` trait in libstd, except stable and tailored towards the
+/// needs of defmt-test. It is implemented for `()`, which always indicates success, and `Result`,
+/// where `Ok` indicates success.
+pub trait TestOutcome: Debug + sealed::Sealed {
+ fn is_success(&self) -> bool;
+}
+
+impl TestOutcome for () {
+ fn is_success(&self) -> bool {
+ true
+ }
+}
+
+impl<T: Debug, E: Debug> TestOutcome for Result<T, E> {
+ fn is_success(&self) -> bool {
+ self.is_ok()
+ }
+}
+
+#[macro_export]
+macro_rules! log {
+ ($s:literal $(, $x:expr)* $(,)?) => {
+ {
+ #[cfg(feature = "semihosting")]
+ ::cortex_m_semihosting::hprintln!($s $(, $x)*);
+ #[cfg(feature = "rtt")]
+ ::rtt_target::rprintln!($s $(, $x)*);
+ #[cfg(not(any(feature = "semihosting", feature="rtt")))]
+ let _ = ($( & $x ),*);
+ }
+ };
+}
+
+/// Stop all tests without failure.
+pub fn exit() -> ! {
+ #[cfg(feature = "rtt")]
+ cortex_m::asm::bkpt();
+ #[cfg(feature = "semihosting")]
+ cortex_m_semihosting::debug::exit(cortex_m_semihosting::debug::EXIT_SUCCESS);
+
+ unreachable!()
+}
+
+/// Stop all tests and report a failure.
+pub fn fail() -> ! {
+ #[cfg(feature = "rtt")]
+ cortex_m::asm::udf();
+ #[cfg(feature = "semihosting")]
+ cortex_m_semihosting::debug::exit(cortex_m_semihosting::debug::EXIT_FAILURE);
+
+ #[cfg(not(feature = "rtt"))]
+ unreachable!()
+}
diff --git a/testsuite/src/main.rs b/testsuite/src/main.rs
new file mode 100644
index 0000000..46ab629
--- /dev/null
+++ b/testsuite/src/main.rs
@@ -0,0 +1,54 @@
+#![no_main]
+#![no_std]
+
+extern crate cortex_m_rt;
+
+#[cfg(target_env = "")] // appease clippy
+#[panic_handler]
+fn panic(info: &core::panic::PanicInfo) -> ! {
+ cortex_m::interrupt::disable();
+ minitest::log!("{}", info);
+ minitest::fail()
+}
+
+#[minitest::tests]
+mod tests {
+ use minitest::log;
+
+ #[init]
+ fn init() -> cortex_m::Peripherals {
+ log!("Hello world!");
+ cortex_m::Peripherals::take().unwrap()
+ }
+
+ #[test]
+ fn double_take() {
+ assert!(cortex_m::Peripherals::take().is_none());
+ }
+
+ #[test]
+ #[cfg(not(feature = "semihosting"))] // QEMU does not model the cycle counter
+ fn cycle_count(p: &mut cortex_m::Peripherals) {
+ #[cfg(not(armv6m))]
+ {
+ use cortex_m::peripheral::DWT;
+
+ assert!(p.DWT.has_cycle_counter());
+
+ p.DCB.enable_trace();
+ p.DWT.disable_cycle_counter();
+
+ const TEST_COUNT: u32 = 0x5555_AAAA;
+ p.DWT.set_cycle_count(TEST_COUNT);
+ assert_eq!(DWT::cycle_count(), TEST_COUNT);
+
+ p.DWT.enable_cycle_counter();
+ assert!(DWT::cycle_count() > TEST_COUNT);
+ }
+
+ #[cfg(armv6m)]
+ {
+ assert!(!p.DWT.has_cycle_counter());
+ }
+ }
+}
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
index f6a57b3..9d96686 100644
--- a/xtask/src/lib.rs
+++ b/xtask/src/lib.rs
@@ -1,117 +1,8 @@
//! `cargo xtask` automation.
//!
//! Please refer to <https://github.com/matklad/cargo-xtask/> for an explanation of the concept.
-//!
-//! Also see the docs in `asm.rs`.
-
-use std::collections::BTreeMap;
-use std::env::current_dir;
-use std::fs::{self, File};
-use std::process::{Command, Stdio};
-
-fn toolchain() -> String {
- fs::read_to_string("asm-toolchain")
- .unwrap()
- .trim()
- .to_string()
-}
-
-fn rustc() -> Command {
- let mut cmd = Command::new("rustc");
- cmd.arg(format!("+{}", toolchain()));
- cmd
-}
-
-fn assemble_really(target: &str, cfgs: &[&str], plugin_lto: bool) {
- let mut cmd = rustc();
-
- // Set the codegen target.
- cmd.arg("--target").arg(target);
- // Set all the `--cfg` directives for the target.
- cmd.args(cfgs.iter().map(|cfg| format!("--cfg={}", cfg)));
-
- // We want some level of debuginfo to allow unwinding through the functions.
- cmd.arg("-g");
- // We always optimize the assembly shims. There's not really any reason not to.
- cmd.arg("-O");
-
- // We use LTO on the archive to ensure the (unused) panic handler is removed, preventing
- // a linker error when the archives are linked into final crates with two panic handlers.
- cmd.arg("-Clto=yes");
-
- // rustc will usually add frame pointers by default to aid with debugging, but that is a high
- // overhead for the tiny assembly routines.
- cmd.arg("-Cforce-frame-pointers=no");
- // We don't want any system-specific paths to show up since we ship the result to other users.
- // Add `--remap-path-prefix $(pwd)=.`.
- let mut dir = current_dir().unwrap().as_os_str().to_os_string();
- dir.push("=.");
- cmd.arg("--remap-path-prefix").arg(dir);
-
- // We let rustc build a single object file, not a staticlib, since the latter pulls in loads of
- // code that will never be used (`compiler_builtins` and `core::fmt`, etc.). We build the static
- // archive by hand after compiling.
- cmd.arg("--emit=obj");
-
- if plugin_lto {
- // Make artifacts compatible with Linker-Plugin LTO (and incompatible with everything else).
- cmd.arg("-Clinker-plugin-lto");
- }
-
- let file_stub = if plugin_lto {
- format!("{}-lto", target)
- } else {
- target.to_string()
- };
-
- let obj_file = format!("bin/{}.o", file_stub);
-
- // Pass output and input file.
- cmd.arg("-o").arg(&obj_file);
- cmd.arg("asm/lib.rs");
-
- println!("{:?}", cmd);
- let status = cmd.status().unwrap();
- assert!(status.success());
-
- // Archive `target.o` -> `bin/target.a`.
- let mut builder = ar::Builder::new(File::create(format!("bin/{}.a", file_stub)).unwrap());
-
- // Use `append`, not `append_path`, to avoid adding any filesystem metadata (modification times,
- // etc.).
- let file = fs::read(&obj_file).unwrap();
- builder
- .append(
- &ar::Header::new(obj_file.as_bytes().to_vec(), file.len() as u64),
- &*file,
- )
- .unwrap();
-
- fs::remove_file(&obj_file).unwrap();
-}
-
-fn assemble(target: &str, cfgs: &[&str]) {
- assemble_really(target, cfgs, false);
- assemble_really(target, cfgs, true);
-}
-
-// `--target` -> `--cfg` list (mirrors what `build.rs` does).
-static TARGETS: &[(&str, &[&str])] = &[
- ("thumbv6m-none-eabi", &[]),
- ("thumbv7m-none-eabi", &["armv7m"]),
- ("thumbv7em-none-eabi", &["armv7m", "armv7em"]),
- ("thumbv7em-none-eabihf", &["armv7m", "armv7em", "has_fpu"]),
- ("thumbv8m.base-none-eabi", &["armv8m", "armv8m_base"]),
- (
- "thumbv8m.main-none-eabi",
- &["armv7m", "armv8m", "armv8m_main"],
- ),
- (
- "thumbv8m.main-none-eabihf",
- &["armv7m", "armv8m", "armv8m_main", "has_fpu"],
- ),
-];
+use std::process::Command;
pub fn install_targets(targets: &mut dyn Iterator<Item = &str>, toolchain: Option<&str>) {
let mut rustup = Command::new("rustup");
@@ -125,90 +16,6 @@ pub fn install_targets(targets: &mut dyn Iterator<Item = &str>, toolchain: Optio
assert!(status.success(), "rustup command failed: {:?}", rustup);
}
-pub fn assemble_blobs() {
- let mut cmd = rustc();
- cmd.arg("-V");
- cmd.stdout(Stdio::null());
- let status = cmd.status().unwrap();
- let toolchain = toolchain();
-
- if !status.success() {
- println!(
- "asm toolchain {} does not seem to be installed. installing it now.",
- toolchain
- );
-
- let mut rustup = Command::new("rustup");
- let status = rustup.arg("install").arg(&toolchain).status().unwrap();
- assert!(status.success(), "rustup command failed: {:?}", rustup);
- }
-
- install_targets(
- &mut TARGETS.iter().map(|(target, _)| *target),
- Some(&*toolchain),
- );
-
- for (target, cfgs) in TARGETS {
- println!("building artifacts for {}", target);
- assemble(target, cfgs);
- }
-}
-
-pub fn check_blobs() {
- // Load each `.a` file in `bin` into memory.
- let mut files_before = BTreeMap::new();
- for entry in fs::read_dir("bin").unwrap() {
- let entry = entry.unwrap();
- if entry.path().extension().unwrap() == "a" {
- files_before.insert(
- entry
- .path()
- .file_name()
- .unwrap()
- .to_str()
- .unwrap()
- .to_string(),
- fs::read(entry.path()).unwrap(),
- );
- }
- }
-
- assemble_blobs();
-
- let mut files_after = BTreeMap::new();
- for entry in fs::read_dir("bin").unwrap() {
- let entry = entry.unwrap();
- if entry.path().extension().unwrap() == "a" {
- files_after.insert(
- entry
- .path()
- .file_name()
- .unwrap()
- .to_str()
- .unwrap()
- .to_string(),
- fs::read(entry.path()).unwrap(),
- );
- }
- }
-
- // Ensure they contain the same files.
- let before = files_before.keys().collect::<Vec<_>>();
- let after = files_after.keys().collect::<Vec<_>>();
- assert_eq!(before, after);
-
- for ((file, before), (_, after)) in files_before.iter().zip(files_after.iter()) {
- if before != after {
- panic!(
- "{} is not up-to-date, please run `cargo xtask assemble`",
- file
- );
- }
- }
-
- println!("Blobs identical.");
-}
-
// Check that serde and PartialOrd works with VectActive
pub fn check_host_side() {
use cortex_m::peripheral::{itm::LocalTimestampOptions, scb::VectActive};
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 26dce31..4673a45 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -1,18 +1,14 @@
use std::{env, process};
-use xtask::{assemble_blobs, check_blobs, check_host_side};
+use xtask::check_host_side;
fn main() {
let subcommand = env::args().nth(1);
match subcommand.as_deref() {
- Some("assemble") => assemble_blobs(),
- Some("check-blobs") => check_blobs(),
Some("check-host-side") => check_host_side(),
_ => {
eprintln!("usage: cargo xtask <subcommand>");
eprintln!();
eprintln!("subcommands:");
- eprintln!(" assemble Reassemble the pre-built artifacts");
- eprintln!(" check-blobs Check that the pre-built artifacts are up-to-date and reproducible");
eprintln!(" check-host-side Build the crate in a non-Cortex-M host application and check host side usage of certain types");
process::exit(1);
}
diff --git a/xtask/tests/ci.rs b/xtask/tests/ci.rs
index 37466e9..1dc4754 100644
--- a/xtask/tests/ci.rs
+++ b/xtask/tests/ci.rs
@@ -1,6 +1,6 @@
use std::process::Command;
use std::{env, str};
-use xtask::{check_blobs, check_host_side, install_targets};
+use xtask::{check_host_side, install_targets};
/// List of all compilation targets we support.
///
@@ -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" {
@@ -44,13 +51,13 @@ fn build(package: &str, target: &str, features: &[&str]) {
#[rustfmt::skip]
static PACKAGE_FEATURES: &[(&str, &[&str], &[&str])] = &[
- ("cortex-m", ALL_TARGETS, &["inline-asm", "cm7-r0p1"]), // no `linker-plugin-lto` since it's experimental
- ("cortex-m-semihosting", ALL_TARGETS, &["inline-asm", "no-semihosting", "jlink-quirks"]),
- ("panic-semihosting", ALL_TARGETS, &["inline-asm", "exit", "jlink-quirks"]),
+ ("cortex-m", ALL_TARGETS, &["cm7-r0p1"]),
+ ("cortex-m-semihosting", ALL_TARGETS, &["no-semihosting", "jlink-quirks"]),
+ ("panic-semihosting", ALL_TARGETS, &["exit", "jlink-quirks"]),
("panic-itm", NON_BASE_TARGETS, &[]),
];
-fn check_crates_build(is_nightly: bool) {
+fn check_crates_build(_is_nightly: bool) {
// Build all crates for each supported target.
for (package, targets, all_features) in PACKAGE_FEATURES {
for target in *targets {
@@ -58,11 +65,8 @@ fn check_crates_build(is_nightly: bool) {
// Relies on all crates in this repo to use the same convention.
let should_use_feature = |feat: &str| {
match feat {
- // This is nightly-only, so don't use it on stable.
- "inline-asm" => is_nightly,
// This only affects thumbv7em targets.
"cm7-r0p1" => target.starts_with("thumbv7em"),
-
_ => true,
}
};
@@ -98,9 +102,6 @@ fn main() {
install_targets(&mut ALL_TARGETS.iter().cloned(), None);
- // Check that the ASM blobs are up-to-date.
- check_blobs();
-
let output = Command::new("rustc").arg("-V").output().unwrap();
let is_nightly = str::from_utf8(&output.stdout).unwrap().contains("nightly");