aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/bors.toml2
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--CHANGELOG.md4
-rw-r--r--Cargo.toml9
-rw-r--r--README.md2
-rw-r--r--asm-toolchain2
-rw-r--r--asm/inline.rs124
-rw-r--r--asm/lib.rs2
-rw-r--r--bin/thumbv6m-none-eabi-lto.abin11788 -> 11196 bytes
-rw-r--r--bin/thumbv6m-none-eabi.abin16344 -> 14576 bytes
-rw-r--r--bin/thumbv7em-none-eabi-lto.abin15928 -> 15280 bytes
-rw-r--r--bin/thumbv7em-none-eabi.abin21532 -> 19336 bytes
-rw-r--r--bin/thumbv7em-none-eabihf-lto.abin16892 -> 16104 bytes
-rw-r--r--bin/thumbv7em-none-eabihf.abin22828 -> 20480 bytes
-rw-r--r--bin/thumbv7m-none-eabi-lto.abin14908 -> 14244 bytes
-rw-r--r--bin/thumbv7m-none-eabi.abin20184 -> 18068 bytes
-rw-r--r--bin/thumbv8m.base-none-eabi-lto.abin15056 -> 14280 bytes
-rw-r--r--bin/thumbv8m.base-none-eabi.abin20772 -> 18680 bytes
-rw-r--r--bin/thumbv8m.main-none-eabi-lto.abin19552 -> 18672 bytes
-rw-r--r--bin/thumbv8m.main-none-eabi.abin27136 -> 24408 bytes
-rw-r--r--bin/thumbv8m.main-none-eabihf-lto.abin20528 -> 19508 bytes
-rw-r--r--bin/thumbv8m.main-none-eabihf.abin28404 -> 25524 bytes
-rw-r--r--build.rs5
-rw-r--r--src/delay.rs22
-rw-r--r--src/lib.rs4
-rw-r--r--src/macros.rs3
-rw-r--r--src/peripheral/ac.rs93
-rw-r--r--src/peripheral/dcb.rs21
-rw-r--r--src/peripheral/dwt.rs293
-rw-r--r--src/peripheral/icb.rs6
-rw-r--r--src/peripheral/itm.rs124
-rw-r--r--src/peripheral/mod.rs57
-rw-r--r--src/peripheral/nvic.rs2
-rw-r--r--src/peripheral/scb.rs20
-rw-r--r--src/peripheral/tpiu.rs135
-rw-r--r--xtask/Cargo.toml2
-rw-r--r--xtask/src/lib.rs21
-rw-r--r--xtask/src/main.rs8
-rw-r--r--xtask/tests/ci.rs5
39 files changed, 854 insertions, 114 deletions
diff --git a/.github/bors.toml b/.github/bors.toml
index 63d883f..0cf10c7 100644
--- a/.github/bors.toml
+++ b/.github/bors.toml
@@ -3,7 +3,7 @@ delete_merged_branches = true
required_approvals = 1
status = [
"ci-linux (stable)",
- "ci-linux (1.38.0)",
+ "ci-linux (1.40.0)",
"rustfmt",
"clippy",
]
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7289085..8600054 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,7 +16,7 @@ jobs:
include:
# Test MSRV
- - rust: 1.38.0
+ - rust: 1.40.0
# Test nightly but don't fail
- rust: nightly
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 67b0a57..fdb8be9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- LSU counter
- Folded-instruction counter
- Added `DWT.set_cycle_count` (#347).
+- Added support for the Cortex-M7 TCM and cache access control registers.
+ There is a feature `cm7` to enable access to these.
+- Added `delay::Delay::with_source`, a constructor that lets you specify
+ the SysTick clock source (#374).
### Deprecated
diff --git a/Cargo.toml b/Cargo.toml
index d932ffd..7c9afd0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,10 +21,17 @@ volatile-register = "0.2.0"
bitfield = "0.13.2"
embedded-hal = "0.2.4"
+[dependencies.serde]
+version = "1"
+features = [ "derive" ]
+optional = true
+
[features]
-cm7-r0p1 = []
+cm7 = []
+cm7-r0p1 = ["cm7"]
inline-asm = []
linker-plugin-lto = []
+std-map = []
[workspace]
members = ["xtask", "cortex-m-semihosting", "panic-semihosting", "panic-itm"]
diff --git a/README.md b/README.md
index 6bd8aed..6011ab2 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ This project is developed and maintained by the [Cortex-M team][team].
## Minimum Supported Rust Version (MSRV)
-This crate is guaranteed to compile on stable Rust 1.38 and up. It might compile with older versions but that may change in any new patch release.
+This crate is guaranteed to compile on stable Rust 1.40 and up. It might compile with older versions but that may change in any new patch release.
## License
diff --git a/asm-toolchain b/asm-toolchain
index a36829b..cc5dbb2 100644
--- a/asm-toolchain
+++ b/asm-toolchain
@@ -1 +1 @@
-nightly-2020-08-26
+nightly-2021-12-16
diff --git a/asm/inline.rs b/asm/inline.rs
index 5887baf..bbc04d2 100644
--- a/asm/inline.rs
+++ b/asm/inline.rs
@@ -6,17 +6,18 @@
//! All of these functions should be blanket-`unsafe`. `cortex-m` provides safe wrappers where
//! applicable.
+use core::arch::asm;
use core::sync::atomic::{compiler_fence, Ordering};
#[inline(always)]
pub unsafe fn __bkpt() {
- asm!("bkpt");
+ asm!("bkpt", options(nomem, nostack, preserves_flags));
}
#[inline(always)]
pub unsafe fn __control_r() -> u32 {
let r;
- asm!("mrs {}, CONTROL", out(reg) r);
+ asm!("mrs {}, CONTROL", out(reg) r, options(nomem, nostack, preserves_flags));
r
}
@@ -27,7 +28,8 @@ pub unsafe fn __control_w(w: u32) {
asm!(
"msr CONTROL, {}",
"isb",
- in(reg) w
+ in(reg) w,
+ options(nomem, nostack, preserves_flags),
);
// Ensure memory accesses are not reordered around the CONTROL update.
@@ -36,7 +38,7 @@ pub unsafe fn __control_w(w: u32) {
#[inline(always)]
pub unsafe fn __cpsid() {
- asm!("cpsid i");
+ asm!("cpsid i", options(nomem, nostack, preserves_flags));
// Ensure no subsequent memory accesses are reordered to before interrupts are disabled.
compiler_fence(Ordering::SeqCst);
@@ -47,7 +49,7 @@ pub unsafe fn __cpsie() {
// Ensure no preceeding memory accesses are reordered to after interrupts are enabled.
compiler_fence(Ordering::SeqCst);
- asm!("cpsie i");
+ asm!("cpsie i", options(nomem, nostack, preserves_flags));
}
#[inline(always)]
@@ -62,48 +64,53 @@ pub unsafe fn __delay(cyc: u32) {
"1:",
"subs {}, #1",
"bne 1b",
- inout(reg) real_cyc => _
+ inout(reg) real_cyc => _,
+ options(nomem, nostack),
);
}
#[inline(always)]
pub unsafe fn __dmb() {
compiler_fence(Ordering::SeqCst);
- asm!("dmb");
+ asm!("dmb", options(nomem, nostack, preserves_flags));
compiler_fence(Ordering::SeqCst);
}
#[inline(always)]
pub unsafe fn __dsb() {
compiler_fence(Ordering::SeqCst);
- asm!("dsb");
+ asm!("dsb", options(nomem, nostack, preserves_flags));
compiler_fence(Ordering::SeqCst);
}
#[inline(always)]
pub unsafe fn __isb() {
compiler_fence(Ordering::SeqCst);
- asm!("isb");
+ asm!("isb", options(nomem, nostack, preserves_flags));
compiler_fence(Ordering::SeqCst);
}
#[inline(always)]
pub unsafe fn __msp_r() -> u32 {
let r;
- asm!("mrs {}, MSP", out(reg) r);
+ asm!("mrs {}, MSP", out(reg) r, options(nomem, nostack, preserves_flags));
r
}
#[inline(always)]
pub unsafe fn __msp_w(val: u32) {
- asm!("msr MSP, {}", in(reg) val);
+ // Technically is writing to the stack pointer "not pushing any data to the stack"?
+ // In any event, if we don't set `nostack` here, this method is useless as the new
+ // stack value is immediately mutated by returning. Really this is just not a good
+ // method and its higher-level use is marked as deprecated in cortex-m.
+ asm!("msr MSP, {}", in(reg) val, options(nomem, nostack, preserves_flags));
}
// NOTE: No FFI shim, this requires inline asm.
#[inline(always)]
pub unsafe fn __apsr_r() -> u32 {
let r;
- asm!("mrs {}, APSR", out(reg) r);
+ asm!("mrs {}, APSR", out(reg) r, options(nomem, nostack, preserves_flags));
r
}
@@ -112,80 +119,82 @@ 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");
+ asm!("nop", options(nomem, nostack, preserves_flags));
}
// NOTE: No FFI shim, this requires inline asm.
#[inline(always)]
pub unsafe fn __pc_r() -> u32 {
let r;
- asm!("mov {}, pc", out(reg) r);
+ asm!("mov {}, pc", out(reg) r, options(nomem, nostack, preserves_flags));
r
}
// NOTE: No FFI shim, this requires inline asm.
#[inline(always)]
pub unsafe fn __pc_w(val: u32) {
- asm!("mov pc, {}", in(reg) val);
+ asm!("mov pc, {}", in(reg) val, options(nomem, nostack, preserves_flags));
}
// NOTE: No FFI shim, this requires inline asm.
#[inline(always)]
pub unsafe fn __lr_r() -> u32 {
let r;
- asm!("mov {}, lr", out(reg) r);
+ asm!("mov {}, lr", out(reg) r, options(nomem, nostack, preserves_flags));
r
}
// NOTE: No FFI shim, this requires inline asm.
#[inline(always)]
pub unsafe fn __lr_w(val: u32) {
- asm!("mov lr, {}", in(reg) val);
+ asm!("mov lr, {}", in(reg) val, options(nomem, nostack, preserves_flags));
}
#[inline(always)]
pub unsafe fn __primask_r() -> u32 {
let r;
- asm!("mrs {}, PRIMASK", out(reg) r);
+ asm!("mrs {}, PRIMASK", out(reg) r, options(nomem, nostack, preserves_flags));
r
}
#[inline(always)]
pub unsafe fn __psp_r() -> u32 {
let r;
- asm!("mrs {}, PSP", out(reg) r);
+ asm!("mrs {}, PSP", out(reg) r, options(nomem, nostack, preserves_flags));
r
}
#[inline(always)]
pub unsafe fn __psp_w(val: u32) {
- asm!("msr PSP, {}", in(reg) val);
+ // See comment on __msp_w. Unlike MSP, there are legitimate use-cases for modifying PSP
+ // if MSP is currently being used as the stack pointer.
+ asm!("msr PSP, {}", in(reg) val, options(nomem, nostack, preserves_flags));
}
#[inline(always)]
pub unsafe fn __sev() {
- asm!("sev");
+ asm!("sev", options(nomem, nostack, preserves_flags));
}
#[inline(always)]
pub unsafe fn __udf() -> ! {
- asm!("udf #0", options(noreturn));
+ asm!("udf #0", options(noreturn, nomem, nostack, preserves_flags));
}
#[inline(always)]
pub unsafe fn __wfe() {
- asm!("wfe");
+ asm!("wfe", options(nomem, nostack, preserves_flags));
}
#[inline(always)]
pub unsafe fn __wfi() {
- asm!("wfi");
+ asm!("wfi", options(nomem, nostack, preserves_flags));
}
/// Semihosting syscall.
#[inline(always)]
pub unsafe fn __sh_syscall(mut nr: u32, arg: u32) -> u32 {
- asm!("bkpt #0xab", inout("r0") nr, in("r1") arg);
+ asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nomem, nostack, preserves_flags));
nr
}
@@ -205,7 +214,7 @@ pub unsafe fn __bootstrap(msp: u32, rv: u32) -> ! {
spsel = in(reg) 2,
msp = in(reg) msp,
rv = in(reg) rv,
- options(noreturn),
+ options(noreturn, nomem, nostack),
);
}
@@ -214,29 +223,30 @@ pub unsafe fn __bootstrap(msp: u32, rv: u32) -> ! {
pub use self::v7m::*;
#[cfg(any(armv7m, armv8m_main))]
mod v7m {
+ use core::arch::asm;
use core::sync::atomic::{compiler_fence, Ordering};
#[inline(always)]
pub unsafe fn __basepri_max(val: u8) {
- asm!("msr BASEPRI_MAX, {}", in(reg) val);
+ asm!("msr BASEPRI_MAX, {}", in(reg) val, options(nomem, nostack, preserves_flags));
}
#[inline(always)]
pub unsafe fn __basepri_r() -> u8 {
let r;
- asm!("mrs {}, BASEPRI", out(reg) r);
+ asm!("mrs {}, BASEPRI", out(reg) r, options(nomem, nostack, preserves_flags));
r
}
#[inline(always)]
pub unsafe fn __basepri_w(val: u8) {
- asm!("msr BASEPRI, {}", in(reg) val);
+ asm!("msr BASEPRI, {}", in(reg) val, options(nomem, nostack, preserves_flags));
}
#[inline(always)]
pub unsafe fn __faultmask_r() -> u32 {
let r;
- asm!("mrs {}, FAULTMASK", out(reg) r);
+ asm!("mrs {}, FAULTMASK", out(reg) r, options(nomem, nostack, preserves_flags));
r
}
@@ -255,6 +265,7 @@ mod v7m {
out(reg) _,
out(reg) _,
out(reg) _,
+ options(nostack),
);
compiler_fence(Ordering::SeqCst);
}
@@ -274,6 +285,7 @@ mod v7m {
out(reg) _,
out(reg) _,
out(reg) _,
+ options(nostack),
);
compiler_fence(Ordering::SeqCst);
}
@@ -283,6 +295,8 @@ mod v7m {
pub use self::v7em::*;
#[cfg(armv7em)]
mod v7em {
+ use core::arch::asm;
+
#[inline(always)]
pub unsafe fn __basepri_max_cm7_r0p1(val: u8) {
asm!(
@@ -295,6 +309,7 @@ mod v7em {
"cpsie i",
in(reg) val,
out(reg) _,
+ options(nomem, nostack, preserves_flags),
);
}
@@ -310,6 +325,7 @@ mod v7em {
"cpsie i",
in(reg) val,
out(reg) _,
+ options(nomem, nostack, preserves_flags),
);
}
}
@@ -319,45 +335,63 @@ pub use self::v8m::*;
/// Baseline and Mainline.
#[cfg(armv8m)]
mod v8m {
+ use core::arch::asm;
+
#[inline(always)]
pub unsafe fn __tt(mut target: u32) -> u32 {
- asm!("tt {target}, {target}", target = inout(reg) target);
+ asm!(
+ "tt {target}, {target}",
+ target = inout(reg) target,
+ options(nomem, nostack, preserves_flags),
+ );
target
}
#[inline(always)]
pub unsafe fn __ttt(mut target: u32) -> u32 {
- asm!("ttt {target}, {target}", target = inout(reg) target);
+ asm!(
+ "ttt {target}, {target}",
+ target = inout(reg) target,
+ options(nomem, nostack, preserves_flags),
+ );
target
}
#[inline(always)]
pub unsafe fn __tta(mut target: u32) -> u32 {
- asm!("tta {target}, {target}", target = inout(reg) target);
+ asm!(
+ "tta {target}, {target}",
+ target = inout(reg) target,
+ options(nomem, nostack, preserves_flags),
+ );
target
}
#[inline(always)]
pub unsafe fn __ttat(mut target: u32) -> u32 {
- asm!("ttat {target}, {target}", target = inout(reg) target);
+ asm!(
+ "ttat {target}, {target}",
+ target = inout(reg) target,
+ options(nomem, nostack, preserves_flags),
+ );
target
}
#[inline(always)]
pub unsafe fn __msp_ns_r() -> u32 {
let r;
- asm!("mrs {}, MSP_NS", out(reg) r);
+ asm!("mrs {}, MSP_NS", out(reg) r, options(nomem, nostack, preserves_flags));
r
}
#[inline(always)]
pub unsafe fn __msp_ns_w(val: u32) {
- asm!("msr MSP_NS, {}", in(reg) val);
+ asm!("msr MSP_NS, {}", in(reg) val, options(nomem, nostack, preserves_flags));
}
#[inline(always)]
pub unsafe fn __bxns(val: u32) {
- asm!("BXNS {}", in(reg) val);
+ asm!("BXNS {}", in(reg) val, options(nomem, nostack, preserves_flags));
}
}
@@ -366,28 +400,30 @@ pub use self::v8m_main::*;
/// Mainline only.
#[cfg(armv8m_main)]
mod v8m_main {
+ use core::arch::asm;
+
#[inline(always)]
pub unsafe fn __msplim_r() -> u32 {
let r;
- asm!("mrs {}, MSPLIM", out(reg) r);
+ asm!("mrs {}, MSPLIM", out(reg) r, options(nomem, nostack, preserves_flags));
r
}
#[inline(always)]
pub unsafe fn __msplim_w(val: u32) {
- asm!("msr MSPLIM, {}", in(reg) val);
+ asm!("msr MSPLIM, {}", in(reg) val, options(nomem, nostack, preserves_flags));
}
#[inline(always)]
pub unsafe fn __psplim_r() -> u32 {
let r;
- asm!("mrs {}, PSPLIM", out(reg) r);
+ asm!("mrs {}, PSPLIM", out(reg) r, options(nomem, nostack, preserves_flags));
r
}
#[inline(always)]
pub unsafe fn __psplim_w(val: u32) {
- asm!("msr PSPLIM, {}", in(reg) val);
+ asm!("msr PSPLIM, {}", in(reg) val, options(nomem, nostack, preserves_flags));
}
}
@@ -396,15 +432,17 @@ pub use self::fpu::*;
/// All targets with FPU.
#[cfg(has_fpu)]
mod fpu {
+ use core::arch::asm;
+
#[inline(always)]
pub unsafe fn __fpscr_r() -> u32 {
let r;
- asm!("vmrs {}, fpscr", out(reg) r);
+ asm!("vmrs {}, fpscr", out(reg) r, options(nomem, nostack, preserves_flags));
r
}
#[inline(always)]
pub unsafe fn __fpscr_w(val: u32) {
- asm!("vmsr fpscr, {}", in(reg) val);
+ asm!("vmsr fpscr, {}", in(reg) val, options(nomem, nostack));
}
}
diff --git a/asm/lib.rs b/asm/lib.rs
index fc8ddc8..48f3dc2 100644
--- a/asm/lib.rs
+++ b/asm/lib.rs
@@ -34,6 +34,8 @@
#![no_std]
#![crate_type = "staticlib"]
#![deny(warnings)]
+// Don't warn about feature(asm) being stable on Rust >= 1.59.0
+#![allow(stable_features)]
mod inline;
diff --git a/bin/thumbv6m-none-eabi-lto.a b/bin/thumbv6m-none-eabi-lto.a
index 6136ec6..a203d7a 100644
--- a/bin/thumbv6m-none-eabi-lto.a
+++ 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 c42f579..9640a69 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
index c2c040a..b34ac64 100644
--- a/bin/thumbv7em-none-eabi-lto.a
+++ 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 660360f..88acbdd 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
index eba1984..6de94bb 100644
--- a/bin/thumbv7em-none-eabihf-lto.a
+++ 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 1561fa4..cf91a7a 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
index d964314..7f677a9 100644
--- a/bin/thumbv7m-none-eabi-lto.a
+++ 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 f541274..ff4bf21 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
index 8a6ed42..f62acaf 100644
--- a/bin/thumbv8m.base-none-eabi-lto.a
+++ 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 33cd908..c0cc96c 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
index 8e6ff0a..1a51515 100644
--- a/bin/thumbv8m.main-none-eabi-lto.a
+++ 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 898fea7..d017a15 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
index d9a636a..fd3dc92 100644
--- a/bin/thumbv8m.main-none-eabihf-lto.a
+++ 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 b0513b7..223ff1d 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 dc9b3a0..23ceeba 100644
--- a/build.rs
+++ b/build.rs
@@ -3,9 +3,14 @@ use std::{env, fs};
fn main() {
let target = env::var("TARGET").unwrap();
+ let host_triple = env::var("HOST").unwrap();
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let name = env::var("CARGO_PKG_NAME").unwrap();
+ if host_triple == target {
+ println!("cargo:rustc-cfg=native");
+ }
+
if target.starts_with("thumb") {
let suffix = if env::var_os("CARGO_FEATURE_LINKER_PLUGIN_LTO").is_some() {
"-lto"
diff --git a/src/delay.rs b/src/delay.rs
index 8ed1fea..66a63bf 100644
--- a/src/delay.rs
+++ b/src/delay.rs
@@ -6,7 +6,7 @@ use embedded_hal::blocking::delay::{DelayMs, DelayUs};
/// System timer (SysTick) as a delay provider.
pub struct Delay {
syst: SYST,
- ahb_frequency: u32,
+ frequency: u32,
}
impl Delay {
@@ -14,13 +14,19 @@ impl Delay {
///
/// `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);
+ pub fn new(syst: SYST, ahb_frequency: u32) -> Self {
+ Self::with_source(syst, ahb_frequency, SystClkSource::Core)
+ }
- Delay {
- syst,
- ahb_frequency,
- }
+ /// Configures the system timer (SysTick) as a delay provider
+ /// with a clock source.
+ ///
+ /// `frequency` is the frequency of your `clock_source` in Hz.
+ #[inline]
+ pub fn with_source(mut syst: SYST, frequency: u32, clock_source: SystClkSource) -> Self {
+ syst.set_clock_source(clock_source);
+
+ Delay { syst, frequency }
}
/// Releases the system timer (SysTick) resource.
@@ -32,7 +38,7 @@ impl Delay {
/// Delay using the Cortex-M systick for a certain duration, in µs.
#[allow(clippy::missing_inline_in_public_items)]
pub fn delay_us(&mut self, us: u32) {
- let ticks = (u64::from(us)) * (u64::from(self.ahb_frequency)) / 1_000_000;
+ let ticks = (u64::from(us)) * (u64::from(self.frequency)) / 1_000_000;
let full_cycles = ticks >> 24;
if full_cycles > 0 {
diff --git a/src/lib.rs b/src/lib.rs
index 6a73692..0914639 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -52,7 +52,7 @@
//!
//! # Minimum Supported Rust Version (MSRV)
//!
-//! This crate is guaranteed to compile on stable Rust 1.38 and up. It *might*
+//! This crate is guaranteed to compile on stable Rust 1.40 and up. It *might*
//! compile with older versions but that may change in any new patch release.
#![cfg_attr(feature = "inline-asm", feature(asm))]
@@ -76,6 +76,8 @@
// - A generated #[derive(Debug)] function (in which case the attribute needs
// to be applied to the struct).
#![deny(clippy::missing_inline_in_public_items)]
+// Don't warn about feature(asm) being stable on Rust >= 1.59.0
+#![allow(stable_features)]
extern crate bare_metal;
extern crate volatile_register;
diff --git a/src/macros.rs b/src/macros.rs
index b578370..66b75b1 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -30,6 +30,9 @@ macro_rules! iprintln {
/// `None` variant the caller must ensure that the macro is called from a function that's executed
/// at most once in the whole lifetime of the program.
///
+/// # Note
+/// This macro is unsound on multi core systems.
+///
/// # Example
///
/// ``` no_run
diff --git a/src/peripheral/ac.rs b/src/peripheral/ac.rs
new file mode 100644
index 0000000..1ac5be1
--- /dev/null
+++ b/src/peripheral/ac.rs
@@ -0,0 +1,93 @@
+//! Cortex-M7 TCM and Cache access control.
+
+use volatile_register::RW;
+
+/// Register block
+#[repr(C)]
+pub struct RegisterBlock {
+ /// Instruction Tightly-Coupled Memory Control Register
+ pub itcmcr: RW<u32>,
+ /// Data Tightly-Coupled Memory Control Register
+ pub dtcmcr: RW<u32>,
+ /// AHBP Control Register
+ pub ahbpcr: RW<u32>,
+ /// L1 Cache Control Register
+ pub cacr: RW<u32>,
+ /// AHB Slave Control Register
+ pub ahbscr: RW<u32>,
+ reserved0: u32,
+ /// Auxilary Bus Fault Status Register
+ pub abfsr: RW<u32>,
+}
+
+/// ITCMCR and DTCMCR TCM enable bit.
+pub const TCM_EN: u32 = 1;
+
+/// ITCMCR and DTCMCR TCM read-modify-write bit.
+pub const TCM_RMW: u32 = 2;
+
+/// ITCMCR and DTCMCR TCM rety phase enable bit.
+pub const TCM_RETEN: u32 = 4;
+
+/// ITCMCR and DTCMCR TCM size mask.
+pub const TCM_SZ_MASK: u32 = 0x78;
+
+/// ITCMCR and DTCMCR TCM shift.
+pub const TCM_SZ_SHIFT: usize = 3;
+
+/// AHBPCR AHBP enable bit.
+pub const AHBPCR_EN: u32 = 1;
+
+/// AHBPCR AHBP size mask.
+pub const AHBPCR_SZ_MASK: u32 = 0x0e;
+
+/// AHBPCR AHBP size shit.
+pub const AHBPCR_SZ_SHIFT: usize = 1;
+
+/// CACR Shared cachedable-is-WT for data cache.
+pub const CACR_SIWT: u32 = 1;
+
+/// CACR ECC in the instruction and data cache (disable).
+pub const CACR_ECCDIS: u32 = 2;
+
+/// CACR Force Write-Through in the data cache.
+pub const CACR_FORCEWT: u32 = 4;
+
+/// AHBSCR AHBS prioritization control mask.
+pub const AHBSCR_CTL_MASK: u32 = 0x03;
+
+/// AHBSCR AHBS prioritization control shift.
+pub const AHBSCR_CTL_SHIFT: usize = 0;
+
+/// AHBSCR Threshold execution prioity for AHBS traffic demotion, mask.
+pub const AHBSCR_TPRI_MASK: u32 = 0x7fc;
+
+/// AHBSCR Threshold execution prioity for AHBS traffic demotion, shift.
+pub const AHBSCR_TPRI_SHIFT: usize = 2;
+
+/// AHBSCR Failness counter initialization value, mask.
+pub const AHBSCR_INITCOUNT_MASK: u32 = 0xf800;
+
+/// AHBSCR Failness counter initialization value, shift.
+pub const AHBSCR_INITCOUNT_SHIFT: usize = 11;
+
+/// ABFSR Async fault on ITCM interface.
+pub const ABFSR_ITCM: u32 = 1;
+
+/// ABFSR Async fault on DTCM interface.
+pub const ABFSR_DTCM: u32 = 2;
+
+/// ABFSR Async fault on AHBP interface.
+pub const ABFSR_AHBP: u32 = 4;
+
+/// ABFSR Async fault on AXIM interface.
+pub const ABFSR_AXIM: u32 = 8;
+
+/// ABFSR Async fault on EPPB interface.
+pub const ABFSR_EPPB: u32 = 16;
+
+/// ABFSR Indicates the type of fault on the AXIM interface, mask.
+pub const ABFSR_AXIMTYPE_MASK: u32 = 0x300;
+
+/// ABFSR Indicates the type of fault on the AXIM interface, shift.
+pub const ABFSR_AXIMTYPE_SHIFT: usize = 8;
diff --git a/src/peripheral/dcb.rs b/src/peripheral/dcb.rs
index 5689cb4..ef879ac 100644
--- a/src/peripheral/dcb.rs
+++ b/src/peripheral/dcb.rs
@@ -6,6 +6,7 @@ use crate::peripheral::DCB;
use core::ptr;
const DCB_DEMCR_TRCENA: u32 = 1 << 24;
+const DCB_DEMCR_MON_EN: u32 = 1 << 16;
/// Register block
#[repr(C)]
@@ -25,6 +26,10 @@ impl DCB {
/// `peripheral::DWT` cycle counter to work properly.
/// As by STM documentation, this flag is not reset on
/// soft-reset, only on power reset.
+ ///
+ /// Note: vendor-specific registers may have to be set to completely
+ /// enable tracing. For example, on the STM32F401RE, `TRACE_MODE`
+ /// and `TRACE_IOEN` must be configured in `DBGMCU_CR` register.
#[inline]
pub fn enable_trace(&mut self) {
// set bit 24 / TRCENA
@@ -42,6 +47,22 @@ impl DCB {
}
}
+ /// Enables the [`DebugMonitor`](crate::peripheral::scb::Exception::DebugMonitor) exception
+ #[inline]
+ pub fn enable_debug_monitor(&mut self) {
+ unsafe {
+ self.demcr.modify(|w| w | DCB_DEMCR_MON_EN);
+ }
+ }
+
+ /// Disables the [`DebugMonitor`](crate::peripheral::scb::Exception::DebugMonitor) exception
+ #[inline]
+ pub fn disable_debug_monitor(&mut self) {
+ unsafe {
+ self.demcr.modify(|w| w & !DCB_DEMCR_MON_EN);
+ }
+ }
+
/// Is there a debugger attached? (see note)
///
/// Note: This function is [reported not to
diff --git a/src/peripheral/dwt.rs b/src/peripheral/dwt.rs
index 9e8e638..db0398d 100644
--- a/src/peripheral/dwt.rs
+++ b/src/peripheral/dwt.rs
@@ -5,12 +5,13 @@ use volatile_register::WO;
use volatile_register::{RO, RW};
use crate::peripheral::DWT;
+use bitfield::bitfield;
/// Register block
#[repr(C)]
pub struct RegisterBlock {
/// Control
- pub ctrl: RW<u32>,
+ pub ctrl: RW<Ctrl>,
/// Cycle Count
#[cfg(not(armv6m))]
pub cyccnt: RW<u32>,
@@ -50,6 +51,21 @@ pub struct RegisterBlock {
pub lsr: RO<u32>,
}
+bitfield! {
+ /// Control register.
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ pub struct Ctrl(u32);
+ cyccntena, set_cyccntena: 0;
+ pcsamplena, set_pcsamplena: 12;
+ exctrcena, set_exctrcena: 16;
+ noprfcnt, _: 24;
+ nocyccnt, _: 25;
+ noexttrig, _: 26;
+ notrcpkt, _: 27;
+ u8, numcomp, _: 31, 28;
+}
+
/// Comparator
#[repr(C)]
pub struct Comparator {
@@ -58,58 +74,66 @@ pub struct Comparator {
/// Comparator Mask
pub mask: RW<u32>,
/// Comparator Function
- pub function: RW<u32>,
+ pub function: RW<Function>,
reserved: u32,
}
-// DWT CTRL register fields
-const NUMCOMP_OFFSET: u32 = 28;
-const NOTRCPKT: u32 = 1 << 27;
-const NOEXTTRIG: u32 = 1 << 26;
-const NOCYCCNT: u32 = 1 << 25;
-const NOPRFCNT: u32 = 1 << 24;
-const CYCCNTENA: u32 = 1 << 0;
+bitfield! {
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ /// Comparator FUNCTIONn register.
+ ///
+ /// See C1.8.17 "Comparator Function registers, DWT_FUNCTIONn"
+ pub struct Function(u32);
+ u8, function, set_function: 3, 0;
+ emitrange, set_emitrange: 5;
+ cycmatch, set_cycmatch: 7;
+ datavmatch, set_datavmatch: 8;
+ lnk1ena, set_lnk1ena: 9;
+ u8, datavsize, set_datavsize: 11, 10;
+ u8, datavaddr0, set_datavaddr0: 15, 12;
+ u8, datavaddr1, set_datavaddr1: 19, 16;
+ matched, _: 24;
+}
impl DWT {
/// Number of comparators implemented
///
/// A value of zero indicates no comparator support.
#[inline]
- pub fn num_comp() -> u8 {
- // NOTE(unsafe) atomic read with no side effects
- unsafe { ((*Self::ptr()).ctrl.read() >> NUMCOMP_OFFSET) as u8 }
+ pub fn num_comp(&self) -> u8 {
+ self.ctrl.read().numcomp()
}
/// Returns `true` if the the implementation supports sampling and exception tracing
#[cfg(not(armv6m))]
#[inline]
- pub fn has_exception_trace() -> bool {
- // NOTE(unsafe) atomic read with no side effects
- unsafe { (*Self::ptr()).ctrl.read() & NOTRCPKT == 0 }
+ pub fn has_exception_trace(&self) -> bool {
+ !self.ctrl.read().notrcpkt()
}
/// Returns `true` if the implementation includes external match signals
#[cfg(not(armv6m))]
#[inline]
- pub fn has_external_match() -> bool {
- // NOTE(unsafe) atomic read with no side effects
- unsafe { (*Self::ptr()).ctrl.read() & NOEXTTRIG == 0 }
+ pub fn has_external_match(&self) -> bool {
+ !self.ctrl.read().noexttrig()
}
/// Returns `true` if the implementation supports a cycle counter
- #[cfg(not(armv6m))]
#[inline]
- pub fn has_cycle_counter() -> bool {
- // NOTE(unsafe) atomic read with no side effects
- unsafe { (*Self::ptr()).ctrl.read() & NOCYCCNT == 0 }
+ pub fn has_cycle_counter(&self) -> bool {
+ #[cfg(not(armv6m))]
+ return !self.ctrl.read().nocyccnt();
+
+ #[cfg(armv6m)]
+ return false;
}
/// Returns `true` if the implementation the profiling counters
#[cfg(not(armv6m))]
#[inline]
- pub fn has_profiling_counter() -> bool {
- // NOTE(unsafe) atomic read with no side effects
- unsafe { (*Self::ptr()).ctrl.read() & NOPRFCNT == 0 }
+ pub fn has_profiling_counter(&self) -> bool {
+ !self.ctrl.read().noprfcnt()
}
/// Enables the cycle counter
@@ -123,22 +147,55 @@ impl DWT {
#[cfg(not(armv6m))]
#[inline]
pub fn enable_cycle_counter(&mut self) {
- unsafe { self.ctrl.modify(|r| r | CYCCNTENA) }
+ unsafe {
+ self.ctrl.modify(|mut r| {
+ r.set_cyccntena(true);
+ r
+ });
+ }
}
- /// Disables the cycle counter
+ /// Returns `true` if the cycle counter is enabled
#[cfg(not(armv6m))]
#[inline]
- pub fn disable_cycle_counter(&mut self) {
- unsafe { self.ctrl.modify(|r| r & !CYCCNTENA) }
+ pub fn cycle_counter_enabled(&self) -> bool {
+ self.ctrl.read().cyccntena()
}
- /// Returns `true` if the cycle counter is enabled
+ /// Enables exception tracing
#[cfg(not(armv6m))]
#[inline]
- pub fn cycle_counter_enabled() -> bool {
- // NOTE(unsafe) atomic read with no side effects
- unsafe { (*Self::ptr()).ctrl.read() & CYCCNTENA != 0 }
+ pub fn enable_exception_tracing(&mut self) {
+ unsafe {
+ self.ctrl.modify(|mut r| {
+ r.set_exctrcena(true);
+ r
+ });
+ }
+ }
+
+ /// Disables exception tracing
+ #[cfg(not(armv6m))]
+ #[inline]
+ pub fn disable_exception_tracing(&mut self) {
+ unsafe {
+ self.ctrl.modify(|mut r| {
+ r.set_exctrcena(false);
+ r
+ });
+ }
+ }
+
+ /// Whether to periodically generate PC samples
+ #[cfg(not(armv6m))]
+ #[inline]
+ pub fn enable_pc_samples(&mut self, bit: bool) {
+ unsafe {
+ self.ctrl.modify(|mut r| {
+ r.set_pcsamplena(bit);
+ r
+ });
+ }
}
/// Returns the current clock cycle count
@@ -266,3 +323,173 @@ impl DWT {
unsafe { self.foldcnt.write(count as u32) }
}
}
+
+/// Whether the comparator should match on read, write or read/write operations.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub enum AccessType {
+ /// Generate packet only when matched address is read from.
+ ReadOnly,
+ /// Generate packet only when matched address is written to.
+ WriteOnly,
+ /// Generate packet when matched address is both read from and written to.
+ ReadWrite,
+}
+
+/// The sequence of packet(s) or events that should be emitted/generated on comparator match.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub enum EmitOption {
+ /// Emit only trace data value packet.
+ Data,
+ /// Emit only trace address packet.
+ Address,
+ /// Emit only trace PC value packet
+ ///
+ /// *NOTE* only compatible with [AccessType::ReadWrite].
+ PC,
+ /// Emit trace address and data value packets.
+ AddressData,
+ /// Emit trace PC value and data value packets.
+ PCData,
+ /// Generate a watchpoint debug event. Either halts execution or fires a `DebugMonitor` exception.
+ ///
+ /// See more in section "Watchpoint debug event generation" page C1-729.
+ WatchpointDebugEvent,
+ /// Generate a `CMPMATCH[N]` event.
+ ///
+ /// See more in section "CMPMATCH[N] event generation" page C1-730.
+ CompareMatchEvent,
+}
+
+/// Settings for address matching
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub struct ComparatorAddressSettings {
+ /// The address to match against.
+ pub address: u32,
+ /// The address mask to match against.
+ pub mask: u32,
+ /// What sequence of packet(s) to emit on comparator match.
+ pub emit: EmitOption,
+ /// Whether to match on read, write or read/write operations.
+ pub access_type: AccessType,
+}
+
+/// Settings for cycle count matching
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub struct CycleCountSettings {
+ /// The function selection used.
+ /// See Table C1-15 for DWT cycle count comparison functions.
+ pub emit: EmitOption,
+ /// The cycle count value to compare against.
+ pub compare: u32,
+}
+
+/// The available functions of a DWT comparator.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+#[non_exhaustive]
+pub enum ComparatorFunction {
+ /// Compare accessed memory addresses.
+ Address(ComparatorAddressSettings),
+ /// Compare cycle count & target value.
+ ///
+ /// **NOTE**: only supported by comparator 0 and if the HW supports the cycle counter.
+ /// Check [`DWT::has_cycle_counter`] for support. See C1.8.1 for more details.
+ CycleCount(CycleCountSettings),
+}
+
+/// Possible error values returned on [Comparator::configure].
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+#[non_exhaustive]
+pub enum DwtError {
+ /// Invalid combination of [AccessType] and [EmitOption].
+ InvalidFunction,
+}
+
+impl Comparator {
+ /// Configure the function of the comparator
+ #[allow(clippy::missing_inline_in_public_items)]
+ pub fn configure(&self, settings: ComparatorFunction) -> Result<(), DwtError> {
+ match settings {
+ ComparatorFunction::Address(settings) => {
+ // FUNCTION, EMITRANGE
+ // See Table C1-14
+ let (function, emit_range) = match (&settings.access_type, &settings.emit) {
+ (AccessType::ReadOnly, EmitOption::Data) => (0b1100, false),
+ (AccessType::ReadOnly, EmitOption::Address) => (0b1100, true),
+ (AccessType::ReadOnly, EmitOption::AddressData) => (0b1110, true),
+ (AccessType::ReadOnly, EmitOption::PCData) => (0b1110, false),
+ (AccessType::ReadOnly, EmitOption::WatchpointDebugEvent) => (0b0101, false),
+ (AccessType::ReadOnly, EmitOption::CompareMatchEvent) => (0b1001, false),
+
+ (AccessType::WriteOnly, EmitOption::Data) => (0b1101, false),
+ (AccessType::WriteOnly, EmitOption::Address) => (0b1101, true),
+ (AccessType::WriteOnly, EmitOption::AddressData) => (0b1111, true),
+ (AccessType::WriteOnly, EmitOption::PCData) => (0b1111, false),
+ (AccessType::WriteOnly, EmitOption::WatchpointDebugEvent) => (0b0110, false),
+ (AccessType::WriteOnly, EmitOption::CompareMatchEvent) => (0b1010, false),
+
+ (AccessType::ReadWrite, EmitOption::Data) => (0b0010, false),
+ (AccessType::ReadWrite, EmitOption::Address) => (0b0001, true),
+ (AccessType::ReadWrite, EmitOption::AddressData) => (0b0010, true),
+ (AccessType::ReadWrite, EmitOption::PCData) => (0b0011, false),
+ (AccessType::ReadWrite, EmitOption::WatchpointDebugEvent) => (0b0111, false),
+ (AccessType::ReadWrite, EmitOption::CompareMatchEvent) => (0b1011, false),
+
+ (AccessType::ReadWrite, EmitOption::PC) => (0b0001, false),
+ (_, EmitOption::PC) => return Err(DwtError::InvalidFunction),
+ };
+
+ unsafe {
+ self.function.modify(|mut r| {
+ r.set_function(function);
+ r.set_emitrange(emit_range);
+ // don't compare data value
+ r.set_datavmatch(false);
+ // don't compare cycle counter value
+ // NOTE: only needed for comparator 0, but is SBZP.
+ r.set_cycmatch(false);
+ // SBZ as needed, see Page 784/C1-724
+ r.set_datavsize(0);
+ r.set_datavaddr0(0);
+ r.set_datavaddr1(0);
+
+ r
+ });
+
+ self.comp.write(settings.address);
+ self.mask.write(settings.mask);
+ }
+ }
+ ComparatorFunction::CycleCount(settings) => {
+ let function = match &settings.emit {
+ EmitOption::PCData => 0b0001,
+ EmitOption::WatchpointDebugEvent => 0b0100,
+ EmitOption::CompareMatchEvent => 0b1000,
+ _ => return Err(DwtError::InvalidFunction),
+ };
+
+ unsafe {
+ self.function.modify(|mut r| {
+ r.set_function(function);
+ // emit_range is N/A for cycle count compare
+ r.set_emitrange(false);
+ // don't compare data
+ r.set_datavmatch(false);
+ // compare cyccnt
+ r.set_cycmatch(true);
+ // SBZ as needed, see Page 784/C1-724
+ r.set_datavsize(0);
+ r.set_datavaddr0(0);
+ r.set_datavaddr1(0);
+
+ r
+ });
+
+ self.comp.write(settings.compare);
+ self.mask.write(0); // SBZ, see Page 784/C1-724
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
diff --git a/src/peripheral/icb.rs b/src/peripheral/icb.rs
index 9b29655..e1de33b 100644
--- a/src/peripheral/icb.rs
+++ b/src/peripheral/icb.rs
@@ -1,6 +1,6 @@
//! Implementation Control Block
-#[cfg(any(armv7m, armv8m, target_arch = "x86_64"))]
+#[cfg(any(armv7m, armv8m, native))]
use volatile_register::RO;
use volatile_register::RW;
@@ -12,12 +12,12 @@ pub struct RegisterBlock {
/// The bottom four bits of this register give the number of implemented
/// interrupt lines, divided by 32. So a value of `0b0010` indicates 64
/// interrupts.
- #[cfg(any(armv7m, armv8m, target_arch = "x86_64"))]
+ #[cfg(any(armv7m, armv8m, native))]
pub ictr: RO<u32>,
/// The ICTR is not defined in the ARMv6-M Architecture Reference manual, so
/// we replace it with this.
- #[cfg(not(any(armv7m, armv8m, target_arch = "x86_64")))]
+ #[cfg(not(any(armv7m, armv8m, native)))]
_reserved: u32,
/// Auxiliary Control Register
diff --git a/src/peripheral/itm.rs b/src/peripheral/itm.rs
index c0d560f..4d0aa22 100644
--- a/src/peripheral/itm.rs
+++ b/src/peripheral/itm.rs
@@ -7,6 +7,9 @@ use core::ptr;
use volatile_register::{RO, RW, WO};
+use crate::peripheral::ITM;
+use bitfield::bitfield;
+
/// Register block
#[repr(C)]
pub struct RegisterBlock {
@@ -20,7 +23,7 @@ pub struct RegisterBlock {
pub tpr: RW<u32>,
reserved2: [u32; 15],
/// Trace Control
- pub tcr: RW<u32>,
+ pub tcr: RW<Tcr>,
reserved3: [u32; 75],
/// Lock Access
pub lar: WO<u32>,
@@ -28,6 +31,22 @@ pub struct RegisterBlock {
pub lsr: RO<u32>,
}
+bitfield! {
+ /// Trace Control Register.
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ pub struct Tcr(u32);
+ itmena, set_itmena: 0;
+ tsena, set_tsena: 1;
+ syncena, set_synena: 2;
+ txena, set_txena: 3;
+ swoena, set_swoena: 4;
+ u8, tsprescale, set_tsprescale: 9, 8;
+ u8, gtsfreq, set_gtsfreq: 11, 10;
+ u8, tracebusid, set_tracebusid: 22, 16;
+ busy, _: 23;
+}
+
/// Stimulus Port
pub struct Stim {
register: UnsafeCell<u32>,
@@ -69,3 +88,106 @@ impl Stim {
unsafe { ptr::read_volatile(self.register.get()) & 0b11 != 0 }
}
}
+
+/// The possible local timestamp options.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub enum LocalTimestampOptions {
+ /// Disable local timestamps.
+ Disabled,
+ /// Enable local timestamps and use no prescaling.
+ Enabled,
+ /// Enable local timestamps and set the prescaler to divide the
+ /// reference clock by 4.
+ EnabledDiv4,
+ /// Enable local timestamps and set the prescaler to divide the
+ /// reference clock by 16.
+ EnabledDiv16,
+ /// Enable local timestamps and set the prescaler to divide the
+ /// reference clock by 64.
+ EnabledDiv64,
+}
+
+/// The possible global timestamp options.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub enum GlobalTimestampOptions {
+ /// Disable global timestamps.
+ Disabled,
+ /// Generate a global timestamp approximately every 128 cycles.
+ Every128Cycles,
+ /// Generate a global timestamp approximately every 8921 cycles.
+ Every8192Cycles,
+ /// Generate a global timestamp after every packet, if the output FIFO is empty.
+ EveryPacket,
+}
+
+/// The possible clock sources for timestamp counters.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub enum TimestampClkSrc {
+ /// Clock timestamp counters using the system processor clock.
+ SystemClock,
+ /// Clock timestamp counters using the asynchronous clock from the
+ /// TPIU interface.
+ ///
+ /// NOTE: The timestamp counter is held in reset while the output
+ /// line is idle.
+ AsyncTPIU,
+}
+
+/// Available settings for the ITM peripheral.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub struct ITMSettings {
+ /// Whether to enable ITM.
+ pub enable: bool,
+ /// Whether DWT packets should be forwarded to ITM.
+ pub forward_dwt: bool,
+ /// The local timestamp options that should be applied.
+ pub local_timestamps: LocalTimestampOptions,
+ /// The global timestamp options that should be applied.
+ pub global_timestamps: GlobalTimestampOptions,
+ /// The trace bus ID to use when multi-trace sources are in use.
+ /// `None` specifies that only a single trace source is in use and
+ /// has the same effect as `Some(0)`.
+ pub bus_id: Option<u8>,
+ /// The clock that should increase timestamp counters.
+ pub timestamp_clk_src: TimestampClkSrc,
+}
+
+impl ITM {
+ /// Removes the software lock on the ITM.
+ #[inline]
+ pub fn unlock(&mut self) {
+ // NOTE(unsafe) atomic write to a stateless, write-only register
+ unsafe { self.lar.write(0xC5AC_CE55) }
+ }
+
+ /// Configures the ITM with the passed [ITMSettings].
+ #[inline]
+ pub fn configure(&mut self, settings: ITMSettings) {
+ unsafe {
+ self.tcr.modify(|mut r| {
+ r.set_itmena(settings.enable);
+ r.set_tsena(settings.local_timestamps != LocalTimestampOptions::Disabled);
+ r.set_txena(settings.forward_dwt);
+ r.set_tsprescale(match settings.local_timestamps {
+ LocalTimestampOptions::Disabled | LocalTimestampOptions::Enabled => 0b00,
+ LocalTimestampOptions::EnabledDiv4 => 0b10,
+ LocalTimestampOptions::EnabledDiv16 => 0b10,
+ LocalTimestampOptions::EnabledDiv64 => 0b11,
+ });
+ r.set_gtsfreq(match settings.global_timestamps {
+ GlobalTimestampOptions::Disabled => 0b00,
+ GlobalTimestampOptions::Every128Cycles => 0b01,
+ GlobalTimestampOptions::Every8192Cycles => 0b10,
+ GlobalTimestampOptions::EveryPacket => 0b11,
+ });
+ r.set_swoena(match settings.timestamp_clk_src {
+ TimestampClkSrc::SystemClock => false,
+ TimestampClkSrc::AsyncTPIU => true,
+ });
+ r.set_tracebusid(settings.bus_id.unwrap_or(0));
+
+ r
+ });
+ }
+ }
+}
diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs
index 8f5678d..d1e119f 100644
--- a/src/peripheral/mod.rs
+++ b/src/peripheral/mod.rs
@@ -10,7 +10,7 @@
//! ``` no_run
//! # use cortex_m::peripheral::Peripherals;
//! let mut peripherals = Peripherals::take().unwrap();
-//! peripherals.DWT.enable_cycle_counter();
+//! peripherals.DCB.enable_trace();
//! ```
//!
//! This method can only be successfully called *once* -- this is why the method returns an
@@ -29,6 +29,7 @@
//! # use cortex_m::peripheral::{DWT, Peripherals};
//! {
//! let mut peripherals = Peripherals::take().unwrap();
+//! peripherals.DCB.enable_trace();
//! peripherals.DWT.enable_cycle_counter();
//! } // all the peripheral singletons are destroyed here
//!
@@ -44,6 +45,7 @@
//! # use cortex_m::peripheral::{DWT, Peripherals};
//! {
//! let mut peripherals = Peripherals::take().unwrap();
+//! peripherals.DCB.enable_trace();
//! peripherals.DWT.enable_cycle_counter();
//! } // all the peripheral singletons are destroyed here
//!
@@ -60,6 +62,8 @@ use core::ops;
use crate::interrupt;
+#[cfg(cm7)]
+pub mod ac;
#[cfg(not(armv6m))]
pub mod cbp;
pub mod cpuid;
@@ -67,8 +71,8 @@ pub mod dcb;
pub mod dwt;
#[cfg(not(armv6m))]
pub mod fpb;
-// NOTE(target_arch) is for documentation purposes
-#[cfg(any(has_fpu, target_arch = "x86_64"))]
+// NOTE(native) is for documentation purposes
+#[cfg(any(has_fpu, native))]
pub mod fpu;
pub mod icb;
#[cfg(all(not(armv6m), not(armv8m_base)))]
@@ -91,6 +95,10 @@ mod test;
#[allow(non_snake_case)]
#[allow(clippy::manual_non_exhaustive)]
pub struct Peripherals {
+ /// Cortex-M7 TCM and cache access control.
+ #[cfg(cm7)]
+ pub AC: AC,
+
/// Cache and branch predictor maintenance operations.
/// Not available on Armv6-M.
pub CBP: CBP,
@@ -172,6 +180,10 @@ impl Peripherals {
TAKEN = true;
Peripherals {
+ #[cfg(cm7)]
+ AC: AC {
+ _marker: PhantomData,
+ },
CBP: CBP {
_marker: PhantomData,
},
@@ -219,7 +231,29 @@ impl Peripherals {
}
}
+/// Access control
+#[cfg(cm7)]
+pub struct AC {
+ _marker: PhantomData<*const ()>,
+}
+
+#[cfg(cm7)]
+unsafe impl Send for AC {}
+
+#[cfg(cm7)]
+impl AC {
+ /// Pointer to the register block
+ pub const PTR: *const self::ac::RegisterBlock = 0xE000_EF90 as *const _;
+
+ /// Returns a pointer to the register block (to be deprecated in 0.7)
+ #[inline(always)]
+ pub const fn ptr() -> *const self::ac::RegisterBlock {
+ Self::PTR
+ }
+}
+
/// Cache and branch predictor maintenance operations
+#[allow(clippy::upper_case_acronyms)]
pub struct CBP {
_marker: PhantomData<*const ()>,
}
@@ -256,6 +290,7 @@ impl ops::Deref for CBP {
}
/// CPUID
+#[allow(clippy::upper_case_acronyms)]
pub struct CPUID {
_marker: PhantomData<*const ()>,
}
@@ -283,6 +318,7 @@ impl ops::Deref for CPUID {
}
/// Debug Control Block
+#[allow(clippy::upper_case_acronyms)]
pub struct DCB {
_marker: PhantomData<*const ()>,
}
@@ -310,6 +346,7 @@ impl ops::Deref for DCB {
}
/// Data Watchpoint and Trace unit
+#[allow(clippy::upper_case_acronyms)]
pub struct DWT {
_marker: PhantomData<*const ()>,
}
@@ -337,6 +374,7 @@ impl ops::Deref for DWT {
}
/// Flash Patch and Breakpoint unit
+#[allow(clippy::upper_case_acronyms)]
pub struct FPB {
_marker: PhantomData<*const ()>,
}
@@ -366,13 +404,14 @@ impl ops::Deref for FPB {
}
/// Floating Point Unit
+#[allow(clippy::upper_case_acronyms)]
pub struct FPU {
_marker: PhantomData<*const ()>,
}
unsafe impl Send for FPU {}
-#[cfg(any(has_fpu, target_arch = "x86_64"))]
+#[cfg(any(has_fpu, native))]
impl FPU {
/// Pointer to the register block
pub const PTR: *const fpu::RegisterBlock = 0xE000_EF30 as *const _;
@@ -384,7 +423,7 @@ impl FPU {
}
}
-#[cfg(any(has_fpu, target_arch = "x86_64"))]
+#[cfg(any(has_fpu, native))]
impl ops::Deref for FPU {
type Target = self::fpu::RegisterBlock;
@@ -400,6 +439,7 @@ impl ops::Deref for FPU {
/// `actlr`. It's called the "implementation control block" in the ARMv8-M
/// standard, but earlier standards contained the registers, just without a
/// name.
+#[allow(clippy::upper_case_acronyms)]
pub struct ICB {
_marker: PhantomData<*const ()>,
}
@@ -434,6 +474,7 @@ impl ops::DerefMut for ICB {
}
/// Instrumentation Trace Macrocell
+#[allow(clippy::upper_case_acronyms)]
pub struct ITM {
_marker: PhantomData<*const ()>,
}
@@ -471,6 +512,7 @@ impl ops::DerefMut for ITM {
}
/// Memory Protection Unit
+#[allow(clippy::upper_case_acronyms)]
pub struct MPU {
_marker: PhantomData<*const ()>,
}
@@ -498,6 +540,7 @@ impl ops::Deref for MPU {
}
/// Nested Vector Interrupt Controller
+#[allow(clippy::upper_case_acronyms)]
pub struct NVIC {
_marker: PhantomData<*const ()>,
}
@@ -525,6 +568,7 @@ impl ops::Deref for NVIC {
}
/// Security Attribution Unit
+#[allow(clippy::upper_case_acronyms)]
pub struct SAU {
_marker: PhantomData<*const ()>,
}
@@ -554,6 +598,7 @@ impl ops::Deref for SAU {
}
/// System Control Block
+#[allow(clippy::upper_case_acronyms)]
pub struct SCB {
_marker: PhantomData<*const ()>,
}
@@ -581,6 +626,7 @@ impl ops::Deref for SCB {
}
/// SysTick: System Timer
+#[allow(clippy::upper_case_acronyms)]
pub struct SYST {
_marker: PhantomData<*const ()>,
}
@@ -608,6 +654,7 @@ impl ops::Deref for SYST {
}
/// Trace Port Interface Unit
+#[allow(clippy::upper_case_acronyms)]
pub struct TPIU {
_marker: PhantomData<*const ()>,
}
diff --git a/src/peripheral/nvic.rs b/src/peripheral/nvic.rs
index 4332707..f0c5457 100644
--- a/src/peripheral/nvic.rs
+++ b/src/peripheral/nvic.rs
@@ -210,7 +210,7 @@ impl NVIC {
/// # Unsafety
///
/// Changing priority levels can break priority-based critical sections (see
- /// [`register::basepri`](../register/basepri/index.html)) and compromise memory safety.
+ /// [`register::basepri`](crate::register::basepri)) and compromise memory safety.
#[inline]
pub unsafe fn set_priority<I>(&mut self, interrupt: I, prio: u8)
where
diff --git a/src/peripheral/scb.rs b/src/peripheral/scb.rs
index b619328..b61c4ff 100644
--- a/src/peripheral/scb.rs
+++ b/src/peripheral/scb.rs
@@ -11,6 +11,8 @@ use super::CBP;
#[cfg(not(armv6m))]
use super::CPUID;
use super::SCB;
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Serialize};
/// Register block
#[repr(C)]
@@ -180,7 +182,7 @@ impl SCB {
5 => VectActive::Exception(Exception::BusFault),
#[cfg(not(armv6m))]
6 => VectActive::Exception(Exception::UsageFault),
- #[cfg(any(armv8m, target_arch = "x86_64"))]
+ #[cfg(any(armv8m, native))]
7 => VectActive::Exception(Exception::SecureFault),
11 => VectActive::Exception(Exception::SVCall),
#[cfg(not(armv6m))]
@@ -194,6 +196,8 @@ impl SCB {
/// Processor core exceptions (internal interrupts)
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "std-map", derive(PartialOrd, Hash))]
pub enum Exception {
/// Non maskable interrupt
NonMaskableInt,
@@ -214,7 +218,7 @@ pub enum Exception {
UsageFault,
/// Secure fault interrupt (only on ARMv8-M)
- #[cfg(any(armv8m, target_arch = "x86_64"))]
+ #[cfg(any(armv8m, native))]
SecureFault,
/// SV call interrupt
@@ -246,7 +250,7 @@ impl Exception {
Exception::BusFault => -11,
#[cfg(not(armv6m))]
Exception::UsageFault => -10,
- #[cfg(any(armv8m, target_arch = "x86_64"))]
+ #[cfg(any(armv8m, native))]
Exception::SecureFault => -9,
Exception::SVCall => -5,
#[cfg(not(armv6m))]
@@ -259,6 +263,8 @@ impl Exception {
/// Active exception number
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "std-map", derive(PartialOrd, Hash))]
pub enum VectActive {
/// Thread mode
ThreadMode,
@@ -287,7 +293,7 @@ impl VectActive {
5 => VectActive::Exception(Exception::BusFault),
#[cfg(not(armv6m))]
6 => VectActive::Exception(Exception::UsageFault),
- #[cfg(any(armv8m, target_arch = "x86_64"))]
+ #[cfg(any(armv8m, native))]
7 => VectActive::Exception(Exception::SecureFault),
11 => VectActive::Exception(Exception::SVCall),
#[cfg(not(armv6m))]
@@ -832,7 +838,7 @@ impl SCB {
}
const SCB_AIRCR_VECTKEY: u32 = 0x05FA << 16;
-const SCB_AIRCR_PRIGROUP_MASK: u32 = 0x5 << 8;
+const SCB_AIRCR_PRIGROUP_MASK: u32 = 0x7 << 8;
const SCB_AIRCR_SYSRESETREQ: u32 = 1 << 2;
impl SCB {
@@ -928,7 +934,7 @@ pub enum SystemHandler {
UsageFault = 6,
/// Secure fault interrupt (only on ARMv8-M)
- #[cfg(any(armv8m, target_arch = "x86_64"))]
+ #[cfg(any(armv8m, native))]
SecureFault = 7,
/// SV call interrupt
@@ -994,7 +1000,7 @@ impl SCB {
/// # Unsafety
///
/// Changing priority levels can break priority-based critical sections (see
- /// [`register::basepri`](../register/basepri/index.html)) and compromise memory safety.
+ /// [`register::basepri`](crate::register::basepri)) and compromise memory safety.
#[inline]
pub unsafe fn set_priority(&mut self, system_handler: SystemHandler, prio: u8) {
let index = system_handler as u8;
diff --git a/src/peripheral/tpiu.rs b/src/peripheral/tpiu.rs
index 11cb79e..3ff5f55 100644
--- a/src/peripheral/tpiu.rs
+++ b/src/peripheral/tpiu.rs
@@ -4,6 +4,9 @@
use volatile_register::{RO, RW, WO};
+use crate::peripheral::TPIU;
+use bitfield::bitfield;
+
/// Register block
#[repr(C)]
pub struct RegisterBlock {
@@ -16,10 +19,10 @@ pub struct RegisterBlock {
pub acpr: RW<u32>,
reserved1: [u32; 55],
/// Selected Pin Control
- pub sppr: RW<u32>,
+ pub sppr: RW<Sppr>,
reserved2: [u32; 132],
/// Formatter and Flush Control
- pub ffcr: RW<u32>,
+ pub ffcr: RW<Ffcr>,
reserved3: [u32; 810],
/// Lock Access
pub lar: WO<u32>,
@@ -27,5 +30,131 @@ pub struct RegisterBlock {
pub lsr: RO<u32>,
reserved4: [u32; 4],
/// TPIU Type
- pub _type: RO<u32>,
+ pub _type: RO<Type>,
+}
+
+bitfield! {
+ /// Formatter and flush control register.
+ #[repr(C)]
+ #[derive(Clone, Copy)]
+ pub struct Ffcr(u32);
+ enfcont, set_enfcont: 1;
+}
+
+bitfield! {
+ /// TPIU Type Register.
+ #[repr(C)]
+ #[derive(Clone, Copy)]
+ pub struct Type(u32);
+ u8, fifosz, _: 8, 6;
+ ptinvalid, _: 9;
+ mancvalid, _: 10;
+ nrzvalid, _: 11;
+}
+
+bitfield! {
+ /// Selected pin protocol register.
+ #[repr(C)]
+ #[derive(Clone, Copy)]
+ pub struct Sppr(u32);
+ u8, txmode, set_txmode: 1, 0;
+}
+
+/// The available protocols for the trace output.
+#[repr(u8)]
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub enum TraceProtocol {
+ /// Parallel trace port mode
+ Parallel = 0b00,
+ /// Asynchronous SWO, using Manchester encoding
+ AsyncSWOManchester = 0b01,
+ /// Asynchronous SWO, using NRZ encoding
+ AsyncSWONRZ = 0b10,
+}
+impl core::convert::TryFrom<u8> for TraceProtocol {
+ type Error = ();
+
+ /// Tries to convert from a `TXMODE` field value. Fails if the set mode is
+ /// unknown (and thus unpredictable).
+ #[inline]
+ fn try_from(value: u8) -> Result<Self, Self::Error> {
+ match value {
+ x if x == Self::Parallel as u8 => Ok(Self::Parallel),
+ x if x == Self::AsyncSWOManchester as u8 => Ok(Self::AsyncSWOManchester),
+ x if x == Self::AsyncSWONRZ as u8 => Ok(Self::AsyncSWONRZ),
+ _ => Err(()), // unknown and unpredictable mode
+ }
+ }
+}
+
+/// The SWO options supported by the TPIU.
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub struct SWOSupports {
+ /// Whether UART/NRZ encoding is supported for SWO.
+ nrz_encoding: bool,
+ /// Whether Manchester encoding is supported for SWO.
+ manchester_encoding: bool,
+ /// Whether parallel trace port operation is supported.
+ parallel_operation: bool,
+ /// The minimum implemented FIFO queue size of the TPIU for trace data.
+ min_queue_size: u8,
+}
+
+impl TPIU {
+ /// Sets the prescaler value for a wanted baud rate of the Serial
+ /// Wire Output (SWO) in relation to a given asynchronous refernce
+ /// clock rate.
+ #[inline]
+ pub fn set_swo_baud_rate(&mut self, ref_clk_rate: u32, baud_rate: u32) {
+ unsafe {
+ self.acpr.write((ref_clk_rate / baud_rate) - 1);
+ }
+ }
+
+ /// The used protocol for the trace output. Return `None` if an
+ /// unknown (and thus unpredicable mode) is configured by means
+ /// other than
+ /// [`trace_output_protocol`](Self::set_trace_output_protocol).
+ #[inline]
+ pub fn trace_output_protocol(&self) -> Option<TraceProtocol> {
+ use core::convert::TryInto;
+ self.sppr.read().txmode().try_into().ok()
+ }
+
+ /// Sets the used protocol for the trace output.
+ #[inline]
+ pub fn set_trace_output_protocol(&mut self, proto: TraceProtocol) {
+ unsafe {
+ self.sppr.modify(|mut r| {
+ r.set_txmode(proto as u8);
+ r
+ });
+ }
+ }
+
+ /// Whether to enable the formatter. If disabled, only ITM and DWT
+ /// trace sources are passed through. Data from the ETM is
+ /// discarded.
+ #[inline]
+ pub fn enable_continuous_formatting(&mut self, bit: bool) {
+ unsafe {
+ self.ffcr.modify(|mut r| {
+ r.set_enfcont(bit);
+ r
+ });
+ }
+ }
+
+ /// Reads the supported trace output modes and the minimum size of
+ /// the TPIU FIFO queue for trace data.
+ #[inline]
+ pub fn swo_supports() -> SWOSupports {
+ let _type = unsafe { (*Self::ptr())._type.read() };
+ SWOSupports {
+ nrz_encoding: _type.nrzvalid(),
+ manchester_encoding: _type.mancvalid(),
+ parallel_operation: !_type.ptinvalid(),
+ min_queue_size: _type.fifosz(),
+ }
+ }
}
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index 06c5143..8742f9b 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -11,3 +11,5 @@ harness = false
[dependencies]
ar = "0.8.0"
+cortex-m = { path = "../", features = ["serde", "std-map"] }
+serde_json = "1"
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
index a7b85e7..c3d8356 100644
--- a/xtask/src/lib.rs
+++ b/xtask/src/lib.rs
@@ -208,3 +208,24 @@ pub fn check_blobs() {
println!("Blobs identical.");
}
+
+// Check that serde and PartialOrd works with VectActive
+pub fn check_host_side() {
+ use cortex_m::peripheral::scb::VectActive;
+
+ // check serde
+ {
+ let v = VectActive::from(22).unwrap();
+ let json = serde_json::to_string(&v).expect("Failed to serialize VectActive");
+ let deser_v: VectActive =
+ serde_json::from_str(&json).expect("Failed to deserialize VectActive");
+ assert_eq!(deser_v, v);
+ }
+
+ // check PartialOrd
+ {
+ let a = VectActive::from(19).unwrap();
+ let b = VectActive::from(20).unwrap();
+ assert_eq!(a < b, true);
+ }
+}
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index ec55bf8..3e4b394 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -1,17 +1,19 @@
use std::{env, process};
-use xtask::{assemble_blobs, check_blobs};
+use xtask::{assemble_blobs, check_blobs, check_host_side};
fn main() {
let subcommand = env::args().skip(1).next();
match subcommand.as_ref().map(|s| &**s) {
Some("assemble") => assemble_blobs(),
Some("check-blobs") => check_blobs(),
+ Some("check-host-side") => check_host_side(),
_ => {
eprintln!("usage: cargo xtask <subcommand>");
eprintln!();
eprintln!("subcommands:");
- eprintln!(" assemble Reassemble the pre-built artifacts");
- eprintln!(" check-blobs Check that the pre-built artifacts are up-to-date and reproducible");
+ eprintln!(" assemble Reassemble the pre-built artifacts");
+ eprintln!(" check-blobs Check that the pre-built artifacts are up-to-date and reproducible");
+ eprintln!(" check-host-side Build the crate in a non-Cortex-M host application and check host side usage of certain types");
process::exit(1);
}
}
diff --git a/xtask/tests/ci.rs b/xtask/tests/ci.rs
index a261783..37466e9 100644
--- a/xtask/tests/ci.rs
+++ b/xtask/tests/ci.rs
@@ -1,6 +1,6 @@
use std::process::Command;
use std::{env, str};
-use xtask::{check_blobs, install_targets};
+use xtask::{check_blobs, check_host_side, install_targets};
/// List of all compilation targets we support.
///
@@ -105,4 +105,7 @@ fn main() {
let is_nightly = str::from_utf8(&output.stdout).unwrap().contains("nightly");
check_crates_build(is_nightly);
+
+ // Check host-side applications of the crate.
+ check_host_side();
}