aboutsummaryrefslogtreecommitdiff
path: root/asm
diff options
context:
space:
mode:
Diffstat (limited to 'asm')
-rw-r--r--asm/inline.rs345
-rw-r--r--asm/lib.rs134
2 files changed, 479 insertions, 0 deletions
diff --git a/asm/inline.rs b/asm/inline.rs
new file mode 100644
index 0000000..67e4925
--- /dev/null
+++ b/asm/inline.rs
@@ -0,0 +1,345 @@
+//! 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.
+
+#[inline(always)]
+pub unsafe fn __bkpt() {
+ asm!("bkpt");
+}
+
+#[inline(always)]
+pub unsafe fn __control_r() -> u32 {
+ let r;
+ asm!("mrs {}, CONTROL", out(reg) r);
+ r
+}
+
+#[inline(always)]
+pub unsafe fn __control_w(w: u32) {
+ asm!("msr CONTROL, {}", in(reg) w);
+}
+
+#[inline(always)]
+pub unsafe fn __cpsid() {
+ asm!("cpsid i");
+}
+
+#[inline(always)]
+pub unsafe fn __cpsie() {
+ asm!("cpsie i");
+}
+
+#[inline(always)]
+pub unsafe fn __delay(cyc: u32) {
+ asm!("
+ 1:
+ nop
+ subs {}, #1
+ bne 1b
+ // Branch to 1 instead of delay does not generate R_ARM_THM_JUMP8 relocation, which breaks
+ // linking on the thumbv6m-none-eabi target
+ ", in(reg) cyc);
+}
+
+// FIXME do we need compiler fences here or should we expect them in the caller?
+#[inline(always)]
+pub unsafe fn __dmb() {
+ asm!("dmb 0xF");
+}
+
+#[inline(always)]
+pub unsafe fn __dsb() {
+ asm!("dsb 0xF");
+}
+
+#[inline(always)]
+pub unsafe fn __isb() {
+ asm!("isb 0xF");
+}
+
+#[inline(always)]
+pub unsafe fn __msp_r() -> u32 {
+ let r;
+ asm!("mrs {}, MSP", out(reg) r);
+ r
+}
+
+#[inline(always)]
+pub unsafe fn __msp_w(val: u32) {
+ asm!("msr MSP, {}", in(reg) val);
+}
+
+// NOTE: No FFI shim, this requires inline asm.
+#[inline(always)]
+pub unsafe fn __apsr_r() -> u32 {
+ let r;
+ asm!("mrs {}, APSR", out(reg) r);
+ 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");
+}
+
+// NOTE: No FFI shim, this requires inline asm.
+#[inline(always)]
+pub unsafe fn __pc_r() -> u32 {
+ let r;
+ asm!("mov {}, R15", out(reg) r);
+ r
+}
+
+// NOTE: No FFI shim, this requires inline asm.
+#[inline(always)]
+pub unsafe fn __pc_w(val: u32) {
+ asm!("mov R15, {}", in(reg) val);
+}
+
+// NOTE: No FFI shim, this requires inline asm.
+#[inline(always)]
+pub unsafe fn __lr_r() -> u32 {
+ let r;
+ asm!("mov {}, R14", out(reg) r);
+ r
+}
+
+// NOTE: No FFI shim, this requires inline asm.
+#[inline(always)]
+pub unsafe fn __lr_w(val: u32) {
+ asm!("mov R14, {}", in(reg) val);
+}
+
+#[inline(always)]
+pub unsafe fn __primask_r() -> u32 {
+ let r;
+ asm!("mrs {}, PRIMASK", out(reg) r);
+ r
+}
+
+#[inline(always)]
+pub unsafe fn __psp_r() -> u32 {
+ let r;
+ asm!("mrs {}, PSP", out(reg) r);
+ r
+}
+
+#[inline(always)]
+pub unsafe fn __psp_w(val: u32) {
+ asm!("msr PSP, {}", in(reg) val);
+}
+
+#[inline(always)]
+pub unsafe fn __sev() {
+ asm!("sev");
+}
+
+#[inline(always)]
+pub unsafe fn __udf() -> ! {
+ asm!("udf #0", options(noreturn));
+}
+
+#[inline(always)]
+pub unsafe fn __wfe() {
+ asm!("wfe");
+}
+
+#[inline(always)]
+pub unsafe fn __wfi() {
+ asm!("wfi");
+}
+
+// v7m *AND* v8m.main, but *NOT* v8m.base
+#[cfg(any(armv7m, armv8m_main))]
+pub use self::v7m::*;
+#[cfg(any(armv7m, armv8m_main))]
+mod v7m {
+ #[inline(always)]
+ pub unsafe fn __basepri_max(val: u8) {
+ asm!("msr BASEPRI_MAX, {}", in(reg) val);
+ }
+
+ #[inline(always)]
+ pub unsafe fn __basepri_r() -> u8 {
+ let r;
+ asm!("mrs {}, BASEPRI", out(reg) r);
+ r
+ }
+
+ #[inline(always)]
+ pub unsafe fn __basepri_w(val: u8) {
+ asm!("msr BASEPRI, {}", in(reg) val);
+ }
+
+ #[inline(always)]
+ pub unsafe fn __faultmask_r() -> u32 {
+ let r;
+ asm!("mrs {}, FAULTMASK", out(reg) r);
+ r
+ }
+
+ // FIXME: compiler_fences necessary?
+ #[inline(always)]
+ pub unsafe fn __enable_icache() {
+ asm!(
+ "
+ ldr r0, =0xE000ED14 @ CCR
+ mrs r2, PRIMASK @ save critical nesting info
+ cpsid i @ mask interrupts
+ ldr r1, [r0] @ read CCR
+ orr.w r1, r1, #(1 << 17) @ Set bit 17, IC
+ str r1, [r0] @ write it back
+ dsb @ ensure store completes
+ isb @ synchronize pipeline
+ msr PRIMASK, r2 @ unnest critical section
+ ",
+ out("r0") _,
+ out("r1") _,
+ out("r2") _,
+ );
+ }
+
+ #[inline(always)]
+ pub unsafe fn __enable_dcache() {
+ asm!(
+ "
+ ldr r0, =0xE000ED14 @ CCR
+ mrs r2, PRIMASK @ save critical nesting info
+ cpsid i @ mask interrupts
+ ldr r1, [r0] @ read CCR
+ orr.w r1, r1, #(1 << 16) @ Set bit 16, DC
+ str r1, [r0] @ write it back
+ dsb @ ensure store completes
+ isb @ synchronize pipeline
+ msr PRIMASK, r2 @ unnest critical section
+ ",
+ out("r0") _,
+ out("r1") _,
+ out("r2") _,
+ );
+ }
+}
+
+#[cfg(armv7em)]
+pub use self::v7em::*;
+#[cfg(armv7em)]
+mod v7em {
+ #[inline(always)]
+ pub unsafe fn __basepri_max_cm7_r0p1(val: u8) {
+ asm!(
+ "
+ mrs r1, PRIMASK
+ cpsid i
+ tst.w r1, #1
+ msr BASEPRI_MAX, {}
+ it ne
+ bxne lr
+ cpsie i
+ ",
+ in(reg) val,
+ out("r1") _,
+ );
+ }
+
+ #[inline(always)]
+ pub unsafe fn __basepri_w_cm7_r0p1(val: u8) {
+ asm!(
+ "
+ mrs r1, PRIMASK
+ cpsid i
+ tst.w r1, #1
+ msr BASEPRI, {}
+ it ne
+ bxne lr
+ cpsie i
+ ",
+ in(reg) val,
+ out("r1") _,
+ );
+ }
+}
+
+#[cfg(armv8m)]
+pub use self::v8m::*;
+/// Baseline and Mainline.
+#[cfg(armv8m)]
+mod v8m {
+ #[inline(always)]
+ pub unsafe fn __tt(mut target: u32) -> u32 {
+ asm!("tt {target}, {target}", target = inout(reg) target);
+ target
+ }
+
+ #[inline(always)]
+ pub unsafe fn __ttt(mut target: u32) -> u32 {
+ asm!("ttt {target}, {target}", target = inout(reg) target);
+ target
+ }
+
+ #[inline(always)]
+ pub unsafe fn __tta(mut target: u32) -> u32 {
+ asm!("tta {target}, {target}", target = inout(reg) target);
+ target
+ }
+
+ #[inline(always)]
+ pub unsafe fn __ttat(mut target: u32) -> u32 {
+ asm!("ttat {target}, {target}", target = inout(reg) target);
+ target
+ }
+}
+
+#[cfg(armv8m_main)]
+pub use self::v8m_main::*;
+/// Mainline only.
+#[cfg(armv8m_main)]
+mod v8m_main {
+ #[inline(always)]
+ pub unsafe fn __msplim_r() -> u32 {
+ let r;
+ asm!("mrs {}, MSPLIM", out(reg) r);
+ r
+ }
+
+ #[inline(always)]
+ pub unsafe fn __msplim_w(val: u32) {
+ asm!("msr MSPLIM, {}", in(reg) val);
+ }
+
+ #[inline(always)]
+ pub unsafe fn __psplim_r() -> u32 {
+ let r;
+ asm!("mrs {}, PSPLIM", out(reg) r);
+ r
+ }
+
+ #[inline(always)]
+ pub unsafe fn __psplim_w(val: u32) {
+ asm!("msr PSPLIM, {}", in(reg) val);
+ }
+}
+
+#[cfg(has_fpu)]
+pub use self::fpu::*;
+/// All targets with FPU.
+#[cfg(has_fpu)]
+mod fpu {
+ #[inline(always)]
+ pub unsafe fn __fpscr_r() -> u32 {
+ let r;
+ asm!("vmrs {}, fpscr", out(reg) r);
+ r
+ }
+
+ #[inline(always)]
+ pub unsafe fn __fpscr_w(val: u32) {
+ asm!("vmsr fpscr, {}", in(reg) val);
+ }
+}
diff --git a/asm/lib.rs b/asm/lib.rs
new file mode 100644
index 0000000..50e1db6
--- /dev/null
+++ b/asm/lib.rs
@@ -0,0 +1,134 @@
+//! 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)]
+
+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 __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();
+}
+
+// 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;
+}
+
+// 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]
+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();
+ }
+}