aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo/config2
-rw-r--r--.travis.yml89
-rw-r--r--CHANGELOG.md1
-rw-r--r--Cargo.toml13
-rw-r--r--asm-cm7-r0p1.s35
-rw-r--r--asm-fpu.s21
-rw-r--r--asm-toolchain1
-rw-r--r--asm-v7.s78
-rw-r--r--asm-v8-main.s43
-rw-r--r--asm-v8.s42
-rw-r--r--asm.rs346
-rw-r--r--asm.s192
-rwxr-xr-xassemble.sh47
-rw-r--r--bin/thumbv6m-none-eabi-lto.abin0 -> 12016 bytes
-rw-r--r--bin/thumbv6m-none-eabi.abin5782 -> 16188 bytes
-rw-r--r--bin/thumbv7em-none-eabi-lto.abin0 -> 16704 bytes
-rw-r--r--bin/thumbv7em-none-eabi.abin11130 -> 20056 bytes
-rw-r--r--bin/thumbv7em-none-eabihf-lto.abin0 -> 17504 bytes
-rw-r--r--bin/thumbv7em-none-eabihf.abin13164 -> 21112 bytes
-rw-r--r--bin/thumbv7m-none-eabi-lto.abin0 -> 15436 bytes
-rw-r--r--bin/thumbv7m-none-eabi.abin8920 -> 19044 bytes
-rw-r--r--bin/thumbv8m.base-none-eabi-lto.abin0 -> 13392 bytes
-rw-r--r--bin/thumbv8m.base-none-eabi.abin8198 -> 17976 bytes
-rw-r--r--bin/thumbv8m.main-none-eabi-lto.abin0 -> 18040 bytes
-rw-r--r--bin/thumbv8m.main-none-eabi.abin13908 -> 22776 bytes
-rw-r--r--bin/thumbv8m.main-none-eabihf-lto.abin0 -> 18776 bytes
-rw-r--r--bin/thumbv8m.main-none-eabihf.abin15948 -> 23764 bytes
-rw-r--r--build.rs13
-rwxr-xr-xcheck-blobs.sh21
-rw-r--r--ci/install.sh15
-rw-r--r--ci/script.sh45
-rw-r--r--src/delay.rs119
-rw-r--r--src/lib.rs28
-rw-r--r--src/peripheral/mod.rs2
-rw-r--r--src/peripheral/nvic.rs4
-rw-r--r--src/prelude.rs3
-rw-r--r--xtask/Cargo.toml13
-rw-r--r--xtask/src/lib.rs206
-rw-r--r--xtask/src/main.rs18
-rw-r--r--xtask/tests/ci.rs57
40 files changed, 830 insertions, 624 deletions
diff --git a/.cargo/config b/.cargo/config
new file mode 100644
index 0000000..8628170
--- /dev/null
+++ b/.cargo/config
@@ -0,0 +1,2 @@
+[alias]
+xtask = "run --package xtask --bin xtask --"
diff --git a/.travis.yml b/.travis.yml
index bbe3d48..14e5602 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,81 +1,26 @@
language: rust
-matrix:
- allow_failures:
- - rust: nightly
- include:
- - env: TARGET=x86_64-unknown-linux-gnu
- rust: stable
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- - env: TARGET=thumbv6m-none-eabi
- rust: stable
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- - env: TARGET=thumbv7m-none-eabi
- rust: stable
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- - env: TARGET=thumbv7em-none-eabi
- rust: stable
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- - env: TARGET=thumbv7em-none-eabihf
- rust: stable
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- # MSRV
- - env: TARGET=thumbv6m-none-eabi
- rust: 1.31.0
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- # MSRV
- - env: TARGET=thumbv7m-none-eabi
- rust: 1.31.0
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- # MSRV
- - env: TARGET=thumbv7em-none-eabi
- rust: 1.31.0
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- # MSRV
- - env: TARGET=thumbv7em-none-eabihf
- rust: 1.31.0
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- - env: TARGET=thumbv6m-none-eabi
- rust: nightly
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- - env: TARGET=thumbv7m-none-eabi
- rust: nightly
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
-
- - env: TARGET=thumbv7em-none-eabi
- rust: nightly
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
+branches:
+ only:
+ - master
+ - staging
+ - trying
- - env: TARGET=thumbv7em-none-eabihf
- rust: nightly
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
+rust:
+ - 1.35.0
+ - stable
+ - nightly
- - env: TARGET=thumbv8m.main-none-eabi
- rust: nightly
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
+if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
- - env: TARGET=thumbv8m.base-none-eabi
- rust: nightly
- if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master)
+matrix:
+ allow_failures:
+ - rust: nightly
before_install: set -e
-install:
- - bash ci/install.sh
- - export PATH="$PATH:$PWD/gcc/bin"
-
script:
- - bash ci/script.sh
+ - cargo test --all
after_script: set +e
@@ -84,12 +29,6 @@ cache: cargo
before_cache:
- chmod -R a+r $HOME/.cargo;
-branches:
- only:
- - master
- - staging
- - trying
-
notifications:
email:
on_success: never
diff --git a/CHANGELOG.md b/CHANGELOG.md
index aa3c32b..4b5a837 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
various NVIC functions, replacing the previous use of `Nr` from bare-metal.
- Associated const `PTR` is introduced to Core Peripherals to
eventually replace the existing `ptr()` API.
+- A delay driver based on SysTick.
## [v0.6.3] - 2020-07-20
diff --git a/Cargo.toml b/Cargo.toml
index e995d82..eddc6fa 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,6 +19,15 @@ links = "cortex-m" # prevent multiple versions of this crate to be linked toget
bare-metal = { version = "0.2.0", features = ["const-fn"] }
volatile-register = "0.2.0"
bitfield = "0.13.2"
+embedded-hal = "0.2.4"
+
+[features]
+cm7-r0p1 = []
+inline-asm = []
+linker-plugin-lto = []
+
+[workspace]
+members = ["xtask"]
[package.metadata.docs.rs]
targets = [
@@ -30,7 +39,3 @@ targets = [
"thumbv8m.base-none-eabi",
"thumbv8m.main-none-eabi"
]
-
-[features]
-cm7-r0p1 = []
-inline-asm = []
diff --git a/asm-cm7-r0p1.s b/asm-cm7-r0p1.s
deleted file mode 100644
index 28c3384..0000000
--- a/asm-cm7-r0p1.s
+++ /dev/null
@@ -1,35 +0,0 @@
- .cfi_sections .debug_frame
-
- .section .text.__basepri_max_cm7_r0p1
- .global __basepri_max_cm7_r0p1
- .syntax unified
- .thumb_func
- .cfi_startproc
-__basepri_max_cm7_r0p1:
- mrs r1, PRIMASK
- cpsid i
- tst.w r1, #1
- msr BASEPRI_MAX, r0
- it ne
- bxne lr
- cpsie i
- bx lr
- .cfi_endproc
- .size __basepri_max_cm7_r0p1, . - __basepri_max_cm7_r0p1
-
- .section .text.__basepri_w_cm7_r0p1
- .global __basepri_w_cm7_r0p1
- .syntax unified
- .thumb_func
- .cfi_startproc
-__basepri_w_cm7_r0p1:
- mrs r1, PRIMASK
- cpsid i
- tst.w r1, #1
- msr BASEPRI, r0
- it ne
- bxne lr
- cpsie i
- bx lr
- .cfi_endproc
- .size __basepri_w_cm7_r0p1, . - __basepri_w_cm7_r0p1
diff --git a/asm-fpu.s b/asm-fpu.s
deleted file mode 100644
index 417d199..0000000
--- a/asm-fpu.s
+++ /dev/null
@@ -1,21 +0,0 @@
- .cfi_sections .debug_frame
-
- .section .text.__get_FPSCR
- .global __get_FPSCR
- .thumb_func
- .cfi_startproc
-__get_FPSCR:
- vmrs r0, fpscr
- bx lr
- .cfi_endproc
- .size __get_FPSCR, . - __get_FPSCR
-
- .section .text.__set_FPSCR
- .global __set_FPSCR
- .thumb_func
- .cfi_startproc
-__set_FPSCR:
- vmsr fpscr, r0
- bx lr
- .cfi_endproc
- .size __set_FPSCR, . - __set_FPSCR
diff --git a/asm-toolchain b/asm-toolchain
new file mode 100644
index 0000000..a36829b
--- /dev/null
+++ b/asm-toolchain
@@ -0,0 +1 @@
+nightly-2020-08-26
diff --git a/asm-v7.s b/asm-v7.s
deleted file mode 100644
index 17d7110..0000000
--- a/asm-v7.s
+++ /dev/null
@@ -1,78 +0,0 @@
- .syntax unified
- .cfi_sections .debug_frame
-
- .section .text.__basepri_max
- .global __basepri_max
- .thumb_func
- .cfi_startproc
-__basepri_max:
- msr BASEPRI_MAX, r0
- bx lr
- .cfi_endproc
- .size __basepri_max, . - __basepri_max
-
- .section .text.__basepri_r
- .global __basepri_r
- .thumb_func
- .cfi_startproc
-__basepri_r:
- mrs r0, BASEPRI
- bx lr
- .cfi_endproc
- .size __basepri_r, . - __basepri_r
-
- .section .text.__basepri_w
- .global __basepri_w
- .thumb_func
- .cfi_startproc
-__basepri_w:
- msr BASEPRI, r0
- bx lr
- .cfi_endproc
- .size __basepri_w, . - __basepri_w
-
- .section .text.__faultmask
- .global __faultmask
- .thumb_func
- .cfi_startproc
-__faultmask:
- mrs r0, FAULTMASK
- bx lr
- .cfi_endproc
- .size __faultmask, . - __faultmask
-
- .section .text.__enable_icache
- .global __enable_icache
- .thumb_func
- .cfi_startproc
-__enable_icache:
- 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
- bx lr
- .cfi_endproc
- .size __enable_icache, . - __enable_icache
-
- .section .text.__enable_dcache
- .global __enable_dcache
- .thumb_func
- .cfi_startproc
-__enable_dcache:
- 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
- bx lr
- .cfi_endproc
- .size __enable_dcache, . - __enable_dcache
diff --git a/asm-v8-main.s b/asm-v8-main.s
deleted file mode 100644
index 1fad155..0000000
--- a/asm-v8-main.s
+++ /dev/null
@@ -1,43 +0,0 @@
-
- .cfi_sections .debug_frame
-
- .section .text.__msplim_r
- .global __msplim_r
- .thumb_func
- .cfi_startproc
-__msplim_r:
- mrs r0, MSPLIM
- bx lr
- .cfi_endproc
- .size __msplim_r, . - __msplim_r
-
- .section .text.__msplim_w
- .global __msplim_w
- .thumb_func
- .cfi_startproc
-__msplim_w:
- msr MSPLIM, r0
- bx lr
- .cfi_endproc
- .size __msplim_w, . - __msplim_w
-
- .section .text.__psplim_r
- .global __psplim_r
- .thumb_func
- .cfi_startproc
-__psplim_r:
- mrs r0, PSPLIM
- bx lr
- .cfi_endproc
- .size __psplim_r, . - __psplim_r
-
- .section .text.__psplim_w
- .global __psplim_w
- .thumb_func
- .cfi_startproc
-__psplim_w:
- msr PSPLIM, r0
- bx lr
- .cfi_endproc
- .size __psplim_w, . - __psplim_w
-
diff --git a/asm-v8.s b/asm-v8.s
deleted file mode 100644
index 7d3a8c9..0000000
--- a/asm-v8.s
+++ /dev/null
@@ -1,42 +0,0 @@
- .cfi_sections .debug_frame
-
- .section .text.__tt
- .global __tt
- .thumb_func
- .cfi_startproc
-__tt:
- tt r0, r0
- bx lr
- .cfi_endproc
- .size __tt, . - __tt
-
- .section .text.__ttt
- .global __ttt
- .thumb_func
- .cfi_startproc
-__ttt:
- ttt r0, r0
- bx lr
- .cfi_endproc
- .size __ttt, . - __ttt
-
- .section .text.__tta
- .global __tta
- .thumb_func
- .cfi_startproc
-__tta:
- tta r0, r0
- bx lr
- .cfi_endproc
- .size __tta, . - __tta
-
-
- .section .text.__ttat
- .global __ttat
- .thumb_func
- .cfi_startproc
-__ttat:
- ttat r0, r0
- bx lr
- .cfi_endproc
- .size __ttat, . - __ttat
diff --git a/asm.rs b/asm.rs
new file mode 100644
index 0000000..f5b0179
--- /dev/null
+++ b/asm.rs
@@ -0,0 +1,346 @@
+//! Assembly stubs for the `cortex-m` crate.
+//!
+//! 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 Rust
+//! file `asm.rs` 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)]
+
+#[no_mangle]
+pub unsafe extern "C" fn __bkpt() {
+ asm!("bkpt");
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __control_r() -> u32 {
+ let r;
+ asm!("mrs {}, CONTROL", out(reg) r);
+ r
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __control_w(w: u32) {
+ asm!("msr CONTROL, {}", in(reg) w);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __cpsid() {
+ asm!("cpsid i");
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __cpsie() {
+ asm!("cpsie i");
+}
+
+#[no_mangle]
+pub unsafe extern "C" 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?
+#[no_mangle]
+pub unsafe extern "C" fn __dmb() {
+ asm!("dmb 0xF");
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __dsb() {
+ asm!("dsb 0xF");
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __isb() {
+ asm!("isb 0xF");
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __msp_r() -> u32 {
+ let r;
+ asm!("mrs {}, MSP", out(reg) r);
+ r
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __msp_w(val: u32) {
+ asm!("msr MSP, {}", in(reg) val);
+}
+
+#[no_mangle]
+pub unsafe extern "C" 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");
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __primask() -> u32 {
+ // FIXME: rename to __primask_r
+ let r;
+ asm!("mrs {}, PRIMASK", out(reg) r);
+ r
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __psp_r() -> u32 {
+ let r;
+ asm!("mrs {}, PSP", out(reg) r);
+ r
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __psp_w(val: u32) {
+ asm!("msr PSP, {}", in(reg) val);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __sev() {
+ asm!("sev");
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __udf() {
+ asm!("udf #0");
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __wfe() {
+ asm!("wfe");
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __wfi() {
+ asm!("wfi");
+}
+
+#[cfg(armv7m)]
+pub mod v7m {
+ #[no_mangle]
+ pub unsafe extern "C" fn __basepri_max(val: u8) {
+ asm!("msr BASEPRI_MAX, {}", in(reg) val);
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn __basepri_r() -> u8 {
+ let r;
+ asm!("mrs {}, BASEPRI", out(reg) r);
+ r
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn __basepri_w(val: u8) {
+ asm!("msr BASEPRI, {}", in(reg) val);
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn __faultmask() -> u32 {
+ let r;
+ asm!("mrs {}, FAULTMASK", out(reg) r);
+ r
+ }
+
+ // FIXME: compiler_fences necessary?
+ #[no_mangle]
+ pub unsafe extern "C" 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") _,
+ );
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" 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)]
+mod v7em {
+ #[no_mangle]
+ pub unsafe extern "C" 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") _,
+ );
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" 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") _,
+ );
+ }
+}
+
+/// Baseline and Mainline.
+#[cfg(armv8m)]
+pub mod v8m {
+ #[no_mangle]
+ pub unsafe extern "C" fn __tt(mut target: u32) -> u32 {
+ asm!("tt {target}, {target}", target = inout(reg) target);
+ target
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn __ttt(mut target: u32) -> u32 {
+ asm!("ttt {target}, {target}", target = inout(reg) target);
+ target
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn __tta(mut target: u32) -> u32 {
+ asm!("tta {target}, {target}", target = inout(reg) target);
+ target
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn __ttat(mut target: u32) -> u32 {
+ asm!("ttat {target}, {target}", target = inout(reg) target);
+ target
+ }
+}
+
+/// Mainline only.
+#[cfg(armv8m_main)]
+pub mod v8m_main {
+ #[no_mangle]
+ pub unsafe extern "C" fn __msplim_r() -> u32 {
+ let r;
+ asm!("mrs {}, MSPLIM", out(reg) r);
+ r
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn __msplim_w(val: u32) {
+ asm!("msr MSPLIM, {}", in(reg) val);
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn __psplim_r() -> u32 {
+ let r;
+ asm!("mrs {}, PSPLIM", out(reg) r);
+ r
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn __psplim_w(val: u32) {
+ asm!("msr PSPLIM, {}", in(reg) val);
+ }
+}
+
+/// All targets with FPU.
+#[cfg(has_fpu)]
+pub mod fpu {
+ #[no_mangle]
+ pub unsafe extern "C" fn __get_FPSCR() -> u32 {
+ let r;
+ asm!("vmrs {}, fpscr", out(reg) r);
+ r
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn __set_FPSCR(val: u32) {
+ asm!("vmsr fpscr, {}", in(reg) val);
+ }
+}
+
+/// 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();
+ }
+}
diff --git a/asm.s b/asm.s
deleted file mode 100644
index 3d1a54b..0000000
--- a/asm.s
+++ /dev/null
@@ -1,192 +0,0 @@
- .cfi_sections .debug_frame
-
- .section .text.__bkpt
- .global __bkpt
- .thumb_func
- .cfi_startproc
-__bkpt:
- bkpt
- bx lr
- .cfi_endproc
- .size __bkpt, . - __bkpt
-
- .section .text.__control_r
- .global __control_r
- .thumb_func
- .cfi_startproc
-__control_r:
- mrs r0, CONTROL
- bx lr
- .cfi_endproc
- .size __control_r, . - __control_r
-
- .section .text.__control_w
- .global __control_w
- .thumb_func
- .cfi_startproc
-__control_w:
- msr CONTROL, r0
- bx lr
- .cfi_endproc
- .size __control_w, . - __control_w
-
-
- .section .text.__cpsid
- .global __cpsid
- .thumb_func
- .cfi_startproc
-__cpsid:
- cpsid i
- bx lr
- .cfi_endproc
- .size __cpsid, . - __cpsid
-
- .section .text.__cpsie
- .global __cpsie
- .thumb_func
- .cfi_startproc
-__cpsie:
- cpsie i
- bx lr
- .cfi_endproc
- .size __cpsie, . - __cpsie
-
- .section .text.__delay
- .global __delay
- .syntax unified
- .thumb_func
- .cfi_startproc
-__delay:
-1:
- nop
- subs r0, #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
- bx lr
- .cfi_endproc
- .size __delay, . - __delay
-
- .section .text.__dmb
- .global __dmb
- .thumb_func
- .cfi_startproc
-__dmb:
- dmb 0xF
- bx lr
- .cfi_endproc
- .size __dmb, . - __dmb
-
- .section .text.__dsb
- .global __dsb
- .thumb_func
- .cfi_startproc
-__dsb:
- dsb 0xF
- bx lr
- .cfi_endproc
- .size __dsb, . - __dsb
-
- .section .text.__isb
- .global __isb
- .thumb_func
- .cfi_startproc
-__isb:
- isb 0xF
- bx lr
- .cfi_endproc
- .size __isb, . - __isb
-
- .section .text.__msp_r
- .global __msp_r
- .thumb_func
- .cfi_startproc
-__msp_r:
- mrs r0, MSP
- bx lr
- .cfi_endproc
- .size __msp_r, . - __msp_r
-
- .section .text.__msp_w
- .global __msp_w
- .thumb_func
-__msp_w:
- msr MSP, r0
- bx lr
- .size __msp_w, . - __msp_w
-
- .section .text.__nop
- .global __nop
- .thumb_func
- .cfi_startproc
-__nop:
- bx lr
- .cfi_endproc
- .size __nop, . - __nop
-
- .section .text.__primask
- .global __primask
- .thumb_func
- .cfi_startproc
-__primask:
- mrs r0, PRIMASK
- bx lr
- .cfi_endproc
- .size __primask, . - __primask
-
- .section .text.__psp_r
- .global __psp_r
- .thumb_func
- .cfi_startproc
-__psp_r:
- mrs r0, PSP
- bx lr
- .cfi_endproc
- .size __psp_r, . - __psp_r
-
- .section .text.__psp_w
- .global __psp_w
- .thumb_func
-__psp_w:
- msr PSP, r0
- bx lr
- .size __psp_w, . - __psp_w
-
- .section .text.__sev
- .global __sev
- .thumb_func
- .cfi_startproc
-__sev:
- sev
- bx lr
- .cfi_endproc
- .size __sev, . - __sev
-
-
- .section .text.__udf
- .global __udf
- .thumb_func
- .cfi_startproc
-__udf:
- udf
- .cfi_endproc
- .size __udf, . - __udf
-
- .section .text.__wfe
- .global __wfe
- .thumb_func
- .cfi_startproc
-__wfe:
- wfe
- bx lr
- .cfi_endproc
- .size __wfe, . - __wfe
-
-
- .section .text.__wfi
- .global __wfi
- .thumb_func
- .cfi_startproc
-__wfi:
- wfi
- bx lr
- .cfi_endproc
- .size __wfi, . - __wfi
diff --git a/assemble.sh b/assemble.sh
deleted file mode 100755
index 070481a..0000000
--- a/assemble.sh
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env bash
-
-set -euxo pipefail
-
-# cflags taken from cc 1.0.22
-
-crate=cortex-m
-
-# remove existing blobs because otherwise this will append object files to the old blobs
-rm -f bin/*.a
-
-pwd=$(pwd)
-flags=(-g --debug-prefix-map "$pwd=.")
-
-arm-none-eabi-as "${flags[@]}" -march=armv6s-m asm.s -o bin/$crate.o
-ar crs bin/thumbv6m-none-eabi.a bin/$crate.o
-
-arm-none-eabi-as "${flags[@]}" -march=armv7-m asm.s -o bin/$crate.o
-arm-none-eabi-as "${flags[@]}" -march=armv7-m asm-v7.s -o bin/$crate-v7.o
-ar crs bin/thumbv7m-none-eabi.a bin/$crate.o bin/$crate-v7.o
-
-arm-none-eabi-as "${flags[@]}" -march=armv7e-m asm.s -o bin/$crate.o
-arm-none-eabi-as "${flags[@]}" -march=armv7e-m asm-fpu.s -mfpu=fpv4-sp-d16 -o bin/$crate-v7-fpu.o
-arm-none-eabi-as "${flags[@]}" -march=armv7e-m asm-cm7-r0p1.s -o bin/$crate-cm7-r0p1.o
-arm-none-eabi-as "${flags[@]}" -march=armv7e-m asm-v7.s -o bin/$crate-v7.o
-ar crs bin/thumbv7em-none-eabi.a bin/$crate.o bin/$crate-v7.o bin/$crate-cm7-r0p1.o
-ar crs bin/thumbv7em-none-eabihf.a bin/$crate.o bin/$crate-v7.o bin/$crate-cm7-r0p1.o bin/$crate-v7-fpu.o
-
-arm-none-eabi-as "${flags[@]}" -march=armv8-m.base asm.s -o bin/$crate.o
-arm-none-eabi-as "${flags[@]}" -march=armv8-m.base asm-v8.s -o bin/$crate-v8.o
-ar crs bin/thumbv8m.base-none-eabi.a bin/$crate.o bin/$crate-v8.o
-
-arm-none-eabi-as "${flags[@]}" -march=armv8-m.main asm.s -o bin/$crate.o
-arm-none-eabi-as "${flags[@]}" -march=armv8-m.main asm-v7.s -o bin/$crate-v7.o
-arm-none-eabi-as "${flags[@]}" -march=armv8-m.main asm-v8.s -o bin/$crate-v8.o
-arm-none-eabi-as "${flags[@]}" -march=armv8-m.main asm-v8-main.s -o bin/$crate-v8-main.o
-arm-none-eabi-as "${flags[@]}" -march=armv8-m.main asm-fpu.s -mfpu=fpv5-sp-d16 -o bin/$crate-v8-fpu.o
-ar crs bin/thumbv8m.main-none-eabi.a bin/$crate.o bin/$crate-v7.o bin/$crate-v8.o bin/$crate-v8-main.o
-ar crs bin/thumbv8m.main-none-eabihf.a bin/$crate.o bin/$crate-v7.o bin/$crate-v8.o bin/$crate-v8-main.o bin/$crate-v8-fpu.o
-
-rm bin/$crate.o
-rm bin/$crate-v7.o
-rm bin/$crate-v7-fpu.o
-rm bin/$crate-v8-fpu.o
-rm bin/$crate-cm7-r0p1.o
-rm bin/$crate-v8.o
-rm bin/$crate-v8-main.o
diff --git a/bin/thumbv6m-none-eabi-lto.a b/bin/thumbv6m-none-eabi-lto.a
new file mode 100644
index 0000000..4d2f02c
--- /dev/null
+++ b/bin/thumbv6m-none-eabi-lto.a
Binary files differ
diff --git a/bin/thumbv6m-none-eabi.a b/bin/thumbv6m-none-eabi.a
index b2f72b4..beedd73 100644
--- a/bin/thumbv6m-none-eabi.a
+++ b/bin/thumbv6m-none-eabi.a
Binary files differ
diff --git a/bin/thumbv7em-none-eabi-lto.a b/bin/thumbv7em-none-eabi-lto.a
new file mode 100644
index 0000000..7b8a4f8
--- /dev/null
+++ b/bin/thumbv7em-none-eabi-lto.a
Binary files differ
diff --git a/bin/thumbv7em-none-eabi.a b/bin/thumbv7em-none-eabi.a
index 5d5c48f..588e5cd 100644
--- a/bin/thumbv7em-none-eabi.a
+++ b/bin/thumbv7em-none-eabi.a
Binary files differ
diff --git a/bin/thumbv7em-none-eabihf-lto.a b/bin/thumbv7em-none-eabihf-lto.a
new file mode 100644
index 0000000..4efadd8
--- /dev/null
+++ b/bin/thumbv7em-none-eabihf-lto.a
Binary files differ
diff --git a/bin/thumbv7em-none-eabihf.a b/bin/thumbv7em-none-eabihf.a
index 1570f83..fd08ba5 100644
--- a/bin/thumbv7em-none-eabihf.a
+++ b/bin/thumbv7em-none-eabihf.a
Binary files differ
diff --git a/bin/thumbv7m-none-eabi-lto.a b/bin/thumbv7m-none-eabi-lto.a
new file mode 100644
index 0000000..28e5860
--- /dev/null
+++ b/bin/thumbv7m-none-eabi-lto.a
Binary files differ
diff --git a/bin/thumbv7m-none-eabi.a b/bin/thumbv7m-none-eabi.a
index 51ab710..4559ad4 100644
--- a/bin/thumbv7m-none-eabi.a
+++ b/bin/thumbv7m-none-eabi.a
Binary files differ
diff --git a/bin/thumbv8m.base-none-eabi-lto.a b/bin/thumbv8m.base-none-eabi-lto.a
new file mode 100644
index 0000000..ee46792
--- /dev/null
+++ b/bin/thumbv8m.base-none-eabi-lto.a
Binary files differ
diff --git a/bin/thumbv8m.base-none-eabi.a b/bin/thumbv8m.base-none-eabi.a
index 33bcff2..26d25bd 100644
--- a/bin/thumbv8m.base-none-eabi.a
+++ b/bin/thumbv8m.base-none-eabi.a
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabi-lto.a b/bin/thumbv8m.main-none-eabi-lto.a
new file mode 100644
index 0000000..e3aa0cf
--- /dev/null
+++ b/bin/thumbv8m.main-none-eabi-lto.a
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabi.a b/bin/thumbv8m.main-none-eabi.a
index 1b2418d..10bbf53 100644
--- a/bin/thumbv8m.main-none-eabi.a
+++ b/bin/thumbv8m.main-none-eabi.a
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabihf-lto.a b/bin/thumbv8m.main-none-eabihf-lto.a
new file mode 100644
index 0000000..1c25494
--- /dev/null
+++ b/bin/thumbv8m.main-none-eabihf-lto.a
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabihf.a b/bin/thumbv8m.main-none-eabihf.a
index 312dcd2..0651788 100644
--- a/bin/thumbv8m.main-none-eabihf.a
+++ b/bin/thumbv8m.main-none-eabihf.a
Binary files differ
diff --git a/build.rs b/build.rs
index d53dea5..dc9b3a0 100644
--- a/build.rs
+++ b/build.rs
@@ -7,10 +7,17 @@ fn main() {
let name = env::var("CARGO_PKG_NAME").unwrap();
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),
+ format!("bin/{}{}.a", target, suffix),
out_dir.join(format!("lib{}.a", name)),
- ).unwrap();
+ )
+ .unwrap();
println!("cargo:rustc-link-lib=static={}", name);
println!("cargo:rustc-link-search={}", out_dir.display());
@@ -25,7 +32,7 @@ fn main() {
} else if target.starts_with("thumbv7em-") {
println!("cargo:rustc-cfg=cortex_m");
println!("cargo:rustc-cfg=armv7m");
- println!("cargo:rustc-cfg=armv7em"); // (not currently used)
+ println!("cargo:rustc-cfg=armv7em"); // (not currently used)
} else if target.starts_with("thumbv8m.base") {
println!("cargo:rustc-cfg=cortex_m");
println!("cargo:rustc-cfg=armv8m");
diff --git a/check-blobs.sh b/check-blobs.sh
deleted file mode 100755
index 166b4a4..0000000
--- a/check-blobs.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env bash
-
-# Checks that the blobs are up to date with the committed assembly files
-
-set -euxo pipefail
-
-for lib in bin/*.a; do
- filename=$(basename "$lib")
- arm-none-eabi-objdump -Cd "$lib" > "bin/${filename%.a}.before"
-done
-
-./assemble.sh
-
-for lib in bin/*.a; do
- filename=$(basename "$lib")
- arm-none-eabi-objdump -Cd "$lib" > "bin/${filename%.a}.after"
-done
-
-for cksum in bin/*.after; do
- diff -u "$cksum" "${cksum%.after}.before"
-done
diff --git a/ci/install.sh b/ci/install.sh
deleted file mode 100644
index 371cc8e..0000000
--- a/ci/install.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-set -euxo pipefail
-
-main() {
- case $TARGET in
- thumbv*-none-eabi*)
- rustup target add $TARGET
- ;;
- esac
-
- mkdir gcc
-
- curl -L https://developer.arm.com/-/media/Files/downloads/gnu-rm/7-2018q2/gcc-arm-none-eabi-7-2018-q2-update-linux.tar.bz2?revision=bc2c96c0-14b5-4bb4-9f18-bceb4050fee7?product=GNU%20Arm%20Embedded%20Toolchain,64-bit,,Linux,7-2018-q2-update | tar --strip-components=1 -C gcc -xj
-}
-
-main
diff --git a/ci/script.sh b/ci/script.sh
deleted file mode 100644
index 7c30993..0000000
--- a/ci/script.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-set -euxo pipefail
-
-main() {
- if [ $TRAVIS_RUST_VERSION = nightly ]; then
- export RUSTFLAGS="-D warnings"
- fi
-
- cargo check --target $TARGET
-
- if [ $TRAVIS_RUST_VERSION = nightly ]; then
- cargo check --target $TARGET --features 'inline-asm'
- fi
-
- case $TARGET in
- thumbv7em-none-eabi*)
- cargo check --target $TARGET --features cm7-r0p1
-
- if [ $TRAVIS_RUST_VERSION = nightly ]; then
- cargo check --target $TARGET --features 'cm7-r0p1 inline-asm'
- fi
- ;;
-
- thumbv*-none-eabi*)
- ;;
-
- x86_64-unknown-linux-gnu)
- cargo test --target $TARGET
- ;;
- esac
-
- if [ $TARGET = x86_64-unknown-linux-gnu ]; then
- ./check-blobs.sh
- fi
-
- if [ $TRAVIS_RUST_VERSION = nightly ]; then
- # Get the latest nightly with a working clippy
- rustup toolchain uninstall nightly
- rustup set profile default
- rustup default nightly
- rustup target add $TARGET
- cargo clippy --target $TARGET -- -D warnings
- fi
-}
-
-main
diff --git a/src/delay.rs b/src/delay.rs
new file mode 100644
index 0000000..051151f
--- /dev/null
+++ b/src/delay.rs
@@ -0,0 +1,119 @@
+//! A delay driver based on SysTick.
+
+use crate::peripheral::{syst::SystClkSource, SYST};
+use embedded_hal::blocking::delay::{DelayMs, DelayUs};
+
+/// System timer (SysTick) as a delay provider.
+pub struct Delay {
+ syst: SYST,
+ ahb_frequency: u32,
+}
+
+impl Delay {
+ /// Configures the system timer (SysTick) as a delay provider.
+ ///
+ /// `ahb_frequency` is a frequency of the AHB bus in Hz.
+ #[inline]
+ pub fn new(mut syst: SYST, ahb_frequency: u32) -> Self {
+ syst.set_clock_source(SystClkSource::Core);
+
+ Delay { syst, ahb_frequency }
+ }
+
+ /// Releases the system timer (SysTick) resource.
+ #[inline]
+ pub fn free(self) -> SYST {
+ self.syst
+ }
+
+ fn _delay_us(&mut self, us: u32) {
+ let ticks = (us as u64) * (self.ahb_frequency as u64) / 1_000_000;
+
+ let full_cycles = ticks >> 24;
+ if full_cycles > 0 {
+ self.syst.set_reload(0xffffff);
+ self.syst.clear_current();
+ self.syst.enable_counter();
+
+ for _ in 0..full_cycles {
+ while !self.syst.has_wrapped() {}
+ }
+ }
+
+ let ticks = (ticks & 0xffffff) as u32;
+ if ticks > 1 {
+ self.syst.set_reload(ticks - 1);
+ self.syst.clear_current();
+ self.syst.enable_counter();
+
+ while !self.syst.has_wrapped() {}
+ }
+
+ self.syst.disable_counter();
+ }
+}
+
+impl DelayMs<u32> for Delay {
+ #[inline]
+ fn delay_ms(&mut self, mut ms: u32) {
+ // 4294967 is the highest u32 value which you can multiply by 1000 without overflow
+ while ms > 4294967 {
+ self.delay_us(4294967000u32);
+ ms -= 4294967;
+ }
+ self.delay_us(ms * 1_000);
+ }
+}
+
+// This is a workaround to allow `delay_ms(42)` construction without specifying a type.
+impl DelayMs<i32> for Delay {
+ #[inline(always)]
+ fn delay_ms(&mut self, ms: i32) {
+ assert!(ms >= 0);
+ self.delay_ms(ms as u32);
+ }
+}
+
+impl DelayMs<u16> for Delay {
+ #[inline(always)]
+ fn delay_ms(&mut self, ms: u16) {
+ self.delay_ms(u32::from(ms));
+ }
+}
+
+impl DelayMs<u8> for Delay {
+ #[inline(always)]
+ fn delay_ms(&mut self, ms: u8) {
+ self.delay_ms(u32::from(ms));
+ }
+}
+
+impl DelayUs<u32> for Delay {
+ #[inline]
+ fn delay_us(&mut self, us: u32) {
+ self._delay_us(us);
+ }
+}
+
+// This is a workaround to allow `delay_us(42)` construction without specifying a type.
+impl DelayUs<i32> for Delay {
+ #[inline(always)]
+ fn delay_us(&mut self, us: i32) {
+ assert!(us >= 0);
+ self.delay_us(us as u32);
+ }
+}
+
+impl DelayUs<u16> for Delay {
+ #[inline(always)]
+ fn delay_us(&mut self, us: u16) {
+ self.delay_us(u32::from(us))
+ }
+}
+
+impl DelayUs<u8> for Delay {
+ #[inline(always)]
+ fn delay_us(&mut self, us: u8) {
+ self.delay_us(u32::from(us))
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 89f420d..723816a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -24,6 +24,32 @@
//!
//! The disadvantage is that `inline-asm` requires a nightly toolchain.
//!
+//! ## `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], 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.31 and up. It *might*
@@ -61,10 +87,12 @@ mod macros;
pub mod asm;
#[cfg(armv8m)]
pub mod cmse;
+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;
diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs
index cae1133..961f31d 100644
--- a/src/peripheral/mod.rs
+++ b/src/peripheral/mod.rs
@@ -55,8 +55,6 @@
//!
//! - ARMv7-M Architecture Reference Manual (Issue E.b) - Chapter B3
-// TODO stand-alone register: STIR
-
use core::marker::PhantomData;
use core::ops;
diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs
index a2f85f4..4332707 100644
--- a/src/peripheral/nvic.rs
+++ b/src/peripheral/nvic.rs
@@ -79,9 +79,11 @@ impl NVIC {
///
/// Writing a value to the INTID field is the same as manually pending an interrupt by setting
/// the corresponding interrupt bit in an Interrupt Set Pending Register. This is similar to
- /// `set_pending`.
+ /// [`NVIC::pend`].
///
/// This method is not available on ARMv6-M chips.
+ ///
+ /// [`NVIC::pend`]: #method.pend
#[cfg(not(armv6m))]
#[inline]
pub fn request<I>(&mut self, interrupt: I)
diff --git a/src/prelude.rs b/src/prelude.rs
new file mode 100644
index 0000000..bc47cc0
--- /dev/null
+++ b/src/prelude.rs
@@ -0,0 +1,3 @@
+//! Prelude
+
+pub use embedded_hal::prelude::*;
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
new file mode 100644
index 0000000..06c5143
--- /dev/null
+++ b/xtask/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "xtask"
+version = "0.0.0"
+authors = ["The Cortex-M Team <cortex-m@teams.rust-embedded.org>"]
+edition = "2018"
+publish = false
+
+[[test]]
+name = "ci"
+harness = false
+
+[dependencies]
+ar = "0.8.0"
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
new file mode 100644
index 0000000..4575f65
--- /dev/null
+++ b/xtask/src/lib.rs
@@ -0,0 +1,206 @@
+//! `cargo xtask` automation.
+//!
+//! Please refer to <https://github.com/matklad/cargo-xtask/> for an explanation of the concept.
+//!
+//! Also see the docs in `asm.rs`.
+
+use process::Stdio;
+use std::env::current_dir;
+use std::{
+ collections::BTreeMap,
+ fs::{self, File},
+ process::{self, Command},
+};
+
+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");
+
+ // 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.rs");
+
+ println!("{:?}", cmd);
+ let status = cmd.status().unwrap();
+ assert!(status.success());
+
+ // Archive `target.o` -> `bin/target.a`.
+ let mut builder = ar::Builder::new(File::create(format!("bin/{}.a", file_stub)).unwrap());
+
+ // Use `append`, not `append_path`, to avoid adding any filesystem metadata (modification times,
+ // etc.).
+ let file = fs::read(&obj_file).unwrap();
+ builder
+ .append(
+ &ar::Header::new(obj_file.as_bytes().to_vec(), file.len() as u64),
+ &*file,
+ )
+ .unwrap();
+
+ fs::remove_file(&obj_file).unwrap();
+}
+
+fn assemble(target: &str, cfgs: &[&str]) {
+ assemble_really(target, cfgs, false);
+ assemble_really(target, cfgs, true);
+}
+
+// `--target` -> `--cfg` list (mirrors what `build.rs` does).
+static TARGETS: &[(&str, &[&str])] = &[
+ ("thumbv6m-none-eabi", &[]),
+ ("thumbv7m-none-eabi", &["armv7m"]),
+ ("thumbv7em-none-eabi", &["armv7m", "armv7em"]),
+ ("thumbv7em-none-eabihf", &["armv7m", "armv7em", "has_fpu"]),
+ ("thumbv8m.base-none-eabi", &["armv8m", "armv8m_base"]),
+ (
+ "thumbv8m.main-none-eabi",
+ &["armv7m", "armv8m", "armv8m_main"],
+ ),
+ (
+ "thumbv8m.main-none-eabihf",
+ &["armv7m", "armv8m", "armv8m_main", "has_fpu"],
+ ),
+];
+
+pub fn install_targets(targets: &mut dyn Iterator<Item = &str>, toolchain: Option<&str>) {
+ let mut rustup = Command::new("rustup");
+ rustup.arg("target").arg("add").args(targets);
+
+ if let Some(toolchain) = toolchain {
+ rustup.arg("--toolchain").arg(toolchain);
+ }
+
+ let status = rustup.status().unwrap();
+ assert!(status.success(), "rustup command failed: {:?}", rustup);
+}
+
+pub fn assemble_blobs() {
+ let mut cmd = rustc();
+ cmd.arg("-V");
+ cmd.stdout(Stdio::null());
+ let status = cmd.status().unwrap();
+ let toolchain = toolchain();
+
+ if !status.success() {
+ println!(
+ "asm toolchain {} does not seem to be installed. installing it now.",
+ toolchain
+ );
+
+ let mut rustup = Command::new("rustup");
+ let status = rustup.arg("install").arg(&toolchain).status().unwrap();
+ assert!(status.success(), "rustup command failed: {:?}", rustup);
+ }
+
+ install_targets(
+ &mut TARGETS.iter().map(|(target, _)| *target),
+ Some(&*toolchain),
+ );
+
+ for (target, cfgs) in TARGETS {
+ println!("building artifacts for {}", target);
+ assemble(target, cfgs);
+ }
+}
+
+pub fn check_blobs() {
+ // Load each `.a` file in `bin` into memory.
+ let mut files_before = BTreeMap::new();
+ for entry in fs::read_dir("bin").unwrap() {
+ let entry = entry.unwrap();
+ if entry.path().extension().unwrap() == "a" {
+ files_before.insert(
+ entry
+ .path()
+ .file_name()
+ .unwrap()
+ .to_str()
+ .unwrap()
+ .to_string(),
+ fs::read(entry.path()).unwrap(),
+ );
+ }
+ }
+
+ assemble_blobs();
+
+ let mut files_after = BTreeMap::new();
+ for entry in fs::read_dir("bin").unwrap() {
+ let entry = entry.unwrap();
+ if entry.path().extension().unwrap() == "a" {
+ files_after.insert(
+ entry
+ .path()
+ .file_name()
+ .unwrap()
+ .to_str()
+ .unwrap()
+ .to_string(),
+ fs::read(entry.path()).unwrap(),
+ );
+ }
+ }
+
+ // Ensure they contain the same files.
+ let before = files_before.keys().collect::<Vec<_>>();
+ let after = files_after.keys().collect::<Vec<_>>();
+ assert_eq!(before, after);
+
+ for ((file, before), (_, after)) in files_before.iter().zip(files_after.iter()) {
+ if before != after {
+ panic!("{} differs between rebuilds", file);
+ }
+ }
+
+ println!("Blobs identical.");
+}
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
new file mode 100644
index 0000000..ec55bf8
--- /dev/null
+++ b/xtask/src/main.rs
@@ -0,0 +1,18 @@
+use std::{env, process};
+use xtask::{assemble_blobs, check_blobs};
+
+fn main() {
+ let subcommand = env::args().skip(1).next();
+ match subcommand.as_ref().map(|s| &**s) {
+ Some("assemble") => assemble_blobs(),
+ Some("check-blobs") => check_blobs(),
+ _ => {
+ eprintln!("usage: cargo xtask <subcommand>");
+ eprintln!();
+ eprintln!("subcommands:");
+ eprintln!(" assemble Reassemble the pre-built artifacts");
+ eprintln!(" check-blobs Check that the pre-built artifacts are up-to-date and reproducible");
+ process::exit(1);
+ }
+ }
+}
diff --git a/xtask/tests/ci.rs b/xtask/tests/ci.rs
new file mode 100644
index 0000000..48356e4
--- /dev/null
+++ b/xtask/tests/ci.rs
@@ -0,0 +1,57 @@
+use std::process::Command;
+use std::{env, str};
+use xtask::{check_blobs, install_targets};
+
+static TARGETS: &[&str] = &[
+ "thumbv6m-none-eabi",
+ "thumbv7m-none-eabi",
+ "thumbv7em-none-eabi",
+ "thumbv7em-none-eabihf",
+ "thumbv8m.base-none-eabi",
+ "thumbv8m.main-none-eabi",
+ "thumbv8m.main-none-eabihf",
+];
+
+fn build(target: &str, features: &[&str]) {
+ println!("building for {} {:?}", target, features);
+ let mut cargo = Command::new("cargo");
+ cargo.args(&["build", "--target", target]);
+ for feat in features {
+ cargo.args(&["--features", *feat]);
+ }
+
+ let status = cargo.status().unwrap();
+ assert!(status.success());
+}
+
+fn main() {
+ // Tests execute in the containing crate's root dir, `cd ..` so that we find `asm` etc.
+ env::set_current_dir("..").unwrap();
+
+ install_targets(&mut 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");
+
+ // Build `cortex-m` for each supported target.
+ for target in TARGETS {
+ build(*target, &[]);
+
+ if is_nightly {
+ // This may fail when nightly breaks. That's fine, the CI job isn't essential.
+ build(*target, &["inline-asm"]);
+ }
+
+ if target.starts_with("thumbv7em") {
+ // These can target Cortex-M7s, which have an errata workaround.
+ build(*target, &["cm7-r0p1"]);
+
+ if is_nightly {
+ build(*target, &["inline-asm", "cm7-r0p1"]);
+ }
+ }
+ }
+}