diff options
-rw-r--r-- | .cargo/config | 2 | ||||
-rw-r--r-- | .travis.yml | 89 | ||||
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | Cargo.toml | 13 | ||||
-rw-r--r-- | asm-cm7-r0p1.s | 35 | ||||
-rw-r--r-- | asm-fpu.s | 21 | ||||
-rw-r--r-- | asm-toolchain | 1 | ||||
-rw-r--r-- | asm-v7.s | 78 | ||||
-rw-r--r-- | asm-v8-main.s | 43 | ||||
-rw-r--r-- | asm-v8.s | 42 | ||||
-rw-r--r-- | asm.rs | 346 | ||||
-rw-r--r-- | asm.s | 192 | ||||
-rwxr-xr-x | assemble.sh | 47 | ||||
-rw-r--r-- | bin/thumbv6m-none-eabi-lto.a | bin | 0 -> 12016 bytes | |||
-rw-r--r-- | bin/thumbv6m-none-eabi.a | bin | 5782 -> 16188 bytes | |||
-rw-r--r-- | bin/thumbv7em-none-eabi-lto.a | bin | 0 -> 16704 bytes | |||
-rw-r--r-- | bin/thumbv7em-none-eabi.a | bin | 11130 -> 20056 bytes | |||
-rw-r--r-- | bin/thumbv7em-none-eabihf-lto.a | bin | 0 -> 17504 bytes | |||
-rw-r--r-- | bin/thumbv7em-none-eabihf.a | bin | 13164 -> 21112 bytes | |||
-rw-r--r-- | bin/thumbv7m-none-eabi-lto.a | bin | 0 -> 15436 bytes | |||
-rw-r--r-- | bin/thumbv7m-none-eabi.a | bin | 8920 -> 19044 bytes | |||
-rw-r--r-- | bin/thumbv8m.base-none-eabi-lto.a | bin | 0 -> 13392 bytes | |||
-rw-r--r-- | bin/thumbv8m.base-none-eabi.a | bin | 8198 -> 17976 bytes | |||
-rw-r--r-- | bin/thumbv8m.main-none-eabi-lto.a | bin | 0 -> 18040 bytes | |||
-rw-r--r-- | bin/thumbv8m.main-none-eabi.a | bin | 13908 -> 22776 bytes | |||
-rw-r--r-- | bin/thumbv8m.main-none-eabihf-lto.a | bin | 0 -> 18776 bytes | |||
-rw-r--r-- | bin/thumbv8m.main-none-eabihf.a | bin | 15948 -> 23764 bytes | |||
-rw-r--r-- | build.rs | 13 | ||||
-rwxr-xr-x | check-blobs.sh | 21 | ||||
-rw-r--r-- | ci/install.sh | 15 | ||||
-rw-r--r-- | ci/script.sh | 45 | ||||
-rw-r--r-- | src/delay.rs | 119 | ||||
-rw-r--r-- | src/lib.rs | 28 | ||||
-rw-r--r-- | src/peripheral/mod.rs | 2 | ||||
-rw-r--r-- | src/peripheral/nvic.rs | 4 | ||||
-rw-r--r-- | src/prelude.rs | 3 | ||||
-rw-r--r-- | xtask/Cargo.toml | 13 | ||||
-rw-r--r-- | xtask/src/lib.rs | 206 | ||||
-rw-r--r-- | xtask/src/main.rs | 18 | ||||
-rw-r--r-- | xtask/tests/ci.rs | 57 |
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 @@ -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 @@ -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(); + } +} @@ -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 Binary files differnew file mode 100644 index 0000000..4d2f02c --- /dev/null +++ b/bin/thumbv6m-none-eabi-lto.a diff --git a/bin/thumbv6m-none-eabi.a b/bin/thumbv6m-none-eabi.a Binary files differindex b2f72b4..beedd73 100644 --- a/bin/thumbv6m-none-eabi.a +++ b/bin/thumbv6m-none-eabi.a diff --git a/bin/thumbv7em-none-eabi-lto.a b/bin/thumbv7em-none-eabi-lto.a Binary files differnew file mode 100644 index 0000000..7b8a4f8 --- /dev/null +++ b/bin/thumbv7em-none-eabi-lto.a diff --git a/bin/thumbv7em-none-eabi.a b/bin/thumbv7em-none-eabi.a Binary files differindex 5d5c48f..588e5cd 100644 --- a/bin/thumbv7em-none-eabi.a +++ b/bin/thumbv7em-none-eabi.a diff --git a/bin/thumbv7em-none-eabihf-lto.a b/bin/thumbv7em-none-eabihf-lto.a Binary files differnew file mode 100644 index 0000000..4efadd8 --- /dev/null +++ b/bin/thumbv7em-none-eabihf-lto.a diff --git a/bin/thumbv7em-none-eabihf.a b/bin/thumbv7em-none-eabihf.a Binary files differindex 1570f83..fd08ba5 100644 --- a/bin/thumbv7em-none-eabihf.a +++ b/bin/thumbv7em-none-eabihf.a diff --git a/bin/thumbv7m-none-eabi-lto.a b/bin/thumbv7m-none-eabi-lto.a Binary files differnew file mode 100644 index 0000000..28e5860 --- /dev/null +++ b/bin/thumbv7m-none-eabi-lto.a diff --git a/bin/thumbv7m-none-eabi.a b/bin/thumbv7m-none-eabi.a Binary files differindex 51ab710..4559ad4 100644 --- a/bin/thumbv7m-none-eabi.a +++ b/bin/thumbv7m-none-eabi.a diff --git a/bin/thumbv8m.base-none-eabi-lto.a b/bin/thumbv8m.base-none-eabi-lto.a Binary files differnew file mode 100644 index 0000000..ee46792 --- /dev/null +++ b/bin/thumbv8m.base-none-eabi-lto.a diff --git a/bin/thumbv8m.base-none-eabi.a b/bin/thumbv8m.base-none-eabi.a Binary files differindex 33bcff2..26d25bd 100644 --- a/bin/thumbv8m.base-none-eabi.a +++ b/bin/thumbv8m.base-none-eabi.a diff --git a/bin/thumbv8m.main-none-eabi-lto.a b/bin/thumbv8m.main-none-eabi-lto.a Binary files differnew file mode 100644 index 0000000..e3aa0cf --- /dev/null +++ b/bin/thumbv8m.main-none-eabi-lto.a diff --git a/bin/thumbv8m.main-none-eabi.a b/bin/thumbv8m.main-none-eabi.a Binary files differindex 1b2418d..10bbf53 100644 --- a/bin/thumbv8m.main-none-eabi.a +++ b/bin/thumbv8m.main-none-eabi.a diff --git a/bin/thumbv8m.main-none-eabihf-lto.a b/bin/thumbv8m.main-none-eabihf-lto.a Binary files differnew file mode 100644 index 0000000..1c25494 --- /dev/null +++ b/bin/thumbv8m.main-none-eabihf-lto.a diff --git a/bin/thumbv8m.main-none-eabihf.a b/bin/thumbv8m.main-none-eabihf.a Binary files differindex 312dcd2..0651788 100644 --- a/bin/thumbv8m.main-none-eabihf.a +++ b/bin/thumbv8m.main-none-eabihf.a @@ -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)) + } +} @@ -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"]); + } + } + } +} |