diff options
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 @@ -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"] @@ -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)); } } @@ -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 Binary files differindex 6136ec6..a203d7a 100644 --- a/bin/thumbv6m-none-eabi-lto.a +++ b/bin/thumbv6m-none-eabi-lto.a diff --git a/bin/thumbv6m-none-eabi.a b/bin/thumbv6m-none-eabi.a Binary files differindex c42f579..9640a69 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 differindex c2c040a..b34ac64 100644 --- a/bin/thumbv7em-none-eabi-lto.a +++ b/bin/thumbv7em-none-eabi-lto.a diff --git a/bin/thumbv7em-none-eabi.a b/bin/thumbv7em-none-eabi.a Binary files differindex 660360f..88acbdd 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 differindex eba1984..6de94bb 100644 --- a/bin/thumbv7em-none-eabihf-lto.a +++ b/bin/thumbv7em-none-eabihf-lto.a diff --git a/bin/thumbv7em-none-eabihf.a b/bin/thumbv7em-none-eabihf.a Binary files differindex 1561fa4..cf91a7a 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 differindex d964314..7f677a9 100644 --- a/bin/thumbv7m-none-eabi-lto.a +++ b/bin/thumbv7m-none-eabi-lto.a diff --git a/bin/thumbv7m-none-eabi.a b/bin/thumbv7m-none-eabi.a Binary files differindex f541274..ff4bf21 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 differindex 8a6ed42..f62acaf 100644 --- a/bin/thumbv8m.base-none-eabi-lto.a +++ 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 33cd908..c0cc96c 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 differindex 8e6ff0a..1a51515 100644 --- a/bin/thumbv8m.main-none-eabi-lto.a +++ 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 898fea7..d017a15 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 differindex d9a636a..fd3dc92 100644 --- a/bin/thumbv8m.main-none-eabihf-lto.a +++ 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 b0513b7..223ff1d 100644 --- a/bin/thumbv8m.main-none-eabihf.a +++ b/bin/thumbv8m.main-none-eabihf.a @@ -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 { @@ -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(); } |