From d5fb6c30e3a2a9d0e371965dc32687429bc5487f Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Tue, 15 Jul 2025 20:55:49 +0100 Subject: [PATCH 1/9] We are in a mess. This commit attempts to fix it. The cortex-m crate in the master branch is 0.7.4 plus a bunch of fixes that have never been released. Some of them are breaking changes and so will never be released. We cannot 'just' release a cortex-m 0.8 with them in because that is ecosystem shattering - the crate contains a 'links=' attribute, and a bunch of no_mangle functions, so you cannot have two versions in the same dependency tree. Instead, there is a 0.7.x branch. This was taken before the cortex-m-rt crate move. It has cortex-m 0.7.7, which has been published. This commit just deletes the cortex-m folder and replaces it with the contents of the cortex-m crate as found on the the 0.7.x branch. If you want to pick up the changes that have just been deleted, cherry- pick them into a new branch and open a PR to merge them into here. Breaking changes for cortex-m into master will no longer be accepted. Sorry. --- cortex-m/CHANGELOG.md | 48 +- cortex-m/Cargo.toml | 22 +- cortex-m/README.md | 2 +- cortex-m/asm-toolchain | 1 + cortex-m/asm/inline.rs | 448 +++++++++++++++++++ cortex-m/asm/lib.rs | 143 ++++++ cortex-m/bin/thumbv6m-none-eabi-lto.a | Bin 0 -> 11228 bytes cortex-m/bin/thumbv6m-none-eabi.a | Bin 0 -> 14576 bytes cortex-m/bin/thumbv7em-none-eabi-lto.a | Bin 0 -> 15316 bytes cortex-m/bin/thumbv7em-none-eabi.a | Bin 0 -> 19336 bytes cortex-m/bin/thumbv7em-none-eabihf-lto.a | Bin 0 -> 16140 bytes cortex-m/bin/thumbv7em-none-eabihf.a | Bin 0 -> 20480 bytes cortex-m/bin/thumbv7m-none-eabi-lto.a | Bin 0 -> 14284 bytes cortex-m/bin/thumbv7m-none-eabi.a | Bin 0 -> 18068 bytes cortex-m/bin/thumbv8m.base-none-eabi-lto.a | Bin 0 -> 14316 bytes cortex-m/bin/thumbv8m.base-none-eabi.a | Bin 0 -> 18680 bytes cortex-m/bin/thumbv8m.main-none-eabi-lto.a | Bin 0 -> 18708 bytes cortex-m/bin/thumbv8m.main-none-eabi.a | Bin 0 -> 24408 bytes cortex-m/bin/thumbv8m.main-none-eabihf-lto.a | Bin 0 -> 19544 bytes cortex-m/bin/thumbv8m.main-none-eabihf.a | Bin 0 -> 25524 bytes cortex-m/build.rs | 34 +- cortex-m/src/asm.rs | 188 ++------ cortex-m/src/call_asm.rs | 24 + cortex-m/src/cmse.rs | 6 +- cortex-m/src/critical_section.rs | 37 +- cortex-m/src/delay.rs | 47 +- cortex-m/src/interrupt.rs | 71 +-- cortex-m/src/itm.rs | 2 +- cortex-m/src/lib.rs | 62 ++- cortex-m/src/macros.rs | 33 +- cortex-m/src/peripheral/ac.rs | 2 +- cortex-m/src/peripheral/dcb.rs | 21 - cortex-m/src/peripheral/dwt.rs | 299 ++----------- cortex-m/src/peripheral/itm.rs | 147 +----- cortex-m/src/peripheral/mod.rs | 135 +++++- cortex-m/src/peripheral/nvic.rs | 19 +- cortex-m/src/peripheral/sau.rs | 5 +- cortex-m/src/peripheral/scb.rs | 68 +-- cortex-m/src/peripheral/syst.rs | 16 +- cortex-m/src/peripheral/tpiu.rs | 135 +----- cortex-m/src/prelude.rs | 3 + cortex-m/src/register/apsr.rs | 9 +- cortex-m/src/register/basepri.rs | 24 +- cortex-m/src/register/basepri_max.rs | 23 +- cortex-m/src/register/control.rs | 23 +- cortex-m/src/register/faultmask.rs | 7 +- cortex-m/src/register/fpscr.rs | 7 +- cortex-m/src/register/lr.rs | 23 +- cortex-m/src/register/mod.rs | 5 + cortex-m/src/register/msp.rs | 21 +- cortex-m/src/register/msplim.rs | 8 +- cortex-m/src/register/pc.rs | 15 +- cortex-m/src/register/primask.rs | 40 +- cortex-m/src/register/psp.rs | 13 +- cortex-m/src/register/psplim.rs | 8 +- xtask/Cargo.toml | 4 +- xtask/src/lib.rs | 216 ++++++++- xtask/src/main.rs | 10 +- xtask/tests/ci.rs | 32 +- 59 files changed, 1281 insertions(+), 1225 deletions(-) create mode 100644 cortex-m/asm-toolchain create mode 100644 cortex-m/asm/inline.rs create mode 100644 cortex-m/asm/lib.rs create mode 100644 cortex-m/bin/thumbv6m-none-eabi-lto.a create mode 100644 cortex-m/bin/thumbv6m-none-eabi.a create mode 100644 cortex-m/bin/thumbv7em-none-eabi-lto.a create mode 100644 cortex-m/bin/thumbv7em-none-eabi.a create mode 100644 cortex-m/bin/thumbv7em-none-eabihf-lto.a create mode 100644 cortex-m/bin/thumbv7em-none-eabihf.a create mode 100644 cortex-m/bin/thumbv7m-none-eabi-lto.a create mode 100644 cortex-m/bin/thumbv7m-none-eabi.a create mode 100644 cortex-m/bin/thumbv8m.base-none-eabi-lto.a create mode 100644 cortex-m/bin/thumbv8m.base-none-eabi.a create mode 100644 cortex-m/bin/thumbv8m.main-none-eabi-lto.a create mode 100644 cortex-m/bin/thumbv8m.main-none-eabi.a create mode 100644 cortex-m/bin/thumbv8m.main-none-eabihf-lto.a create mode 100644 cortex-m/bin/thumbv8m.main-none-eabihf.a create mode 100644 cortex-m/src/call_asm.rs create mode 100644 cortex-m/src/prelude.rs diff --git a/cortex-m/CHANGELOG.md b/cortex-m/CHANGELOG.md index b13405da..ca9609ce 100644 --- a/cortex-m/CHANGELOG.md +++ b/cortex-m/CHANGELOG.md @@ -7,37 +7,29 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -### Breaking changes +## [v0.7.7] - 2023-01-03 -- `NVIC::request()` no longer requires `&mut self`. -- `embedded-hal` version 0.2 delay implementations now required the `eh0` feature. +- Add missing documentation for `critical-section-single-core` feature added + in v0.7.6. -### Added -- Updated `SCB.ICSR.VECTACTIVE`/`SCB::vect_active()` to be 9 bits instead of 8. - Also fixes `VectActive::from` to take a `u16` and subtract `16` for - `VectActive::Interrupt`s to match `SBC::vect_active()` (#373). -- DWT: add `configure` API for address, cycle count comparison (#342, #367). -- ITM: add `configure` API (#342). -- TPIU: add API for *Formatter and Flush Control* (FFCR) and *Selected Pin Control* (SPPR) registers (#342). -- 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) -- Added support for `embedded-hal` version 1 delay traits, requiring rust 1.60. -- `singleton!()` now forwards attributes (#522). -- Added `set_sevonpend` and `clear_sevonpend` (#539). +## [v0.7.6] - 2022-08-12 -### 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) +- Added `critical-section-single-core` feature which provides an implementation for the `critical-section` crate for single-core systems, based on disabling all interrupts. (#448) + +## [v0.7.5] - 2022-05-15 + +### Deprecated +- the `ptr()` function on all peripherals register blocks in favor of + the associated constant `PTR` (#386). ### Changed -- Inline assembly is now always used, requiring Rust 1.59. -- Bumped MSRV to 1.61 for compatibility with syn versions >=2.0.68. -### Removed -- removed all peripherals `ptr()` functions in favor of the associated constant `PTR` (#385). -- removed `inline-asm` feature which is now always enabled +- The `inline-asm` feature no longer requires a nightly Rust compiler, but + does require Rust 1.59 or above. + +### Fixed +- Fixed `singleton!()` statics sometimes ending up in `.data` instead of `.bss` (#364, #380). + (Backported from upcoming 0.8 release). ## [v0.7.4] - 2021-12-31 @@ -70,7 +62,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). [C-GETTER]: https://rust-lang.github.io/api-guidelines/naming.html#c-getter - ## [v0.7.3] - 2021-07-03 ### Fixed @@ -764,7 +755,10 @@ fn main() { - Functions to get the vector table - Wrappers over miscellaneous instructions like `bkpt` -[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/v0.7.4...HEAD +[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/v0.7.7...HEAD +[v0.7.7]: https://github.com/rust-embedded/cortex-m/compare/v0.7.6...v0.7.7 +[v0.7.6]: https://github.com/rust-embedded/cortex-m/compare/v0.7.5...v0.7.6 +[v0.7.5]: https://github.com/rust-embedded/cortex-m/compare/v0.7.4...v0.7.5 [v0.7.4]: https://github.com/rust-embedded/cortex-m/compare/v0.7.3...v0.7.4 [v0.7.3]: https://github.com/rust-embedded/cortex-m/compare/v0.7.2...v0.7.3 [v0.7.2]: https://github.com/rust-embedded/cortex-m/compare/v0.7.1...v0.7.2 diff --git a/cortex-m/Cargo.toml b/cortex-m/Cargo.toml index 0c317c64..dca194ae 100644 --- a/cortex-m/Cargo.toml +++ b/cortex-m/Cargo.toml @@ -11,29 +11,33 @@ license = "MIT OR Apache-2.0" name = "cortex-m" readme = "README.md" repository = "https://github.com/rust-embedded/cortex-m" -version = "0.7.4" -edition = "2021" -rust-version = "1.61" +version = "0.7.7" +edition = "2018" links = "cortex-m" # prevent multiple versions of this crate to be linked together [dependencies] -critical-section = "1.0.0" -volatile-register = "0.2.2" -bitfield = "0.15.0" -eh0 = { package = "embedded-hal", version = "0.2.4", optional = true } -eh1 = { package = "embedded-hal", version = "1.0.0" } +bare-metal = { version = "0.2.4", features = ["const-fn"] } +critical-section = { version = "1.0.0", optional = true } +volatile-register = "0.2.0" +bitfield = "0.13.2" +embedded-hal = "0.2.4" [dependencies.serde] version = "1" features = [ "derive" ] optional = true +[dependencies.serde_json] +version = "1" +optional = true + [features] cm7 = [] cm7-r0p1 = ["cm7"] +inline-asm = [] linker-plugin-lto = [] std = [] -critical-section-single-core = ["critical-section/restore-state-u32"] +critical-section-single-core = ["critical-section/restore-state-bool"] [package.metadata.docs.rs] targets = [ diff --git a/cortex-m/README.md b/cortex-m/README.md index aacc6ddc..b2af4a7c 100644 --- a/cortex-m/README.md +++ b/cortex-m/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.61.0 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/cortex-m/asm-toolchain b/cortex-m/asm-toolchain new file mode 100644 index 00000000..cc5dbb24 --- /dev/null +++ b/cortex-m/asm-toolchain @@ -0,0 +1 @@ +nightly-2021-12-16 diff --git a/cortex-m/asm/inline.rs b/cortex-m/asm/inline.rs new file mode 100644 index 00000000..f5d6b32d --- /dev/null +++ b/cortex-m/asm/inline.rs @@ -0,0 +1,448 @@ +//! 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(nostack, preserves_flags)); + compiler_fence(Ordering::SeqCst); +} + +#[inline(always)] +pub unsafe fn __dsb() { + compiler_fence(Ordering::SeqCst); + asm!("dsb", options(nostack, preserves_flags)); + compiler_fence(Ordering::SeqCst); +} + +#[inline(always)] +pub unsafe fn __isb() { + compiler_fence(Ordering::SeqCst); + asm!("isb", options(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/cortex-m/asm/lib.rs b/cortex-m/asm/lib.rs new file mode 100644 index 00000000..48f3dc21 --- /dev/null +++ b/cortex-m/asm/lib.rs @@ -0,0 +1,143 @@ +//! 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/cortex-m/bin/thumbv6m-none-eabi-lto.a b/cortex-m/bin/thumbv6m-none-eabi-lto.a new file mode 100644 index 0000000000000000000000000000000000000000..c9600fde060d45e990a4fd33e741a8e06f0865ae GIT binary patch literal 11228 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 Ur*~USW-C+RKG3`soEkX(7ejz$<^TWy literal 0 HcmV?d00001 diff --git a/cortex-m/bin/thumbv6m-none-eabi.a b/cortex-m/bin/thumbv6m-none-eabi.a new file mode 100644 index 0000000000000000000000000000000000000000..9640a6994aaee8fe74f5d48c765bd33b641f17fa GIT binary patch literal 14576 zcmbta36vbgd9I$>mG;(+BqZ=k2(c{eY|lL-B(y>Z9hP7v1QHmtnro%C_R{REjv#{~ zAQ6Oc_ywL~%OJoGigCD1Y>WvIKN5IA97voO;b07o?Z7h*u@#d%8Rtyv(? zC3S!QU;kD0S9MiS^)6VND;IX0-*iT_JCTM7GpeHYCVdxCx=HM4s9+Zq6 zIMHyZVYTDM$<_a&&?id>m!*0MO}OgR39I)W5W3V*{0I?!)alFX!eNxXH-Ja&ePgo%<~6aJ{OI}Kr~Kv1_XEgXw6qE zWcbz#5<-{owwbppa0|bJ+fBGEtF8c0bvwz_jo}`uy9W1g-9c8X`a9tDMO@(*nyg8$ zIYbg7l+YJ=yNkC!#Z5c?CI~n^0#J4LlBs(D_fXwKxQB76(6gl2fbZdg?(+au_aYfw zleh6^h5iW;dJVURH*sr%DAZ~-P6MtWJ-r_NwwjtZl9@pU!+t54>sc|^YQ0UP7Bu__ zbQ5N7sKaWQ4qQgswpvyJM@eU_*1a03-_*RH^fJe3!;N4bV#Qjk;c?&(N%vX}{|anD zk3w6l#_7Oiq_3utX;z=(p=A{$qm-5qMxz!q><9f-^1H3Z2Z8@W`iouF0Em*3~ZEy3Qrll#4-MOa3LR^=6G!Z@twem72=ofM;cG;mBF`w6|D;f}|-c zK21$0Gl|R=j;CeQ$FyIt7DbyvOZ(xHWo_ZOTJ{X9mji1>sXmhrK1%~GIGttn&PL75 zKB=N8w5a}+xX;@E=^D>02%wk$gx+>n8y%&*<@C4K^~w zm?$!oXk?qN@idapG8%cRXqraObIrE!35UjFzc26O$mbp!v49Ux6s`u)I6t7FM?yDl z3)cvzld~^GY%UW{re|0ixll;j*ZDo5M^~a|mbI~@1-jX%!rt1#)kNom%ZclX3yQef z1q;SLh3fi2|GeYskandeZ$Ppq*Quu_P^_KrfbR47D~X}djU$D&=)SXVT8_Sq^oHq<>hkdKtdRHR!K zM#?2Mwr{wga+Q&xfr1*(s7|?OHijQJ0j9rSib= zcGW*rQRRx-9aZZ~{S~!|@zu)GaHUjMmksY69@#UjRtyeoA09%TDwoE_2VuQattyuh zLUU-38W0$!6Xgra(y&m?c zJ+w|RiwR~ixj$;2w8-m9WUn9o34(rE1H z*gz5X8T2EOh!%==yx;E6$8<9&t8K!NsdO@$$Ye6_;+XXZuUJT&C2Y z#4s@qBms5K%+d0|P;P9eZe6jE?oae*^7(8co3eAdb*?;Mi%yM7r?TmIG@Xh?=|5TM&!j=OL|S_Pf4@z-U(W-t>Yw41>+>e3LEHiU8YC8QzVrtc zv2#$RcM}_g>?p5;l*K)ygK=(Lgi5o&+zVRvi$z+2v*Alr&|685qpG*01E8_j@aHnO zog0DJcb*4uH@Qfx2h=^fhtA6NbBD${v=w@?do9)D#MS1HHN}PG+uFQ3z>h**wkuF- zC9di~^H;a*Ui@wD?uPu8#BJpjRAMi^8BpE)z^D~b+;TjvQ`T7^SH>cCR!wS|yrDrg zT5W2YHPf1`~Uh2+W)RRU$3sjB&kyeLE-fDw{5KiJlH%RvGB&Ey7jwOxBE_&h+|cX z#*?W`-pL;+@Im5>7rZ)8gfPyNLPk%p zdVLRfTXmuO`lj&JR-IMf7G4~|4;C~kAT2u5_H{L-gH~O*es=g9vkqz>KmD9lQ*EE4 z_Ghyq6RE*VQ}~`)x=CTUq28n#Et=C5Znf%XPKwmmPwLjrcAwNTpRW(#i%q;?*1As= z`}&=Z{)aO^nf}e9|Ng}KI?J|`$IU0TR!f&S(si|^FXGINL-gczH8O$`d{^X$ud8i; zLgaT$Apfn%Z<;`U0B3rPf5QavZ?vos){c|r&dp-?n`>*g`!|R`-}A}2PgP4;b*d4cyt66<6;@rU=#zH^ zD>cRT-Z=8;~zmT|W8JfPA^S-6vljkY5ns zFAVUj0{q$l|LFkFcdKd%tIg_LVENc^Z<~%Dm%)(dOrMVX0FXTQ>gky8PhiWa>bIza zK7I;jq*s4B-Y>lVW~mQF-|;_3@u_O(B*?)LF1)#c-}>H}Z=rRrtjxwlVO&iHl0hNY81h_Y;S z@-Uk`lS!22pc7xxG|v|>lumifJ7)^dvoVxTd3-zNoThWW)XQVp*)IG-!9{|L1v>=s z7_n&~#OD)3TtkGMO%0)vO$*_1VN-}~LMVnKYvCCp)U6bsSjx{8WWLES5nLg-T9D&O z`K5yVWJP|PU{P>LaKGU13;u!NZGv|R9ud4x@R;CZg5MGRuHXxTuL>R){Gs5F1%D>^ z?}EP)2!|P#3eR;5uO{ldQva&(ZxAu*-%;p**jeZU`Dub3g3E|gabiz|-X`Jmg8Kw-CE}qzOoZP3!hb{XMZq5tvHrY6gx-G( zUyt(uylSZ_MAYM}w#Kuhekl>_)J0OiQTVHgSUh$}{T|_OAYvWUpBUlq%ff$+h;{98 zBI^H2@MWogi`cHz&!zr-;lmiK&_aB(B(ipsu~HkQ{wm=Ih*%HD zr2bkW#_OQ)w+Y@&gx-@xtgFvS{oe`xK=2)rPnrxpv zo^Mdq^Cv^Z{OKWL{MQg++)KoGZY5&8wh=KtJBV;PN<`lG5|QTvMCA1lv0bU#iOAPs zBJy*TxLB!U#15q%A);R_Y$uwiv4IE%P0~1(h)!|KA|kYIKSFA3C*nCk2#s*9`-7Bf zw2A0fLNG&w)1`tvMD+7Q!8Jq#TQAs4M87u+ZY81-R|#$-A`eBu9Yi#4P;iupyi^4D z65Ey9FL;26Jl!C8h=|VIB6vFydHWN=!$frQh~QBo^7s`&q)3myK{Qo`uz8|MI=?X0 zyv|RZQW@cw5@EDPc-{X78GqPc#d_p2)*NMpU+rqy~oR9TG_z#H4a|8Dg#MAp{JLZe<^NGl_ z-ZvpXmpt;E5xL$sk!QVMLcX5$9q7NvuOcGP`uza&^)DC?`Fn|oy#0#UfN${BL!Oos zTa?oKEb?+EdF117h{(gQiRk|f_+r1KMD%wf5&hgnME~v}qF;{^(Vtg|3$cC@(f-CT z5bZvji1zY#nSQP!<+Zg{RzDG%hwD)_mD!IbW2@Lo_qIPH=c6);AZ=4>!zl_9BV9HN_6Ulf)UOS-vVCV}uQkX!M zhu*cF@L+rb3C__Y82d_&xNnHKs1k{0dDpjShNQkl(2Xg5mKI&U zj5}#8Pr4)o4Pg1wlnKy0^>P%Zfx^eT4o*Q=!WVAQBChpbH^TuuW#Kr588@B-38@CrcmTb3}{_-~@ z^xU{tfoEJ3!^FMJQwLIYc;eEp>F;B3?2TI#8Esfh++&WO8+Qj}jEn6=7Zdj@&@*kJ z!1ly#1#jZ=Zxg+7M@2>(7894hfxP{#K*qS{^w7i|4#drP;$n+)I>cR%=AdB!WY zFYY1mjEikj7ZaDhy#4L*#Kqsgx^buBS@6ca9Wpal=+N|cvZLqb`7mURYfd{&+%WXK zao2d_V(WI}{+E+KH`YF;XL-ng%TuN8Nhqvysw4xVur;9@axdAjP2yTcQAq3F@>>rOx1 zSZ|69pbOHb3;%c!|J}GhK`reUIbt{N-B9wz9reUL6S}6qw*=zy-M3bM4+i4?C-fK> z+k-Bqzt;xh?)Aj&fToGdGbQ|YV}0PE>Cp7IGZ2@5RnEAbR6;Rv7o*@J0aL5L3miQ+&oiONxLvqdOk9NV#XaPS8xcL)U60Efm%kZ|4~X3U?sN3qxa|_x zrV@&=83*m{@9mzr_=(~6cLfsZjmzIC#s~eGJo9V|A~){264!iPFg833^Ts{wiOY8Y z>ao9v193Ut#s|c1+*<>2`J0LMDJr2Do0|i1k9y*!MUQqoyMqgNOnWAXds-mwg%X#~ zD~pLcHxT!jC+-r_V_cpwdgHF2Anv=4o||W`bF^QIi^ar!8};5iKjMkIO!R2SKmNsk zx4-;NiJy3GLGyKa%F%P0HVe7!MopFq7g?h~H4J`+Ctnljlv4oAzxC_{-o$?P@f{6ythBz~9lu&bbrww;7l5cP4D1 T>XyTp^amiMpExe+O~v(p7qD$b literal 0 HcmV?d00001 diff --git a/cortex-m/bin/thumbv7em-none-eabi-lto.a b/cortex-m/bin/thumbv7em-none-eabi-lto.a new file mode 100644 index 0000000000000000000000000000000000000000..5d9350fcb59e25abae09517306177390c84f74e1 GIT binary patch literal 15316 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 F{|B(aMA-lU literal 0 HcmV?d00001 diff --git a/cortex-m/bin/thumbv7em-none-eabihf.a b/cortex-m/bin/thumbv7em-none-eabihf.a new file mode 100644 index 0000000000000000000000000000000000000000..cf91a7a59012c7d74c13c0c6b75f40928440d285 GIT binary patch literal 20480 zcmd^Hd6ZSfov!;{H}payG$0_L4WeQPnzt_ol~qw1#1$mQ=B?17yJ=rH+k_~=CFnTO zBGTw;_&&xp|&oud<+m;~bzGcj(RIL^4u_tpK?dv!1GVdmt|DZTfr z-(K~rx^?Tmdz-0qGNs(wlbVhW&yJ+pDkW5@wz+lt+}0Ke#ljJ-v$`qU-8Xx1?S|g$ z#$=&)R^R%*!mL6j+r75fv0h!WsPh!Zag-^JnxQI`QY%l?lKPm28EONi5A~l)l6r3r zjos!_F1>AN?2dhjn=JamSS_WFKvsKFP% zVTT=CJzVkq=FljQ?wdo69(vnQqsMlxWvhPs(Qi$k+bn*_s{g>si<%ZV8oAki(}L}i zk!?c_I~q=Py_j6Jdg8q~GSLCCs$lrcrRFgdhFZ$?@ zQP?h)VK^MMoG=1Mof>kMe+v~|swh?y1;B(4LY+QrAI@m!n2}@ZgCj?cpEUBG`4dMw z=Z;q@kamJ2l?v85r;X{I7#!K$yu&#R<}*}qB5tbnG`qE&+FmX7H_?^}n>ouC`veDXx4#!gY;F=Vc6KEHVd490nNP8&n5 z!|PQ*jM(V$Qyj0+1j@sZOsrRvTUa>)obhfLT1+BI{^Vxe1f{0L#N_DUXG$Gq zg{Wt{RAZAnoH*+RX|kFJE=#8i`W!d|5IBdY^KlA3jnkDl9H%Y^P<87_)D7SqsQWz5 z!Mg2atNO3N>(6ln_cb}go_8y8h)@Dw<>>~VzK)Z2x(5|F-3d^2x00y49p^yZ9XJPZ zsK8#<*np>SK=(0#s{1(!9K%=h+Xa%0gddL~E2-weh%4ylGk@`)|TUkHRb=q(_ zh&#wQ&1tv?_#4)jI}I-ZN1#W66;5L-@I=-xq>*V>m+N7~B1rlvEg+0WEoj(^`gcih zbQ*sE{59)0*&c3oJv9CSl2%-_fm?0qZLW005s)mRbeH(wr3DR3!P`jwozBRu!0)nt zkJIo2;IpjX?=<`eFj$ZJLr!BO@C4Q$)lphbE!SI4FIUxJ%TT|V^b=0YRT`<@^5t?> z1-^p%i0g5Bg3MQ?S+8=?RpayEwO}O+{E4(l*6UiRMoffcDW&g-)jO`$`QTkc{(tGl z$;iK_d#W0~0m*Zee&mdL1vq6m>aDn82WFvGAHk_a)-1;vriWmbquthD3GQQ*oM~_LbAJ4Uu1&yH73C{BI;EeAQ z7Da*S^%LuQSW})>p&d?W{L$h-TBR*nX9>)#YRL--mb0r^x^}RP_$+zfa#rwib3#W@ zi&vcV;nQ%$kkW@q$D@8Kc$1kN=3$DnoEJBW&W4*aUX^IYYH5Y!OSfOE;uJXDv_i9~ z!V>5-Y>z`;+Hlx&D2HbnORvMT4cp_8>jNBaK9s|i#?tF>m0{!F!zR03<{~50z4Be_zE*2_LEYxKBc z`5PHvOcWVPG;*ZoSdHX@hDIJCnx>IQmd#dhnS;i$USIaa=i&#Ah|h-=g-aqdPV#B! zk!G+T8BF5fxmrrvPxpF2k5+**!8x;_1-jY18k`keQguGKrj}iC zRTWnk!Gf_5r@Fq-zu>yspB{Ugs46CM_z4v za&d9?JO8Cu9liW@#ko6kxpvE>_=8v5b{Uma|Iy_PUyAE&c%2#naFbi7%u@kO-wUj`oQo`ezxq-E-yp|;#h_o`l~(#`A)k(tzrit#a#21vRjxFQ+nt>wCL%YC}q$Rp`lVF64=Y%s@fS%U!gg8)`tX z)Y*lL77PrkGfMeFsk?8DD)tVl(xBQHR-J|7pgN23)q+CbV4C*VDbG zuNOR3DhzDsf%Q7Is8m7-O`$!iyRWCaub{eCEsKIoxMZTME4!|LFq~W)j)rsDTr!i3 zB@4M!EVD?tBA-Dx+|`v^-#1uV-_uosSuPZh=W?NZCKL@tLZLLx%5t?K8d0S~c-RCF z$#^arN#8k$gdr2jcsQ0yrIM*= zC>P4*jY;8PlRSoe3rs?ZTqGGwXJg@FA&DwYv`po; zA@O-f%6bR-_1I^l>0G`Ljbt*3SU8`KvN>8--GqakFCxK-NHLyCrnBLgo-Q=mq&8-H zd}io&-}-*`Bc2GQi-k-kRVcQ~lDZbTS%FCL-ZzIugpovIz#$vg#)6R3@28=VO^rK9SExBPl%(Xrkw?b+MsC z1BHz+h-HiUOg}jH%yq-Cbr2Hd8yW zu`;Vle{V@cV>``t;)!k;ifSPO^j@PcZklYHU>^~mC*3&<^mWX1vItp8DJw>*`0$Z5J z0Nj-{YAe%cOc3=Jw!Hc-r&dBoc0hAIY9&?l(3{j(@yyoM#aq-vI%H+(_z~l|Tc2Z5 z)tfN>wrh4;=c8eq47cm#oAJE~Tnq*FL+W-Yo4wV8nqAq{gmhc=zCnGiTra4!XC&=u zuYT`wu}S?3?ztyB%WV^PeZR1pvPpdk0=;V-u6xGS{uHpxj&ZJA*Skpm^vo^d5UbguW2!^*K1(sSDKCHw7YVzK!DBn{YaANoljdD% z+v!!MJx*P)ethts$8Fa>Uiq9;Rc(u__Puc(wbbCHDfq2%x=BH}q2916EgIVtZ0V@4 zU!whNJg89~Hy^+aTX)&G)BmQhm#%a5KOFnF>0c%K@7C7W2@bg_Jm;X+YUv5C^z`b| zpW}rZN9Tb{sw4x0_qxb$yrjDQkjTGML;k+VudE^8h8K2>e_4(Ampe`X%i2NH<|?uK zpNp%v`x=q|wTAq9k-u3(zSB89aJWBiV^i>6vHQivRq_EjUfbyYJO=qe?S}jjk^i)Y z{o^A4aSiz|A?KWXdQ8LaT9YnNk3cVp;)9>0_>n}s;^Kxm2VY$9^yPrtrFXwjLt#hI zRrp|Q^_+ZJ3##erXixs8`{c)|`#k=S^U2%Q ztsZ&1Pd-as>ygj$$wTTAkDR;hN(ri{>hs8>6(MRtl~AAc$P+$!T6KEl+-g-yP|Z=F z_Q>Z{gs278$tvc_&&fXdd^N=*pYM||Qg?g&FY?J3tD8OY#Xk8Gb(KfH#3x^-ws_>r zeDX8ZT95oppZpxP!XxK*9hDMPE7U0-`HG4VwV+z1;vV@bpZv3`!;_!S`sAy9bk;}b zeDoS0-L1yMe_NTmOzlQ{Dzv}9{bfy7g7_7duekBQ-Yd6)^On$DNm{YKd>=?T_sXqU zPxj)OrJ_GV&G*oU>9?1Zb0ulTe@CG`q+NgGRK16up#CKGu6&Yu7RnX2q!5Ma=eKQ zNrWqnrwT3=3a{ULkmm;0=O11#c7FC3v^sj|G1wxKHpI!To~2 z5&XU2tAcL`z9Yzu3EQthtQjwk@I9+gtU`Q|~I7e`<;9|jZ1T%sK!Ct|O1h)u& zLGW_HD+O;5{I=ljf_Do3Sny%N#{{1c+%Ncw;2#Cw6$}R4{tg#BOmLFmQG&+_eo8PZ zm=ru&aDm_w!7~IuBUlvVddEC(6uemQO2O*|ZxZ~b;P(XY7ThEFh~U$L&kDXE_?qDB zg8wZz4C@fv(Im(Z^++Es*d}<4;PHYH!IK3S2zCmt66_Mp30@?4iQuJzR}0=uMBDUd ze`v>DLjQzVhfmQ&@85-fiHM8+&%%FKkoP0YJ!}aA(;zFhN5a%lOfY6T%5}Z%O zI^0Qw-U^}D22cxz zo8a#R-w=5d=1c%LiLpesPjII2=MiyVTO#~bLa!m>K6jDu|3UCd;eV4DQEHd)?-lw{ zBJPJz3IBPa|0wt|5% z-`*|!p9=jH5%=Hy!hcclE#VKtHN*B`SXE#??khxG*O*%RI&LFEID?2jAT|0rLsRv2 zG?$3$WHAxO%ZZrxD~XubtBIJ$Yl(2$PsIG(OvHTLM#TKvL2Og%S|aAvjl}6n-9p5? z*+razb%u!XzmJIV-Alwc?jwej{+XNOv!67^;{XwPevO#IJSHNK?-7wNZqAq=7WTVN zuz?5yONGW!MC1Wo(l~(#C3Hb!8xgrglQqsD!Wd1|h^Fa0p-CFi6rC?Nfq9!tMBWw( zE+!&ar{HoT@_3HmN+KF@f#7N)@|qW1OGM*(1pA4|^Pu2nVw+N11-B8A_sax#5Yd^d z1+OJy9Ig|*k%&(26ugCqarutmE+RU=TktL-#_1lx`-mZ>9u(Y5#JD{wxQ`f9>Pf+; zi5SOc1@{wEO1&U>fQWH@S@1RD9Hm|te2a*2{)-@nQgOZ;oIv_i8BfdubWPLff*yZ_ z(llJ^@n0--C$T~1JM1qYjd`$Em7y1Jt=7HYNW1N4;eAMClej+Br zJH!V3p$qjejwcgGU>zf3+^!{Jy!H|?PJCYH`0(>yj>}9U#-o#nap)l;|5p={_uWL~ z`$^(7%vU1vTNeZ(ul!n*`CLdu9xoswfBX{*=4~et`QqzO=II3@@}u`r$V&(M!hD=T zL>~Hy=)c|{q2H>bROsoLch=I5!shP$=8nO_=D`m0wNrJO{YJRDhIyY}UFv>xQ(fl1 zNUtt4?{+uUXtQZoEjhp4>Btwd8`gAXve^>jN1nYno%p?y3}Y_ z<@>Da5zIi;iolN_YFL`jj_gc#_Z8RI(6>Hi>gZcPSm-!)*}1a@1~a*JH4OO4RQ1q& zd9NkzsOYaSExK63z}7J3M+UWI-Yp`K8}y%NuSrr>s>%ZE6#&ix4smUvHr8(sK! zgnm6EqXKR(x(s*q`nL;>u=QF7)FGn4>>2fjs{W>_%EBE&T(2Q$hn7I5- z%kWTD*IIGu*Tj7QiL&G7MMfJI6PLe(*m2iF#<$&zPSA& zbG=pK@^>CPF2{>;%^O1#x5F3r7Ax*3@ontv{%(cL^a0g!e>rvSxH~}89$O<_OkDc1 z`@74E%fCc1{hf%49rs$u%v_;Eljp-+z4G|p2pQv=H=HJJGb(o6yR5j_u9f3{?91~l zkX4KOS6`lYL631K;9xOv-}dGCJ}d4-(WBifzPNXRS1qpogEebD-v>R~n>Vs1?yr1t z_gZnW4K4TgDJ0p>^IpiR#eLM(E9ZG1^cWXgSzS!rz0kAsyw8ez6f~(vyD#Ce<30_# zTHG(VdgZwLLDPOJ4i*!43+i^(ftM2~jdhud)vhzy{MnO9j?uN?PfNNGRaRV&B6 z5PEjp{Z`y#plkX&(--%3$g1^snlJ8OpvSn!;b1ZSJ<=EVfE9O!=+W+Obk^?g2jEqU z%jYcoU+!-m2-@S(Ll+bGJK)>>ea(u?&mv8K*P#y8a;y;`s>RK?dgZudp~twhaIl!T zd}C(EeaniA$DwlEmr%FkP6lCoKvZ5go_F=iaoZ$rh)O8N<~h{uxbInU`OeAo*YRDy zGit>BFW>mil(^=L0uy)0H@+MQ=7sBd%!+%9FD~En7#~n8_xA=@uiW31#KohcF2?4o z(6js7V8u;B)AW~r@(35@F`Zi@uKo*TYhEpsxM??RxvkIp`a8;si^tb;T)scC<961F z%jau|%Kcp~ap(Br?()T*V8uPrihG4G?n?N>V|}?mtPB681v~Db#5G@Rm^|}u#qB(| zS#jsWlgTr$DYz)-c`Jx&*U?m0uiW3uB<_6hSxnq1sM~R8SaBDK9_Lx7+mCXrtHmEg zxXqYiU1GZz<6tpy z{{?kBZpw;#n&{E4$=BcAHRAGuf~Xw#9*MgIK3GiL55TwM&b8upiXP*xbg#>DA0LF> zbUKn^{4I0!%5e`!++{w!)1ha_U2MfY1G@CbxLbX3`8!#)xFuiQ5$Hed&&0uE+Slic zyWEO>VaLtah`YemE644TxV+!8n7CZO?700_+zUmIai5fVg{m%t z@YibyAlt;U&o^H-!yos~yuYo+Ve
c$3D^&q%6Ob2dWPRJ5&w)Ea$pK|SmvW* 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 R$--=K?`zq}U@+nHe*xlG244UG literal 0 HcmV?d00001 diff --git a/cortex-m/bin/thumbv7m-none-eabi.a b/cortex-m/bin/thumbv7m-none-eabi.a new file mode 100644 index 0000000000000000000000000000000000000000..ff4bf211c38fec50bc177b59b2d6da9eb0c73c68 GIT binary patch literal 18068 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 C*LS@D literal 0 HcmV?d00001 diff --git a/cortex-m/bin/thumbv8m.base-none-eabi-lto.a b/cortex-m/bin/thumbv8m.base-none-eabi-lto.a new file mode 100644 index 0000000000000000000000000000000000000000..9016d369f4f07f37a5d334840e44e0628717eb5e GIT binary patch literal 14316 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 literal 0 HcmV?d00001 diff --git a/cortex-m/bin/thumbv8m.base-none-eabi.a b/cortex-m/bin/thumbv8m.base-none-eabi.a new file mode 100644 index 0000000000000000000000000000000000000000..c0cc96c4a440876c4b514f593aba2540390bccf2 GIT binary patch literal 18680 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 literal 0 HcmV?d00001 diff --git a/cortex-m/bin/thumbv8m.main-none-eabi-lto.a b/cortex-m/bin/thumbv8m.main-none-eabi-lto.a new file mode 100644 index 0000000000000000000000000000000000000000..de23794e617362f63cfc1a297cd35349e39cd1b6 GIT binary patch literal 18708 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
  • %~DsA5n0~zwzK8=P{TFrDw;bXXVlf3LhuEvH?mu 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 literal 0 HcmV?d00001 diff --git a/cortex-m/build.rs b/cortex-m/build.rs index cfcd394e..23ceebad 100644 --- a/cortex-m/build.rs +++ b/cortex-m/build.rs @@ -1,23 +1,33 @@ -use std::env; +use std::path::PathBuf; +use std::{env, fs}; fn main() { let target = env::var("TARGET").unwrap(); let host_triple = env::var("HOST").unwrap(); - - println!("cargo:rustc-check-cfg=cfg(armv6m)"); - println!("cargo:rustc-check-cfg=cfg(armv7em)"); - println!("cargo:rustc-check-cfg=cfg(armv7m)"); - println!("cargo:rustc-check-cfg=cfg(armv8m)"); - println!("cargo:rustc-check-cfg=cfg(armv8m_base)"); - println!("cargo:rustc-check-cfg=cfg(armv8m_main)"); - println!("cargo:rustc-check-cfg=cfg(cortex_m)"); - println!("cargo:rustc-check-cfg=cfg(has_fpu)"); - println!("cargo:rustc-check-cfg=cfg(native)"); + 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"); @@ -27,7 +37,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"); + println!("cargo:rustc-cfg=armv7em"); // (not currently used) } else if target.starts_with("thumbv8m.base") { println!("cargo:rustc-cfg=cortex_m"); println!("cargo:rustc-cfg=armv8m"); diff --git a/cortex-m/src/asm.rs b/cortex-m/src/asm.rs index 477a5f71..4dc1ab07 100644 --- a/cortex-m/src/asm.rs +++ b/cortex-m/src/asm.rs @@ -1,111 +1,73 @@ //! Miscellaneous assembly instructions -#[cfg(cortex_m)] -use core::arch::asm; -use core::sync::atomic::{compiler_fence, Ordering}; +// 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; /// 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() { - unsafe { asm!("bkpt", options(nomem, nostack, preserves_flags)) }; + call_asm!(__bkpt()); } /// Blocks the program for *at least* `cycles` CPU cycles. /// -/// This is implemented in assembly as a fixed number of iterations of a loop, so that execution -/// time is independent of the optimization level. +/// This is implemented in assembly so its execution time is independent of the optimization +/// level, however it is dependent on the specific architecture and core configuration. /// -/// The loop code is the same for all architectures, however the number of CPU cycles required for -/// one iteration varies substantially between architectures. This means that with a 48MHz CPU -/// clock, a call to `delay(48_000_000)` is guaranteed to take at least 1 second, but for example -/// could take 2 seconds. -/// -/// NOTE that the delay can take much longer if interrupts are serviced during its execution 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)] +/// NOTE that the delay can take much longer if interrupts are serviced during its execution +/// 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. #[inline] pub fn 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!( - // The `bne` on some cores (eg Cortex-M4) will take a different number of cycles - // depending on the alignment of the branch target. Set the alignment of the top of the - // loop to prevent surprising timing changes when the alignment of `fn delay()` changes. - ".p2align 3", - // 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), - ) - }; + call_asm!(__delay(cycles: u32)); } /// A no-operation. Useful to prevent delay loops from being optimized away. -#[inline(always)] +#[inline] pub 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. - #[cfg(cortex_m)] - unsafe { - asm!("nop", options(nomem, nostack, preserves_flags)) - }; + call_asm!(__nop()); } /// Generate an Undefined Instruction exception. /// /// Can be used as a stable alternative to `core::intrinsics::abort`. -#[cfg(cortex_m)] -#[inline(always)] +#[inline] pub fn udf() -> ! { - unsafe { asm!("udf #0", options(noreturn, nomem, nostack, preserves_flags)) }; + call_asm!(__udf() -> !) } /// Wait For Event -#[cfg(cortex_m)] -#[inline(always)] +#[inline] pub fn wfe() { - unsafe { asm!("wfe", options(nomem, nostack, preserves_flags)) }; + call_asm!(__wfe()) } /// Wait For Interrupt -#[cfg(cortex_m)] -#[inline(always)] +#[inline] pub fn wfi() { - unsafe { asm!("wfi", options(nomem, nostack, preserves_flags)) }; + call_asm!(__wfi()) } /// Send Event -#[cfg(cortex_m)] -#[inline(always)] +#[inline] pub fn sev() { - unsafe { asm!("sev", options(nomem, nostack, preserves_flags)) }; + call_asm!(__sev()) } /// 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(always)] +#[inline] pub fn isb() { - compiler_fence(Ordering::SeqCst); - #[cfg(cortex_m)] - unsafe { - asm!("isb", options(nostack, preserves_flags)) - }; - compiler_fence(Ordering::SeqCst); + call_asm!(__isb()) } /// Data Synchronization Barrier @@ -115,14 +77,9 @@ 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(always)] +#[inline] pub fn dsb() { - compiler_fence(Ordering::SeqCst); - #[cfg(cortex_m)] - unsafe { - asm!("dsb", options(nostack, preserves_flags)) - }; - compiler_fence(Ordering::SeqCst); + call_asm!(__dsb()) } /// Data Memory Barrier @@ -130,14 +87,9 @@ 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(always)] +#[inline] pub fn dmb() { - compiler_fence(Ordering::SeqCst); - #[cfg(cortex_m)] - unsafe { - asm!("dmb", options(nostack, preserves_flags)) - }; - compiler_fence(Ordering::SeqCst); + call_asm!(__dmb()) } /// Test Target @@ -145,20 +97,13 @@ 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(always)] +#[inline] #[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 mut target = addr as u32; - unsafe { - asm!( - "tt {target}, {target}", - target = inout(reg) target, - options(nomem, nostack, preserves_flags), - ) - }; - target + let addr = addr as u32; + call_asm!(__tt(addr: u32) -> u32) } /// Test Target Unprivileged @@ -167,20 +112,13 @@ 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(always)] +#[inline] #[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 mut target = addr as u32; - unsafe { - asm!( - "ttt {target}, {target}", - target = inout(reg) target, - options(nomem, nostack, preserves_flags), - ) - }; - target + let addr = addr as u32; + call_asm!(__ttt(addr: u32) -> u32) } /// Test Target Alternate Domain @@ -190,20 +128,13 @@ 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(always)] +#[inline] #[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 mut target = addr as u32; - unsafe { - asm!( - "tta {target}, {target}", - target = inout(reg) target, - options(nomem, nostack, preserves_flags), - ) - }; - target + let addr = addr as u32; + call_asm!(__tta(addr: u32) -> u32) } /// Test Target Alternate Domain Unprivileged @@ -213,40 +144,31 @@ 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(always)] +#[inline] #[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 mut target = addr as u32; - unsafe { - asm!( - "ttat {target}, {target}", - target = inout(reg) target, - options(nomem, nostack, preserves_flags), - ) - }; - target + let addr = addr as u32; + call_asm!(__ttat(addr: u32) -> u32) } /// 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(always)] +#[inline] #[cfg(armv8m)] pub unsafe fn bx_ns(addr: u32) { - asm!("bxns {}", in(reg) addr, options(nomem, nostack, preserves_flags)); + call_asm!(__bxns(addr: u32)); } /// Semihosting syscall. /// /// This method is used by cortex-m-semihosting to provide semihosting syscalls. -#[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 +#[inline] +pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 { + call_asm!(__sh_syscall(nr: u32, arg: u32) -> u32) } /// Bootstrap. @@ -259,27 +181,12 @@ pub unsafe fn semihosting_syscall(mut 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; - 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), - ); + call_asm!(__bootstrap(msp: u32, rv: u32) -> !); } /// Bootload. @@ -294,7 +201,6 @@ 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/cortex-m/src/call_asm.rs b/cortex-m/src/call_asm.rs new file mode 100644 index 00000000..295277f3 --- /dev/null +++ b/cortex-m/src/call_asm.rs @@ -0,0 +1,24 @@ +/// 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/cortex-m/src/cmse.rs b/cortex-m/src/cmse.rs index 7826bb8d..36d74475 100644 --- a/cortex-m/src/cmse.rs +++ b/cortex-m/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 { - if self.tt_resp.mrvalid() { - // Cast is safe as MREGION field is defined on 8 bits. - Some(self.tt_resp.mregion() as u8) + if self.tt_resp.srvalid() { + // Cast is safe as SREGION field is defined on 8 bits. + Some(self.tt_resp.sregion() as u8) } else { None } diff --git a/cortex-m/src/critical_section.rs b/cortex-m/src/critical_section.rs index 6bedfffa..d33e90ff 100644 --- a/cortex-m/src/critical_section.rs +++ b/cortex-m/src/critical_section.rs @@ -1,24 +1,25 @@ -use critical_section::{set_impl, Impl, RawRestoreState}; +#[cfg(all(cortex_m, feature = "critical-section-single-core"))] +mod single_core_critical_section { + use critical_section::{set_impl, Impl, RawRestoreState}; -use crate::interrupt; -use crate::register::primask; + use crate::interrupt; + use crate::register::primask; -struct SingleCoreCriticalSection; -set_impl!(SingleCoreCriticalSection); + struct SingleCoreCriticalSection; + set_impl!(SingleCoreCriticalSection); -unsafe impl Impl for SingleCoreCriticalSection { - unsafe fn acquire() -> RawRestoreState { - // Backup previous state of PRIMASK register. We access the entire register directly as a - // u32 instead of using the primask::read() function to minimize the number of processor - // cycles during which interrupts are disabled. - let restore_state = primask::read_raw(); - // NOTE: Fence guarantees are provided by interrupt::disable(), which performs a `compiler_fence(SeqCst)`. - interrupt::disable(); - restore_state - } + unsafe impl Impl for SingleCoreCriticalSection { + unsafe fn acquire() -> RawRestoreState { + let was_active = primask::read().is_active(); + interrupt::disable(); + was_active + } - unsafe fn release(restore_state: RawRestoreState) { - // NOTE: Fence guarantees are provided by primask::write_raw(), which performs a `compiler_fence(SeqCst)`. - primask::write_raw(restore_state); + 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/cortex-m/src/delay.rs b/cortex-m/src/delay.rs index 11b0c284..66a63bf6 100644 --- a/cortex-m/src/delay.rs +++ b/cortex-m/src/delay.rs @@ -1,7 +1,7 @@ //! A delay driver based on SysTick. use crate::peripheral::{syst::SystClkSource, SYST}; -use eh1::delay::DelayNs; +use embedded_hal::blocking::delay::{DelayMs, DelayUs}; /// System timer (SysTick) as a delay provider. pub struct Delay { @@ -75,8 +75,7 @@ impl Delay { } } -#[cfg(feature = "eh0")] -impl eh0::blocking::delay::DelayMs for Delay { +impl DelayMs for Delay { #[inline] fn delay_ms(&mut self, ms: u32) { Delay::delay_ms(self, ms); @@ -84,8 +83,7 @@ impl eh0::blocking::delay::DelayMs for Delay { } // This is a workaround to allow `delay_ms(42)` construction without specifying a type. -#[cfg(feature = "eh0")] -impl eh0::blocking::delay::DelayMs for Delay { +impl DelayMs for Delay { #[inline(always)] fn delay_ms(&mut self, ms: i32) { assert!(ms >= 0); @@ -93,24 +91,21 @@ impl eh0::blocking::delay::DelayMs for Delay { } } -#[cfg(feature = "eh0")] -impl eh0::blocking::delay::DelayMs for Delay { +impl DelayMs for Delay { #[inline(always)] fn delay_ms(&mut self, ms: u16) { Delay::delay_ms(self, u32::from(ms)); } } -#[cfg(feature = "eh0")] -impl eh0::blocking::delay::DelayMs for Delay { +impl DelayMs for Delay { #[inline(always)] fn delay_ms(&mut self, ms: u8) { Delay::delay_ms(self, u32::from(ms)); } } -#[cfg(feature = "eh0")] -impl eh0::blocking::delay::DelayUs for Delay { +impl DelayUs for Delay { #[inline] fn delay_us(&mut self, us: u32) { Delay::delay_us(self, us); @@ -118,8 +113,7 @@ impl eh0::blocking::delay::DelayUs for Delay { } // This is a workaround to allow `delay_us(42)` construction without specifying a type. -#[cfg(feature = "eh0")] -impl eh0::blocking::delay::DelayUs for Delay { +impl DelayUs for Delay { #[inline(always)] fn delay_us(&mut self, us: i32) { assert!(us >= 0); @@ -127,39 +121,16 @@ impl eh0::blocking::delay::DelayUs for Delay { } } -#[cfg(feature = "eh0")] -impl eh0::blocking::delay::DelayUs for Delay { +impl DelayUs for Delay { #[inline(always)] fn delay_us(&mut self, us: u16) { Delay::delay_us(self, u32::from(us)) } } -#[cfg(feature = "eh0")] -impl eh0::blocking::delay::DelayUs for Delay { +impl DelayUs for Delay { #[inline(always)] fn delay_us(&mut self, us: u8) { Delay::delay_us(self, u32::from(us)) } } - -impl DelayNs for Delay { - #[inline] - fn delay_ns(&mut self, ns: u32) { - // from the rp2040-hal: - let us = ns / 1000 + if ns % 1000 == 0 { 0 } else { 1 }; - // With rustc 1.73, this can be replaced by: - // let us = ns.div_ceil(1000); - Delay::delay_us(self, us) - } - - #[inline] - fn delay_us(&mut self, us: u32) { - Delay::delay_us(self, us) - } - - #[inline] - fn delay_ms(&mut self, ms: u32) { - Delay::delay_ms(self, ms) - } -} diff --git a/cortex-m/src/interrupt.rs b/cortex-m/src/interrupt.rs index aa792201..0fd1284b 100644 --- a/cortex-m/src/interrupt.rs +++ b/cortex-m/src/interrupt.rs @@ -1,9 +1,6 @@ //! Interrupts -#[cfg(cortex_m)] -use core::arch::asm; -#[cfg(cortex_m)] -use core::sync::atomic::{compiler_fence, Ordering}; +pub use bare_metal::{CriticalSection, Mutex, Nr}; /// Trait for enums of external interrupt numbers. /// @@ -26,71 +23,51 @@ pub unsafe trait InterruptNumber: Copy { fn number(self) -> u16; } -/// Disables all interrupts in the current core. -#[cfg(cortex_m)] -#[inline] -pub fn disable() { - unsafe { - asm!("cpsid i", options(nomem, nostack, preserves_flags)); +/// Implement InterruptNumber for the old bare_metal::Nr trait. +/// This implementation is for backwards compatibility only and will be removed in cortex-m 0.8. +unsafe impl InterruptNumber for T { + #[inline] + fn number(self) -> u16 { + self.nr() as u16 } +} - // Ensure no subsequent memory accesses are reordered to before interrupts are disabled. - compiler_fence(Ordering::SeqCst); +/// Disables all interrupts +#[inline] +pub fn disable() { + call_asm!(__cpsid()); } -/// Enables all the interrupts in the current core. +/// Enables all the interrupts /// /// # Safety /// -/// - Do not call this function inside a critical section. -#[cfg(cortex_m)] +/// - Do not call this function inside an `interrupt::free` critical section #[inline] pub unsafe fn enable() { - // Ensure no preceeding memory accesses are reordered to after interrupts are enabled. - compiler_fence(Ordering::SeqCst); - - asm!("cpsie i", options(nomem, nostack, preserves_flags)); + call_asm!(__cpsie()); } -/// Execute closure `f` with interrupts disabled in the current core. +/// Execute closure `f` in an interrupt-free context. /// -/// This method does not synchronize 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)] +/// This as also known as a "critical section". #[inline] pub fn free(f: F) -> R where - F: FnOnce() -> R, + F: FnOnce(&CriticalSection) -> R, { - // Backup previous state of PRIMASK register. We access the entire register directly as a - // u32 instead of using the primask::read() function to minimize the number of processor - // cycles during which interrupts are disabled. - let primask = crate::register::primask::read_raw(); + let primask = crate::register::primask::read(); // disable interrupts disable(); - let r = f(); + let r = f(unsafe { &CriticalSection::new() }); - unsafe { - crate::register::primask::write_raw(primask); + // If the interrupts were active before our `disable` call, then re-enable + // them. Otherwise, keep them disabled + if primask.is_active() { + unsafe { enable() } } 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 -where - F: FnOnce() -> R, -{ - panic!("cortex_m::interrupt::free() is only functional on cortex-m platforms"); -} diff --git a/cortex-m/src/itm.rs b/cortex-m/src/itm.rs index 905aefb8..72cb0d9a 100644 --- a/cortex-m/src/itm.rs +++ b/cortex-m/src/itm.rs @@ -57,7 +57,7 @@ unsafe fn write_aligned_impl(port: &mut Stim, buffer: &[u8]) { struct Port<'p>(&'p mut Stim); -impl fmt::Write for Port<'_> { +impl<'p> fmt::Write for Port<'p> { #[inline] fn write_str(&mut self, s: &str) -> fmt::Result { write_all(self.0, s.as_bytes()); diff --git a/cortex-m/src/lib.rs b/cortex-m/src/lib.rs index b0ca35c9..4ba0ad86 100644 --- a/cortex-m/src/lib.rs +++ b/cortex-m/src/lib.rs @@ -9,30 +9,61 @@ //! //! # Optional features //! +//! ## `inline-asm` +//! +//! 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: +//! +//! - 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 Rust version at least 1.59 to use the `asm!()` +//! macro. In the future 0.8 and above versions of `cortex-m`, this feature will always be enabled. +//! //! ## `critical-section-single-core` //! //! This feature enables a [`critical-section`](https://github.com/rust-embedded/critical-section) //! implementation suitable for single-core targets, based on disabling interrupts globally. //! //! 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 not be disabled +//! 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. //! -//! The critical section has been optimized to block interrupts for as few cycles as possible, -//! but -- due to `critical-section` implementation details -- incurs branches in a normal build -//! configuration. For minimal interrupt latency, you can achieve inlining by enabling -//! [linker-plugin-based LTO](https://doc.rust-lang.org/rustc/linker-plugin-lto.html). -//! //! ## `cm7-r0p1` //! //! This feature enables workarounds for errata found on Cortex-M7 chips with revision r0p1. Some //! 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.61 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. #![deny(missing_docs)] @@ -58,27 +89,24 @@ // 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; pub mod asm; #[cfg(armv8m)] pub mod cmse; +mod critical_section; pub mod delay; 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/cortex-m/src/macros.rs b/cortex-m/src/macros.rs index c4483b87..512c9323 100644 --- a/cortex-m/src/macros.rs +++ b/cortex-m/src/macros.rs @@ -31,13 +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 -/// debugger and is not referenceable from other code. See example below. +/// 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. /// /// # Example /// @@ -64,12 +61,11 @@ macro_rules! iprintln { /// ``` #[macro_export] macro_rules! singleton { - ($(#[$meta:meta])* $name:ident: $ty:ty = $expr:expr) => { - $crate::_export::critical_section::with(|_| { + ($name:ident: $ty:ty = $expr:expr) => { + $crate::interrupt::free(|_| { // 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`... - $(#[$meta])* static mut $name: (::core::mem::MaybeUninit<$ty>, bool) = (::core::mem::MaybeUninit::uninit(), false); @@ -83,13 +79,14 @@ macro_rules! singleton { #[allow(unsafe_code)] unsafe { $name.1 = true; - Some($name.0.write(expr)) + $name.0 = ::core::mem::MaybeUninit::new(expr); + Some(&mut *$name.0.as_mut_ptr()) } } }) }; - ($(#[$meta:meta])* : $ty:ty = $expr:expr) => { - $crate::singleton!($(#[$meta])* VAR: $ty = $expr) + (: $ty:ty = $expr:expr) => { + $crate::singleton!(VAR: $ty = $expr) }; } @@ -115,15 +112,3 @@ const CFAIL: () = (); /// ``` #[allow(dead_code)] const CPASS: () = (); - -/// ``` -/// use cortex_m::singleton; -/// -/// fn foo() { -/// // check that attributes are forwarded -/// singleton!(#[link_section = ".bss"] FOO: u8 = 0); -/// singleton!(#[link_section = ".bss"]: u8 = 1); -/// } -/// ``` -#[allow(dead_code)] -const CPASS_ATTR: () = (); diff --git a/cortex-m/src/peripheral/ac.rs b/cortex-m/src/peripheral/ac.rs index 6169b654..1ac5be10 100644 --- a/cortex-m/src/peripheral/ac.rs +++ b/cortex-m/src/peripheral/ac.rs @@ -16,7 +16,7 @@ pub struct RegisterBlock { /// AHB Slave Control Register pub ahbscr: RW, reserved0: u32, - /// Auxiliary Bus Fault Status Register + /// Auxilary Bus Fault Status Register pub abfsr: RW, } diff --git a/cortex-m/src/peripheral/dcb.rs b/cortex-m/src/peripheral/dcb.rs index a4db9fc3..4a63c889 100644 --- a/cortex-m/src/peripheral/dcb.rs +++ b/cortex-m/src/peripheral/dcb.rs @@ -6,7 +6,6 @@ use crate::peripheral::DCB; use core::ptr; const DCB_DEMCR_TRCENA: u32 = 1 << 24; -const DCB_DEMCR_MON_EN: u32 = 1 << 16; /// Register block #[repr(C)] @@ -26,10 +25,6 @@ impl DCB { /// `peripheral::DWT` cycle counter to work properly. /// As by STM documentation, this flag is not reset on /// soft-reset, only on power reset. - /// - /// Note: vendor-specific registers may have to be set to completely - /// enable tracing. For example, on the STM32F401RE, `TRACE_MODE` - /// and `TRACE_IOEN` must be configured in `DBGMCU_CR` register. #[inline] pub fn enable_trace(&mut self) { // set bit 24 / TRCENA @@ -47,22 +42,6 @@ impl DCB { } } - /// Enables the [`DebugMonitor`](crate::peripheral::scb::Exception::DebugMonitor) exception - #[inline] - pub fn enable_debug_monitor(&mut self) { - unsafe { - self.demcr.modify(|w| w | DCB_DEMCR_MON_EN); - } - } - - /// Disables the [`DebugMonitor`](crate::peripheral::scb::Exception::DebugMonitor) exception - #[inline] - pub fn disable_debug_monitor(&mut self) { - unsafe { - self.demcr.modify(|w| w & !DCB_DEMCR_MON_EN); - } - } - /// Is there a debugger attached? (see note) /// /// Note: This function is [reported not to diff --git a/cortex-m/src/peripheral/dwt.rs b/cortex-m/src/peripheral/dwt.rs index 05657f33..58d91fd3 100644 --- a/cortex-m/src/peripheral/dwt.rs +++ b/cortex-m/src/peripheral/dwt.rs @@ -5,13 +5,12 @@ use volatile_register::WO; use volatile_register::{RO, RW}; use crate::peripheral::DWT; -use bitfield::bitfield; /// Register block #[repr(C)] pub struct RegisterBlock { /// Control - pub ctrl: RW, + pub ctrl: RW, /// Cycle Count #[cfg(not(armv6m))] pub cyccnt: RW, @@ -51,21 +50,6 @@ pub struct RegisterBlock { pub lsr: RO, } -bitfield! { - /// Control register. - #[repr(C)] - #[derive(Copy, Clone)] - pub struct Ctrl(u32); - cyccntena, set_cyccntena: 0; - pcsamplena, set_pcsamplena: 12; - exctrcena, set_exctrcena: 16; - noprfcnt, _: 24; - nocyccnt, _: 25; - noexttrig, _: 26; - notrcpkt, _: 27; - u8, numcomp, _: 31, 28; -} - /// Comparator #[repr(C)] pub struct Comparator { @@ -74,66 +58,58 @@ pub struct Comparator { /// Comparator Mask pub mask: RW, /// Comparator Function - pub function: RW, + pub function: RW, reserved: u32, } -bitfield! { - #[repr(C)] - #[derive(Copy, Clone)] - /// Comparator FUNCTIONn register. - /// - /// See C1.8.17 "Comparator Function registers, DWT_FUNCTIONn" - pub struct Function(u32); - u8, function, set_function: 3, 0; - emitrange, set_emitrange: 5; - cycmatch, set_cycmatch: 7; - datavmatch, set_datavmatch: 8; - lnk1ena, set_lnk1ena: 9; - u8, datavsize, set_datavsize: 11, 10; - u8, datavaddr0, set_datavaddr0: 15, 12; - u8, datavaddr1, set_datavaddr1: 19, 16; - matched, _: 24; -} +// DWT CTRL register fields +const NUMCOMP_OFFSET: u32 = 28; +const NOTRCPKT: u32 = 1 << 27; +const NOEXTTRIG: u32 = 1 << 26; +const NOCYCCNT: u32 = 1 << 25; +const NOPRFCNT: u32 = 1 << 24; +const CYCCNTENA: u32 = 1 << 0; impl DWT { /// Number of comparators implemented /// /// A value of zero indicates no comparator support. #[inline] - pub fn num_comp(&self) -> u8 { - self.ctrl.read().numcomp() + pub fn num_comp() -> u8 { + // NOTE(unsafe) atomic read with no side effects + unsafe { ((*Self::PTR).ctrl.read() >> NUMCOMP_OFFSET) as u8 } } /// Returns `true` if the the implementation supports sampling and exception tracing #[cfg(not(armv6m))] #[inline] - pub fn has_exception_trace(&self) -> bool { - !self.ctrl.read().notrcpkt() + pub fn has_exception_trace() -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Self::PTR).ctrl.read() & NOTRCPKT == 0 } } /// Returns `true` if the implementation includes external match signals #[cfg(not(armv6m))] #[inline] - pub fn has_external_match(&self) -> bool { - !self.ctrl.read().noexttrig() + pub fn has_external_match() -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Self::PTR).ctrl.read() & NOEXTTRIG == 0 } } /// Returns `true` if the implementation supports a cycle counter + #[cfg(not(armv6m))] #[inline] - pub fn has_cycle_counter(&self) -> bool { - #[cfg(not(armv6m))] - return !self.ctrl.read().nocyccnt(); - - #[cfg(armv6m)] - return false; + pub fn has_cycle_counter() -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Self::PTR).ctrl.read() & NOCYCCNT == 0 } } /// Returns `true` if the implementation the profiling counters #[cfg(not(armv6m))] #[inline] - pub fn has_profiling_counter(&self) -> bool { - !self.ctrl.read().noprfcnt() + pub fn has_profiling_counter() -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Self::PTR).ctrl.read() & NOPRFCNT == 0 } } /// Enables the cycle counter @@ -147,67 +123,22 @@ impl DWT { #[cfg(not(armv6m))] #[inline] pub fn enable_cycle_counter(&mut self) { - unsafe { - self.ctrl.modify(|mut r| { - r.set_cyccntena(true); - r - }); - } + unsafe { self.ctrl.modify(|r| r | CYCCNTENA) } } /// 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 - }); - } + unsafe { self.ctrl.modify(|r| r & !CYCCNTENA) } } /// Returns `true` if the cycle counter is enabled #[cfg(not(armv6m))] #[inline] - pub fn cycle_counter_enabled(&self) -> bool { - self.ctrl.read().cyccntena() - } - - /// Enables exception tracing - #[cfg(not(armv6m))] - #[inline] - pub fn enable_exception_tracing(&mut self) { - unsafe { - self.ctrl.modify(|mut r| { - r.set_exctrcena(true); - r - }); - } - } - - /// Disables exception tracing - #[cfg(not(armv6m))] - #[inline] - pub fn disable_exception_tracing(&mut self) { - unsafe { - self.ctrl.modify(|mut r| { - r.set_exctrcena(false); - r - }); - } - } - - /// Whether to periodically generate PC samples - #[cfg(not(armv6m))] - #[inline] - pub fn enable_pc_samples(&mut self, bit: bool) { - unsafe { - self.ctrl.modify(|mut r| { - r.set_pcsamplena(bit); - r - }); - } + pub fn cycle_counter_enabled() -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Self::PTR).ctrl.read() & CYCCNTENA != 0 } } /// Returns the current clock cycle count @@ -335,173 +266,3 @@ impl DWT { unsafe { self.foldcnt.write(count as u32) } } } - -/// Whether the comparator should match on read, write or read/write operations. -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub enum AccessType { - /// Generate packet only when matched address is read from. - ReadOnly, - /// Generate packet only when matched address is written to. - WriteOnly, - /// Generate packet when matched address is both read from and written to. - ReadWrite, -} - -/// The sequence of packet(s) or events that should be emitted/generated on comparator match. -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub enum EmitOption { - /// Emit only trace data value packet. - Data, - /// Emit only trace address packet. - Address, - /// Emit only trace PC value packet - /// - /// *NOTE* only compatible with [AccessType::ReadWrite]. - PC, - /// Emit trace address and data value packets. - AddressData, - /// Emit trace PC value and data value packets. - PCData, - /// Generate a watchpoint debug event. Either halts execution or fires a `DebugMonitor` exception. - /// - /// See more in section "Watchpoint debug event generation" page C1-729. - WatchpointDebugEvent, - /// Generate a `CMPMATCH[N]` event. - /// - /// See more in section "`CMPMATCH[N]` event generation" page C1-730. - CompareMatchEvent, -} - -/// Settings for address matching -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub struct ComparatorAddressSettings { - /// The address to match against. - pub address: u32, - /// The address mask to match against. - pub mask: u32, - /// What sequence of packet(s) to emit on comparator match. - pub emit: EmitOption, - /// Whether to match on read, write or read/write operations. - pub access_type: AccessType, -} - -/// Settings for cycle count matching -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub struct CycleCountSettings { - /// The function selection used. - /// See Table C1-15 for DWT cycle count comparison functions. - pub emit: EmitOption, - /// The cycle count value to compare against. - pub compare: u32, -} - -/// The available functions of a DWT comparator. -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -#[non_exhaustive] -pub enum ComparatorFunction { - /// Compare accessed memory addresses. - Address(ComparatorAddressSettings), - /// Compare cycle count & target value. - /// - /// **NOTE**: only supported by comparator 0 and if the HW supports the cycle counter. - /// Check [`DWT::has_cycle_counter`] for support. See C1.8.1 for more details. - CycleCount(CycleCountSettings), -} - -/// Possible error values returned on [Comparator::configure]. -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -#[non_exhaustive] -pub enum DwtError { - /// Invalid combination of [AccessType] and [EmitOption]. - InvalidFunction, -} - -impl Comparator { - /// Configure the function of the comparator - #[allow(clippy::missing_inline_in_public_items)] - pub fn configure(&self, settings: ComparatorFunction) -> Result<(), DwtError> { - match settings { - ComparatorFunction::Address(settings) => { - // FUNCTION, EMITRANGE - // See Table C1-14 - let (function, emit_range) = match (&settings.access_type, &settings.emit) { - (AccessType::ReadOnly, EmitOption::Data) => (0b1100, false), - (AccessType::ReadOnly, EmitOption::Address) => (0b1100, true), - (AccessType::ReadOnly, EmitOption::AddressData) => (0b1110, true), - (AccessType::ReadOnly, EmitOption::PCData) => (0b1110, false), - (AccessType::ReadOnly, EmitOption::WatchpointDebugEvent) => (0b0101, false), - (AccessType::ReadOnly, EmitOption::CompareMatchEvent) => (0b1001, false), - - (AccessType::WriteOnly, EmitOption::Data) => (0b1101, false), - (AccessType::WriteOnly, EmitOption::Address) => (0b1101, true), - (AccessType::WriteOnly, EmitOption::AddressData) => (0b1111, true), - (AccessType::WriteOnly, EmitOption::PCData) => (0b1111, false), - (AccessType::WriteOnly, EmitOption::WatchpointDebugEvent) => (0b0110, false), - (AccessType::WriteOnly, EmitOption::CompareMatchEvent) => (0b1010, false), - - (AccessType::ReadWrite, EmitOption::Data) => (0b0010, false), - (AccessType::ReadWrite, EmitOption::Address) => (0b0001, true), - (AccessType::ReadWrite, EmitOption::AddressData) => (0b0010, true), - (AccessType::ReadWrite, EmitOption::PCData) => (0b0011, false), - (AccessType::ReadWrite, EmitOption::WatchpointDebugEvent) => (0b0111, false), - (AccessType::ReadWrite, EmitOption::CompareMatchEvent) => (0b1011, false), - - (AccessType::ReadWrite, EmitOption::PC) => (0b0001, false), - (_, EmitOption::PC) => return Err(DwtError::InvalidFunction), - }; - - unsafe { - self.function.modify(|mut r| { - r.set_function(function); - r.set_emitrange(emit_range); - // don't compare data value - r.set_datavmatch(false); - // don't compare cycle counter value - // NOTE: only needed for comparator 0, but is SBZP. - r.set_cycmatch(false); - // SBZ as needed, see Page 784/C1-724 - r.set_datavsize(0); - r.set_datavaddr0(0); - r.set_datavaddr1(0); - - r - }); - - self.comp.write(settings.address); - self.mask.write(settings.mask); - } - } - ComparatorFunction::CycleCount(settings) => { - let function = match &settings.emit { - EmitOption::PCData => 0b0001, - EmitOption::WatchpointDebugEvent => 0b0100, - EmitOption::CompareMatchEvent => 0b1000, - _ => return Err(DwtError::InvalidFunction), - }; - - unsafe { - self.function.modify(|mut r| { - r.set_function(function); - // emit_range is N/A for cycle count compare - r.set_emitrange(false); - // don't compare data - r.set_datavmatch(false); - // compare cyccnt - r.set_cycmatch(true); - // SBZ as needed, see Page 784/C1-724 - r.set_datavsize(0); - r.set_datavaddr0(0); - r.set_datavaddr1(0); - - r - }); - - self.comp.write(settings.compare); - self.mask.write(0); // SBZ, see Page 784/C1-724 - } - } - } - - Ok(()) - } -} diff --git a/cortex-m/src/peripheral/itm.rs b/cortex-m/src/peripheral/itm.rs index 7291ae06..c0d560f5 100644 --- a/cortex-m/src/peripheral/itm.rs +++ b/cortex-m/src/peripheral/itm.rs @@ -7,12 +7,6 @@ use core::ptr; use volatile_register::{RO, RW, WO}; -use crate::peripheral::ITM; -use bitfield::bitfield; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - /// Register block #[repr(C)] pub struct RegisterBlock { @@ -26,7 +20,7 @@ pub struct RegisterBlock { pub tpr: RW, reserved2: [u32; 15], /// Trace Control - pub tcr: RW, + pub tcr: RW, reserved3: [u32; 75], /// Lock Access pub lar: WO, @@ -34,22 +28,6 @@ pub struct RegisterBlock { pub lsr: RO, } -bitfield! { - /// Trace Control Register. - #[repr(C)] - #[derive(Copy, Clone)] - pub struct Tcr(u32); - itmena, set_itmena: 0; - tsena, set_tsena: 1; - syncena, set_synena: 2; - txena, set_txena: 3; - swoena, set_swoena: 4; - u8, tsprescale, set_tsprescale: 9, 8; - u8, gtsfreq, set_gtsfreq: 11, 10; - u8, tracebusid, set_tracebusid: 22, 16; - busy, _: 23; -} - /// Stimulus Port pub struct Stim { register: UnsafeCell, @@ -91,126 +69,3 @@ impl Stim { unsafe { ptr::read_volatile(self.register.get()) & 0b11 != 0 } } } - -/// The possible local timestamp options. -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum LocalTimestampOptions { - /// Disable local timestamps. - Disabled, - /// Enable local timestamps and use no prescaling. - Enabled, - /// Enable local timestamps and set the prescaler to divide the - /// reference clock by 4. - EnabledDiv4, - /// Enable local timestamps and set the prescaler to divide the - /// reference clock by 16. - EnabledDiv16, - /// Enable local timestamps and set the prescaler to divide the - /// reference clock by 64. - EnabledDiv64, -} - -#[cfg(feature = "std")] -impl core::convert::TryFrom for LocalTimestampOptions { - type Error = (); - - /// Converts an integer value to an enabled [LocalTimestampOptions] - /// variant. Accepted values are: 1, 4, 16, 64. Any other value - /// yields `Err(())`. - #[inline] - fn try_from(value: u8) -> Result { - match value { - 1 => Ok(Self::Enabled), - 4 => Ok(Self::EnabledDiv4), - 16 => Ok(Self::EnabledDiv16), - 64 => Ok(Self::EnabledDiv64), - _ => Err(()), - } - } -} - -/// The possible global timestamp options. -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub enum GlobalTimestampOptions { - /// Disable global timestamps. - Disabled, - /// Generate a global timestamp approximately every 128 cycles. - Every128Cycles, - /// Generate a global timestamp approximately every 8921 cycles. - Every8192Cycles, - /// Generate a global timestamp after every packet, if the output FIFO is empty. - EveryPacket, -} - -/// The possible clock sources for timestamp counters. -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub enum TimestampClkSrc { - /// Clock timestamp counters using the system processor clock. - SystemClock, - /// Clock timestamp counters using the asynchronous clock from the - /// TPIU interface. - /// - /// NOTE: The timestamp counter is held in reset while the output - /// line is idle. - AsyncTPIU, -} - -/// Available settings for the ITM peripheral. -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub struct ITMSettings { - /// Whether to enable ITM. - pub enable: bool, - /// Whether DWT packets should be forwarded to ITM. - pub forward_dwt: bool, - /// The local timestamp options that should be applied. - pub local_timestamps: LocalTimestampOptions, - /// The global timestamp options that should be applied. - pub global_timestamps: GlobalTimestampOptions, - /// The trace bus ID to use when multi-trace sources are in use. - /// `None` specifies that only a single trace source is in use and - /// has the same effect as `Some(0)`. - pub bus_id: Option, - /// The clock that should increase timestamp counters. - pub timestamp_clk_src: TimestampClkSrc, -} - -impl ITM { - /// Removes the software lock on the ITM. - #[inline] - pub fn unlock(&mut self) { - // NOTE(unsafe) atomic write to a stateless, write-only register - unsafe { self.lar.write(0xC5AC_CE55) } - } - - /// Configures the ITM with the passed [ITMSettings]. - #[inline] - pub fn configure(&mut self, settings: ITMSettings) { - unsafe { - self.tcr.modify(|mut r| { - r.set_itmena(settings.enable); - r.set_tsena(settings.local_timestamps != LocalTimestampOptions::Disabled); - r.set_txena(settings.forward_dwt); - r.set_tsprescale(match settings.local_timestamps { - LocalTimestampOptions::Disabled | LocalTimestampOptions::Enabled => 0b00, - LocalTimestampOptions::EnabledDiv4 => 0b10, - LocalTimestampOptions::EnabledDiv16 => 0b10, - LocalTimestampOptions::EnabledDiv64 => 0b11, - }); - r.set_gtsfreq(match settings.global_timestamps { - GlobalTimestampOptions::Disabled => 0b00, - GlobalTimestampOptions::Every128Cycles => 0b01, - GlobalTimestampOptions::Every8192Cycles => 0b10, - GlobalTimestampOptions::EveryPacket => 0b11, - }); - r.set_swoena(match settings.timestamp_clk_src { - TimestampClkSrc::SystemClock => false, - TimestampClkSrc::AsyncTPIU => true, - }); - r.set_tracebusid(settings.bus_id.unwrap_or(0)); - - r - }); - } - } -} diff --git a/cortex-m/src/peripheral/mod.rs b/cortex-m/src/peripheral/mod.rs index 8b610a7b..d8fd2d46 100644 --- a/cortex-m/src/peripheral/mod.rs +++ b/cortex-m/src/peripheral/mod.rs @@ -60,7 +60,9 @@ use core::marker::PhantomData; use core::ops; -#[cfg(feature = "cm7")] +use crate::interrupt; + +#[cfg(cm7)] pub mod ac; #[cfg(not(armv6m))] pub mod cbp; @@ -94,7 +96,7 @@ mod test; #[allow(clippy::manual_non_exhaustive)] pub struct Peripherals { /// Cortex-M7 TCM and cache access control. - #[cfg(feature = "cm7")] + #[cfg(cm7)] pub AC: AC, /// Cache and branch predictor maintenance operations. @@ -163,7 +165,7 @@ impl Peripherals { /// Returns all the core peripherals *once* #[inline] pub fn take() -> Option { - critical_section::with(|_| { + interrupt::free(|_| { if unsafe { TAKEN } { None } else { @@ -178,7 +180,7 @@ impl Peripherals { TAKEN = true; Peripherals { - #[cfg(feature = "cm7")] + #[cfg(cm7)] AC: AC { _marker: PhantomData, }, @@ -230,22 +232,28 @@ impl Peripherals { } /// Access control -#[cfg(feature = "cm7")] +#[cfg(cm7)] pub struct AC { _marker: PhantomData<*const ()>, } -#[cfg(feature = "cm7")] +#[cfg(cm7)] unsafe impl Send for AC {} -#[cfg(feature = "cm7")] +#[cfg(cm7)] impl AC { /// Pointer to the register block pub const PTR: *const self::ac::RegisterBlock = 0xE000_EF90 as *const _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *const self::ac::RegisterBlock { + Self::PTR + } } /// Cache and branch predictor maintenance operations -#[allow(clippy::upper_case_acronyms)] pub struct CBP { _marker: PhantomData<*const ()>, } @@ -263,6 +271,13 @@ impl CBP { /// Pointer to the register block pub const PTR: *const self::cbp::RegisterBlock = 0xE000_EF50 as *const _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *const self::cbp::RegisterBlock { + Self::PTR + } } #[cfg(not(armv6m))] @@ -276,7 +291,6 @@ impl ops::Deref for CBP { } /// CPUID -#[allow(clippy::upper_case_acronyms)] pub struct CPUID { _marker: PhantomData<*const ()>, } @@ -286,6 +300,13 @@ unsafe impl Send for CPUID {} impl CPUID { /// Pointer to the register block pub const PTR: *const self::cpuid::RegisterBlock = 0xE000_ED00 as *const _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *const self::cpuid::RegisterBlock { + Self::PTR + } } impl ops::Deref for CPUID { @@ -298,7 +319,6 @@ impl ops::Deref for CPUID { } /// Debug Control Block -#[allow(clippy::upper_case_acronyms)] pub struct DCB { _marker: PhantomData<*const ()>, } @@ -308,6 +328,13 @@ unsafe impl Send for DCB {} impl DCB { /// Pointer to the register block pub const PTR: *const dcb::RegisterBlock = 0xE000_EDF0 as *const _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *const dcb::RegisterBlock { + Self::PTR + } } impl ops::Deref for DCB { @@ -320,7 +347,6 @@ impl ops::Deref for DCB { } /// Data Watchpoint and Trace unit -#[allow(clippy::upper_case_acronyms)] pub struct DWT { _marker: PhantomData<*const ()>, } @@ -330,6 +356,13 @@ unsafe impl Send for DWT {} impl DWT { /// Pointer to the register block pub const PTR: *const dwt::RegisterBlock = 0xE000_1000 as *const _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *const dwt::RegisterBlock { + Self::PTR + } } impl ops::Deref for DWT { @@ -342,7 +375,6 @@ impl ops::Deref for DWT { } /// Flash Patch and Breakpoint unit -#[allow(clippy::upper_case_acronyms)] pub struct FPB { _marker: PhantomData<*const ()>, } @@ -353,6 +385,13 @@ unsafe impl Send for FPB {} impl FPB { /// Pointer to the register block pub const PTR: *const fpb::RegisterBlock = 0xE000_2000 as *const _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *const fpb::RegisterBlock { + Self::PTR + } } #[cfg(not(armv6m))] @@ -366,7 +405,6 @@ impl ops::Deref for FPB { } /// Floating Point Unit -#[allow(clippy::upper_case_acronyms)] pub struct FPU { _marker: PhantomData<*const ()>, } @@ -377,6 +415,13 @@ unsafe impl Send for FPU {} impl FPU { /// Pointer to the register block pub const PTR: *const fpu::RegisterBlock = 0xE000_EF30 as *const _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *const fpu::RegisterBlock { + Self::PTR + } } #[cfg(any(has_fpu, native))] @@ -395,7 +440,6 @@ impl ops::Deref for FPU { /// `actlr`. It's called the "implementation control block" in the ARMv8-M /// standard, but earlier standards contained the registers, just without a /// name. -#[allow(clippy::upper_case_acronyms)] pub struct ICB { _marker: PhantomData<*const ()>, } @@ -405,6 +449,13 @@ unsafe impl Send for ICB {} impl ICB { /// Pointer to the register block pub const PTR: *mut icb::RegisterBlock = 0xE000_E004 as *mut _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *mut icb::RegisterBlock { + Self::PTR + } } impl ops::Deref for ICB { @@ -424,7 +475,6 @@ impl ops::DerefMut for ICB { } /// Instrumentation Trace Macrocell -#[allow(clippy::upper_case_acronyms)] pub struct ITM { _marker: PhantomData<*const ()>, } @@ -435,6 +485,13 @@ unsafe impl Send for ITM {} impl ITM { /// Pointer to the register block pub const PTR: *mut itm::RegisterBlock = 0xE000_0000 as *mut _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *mut itm::RegisterBlock { + Self::PTR + } } #[cfg(all(not(armv6m), not(armv8m_base)))] @@ -456,7 +513,6 @@ impl ops::DerefMut for ITM { } /// Memory Protection Unit -#[allow(clippy::upper_case_acronyms)] pub struct MPU { _marker: PhantomData<*const ()>, } @@ -466,6 +522,13 @@ unsafe impl Send for MPU {} impl MPU { /// Pointer to the register block pub const PTR: *const mpu::RegisterBlock = 0xE000_ED90 as *const _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *const mpu::RegisterBlock { + Self::PTR + } } impl ops::Deref for MPU { @@ -478,7 +541,6 @@ impl ops::Deref for MPU { } /// Nested Vector Interrupt Controller -#[allow(clippy::upper_case_acronyms)] pub struct NVIC { _marker: PhantomData<*const ()>, } @@ -488,6 +550,13 @@ unsafe impl Send for NVIC {} impl NVIC { /// Pointer to the register block pub const PTR: *const nvic::RegisterBlock = 0xE000_E100 as *const _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *const nvic::RegisterBlock { + Self::PTR + } } impl ops::Deref for NVIC { @@ -500,7 +569,6 @@ impl ops::Deref for NVIC { } /// Security Attribution Unit -#[allow(clippy::upper_case_acronyms)] pub struct SAU { _marker: PhantomData<*const ()>, } @@ -511,6 +579,13 @@ unsafe impl Send for SAU {} impl SAU { /// Pointer to the register block pub const PTR: *const sau::RegisterBlock = 0xE000_EDD0 as *const _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *const sau::RegisterBlock { + Self::PTR + } } #[cfg(armv8m)] @@ -524,7 +599,6 @@ impl ops::Deref for SAU { } /// System Control Block -#[allow(clippy::upper_case_acronyms)] pub struct SCB { _marker: PhantomData<*const ()>, } @@ -534,6 +608,13 @@ unsafe impl Send for SCB {} impl SCB { /// Pointer to the register block pub const PTR: *const scb::RegisterBlock = 0xE000_ED04 as *const _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *const scb::RegisterBlock { + Self::PTR + } } impl ops::Deref for SCB { @@ -546,7 +627,6 @@ impl ops::Deref for SCB { } /// SysTick: System Timer -#[allow(clippy::upper_case_acronyms)] pub struct SYST { _marker: PhantomData<*const ()>, } @@ -556,6 +636,13 @@ unsafe impl Send for SYST {} impl SYST { /// Pointer to the register block pub const PTR: *const syst::RegisterBlock = 0xE000_E010 as *const _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *const syst::RegisterBlock { + Self::PTR + } } impl ops::Deref for SYST { @@ -568,7 +655,6 @@ impl ops::Deref for SYST { } /// Trace Port Interface Unit -#[allow(clippy::upper_case_acronyms)] pub struct TPIU { _marker: PhantomData<*const ()>, } @@ -579,6 +665,13 @@ unsafe impl Send for TPIU {} impl TPIU { /// Pointer to the register block pub const PTR: *const tpiu::RegisterBlock = 0xE004_0000 as *const _; + + /// Returns a pointer to the register block + #[inline(always)] + #[deprecated(since = "0.7.5", note = "Use the associated constant `PTR` instead")] + pub const fn ptr() -> *const tpiu::RegisterBlock { + Self::PTR + } } #[cfg(not(armv6m))] diff --git a/cortex-m/src/peripheral/nvic.rs b/cortex-m/src/peripheral/nvic.rs index fccd6a2c..57fa94b7 100644 --- a/cortex-m/src/peripheral/nvic.rs +++ b/cortex-m/src/peripheral/nvic.rs @@ -36,15 +36,7 @@ pub struct RegisterBlock { #[cfg(armv6m)] _reserved4: [u32; 16], - _reserved5: [u32; 16], - - #[cfg(armv8m)] - /// Interrupt Target Non-secure (only present on Arm v8-M) - pub itns: [RW; 16], - #[cfg(not(armv8m))] - _reserved6: [u32; 16], - - _reserved7: [u32; 16], + _reserved5: [u32; 48], /// Interrupt Priority /// @@ -75,7 +67,7 @@ pub struct RegisterBlock { pub ipr: [RW; 8], #[cfg(not(armv6m))] - _reserved8: [u32; 580], + _reserved6: [u32; 580], /// Software Trigger Interrupt #[cfg(not(armv6m))] @@ -94,14 +86,15 @@ impl NVIC { /// [`NVIC::pend`]: #method.pend #[cfg(not(armv6m))] #[inline] - pub fn request(interrupt: I) + pub fn request(&mut self, interrupt: I) where I: InterruptNumber, { let nr = interrupt.number(); - // NOTE(ptr) this is a write to a stateless register - unsafe { (*Self::PTR).stir.write(u32::from(nr)) } + unsafe { + self.stir.write(u32::from(nr)); + } } /// Disables `interrupt` diff --git a/cortex-m/src/peripheral/sau.rs b/cortex-m/src/peripheral/sau.rs index 6b8477f3..da91aca9 100644 --- a/cortex-m/src/peripheral/sau.rs +++ b/cortex-m/src/peripheral/sau.rs @@ -7,6 +7,7 @@ //! //! 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}; @@ -161,7 +162,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> { - critical_section::with(|_| { + interrupt::free(|_| { let base_address = region.base_address; let limit_address = region.limit_address; let attribute = region.attribute; @@ -214,7 +215,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 { - critical_section::with(|_| { + interrupt::free(|_| { if region_number >= self.region_numbers() { Err(SauError::RegionNumberTooBig) } else { diff --git a/cortex-m/src/peripheral/scb.rs b/cortex-m/src/peripheral/scb.rs index 46eb3a4e..ecf98e5a 100644 --- a/cortex-m/src/peripheral/scb.rs +++ b/cortex-m/src/peripheral/scb.rs @@ -170,28 +170,10 @@ impl SCB { /// Returns the active exception number #[inline] pub fn vect_active() -> VectActive { - let icsr = - unsafe { ptr::read_volatile(&(*SCB::PTR).icsr as *const _ as *const u32) } & 0x1FF; + let icsr = unsafe { ptr::read(&(*SCB::PTR).icsr as *const _ as *const u32) }; - match icsr as u16 { - 0 => VectActive::ThreadMode, - 2 => VectActive::Exception(Exception::NonMaskableInt), - 3 => VectActive::Exception(Exception::HardFault), - #[cfg(not(armv6m))] - 4 => VectActive::Exception(Exception::MemoryManagement), - #[cfg(not(armv6m))] - 5 => VectActive::Exception(Exception::BusFault), - #[cfg(not(armv6m))] - 6 => VectActive::Exception(Exception::UsageFault), - #[cfg(any(armv8m, native))] - 7 => VectActive::Exception(Exception::SecureFault), - 11 => VectActive::Exception(Exception::SVCall), - #[cfg(not(armv6m))] - 12 => VectActive::Exception(Exception::DebugMonitor), - 14 => VectActive::Exception(Exception::PendSV), - 15 => VectActive::Exception(Exception::SysTick), - irqn => VectActive::Interrupt { irqn: irqn - 16 }, - } + // NOTE(unsafe): Assume correctly selected target. + unsafe { VectActive::from(icsr as u8).unwrap_unchecked() } } } @@ -275,15 +257,15 @@ pub enum VectActive { /// Device specific exception (external interrupts) Interrupt { - /// Interrupt number. This number is always within half open range `[0, 512)` (9 bit) - irqn: u16, + /// Interrupt number. This number is always within half open range `[0, 240)` + irqn: u8, }, } impl VectActive { - /// Converts a vector number into `VectActive` + /// Converts a `byte` into `VectActive` #[inline] - pub fn from(vect_active: u16) -> Option { + pub fn from(vect_active: u8) -> Option { Some(match vect_active { 0 => VectActive::ThreadMode, 2 => VectActive::Exception(Exception::NonMaskableInt), @@ -301,7 +283,7 @@ impl VectActive { 12 => VectActive::Exception(Exception::DebugMonitor), 14 => VectActive::Exception(Exception::PendSV), 15 => VectActive::Exception(Exception::SysTick), - irqn if (16..512).contains(&irqn) => VectActive::Interrupt { irqn: irqn - 16 }, + irqn if irqn >= 16 => VectActive::Interrupt { irqn: irqn - 16 }, _ => return None, }) } @@ -362,7 +344,7 @@ impl SCB { let mut cbp = unsafe { CBP::new() }; // Disable I-cache - // NOTE(unsafe): We have synchronized access by &mut self + // NOTE(unsafe): We have synchronised access by &mut self unsafe { self.ccr.modify(|r| r & !SCB_CCR_IC_MASK) }; // Invalidate I-cache @@ -435,7 +417,7 @@ impl SCB { } // Turn off the D-cache - // NOTE(unsafe): We have synchronized access by &mut self + // NOTE(unsafe): We have synchronised access by &mut self unsafe { self.ccr.modify(|r| r & !SCB_CCR_DC_MASK) }; // Clean and invalidate whatever was left in it @@ -664,7 +646,10 @@ impl SCB { /// a runtime-dependent `panic!()` call. #[inline] pub unsafe fn invalidate_dcache_by_slice(&mut self, slice: &mut [T]) { - self.invalidate_dcache_by_address(slice.as_ptr() as usize, core::mem::size_of_val(slice)); + self.invalidate_dcache_by_address( + slice.as_ptr() as usize, + slice.len() * core::mem::size_of::(), + ); } /// Cleans D-cache by address. @@ -747,7 +732,10 @@ impl SCB { /// to main memory, overwriting whatever was in main memory. #[inline] pub fn clean_dcache_by_slice(&mut self, slice: &[T]) { - self.clean_dcache_by_address(slice.as_ptr() as usize, core::mem::size_of_val(slice)); + self.clean_dcache_by_address( + slice.as_ptr() as usize, + slice.len() * core::mem::size_of::(), + ); } /// Cleans and invalidates D-cache by address. @@ -832,26 +820,6 @@ impl SCB { } } -const SCB_SCR_SEVONPEND: u32 = 0x1 << 4; - -impl SCB { - /// Set the SEVONPEND bit in the SCR register - #[inline] - pub fn set_sevonpend(&mut self) { - unsafe { - self.scr.modify(|scr| scr | SCB_SCR_SEVONPEND); - } - } - - /// Clear the SEVONPEND bit in the SCR register - #[inline] - pub fn clear_sevonpend(&mut self) { - unsafe { - self.scr.modify(|scr| scr & !SCB_SCR_SEVONPEND); - } - } -} - const SCB_AIRCR_VECTKEY: u32 = 0x05FA << 16; const SCB_AIRCR_PRIGROUP_MASK: u32 = 0x7 << 8; const SCB_AIRCR_SYSRESETREQ: u32 = 1 << 2; diff --git a/cortex-m/src/peripheral/syst.rs b/cortex-m/src/peripheral/syst.rs index 9d6f2919..345acc2f 100644 --- a/cortex-m/src/peripheral/syst.rs +++ b/cortex-m/src/peripheral/syst.rs @@ -1,18 +1,4 @@ //! SysTick: System Timer -//! -//! # Example -//! -//! ```no_run -//! use cortex_m::peripheral::{Peripherals, SYST}; -//! -//! let core_periph = cortex_m::peripheral::Peripherals::take().unwrap(); -//! let mut syst = core_periph.SYST; -//! syst.set_reload(0xffffff); -//! syst.clear_current(); -//! syst.enable_counter(); -//! -//! let syst_value: u32 = SYST::get_current(); -//! ``` use volatile_register::{RO, RW}; @@ -53,7 +39,7 @@ const SYST_CALIB_NOREF: u32 = 1 << 31; impl SYST { /// Clears current value to 0 /// - /// After calling `clear_current()`, the next call to `has_wrapped()`, unless called after the reload time (if the counter is enabled), will return `false`. + /// After calling `clear_current()`, the next call to `has_wrapped()` will return `false`. #[inline] pub fn clear_current(&mut self) { unsafe { self.cvr.write(0) } diff --git a/cortex-m/src/peripheral/tpiu.rs b/cortex-m/src/peripheral/tpiu.rs index 14dd35ca..11cb79e9 100644 --- a/cortex-m/src/peripheral/tpiu.rs +++ b/cortex-m/src/peripheral/tpiu.rs @@ -4,9 +4,6 @@ use volatile_register::{RO, RW, WO}; -use crate::peripheral::TPIU; -use bitfield::bitfield; - /// Register block #[repr(C)] pub struct RegisterBlock { @@ -19,10 +16,10 @@ pub struct RegisterBlock { pub acpr: RW, reserved1: [u32; 55], /// Selected Pin Control - pub sppr: RW, + pub sppr: RW, reserved2: [u32; 132], /// Formatter and Flush Control - pub ffcr: RW, + pub ffcr: RW, reserved3: [u32; 810], /// Lock Access pub lar: WO, @@ -30,131 +27,5 @@ pub struct RegisterBlock { pub lsr: RO, reserved4: [u32; 4], /// TPIU Type - pub _type: RO, -} - -bitfield! { - /// Formatter and flush control register. - #[repr(C)] - #[derive(Clone, Copy)] - pub struct Ffcr(u32); - enfcont, set_enfcont: 1; -} - -bitfield! { - /// TPIU Type Register. - #[repr(C)] - #[derive(Clone, Copy)] - pub struct Type(u32); - u8, fifosz, _: 8, 6; - ptinvalid, _: 9; - mancvalid, _: 10; - nrzvalid, _: 11; -} - -bitfield! { - /// Selected pin protocol register. - #[repr(C)] - #[derive(Clone, Copy)] - pub struct Sppr(u32); - u8, txmode, set_txmode: 1, 0; -} - -/// The available protocols for the trace output. -#[repr(u8)] -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub enum TraceProtocol { - /// Parallel trace port mode - Parallel = 0b00, - /// Asynchronous SWO, using Manchester encoding - AsyncSWOManchester = 0b01, - /// Asynchronous SWO, using NRZ encoding - AsyncSWONRZ = 0b10, -} -impl core::convert::TryFrom for TraceProtocol { - type Error = (); - - /// Tries to convert from a `TXMODE` field value. Fails if the set mode is - /// unknown (and thus unpredictable). - #[inline] - fn try_from(value: u8) -> Result { - match value { - x if x == Self::Parallel as u8 => Ok(Self::Parallel), - x if x == Self::AsyncSWOManchester as u8 => Ok(Self::AsyncSWOManchester), - x if x == Self::AsyncSWONRZ as u8 => Ok(Self::AsyncSWONRZ), - _ => Err(()), // unknown and unpredictable mode - } - } -} - -/// The SWO options supported by the TPIU, and the mimimum size of the -/// FIFO output queue for trace data. -#[derive(Debug, Eq, PartialEq, Copy, Clone)] -pub struct SWOSupports { - /// Whether UART/NRZ encoding is supported for SWO. - pub nrz_encoding: bool, - /// Whether Manchester encoding is supported for SWO. - pub manchester_encoding: bool, - /// Whether parallel trace port operation is supported. - pub parallel_operation: bool, - /// The minimum implemented FIFO queue size of the TPIU for trace data. - pub min_queue_size: u8, -} - -impl TPIU { - /// Sets the prescaler value for a wanted baud rate of the Serial - /// Wire Output (SWO) in relation to a given asynchronous refernce - /// clock rate. - #[inline] - pub fn set_swo_baud_rate(&mut self, ref_clk_rate: u32, baud_rate: u32) { - unsafe { - self.acpr.write((ref_clk_rate / baud_rate) - 1); - } - } - - /// The used protocol for the trace output. Return `None` if an - /// unknown (and thus unpredicable mode) is configured by means - /// other than - /// [`trace_output_protocol`](Self::set_trace_output_protocol). - #[inline] - pub fn trace_output_protocol(&self) -> Option { - self.sppr.read().txmode().try_into().ok() - } - - /// Sets the used protocol for the trace output. - #[inline] - pub fn set_trace_output_protocol(&mut self, proto: TraceProtocol) { - unsafe { - self.sppr.modify(|mut r| { - r.set_txmode(proto as u8); - r - }); - } - } - - /// Whether to enable the formatter. If disabled, only ITM and DWT - /// trace sources are passed through. Data from the ETM is - /// discarded. - #[inline] - pub fn enable_continuous_formatting(&mut self, bit: bool) { - unsafe { - self.ffcr.modify(|mut r| { - r.set_enfcont(bit); - r - }); - } - } - - /// Reads the supported trace output modes and the minimum size of - /// the TPIU FIFO queue for trace data. - #[inline] - pub fn swo_supports() -> SWOSupports { - let _type = unsafe { (*Self::PTR)._type.read() }; - SWOSupports { - nrz_encoding: _type.nrzvalid(), - manchester_encoding: _type.mancvalid(), - parallel_operation: !_type.ptinvalid(), - min_queue_size: _type.fifosz(), - } - } + pub _type: RO, } diff --git a/cortex-m/src/prelude.rs b/cortex-m/src/prelude.rs new file mode 100644 index 00000000..bc47cc02 --- /dev/null +++ b/cortex-m/src/prelude.rs @@ -0,0 +1,3 @@ +//! Prelude + +pub use embedded_hal::prelude::*; diff --git a/cortex-m/src/register/apsr.rs b/cortex-m/src/register/apsr.rs index edb87373..e83435ce 100644 --- a/cortex-m/src/register/apsr.rs +++ b/cortex-m/src/register/apsr.rs @@ -1,8 +1,5 @@ //! Application Program Status Register -#[cfg(cortex_m)] -use core::arch::asm; - /// Application Program Status Register #[derive(Clone, Copy, Debug)] pub struct Apsr { @@ -48,10 +45,10 @@ impl Apsr { } /// Reads the CPU register -#[cfg(cortex_m)] +/// +/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. #[inline] pub fn read() -> Apsr { - let bits; - unsafe { asm!("mrs {}, APSR", out(reg) bits, options(nomem, nostack, preserves_flags)) }; + let bits: u32 = call_asm!(__apsr_r() -> u32); Apsr { bits } } diff --git a/cortex-m/src/register/basepri.rs b/cortex-m/src/register/basepri.rs index cffb3791..07084cd2 100644 --- a/cortex-m/src/register/basepri.rs +++ b/cortex-m/src/register/basepri.rs @@ -1,42 +1,24 @@ //! Base Priority Mask Register -#[cfg(cortex_m)] -use core::arch::asm; - /// Reads the CPU register -#[cfg(cortex_m)] #[inline] pub fn read() -> u8 { - let r; - unsafe { asm!("mrs {}, BASEPRI", out(reg) r, options(nomem, nostack, preserves_flags)) }; - r + call_asm!(__basepri_r() -> u8) } /// 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")] { - 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), - ); + call_asm!(__basepri_w_cm7_r0p1(basepri: u8)); } #[cfg(not(feature = "cm7-r0p1"))] { - asm!("msr BASEPRI, {}", in(reg) basepri, options(nomem, nostack, preserves_flags)); + call_asm!(__basepri_w(basepri: u8)); } } diff --git a/cortex-m/src/register/basepri_max.rs b/cortex-m/src/register/basepri_max.rs index 2881c4fe..cea38383 100644 --- a/cortex-m/src/register/basepri_max.rs +++ b/cortex-m/src/register/basepri_max.rs @@ -1,8 +1,5 @@ //! Base Priority Mask Register (conditional write) -#[cfg(cortex_m)] -use core::arch::asm; - /// Writes to BASEPRI *if* /// /// - `basepri != 0` AND `basepri::read() == 0`, OR @@ -10,31 +7,15 @@ use core::arch::asm; /// /// **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")] { - 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), - ); - } + call_asm!(__basepri_max_cm7_r0p1(basepri: u8)); } #[cfg(not(feature = "cm7-r0p1"))] { - unsafe { - asm!("msr BASEPRI_MAX, {}", in(reg) basepri, options(nomem, nostack, preserves_flags)); - } + call_asm!(__basepri_max(basepri: u8)); } } diff --git a/cortex-m/src/register/control.rs b/cortex-m/src/register/control.rs index d7819139..a991625b 100644 --- a/cortex-m/src/register/control.rs +++ b/cortex-m/src/register/control.rs @@ -1,10 +1,5 @@ //! 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 { @@ -155,29 +150,15 @@ impl Fpca { } /// Reads the CPU register -#[cfg(cortex_m)] #[inline] pub fn read() -> Control { - let bits; - unsafe { asm!("mrs {}, CONTROL", out(reg) bits, options(nomem, nostack, preserves_flags)) }; + let bits: u32 = call_asm!(__control_r() -> u32); Control { bits } } /// Writes to the CPU register. -#[cfg(cortex_m)] #[inline] pub unsafe fn write(control: Control) { let control = control.bits(); - - // 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); + call_asm!(__control_w(control: u32)); } diff --git a/cortex-m/src/register/faultmask.rs b/cortex-m/src/register/faultmask.rs index 1d327095..e57fa28d 100644 --- a/cortex-m/src/register/faultmask.rs +++ b/cortex-m/src/register/faultmask.rs @@ -1,8 +1,5 @@ //! Fault Mask Register -#[cfg(cortex_m)] -use core::arch::asm; - /// All exceptions are ... #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Faultmask { @@ -27,11 +24,9 @@ impl Faultmask { } /// Reads the CPU register -#[cfg(cortex_m)] #[inline] pub fn read() -> Faultmask { - let r: u32; - unsafe { asm!("mrs {}, FAULTMASK", out(reg) r, options(nomem, nostack, preserves_flags)) }; + let r: u32 = call_asm!(__faultmask_r() -> u32); if r & (1 << 0) == (1 << 0) { Faultmask::Inactive } else { diff --git a/cortex-m/src/register/fpscr.rs b/cortex-m/src/register/fpscr.rs index bffed6cd..68692c73 100644 --- a/cortex-m/src/register/fpscr.rs +++ b/cortex-m/src/register/fpscr.rs @@ -1,7 +1,5 @@ //! Floating-point Status Control Register -use core::arch::asm; - /// Floating-point Status Control Register #[derive(Clone, Copy, Debug)] pub struct Fpscr { @@ -295,8 +293,7 @@ impl RMode { /// Read the FPSCR register #[inline] pub fn read() -> Fpscr { - let r; - unsafe { asm!("vmrs {}, fpscr", out(reg) r, options(nomem, nostack, preserves_flags)) }; + let r: u32 = call_asm!(__fpscr_r() -> u32); Fpscr::from_bits(r) } @@ -304,5 +301,5 @@ pub fn read() -> Fpscr { #[inline] pub unsafe fn write(fpscr: Fpscr) { let fpscr = fpscr.bits(); - asm!("vmsr fpscr, {}", in(reg) fpscr, options(nomem, nostack)); + call_asm!(__fpscr_w(fpscr: u32)); } diff --git a/cortex-m/src/register/lr.rs b/cortex-m/src/register/lr.rs index 5752ff72..0da35d9f 100644 --- a/cortex-m/src/register/lr.rs +++ b/cortex-m/src/register/lr.rs @@ -1,18 +1,21 @@ //! Link register -#[cfg(cortex_m)] -use core::arch::asm; - /// Reads the CPU register /// -/// Note that this function can't be used reliably: The value returned at least depends -/// on whether the compiler chooses to inline the function or not. -#[cfg(cortex_m)] +/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. #[inline] pub fn read() -> u32 { - let r; - unsafe { asm!("mov {}, lr", out(reg) r, options(nomem, nostack, preserves_flags)) }; - r + call_asm!(__lr_r() -> u32) } -// No `write` function for the LR register, as it can't be used soundly. +/// Writes `bits` to the CPU register +/// +/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. +/// +/// # Safety +/// This function can't be used soundly. +#[inline] +#[deprecated = "This function can't be used soundly."] +pub unsafe fn write(bits: u32) { + call_asm!(__lr_w(bits: u32)); +} diff --git a/cortex-m/src/register/mod.rs b/cortex-m/src/register/mod.rs index aee7d213..48d157a5 100644 --- a/cortex-m/src/register/mod.rs +++ b/cortex-m/src/register/mod.rs @@ -56,8 +56,13 @@ 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/cortex-m/src/register/msp.rs b/cortex-m/src/register/msp.rs index 22ce7d97..bccc2ae8 100644 --- a/cortex-m/src/register/msp.rs +++ b/cortex-m/src/register/msp.rs @@ -1,27 +1,16 @@ //! Main Stack Pointer -#[cfg(cortex_m)] -use core::arch::asm; - /// Reads the CPU register -#[cfg(cortex_m)] #[inline] pub fn read() -> u32 { - let r; - unsafe { asm!("mrs {}, MSP", out(reg) r, options(nomem, nostack, preserves_flags)) }; - r + call_asm!(__msp_r() -> u32) } /// 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) { - // 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)); + call_asm!(__msp_w(bits: u32)); } /// Reads the Non-Secure CPU register from Secure state. @@ -30,9 +19,7 @@ pub unsafe fn write(bits: u32) { #[cfg(armv8m)] #[inline] pub fn read_ns() -> u32 { - let r; - unsafe { asm!("mrs {}, MSP_NS", out(reg) r, options(nomem, nostack, preserves_flags)) }; - r + call_asm!(__msp_ns_r() -> u32) } /// Writes `bits` to the Non-Secure CPU register from Secure state. @@ -41,5 +28,5 @@ pub fn read_ns() -> u32 { #[cfg(armv8m)] #[inline] pub unsafe fn write_ns(bits: u32) { - asm!("msr MSP_NS, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); + call_asm!(__msp_ns_w(bits: u32)); } diff --git a/cortex-m/src/register/msplim.rs b/cortex-m/src/register/msplim.rs index 7b45b33a..ac6f9ed6 100644 --- a/cortex-m/src/register/msplim.rs +++ b/cortex-m/src/register/msplim.rs @@ -1,17 +1,13 @@ //! Main Stack Pointer Limit Register -use core::arch::asm; - /// Reads the CPU register #[inline] pub fn read() -> u32 { - let r; - unsafe { asm!("mrs {}, MSPLIM", out(reg) r, options(nomem, nostack, preserves_flags)) }; - r + call_asm!(__msplim_r() -> u32) } /// Writes `bits` to the CPU register #[inline] pub unsafe fn write(bits: u32) { - asm!("msr MSPLIM, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); + call_asm!(__msplim_w(bits: u32)) } diff --git a/cortex-m/src/register/pc.rs b/cortex-m/src/register/pc.rs index 34606641..0b33629a 100644 --- a/cortex-m/src/register/pc.rs +++ b/cortex-m/src/register/pc.rs @@ -1,20 +1,17 @@ //! Program counter -#[cfg(cortex_m)] -use core::arch::asm; - /// Reads the CPU register -#[cfg(cortex_m)] +/// +/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. #[inline] pub fn read() -> u32 { - let r; - unsafe { asm!("mov {}, pc", out(reg) r, options(nomem, nostack, preserves_flags)) }; - r + call_asm!(__pc_r() -> u32) } /// Writes `bits` to the CPU register -#[cfg(cortex_m)] +/// +/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. #[inline] pub unsafe fn write(bits: u32) { - asm!("mov pc, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); + call_asm!(__pc_w(bits: u32)); } diff --git a/cortex-m/src/register/primask.rs b/cortex-m/src/register/primask.rs index 58b8c287..842ca49a 100644 --- a/cortex-m/src/register/primask.rs +++ b/cortex-m/src/register/primask.rs @@ -1,10 +1,5 @@ //! Priority mask register -#[cfg(cortex_m)] -use core::arch::asm; -#[cfg(cortex_m)] -use core::sync::atomic::{compiler_fence, Ordering}; - /// All exceptions with configurable priority are ... #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Primask { @@ -28,42 +23,13 @@ impl Primask { } } -/// Reads the prioritizable interrupt mask -#[cfg(cortex_m)] +/// Reads the CPU register #[inline] pub fn read() -> Primask { - if read_raw() & (1 << 0) == (1 << 0) { + let r: u32 = call_asm!(__primask_r() -> u32); + if r & (1 << 0) == (1 << 0) { Primask::Inactive } else { Primask::Active } } - -/// Reads the entire PRIMASK register -/// Note that bits [31:1] are reserved and UNK (Unknown) -#[cfg(cortex_m)] -#[inline] -pub fn read_raw() -> u32 { - let r: u32; - unsafe { asm!("mrs {}, PRIMASK", out(reg) r, options(nomem, nostack, preserves_flags)) }; - r -} - -/// Writes the entire PRIMASK register -/// Note that bits [31:1] are reserved and SBZP (Should-Be-Zero-or-Preserved) -/// -/// # Safety -/// -/// This method is unsafe as other unsafe code may rely on interrupts remaining disabled, for -/// example during a critical section, and being able to safely re-enable them would lead to -/// undefined behaviour. Do not call this function in a context where interrupts are expected to -/// remain disabled -- for example, in the midst of a critical section or `interrupt::free()` call. -#[cfg(cortex_m)] -#[inline] -pub unsafe fn write_raw(r: u32) { - // Ensure no preceeding memory accesses are reordered to after interrupts are possibly enabled. - compiler_fence(Ordering::SeqCst); - unsafe { asm!("msr PRIMASK, {}", in(reg) r, options(nomem, nostack, preserves_flags)) }; - // Ensure no subsequent memory accesses are reordered to before interrupts are possibly disabled. - compiler_fence(Ordering::SeqCst); -} diff --git a/cortex-m/src/register/psp.rs b/cortex-m/src/register/psp.rs index c8f53b98..0bca22c3 100644 --- a/cortex-m/src/register/psp.rs +++ b/cortex-m/src/register/psp.rs @@ -1,22 +1,13 @@ //! Process Stack Pointer -#[cfg(cortex_m)] -use core::arch::asm; - /// Reads the CPU register -#[cfg(cortex_m)] #[inline] pub fn read() -> u32 { - let r; - unsafe { asm!("mrs {}, PSP", out(reg) r, options(nomem, nostack, preserves_flags)) }; - r + call_asm!(__psp_r() -> u32) } /// Writes `bits` to the CPU register -#[cfg(cortex_m)] #[inline] pub unsafe fn write(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)); + call_asm!(__psp_w(bits: u32)) } diff --git a/cortex-m/src/register/psplim.rs b/cortex-m/src/register/psplim.rs index 832f9c67..8ee1e945 100644 --- a/cortex-m/src/register/psplim.rs +++ b/cortex-m/src/register/psplim.rs @@ -1,17 +1,13 @@ //! Process Stack Pointer Limit Register -use core::arch::asm; - /// Reads the CPU register #[inline] pub fn read() -> u32 { - let r; - unsafe { asm!("mrs {}, PSPLIM", out(reg) r, options(nomem, nostack, preserves_flags)) }; - r + call_asm!(__psplim_r() -> u32) } /// Writes `bits` to the CPU register #[inline] pub unsafe fn write(bits: u32) { - asm!("msr PSPLIM, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); + call_asm!(__psplim_w(bits: u32)) } diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 0abe8e19..b5b5c5f4 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -10,6 +10,6 @@ name = "ci" harness = false [dependencies] -ar = "0.9.0" -cortex-m = { path = "../cortex-m", features = ["serde", "std"] } +ar = "0.8.0" +cortex-m = { path = "../", features = ["serde", "std"] } serde_json = "1" diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 9d966868..c3d83560 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -1,8 +1,117 @@ //! `cargo xtask` automation. //! //! Please refer to 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); +} -use std::process::Command; +// `--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"], + ), +]; pub fn install_targets(targets: &mut dyn Iterator, toolchain: Option<&str>) { let mut rustup = Command::new("rustup"); @@ -16,9 +125,93 @@ pub fn install_targets(targets: &mut dyn Iterator, 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::>(); + let after = files_after.keys().collect::>(); + 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}; + use cortex_m::peripheral::scb::VectActive; // check serde { @@ -27,29 +220,12 @@ pub fn check_host_side() { let deser_v: VectActive = serde_json::from_str(&json).expect("Failed to deserialize VectActive"); assert_eq!(deser_v, v); - - let lts = LocalTimestampOptions::EnabledDiv4; - let json = serde_json::to_string(<s).expect("Failed to serialize LocalTimestampOptions"); - let deser_lts: LocalTimestampOptions = - serde_json::from_str(&json).expect("Failed to deserilaize LocalTimestampOptions"); - assert_eq!(deser_lts, lts); } // check PartialOrd { let a = VectActive::from(19).unwrap(); let b = VectActive::from(20).unwrap(); - assert!(a < b); - } - - // check TryFrom - { - use core::convert::TryInto; - use std::convert::TryFrom; - - let lts: LocalTimestampOptions = (16_u8).try_into().unwrap(); - assert_eq!(lts, LocalTimestampOptions::EnabledDiv16); - - assert!(LocalTimestampOptions::try_from(42).is_err()); + assert_eq!(a < b, true); } } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 4673a455..3e4b3942 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,14 +1,18 @@ use std::{env, process}; -use xtask::check_host_side; +use xtask::{assemble_blobs, check_blobs, check_host_side}; fn main() { - let subcommand = env::args().nth(1); - match subcommand.as_deref() { + let subcommand = env::args().skip(1).next(); + match subcommand.as_ref().map(|s| &**s) { + Some("assemble") => assemble_blobs(), + Some("check-blobs") => check_blobs(), Some("check-host-side") => check_host_side(), _ => { eprintln!("usage: cargo xtask "); 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 3c3ef990..b7bfc3dc 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_host_side, install_targets}; +use xtask::{check_blobs, check_host_side, install_targets}; /// List of all compilation targets we support. /// @@ -27,16 +27,9 @@ static NON_BASE_TARGETS: &[&str] = &[ fn build(package: &str, target: &str, features: &[&str]) { println!("building {} for {} {:?}", package, target, features); let mut cargo = Command::new("cargo"); - cargo.args(["build", "-p", package, "--target", target]); + cargo.args(&["build", "-p", package, "--target", target]); for feat in features { - 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.args(&["--features", *feat]); } // Cargo features don't work right when invoked from the workspace root, so change to the @@ -51,13 +44,13 @@ fn build(package: &str, target: &str, features: &[&str]) { #[rustfmt::skip] static PACKAGE_FEATURES: &[(&str, &[&str], &[&str])] = &[ - ("cortex-m", ALL_TARGETS, &["cm7-r0p1"]), - ("cortex-m-semihosting", ALL_TARGETS, &["no-semihosting", "jlink-quirks"]), - ("panic-semihosting", ALL_TARGETS, &["exit", "jlink-quirks"]), + ("cortex-m", ALL_TARGETS, &["inline-asm", "cm7-r0p1", "critical-section-single-core"]), // 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"]), ("panic-itm", NON_BASE_TARGETS, &[]), ]; -fn check_crates_build(_is_nightly: bool) { +fn check_crates_build(is_nightly: bool, is_msrv: bool) { // Build all crates for each supported target. for (package, targets, all_features) in PACKAGE_FEATURES { for target in *targets { @@ -65,8 +58,11 @@ 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, } }; @@ -77,7 +73,7 @@ fn check_crates_build(_is_nightly: bool) { let used_features = &*all_features .iter() .copied() - .filter(|feat| should_use_feature(feat)) + .filter(|feat| should_use_feature(*feat)) .collect::>(); // (note: we don't test with default features disabled, since we don't use them yet) @@ -102,10 +98,14 @@ 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"); + let is_msrv = str::from_utf8(&output.stdout).unwrap().contains("1.59"); - check_crates_build(is_nightly); + check_crates_build(is_nightly, is_msrv); // Check host-side applications of the crate. check_host_side(); From 620d3ef3292b8074b2a7764806ac9b1b99fc6649 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Tue, 15 Jul 2025 21:07:02 +0100 Subject: [PATCH 2/9] Fix-up xtask now that cortex-m crate is in a sub-folder. Binaries are unchanged. --- xtask/Cargo.toml | 2 +- xtask/src/lib.rs | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index b5b5c5f4..73047eef 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -11,5 +11,5 @@ harness = false [dependencies] ar = "0.8.0" -cortex-m = { path = "../", features = ["serde", "std"] } +cortex-m = { path = "../cortex-m", features = ["serde", "std"] } serde_json = "1" diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index c3d83560..b4f41526 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -7,10 +7,11 @@ use std::collections::BTreeMap; use std::env::current_dir; use std::fs::{self, File}; +use std::path::Path; use std::process::{Command, Stdio}; fn toolchain() -> String { - fs::read_to_string("asm-toolchain") + fs::read_to_string("cortex-m/asm-toolchain") .unwrap() .trim() .to_string() @@ -45,7 +46,11 @@ fn assemble_really(target: &str, cfgs: &[&str], plugin_lto: bool) { // 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(); + let mut dir = current_dir() + .unwrap() + .join("cortex-m") + .as_os_str() + .to_os_string(); dir.push("=."); cmd.arg("--remap-path-prefix").arg(dir); @@ -70,17 +75,21 @@ fn assemble_really(target: &str, cfgs: &[&str], plugin_lto: bool) { // Pass output and input file. cmd.arg("-o").arg(&obj_file); cmd.arg("asm/lib.rs"); + cmd.current_dir("cortex-m"); println!("{:?}", cmd); let status = cmd.status().unwrap(); assert!(status.success()); + let full_obj_file_path = Path::new("cortex-m").join(&obj_file); + // Archive `target.o` -> `bin/target.a`. - let mut builder = ar::Builder::new(File::create(format!("bin/{}.a", file_stub)).unwrap()); + let mut builder = + ar::Builder::new(File::create(format!("cortex-m/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(); + let file = fs::read(&full_obj_file_path).unwrap(); builder .append( &ar::Header::new(obj_file.as_bytes().to_vec(), file.len() as u64), @@ -88,7 +97,7 @@ fn assemble_really(target: &str, cfgs: &[&str], plugin_lto: bool) { ) .unwrap(); - fs::remove_file(&obj_file).unwrap(); + fs::remove_file(&full_obj_file_path).unwrap(); } fn assemble(target: &str, cfgs: &[&str]) { @@ -157,7 +166,7 @@ pub fn assemble_blobs() { 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() { + for entry in fs::read_dir("cortex-m/bin").unwrap() { let entry = entry.unwrap(); if entry.path().extension().unwrap() == "a" { files_before.insert( @@ -176,7 +185,7 @@ pub fn check_blobs() { assemble_blobs(); let mut files_after = BTreeMap::new(); - for entry in fs::read_dir("bin").unwrap() { + for entry in fs::read_dir("cortex-m/bin").unwrap() { let entry = entry.unwrap(); if entry.path().extension().unwrap() == "a" { files_after.insert( From a5c9c02f4ffcc556cd5463ffb4cf82d85a12cdcb Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Tue, 15 Jul 2025 21:21:00 +0100 Subject: [PATCH 3/9] Run clippy from MSRV. I don't want lints that tell me to fix things that don't work in the MSRV. Unfortunately I also have to unify the MSRV for cortex-m and cortex-m-rt, raising the cortex-m MSRV to 1.61. --- .github/workflows/clippy.yml | 2 +- cortex-m/CHANGELOG.md | 2 ++ cortex-m/Cargo.toml | 1 + cortex-m/README.md | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index d0883485..47bc5b28 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@1.61 with: components: clippy - run: cargo clippy --all --features cortex-m/critical-section-single-core -- --deny warnings diff --git a/cortex-m/CHANGELOG.md b/cortex-m/CHANGELOG.md index ca9609ce..7508e6cb 100644 --- a/cortex-m/CHANGELOG.md +++ b/cortex-m/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- MSRV is 1.61 to match cortex-m-rt crate + ## [v0.7.7] - 2023-01-03 - Add missing documentation for `critical-section-single-core` feature added diff --git a/cortex-m/Cargo.toml b/cortex-m/Cargo.toml index dca194ae..2858631f 100644 --- a/cortex-m/Cargo.toml +++ b/cortex-m/Cargo.toml @@ -14,6 +14,7 @@ repository = "https://github.com/rust-embedded/cortex-m" version = "0.7.7" edition = "2018" links = "cortex-m" # prevent multiple versions of this crate to be linked together +rust-version = "1.61" [dependencies] bare-metal = { version = "0.2.4", features = ["const-fn"] } diff --git a/cortex-m/README.md b/cortex-m/README.md index b2af4a7c..6a7f98c2 100644 --- a/cortex-m/README.md +++ b/cortex-m/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.59 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.61 and up. It might compile with older versions but that may change in any new patch release. ## License From b7101e5eab5104a88241db0734c2601527b7336c Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Tue, 15 Jul 2025 21:28:47 +0100 Subject: [PATCH 4/9] Crates no longer have inline-asm feature so we shouldn't test it. --- xtask/tests/ci.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xtask/tests/ci.rs b/xtask/tests/ci.rs index b7bfc3dc..6e4023d1 100644 --- a/xtask/tests/ci.rs +++ b/xtask/tests/ci.rs @@ -45,8 +45,8 @@ fn build(package: &str, target: &str, features: &[&str]) { #[rustfmt::skip] static PACKAGE_FEATURES: &[(&str, &[&str], &[&str])] = &[ ("cortex-m", ALL_TARGETS, &["inline-asm", "cm7-r0p1", "critical-section-single-core"]), // 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-semihosting", ALL_TARGETS, &["no-semihosting", "jlink-quirks"]), + ("panic-semihosting", ALL_TARGETS, &["exit", "jlink-quirks"]), ("panic-itm", NON_BASE_TARGETS, &[]), ]; From 069269c52c7128f4e8e5b14b7967cef09a503223 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Tue, 15 Jul 2025 21:34:08 +0100 Subject: [PATCH 5/9] Stop warning about these non-feature cfgs. --- cortex-m/build.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cortex-m/build.rs b/cortex-m/build.rs index 23ceebad..37af277c 100644 --- a/cortex-m/build.rs +++ b/cortex-m/build.rs @@ -28,6 +28,18 @@ fn main() { println!("cargo:rustc-link-search={}", out_dir.display()); } + println!("cargo:rustc-check-cfg=cfg(armv6m)"); + println!("cargo:rustc-check-cfg=cfg(armv7em)"); + println!("cargo:rustc-check-cfg=cfg(armv7m)"); + println!("cargo:rustc-check-cfg=cfg(armv7m)"); + println!("cargo:rustc-check-cfg=cfg(armv8m)"); + println!("cargo:rustc-check-cfg=cfg(armv8m)"); + println!("cargo:rustc-check-cfg=cfg(armv8m_base)"); + println!("cargo:rustc-check-cfg=cfg(armv8m_main)"); + println!("cargo:rustc-check-cfg=cfg(cortex_m)"); + println!("cargo:rustc-check-cfg=cfg(has_fpu)"); + println!("cargo:rustc-check-cfg=cfg(native)"); + if target.starts_with("thumbv6m-") { println!("cargo:rustc-cfg=cortex_m"); println!("cargo:rustc-cfg=armv6m"); From 0c5d85a12e95cc1bfd7dfaa180cb87bec2f2d130 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Tue, 15 Jul 2025 21:34:42 +0100 Subject: [PATCH 6/9] Fix cm7 / feature="cm7" mix-up OK, that seems to be a genuine bug that this annoying new feature caught. --- cortex-m/src/peripheral/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cortex-m/src/peripheral/mod.rs b/cortex-m/src/peripheral/mod.rs index d8fd2d46..4fae295a 100644 --- a/cortex-m/src/peripheral/mod.rs +++ b/cortex-m/src/peripheral/mod.rs @@ -62,7 +62,7 @@ use core::ops; use crate::interrupt; -#[cfg(cm7)] +#[cfg(feature = "cm7")] pub mod ac; #[cfg(not(armv6m))] pub mod cbp; @@ -96,7 +96,7 @@ mod test; #[allow(clippy::manual_non_exhaustive)] pub struct Peripherals { /// Cortex-M7 TCM and cache access control. - #[cfg(cm7)] + #[cfg(feature = "cm7")] pub AC: AC, /// Cache and branch predictor maintenance operations. @@ -180,7 +180,7 @@ impl Peripherals { TAKEN = true; Peripherals { - #[cfg(cm7)] + #[cfg(feature = "cm7")] AC: AC { _marker: PhantomData, }, @@ -232,15 +232,15 @@ impl Peripherals { } /// Access control -#[cfg(cm7)] +#[cfg(feature = "cm7")] pub struct AC { _marker: PhantomData<*const ()>, } -#[cfg(cm7)] +#[cfg(feature = "cm7")] unsafe impl Send for AC {} -#[cfg(cm7)] +#[cfg(feature = "cm7")] impl AC { /// Pointer to the register block pub const PTR: *const self::ac::RegisterBlock = 0xE000_EF90 as *const _; From 4b6e7f2740faa4d0d6d15e4cdf5054a6897ed0bb Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Tue, 15 Jul 2025 21:38:40 +0100 Subject: [PATCH 7/9] Fix the testsuite to use the interrupt::free syntax we now have. --- testsuite/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testsuite/src/main.rs b/testsuite/src/main.rs index d742c160..ed2b1588 100644 --- a/testsuite/src/main.rs +++ b/testsuite/src/main.rs @@ -77,8 +77,8 @@ mod tests { #[test] fn interrupt_free_nesting() { EXCEPTION_FLAG.store(false, Ordering::SeqCst); - cortex_m::interrupt::free(|| { - cortex_m::interrupt::free(|| { + cortex_m::interrupt::free(|_| { + cortex_m::interrupt::free(|_| { cortex_m::peripheral::SCB::set_pendsv(); assert!(!EXCEPTION_FLAG.load(Ordering::SeqCst)); }); From cba11784a7562e67ddb4726a318175e6c348f60d Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Wed, 16 Jul 2025 21:14:46 +0100 Subject: [PATCH 8/9] Replace use of static mut in cortex-m-semihosting. Will avoid a bunch of clippy warnings that are hard to get rid of in both the MSRV and latest stable. --- cortex-m-semihosting/src/export.rs | 67 ++++++++++++++++++------------ 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/cortex-m-semihosting/src/export.rs b/cortex-m-semihosting/src/export.rs index 03604bf0..76709302 100644 --- a/cortex-m-semihosting/src/export.rs +++ b/cortex-m-semihosting/src/export.rs @@ -1,53 +1,68 @@ //! IMPLEMENTATION DETAILS USED BY MACROS -// This must be replaced by a different solution before rust edition 2024 -// https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html -#![allow(static_mut_refs)] - +use core::cell::RefCell; use core::fmt::{self, Write}; use crate::hio::{self, HostStream}; -static mut HSTDOUT: Option = None; +static HSTDOUT: critical_section::Mutex>> = + critical_section::Mutex::new(RefCell::new(None)); pub fn hstdout_str(s: &str) { - let _result = critical_section::with(|_| unsafe { - if HSTDOUT.is_none() { - HSTDOUT = Some(hio::hstdout()?); + critical_section::with(|cs| { + let mut hstdout_opt = HSTDOUT.borrow_ref_mut(cs); + if hstdout_opt.is_none() { + if let Ok(hstdout) = hio::hstdout() { + hstdout_opt.replace(hstdout); + } else { + return; + } } - - HSTDOUT.as_mut().unwrap().write_str(s).map_err(drop) + let hstdout = hstdout_opt.as_mut().unwrap(); + let _ = hstdout.write_str(s); }); } pub fn hstdout_fmt(args: fmt::Arguments) { - let _result = critical_section::with(|_| unsafe { - if HSTDOUT.is_none() { - HSTDOUT = Some(hio::hstdout()?); + critical_section::with(|cs| { + let mut hstdout_opt = HSTDOUT.borrow_ref_mut(cs); + if hstdout_opt.is_none() { + if let Ok(hstdout) = hio::hstdout() { + hstdout_opt.replace(hstdout); + } else { + return; + } } - - HSTDOUT.as_mut().unwrap().write_fmt(args).map_err(drop) + let hstdout = hstdout_opt.as_mut().unwrap(); + let _ = hstdout.write_fmt(args); }); } -static mut HSTDERR: Option = None; +static HSTDERR: critical_section::Mutex>> = + critical_section::Mutex::new(RefCell::new(None)); pub fn hstderr_str(s: &str) { - let _result = critical_section::with(|_| unsafe { - if HSTDERR.is_none() { - HSTDERR = Some(hio::hstderr()?); + critical_section::with(|cs| { + let mut hstderr_opt = HSTDERR.borrow_ref_mut(cs); + if let Ok(hstderr) = hio::hstderr() { + hstderr_opt.replace(hstderr); + } else { + return; } - - HSTDERR.as_mut().unwrap().write_str(s).map_err(drop) + let hstderr = hstderr_opt.as_mut().unwrap(); + let _ = hstderr.write_str(s); }); } pub fn hstderr_fmt(args: fmt::Arguments) { - let _result = critical_section::with(|_| unsafe { - if HSTDERR.is_none() { - HSTDERR = Some(hio::hstderr()?); + critical_section::with(|cs| { + let mut hstderr_opt = HSTDERR.borrow_ref_mut(cs); + if let Ok(hstderr) = hio::hstderr() { + hstderr_opt.replace(hstderr); + } else { + return; } - - HSTDERR.as_mut().unwrap().write_fmt(args).map_err(drop) + let hstderr = hstderr_opt.as_mut().unwrap(); + let _ = hstderr.write_fmt(args); }); } From ba86f7b0d83c718be63cee012d452e979dfb62f8 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Wed, 16 Jul 2025 21:14:57 +0100 Subject: [PATCH 9/9] Resolve more clippy lints. --- xtask/src/lib.rs | 2 +- xtask/src/main.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index b4f41526..ffcabba4 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -235,6 +235,6 @@ pub fn check_host_side() { { let a = VectActive::from(19).unwrap(); let b = VectActive::from(20).unwrap(); - assert_eq!(a < b, true); + assert!(a < b); } } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 3e4b3942..26dce31b 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -2,8 +2,8 @@ use std::{env, process}; use xtask::{assemble_blobs, check_blobs, check_host_side}; fn main() { - let subcommand = env::args().skip(1).next(); - match subcommand.as_ref().map(|s| &**s) { + 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(),