Skip to content

Commit 14d0e9a

Browse files
committed
Add asm_cfg attribute.
The purpose of this attribute is to allow platforms that do not support the asm instructions to compile code code that calls those functions. This is needed so that clippy can be run once in CI and when testing locally. This reverts 8d579b4 since it had a conflicting method of making clippy builds include the function on all targets.
1 parent b3d7c05 commit 14d0e9a

12 files changed

Lines changed: 161 additions & 236 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
resolver = "3"
33
members = [
44
"cortex-m",
5+
"cortex-m/macros",
56
"cortex-m-types",
67
"cortex-m-rt",
78
"cortex-m-semihosting",

cortex-m/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ volatile-register = "0.2.2"
1919
bitfield = "0.13.2"
2020
eh0 = { package = "embedded-hal", version = "0.2.4" }
2121
eh1 = { package = "embedded-hal", version = "1.0.0" }
22+
cortex-m-macros = { path = "macros", version = "=0.1.0" }
2223

2324
[dependencies.serde]
2425
version = "1"

cortex-m/macros/Cargo.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "cortex-m-macros"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[lib]
7+
proc-macro = true
8+
9+
[dependencies]
10+
proc-macro2 = "1.0.106"
11+
quote = "1.0.45"
12+
syn = { version = "2.0.117", features = ["extra-traits", "full"] }
13+
14+
[dev-dependencies]
15+
macrotest = "1.2.1"
16+
17+
[[bin]]
18+
name = "asm_cfg_fn_test"
19+
test = false
20+
bench = false
21+
path = "test_bin/asm_cfg_fn_test.rs"

cortex-m/macros/build.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
println!("cargo:rustc-check-cfg=cfg(testcfg)");
3+
}

cortex-m/macros/src/lib.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//! Internal implementation details of `cortex-m`.
2+
//!
3+
//! Do not use this crate directly.
4+
5+
extern crate proc_macro;
6+
7+
use proc_macro::TokenStream;
8+
use quote::quote;
9+
use syn::{Item, Meta, parse_macro_input, parse_quote};
10+
11+
#[proc_macro_attribute]
12+
pub fn asm_cfg(attr: TokenStream, item: TokenStream) -> TokenStream {
13+
let cfg_expr = parse_macro_input!(attr as Meta);
14+
let wrapped_item = parse_macro_input!(item as Item);
15+
16+
let new_item = match wrapped_item {
17+
Item::Fn(f) => asm_cfg_wrap_fn(cfg_expr, f),
18+
// TODO(wt): we should probably support modules as well
19+
// Item::Mod(m) => asm_wrapper_wrap_mod(cfg_expr, m),
20+
_ => unimplemented!(),
21+
};
22+
23+
quote! {
24+
#new_item
25+
}
26+
.into()
27+
}
28+
29+
fn asm_cfg_wrap_fn(cfg_expr: Meta, mut f: syn::ItemFn) -> Item {
30+
let old_block = f.block;
31+
f.block = parse_quote! {
32+
{
33+
#[cfg(#cfg_expr)]
34+
#old_block
35+
36+
#[cfg(not(#cfg_expr))]
37+
unimplemented!()
38+
}
39+
};
40+
parse_quote! {
41+
#[allow(unused)]
42+
#f
43+
}
44+
}
45+
46+
#[cfg(test)]
47+
mod tests {
48+
49+
#[test]
50+
fn test_asm_cfg() {
51+
macrotest::expand("proc_macro_tests/*.rs");
52+
}
53+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
use cortex_m_macros::asm_wrapper;
2+
#[allow(unused)]
3+
fn blah() {
4+
::core::panicking::panic("not implemented")
5+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use cortex_m_macros::asm_cfg;
2+
3+
fn main() {}
4+
5+
#[asm_cfg(testcfg)]
6+
fn blah() {
7+
println!("")
8+
}

cortex-m/src/asm.rs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
33
#![allow(missing_docs)]
44

5-
#[cfg_attr(cortex_m, path = "asm/inner.rs")]
6-
#[cfg_attr(not(cortex_m), path = "asm/inner_mock.rs")]
5+
#[cfg(cortex_m)]
6+
use core::arch::asm;
7+
78
pub mod inner;
89

910
/// Puts the processor in Debug state. Debuggers can pick this up as a "breakpoint".
1011
///
1112
/// **NOTE** calling `bkpt` when the processor is not connected to a debugger will cause an
1213
/// exception.
1314
#[inline(always)]
15+
#[cortex_m_macros::asm_cfg(cortex_m)]
1416
pub fn bkpt() {
15-
unsafe { inner::__bkpt() };
17+
unsafe { asm!("bkpt", options(nomem, nostack, preserves_flags)) };
1618
}
1719

1820
/// Blocks the program for *at least* `cycles` CPU cycles.
@@ -30,12 +32,14 @@ pub fn bkpt() {
3032
/// initialization of peripherals if and only if accurate timing is not essential. In any other case
3133
/// please use a more accurate method to produce a delay.
3234
#[inline]
35+
#[cortex_m_macros::asm_cfg(cortex_m)]
3336
pub fn delay(cycles: u32) {
3437
unsafe { inner::__delay(cycles) };
3538
}
3639

3740
/// A no-operation. Useful to prevent delay loops from being optimized away.
3841
#[inline]
42+
#[cortex_m_macros::asm_cfg(cortex_m)]
3943
pub fn nop() {
4044
unsafe { inner::__nop() };
4145
}
@@ -44,24 +48,28 @@ pub fn nop() {
4448
///
4549
/// Can be used as a stable alternative to `core::intrinsics::abort`.
4650
#[inline]
51+
#[cortex_m_macros::asm_cfg(cortex_m)]
4752
pub fn udf() -> ! {
4853
unsafe { inner::__udf() }
4954
}
5055

5156
/// Wait For Event
5257
#[inline]
58+
#[cortex_m_macros::asm_cfg(cortex_m)]
5359
pub fn wfe() {
5460
unsafe { inner::__wfe() }
5561
}
5662

5763
/// Wait For Interrupt
5864
#[inline]
65+
#[cortex_m_macros::asm_cfg(cortex_m)]
5966
pub fn wfi() {
6067
unsafe { inner::__wfi() }
6168
}
6269

6370
/// Send Event
6471
#[inline]
72+
#[cortex_m_macros::asm_cfg(cortex_m)]
6573
pub fn sev() {
6674
unsafe { inner::__sev() }
6775
}
@@ -71,6 +79,7 @@ pub fn sev() {
7179
/// Flushes the pipeline in the processor, so that all instructions following the `ISB` are fetched
7280
/// from cache or memory, after the instruction has been completed.
7381
#[inline]
82+
#[cortex_m_macros::asm_cfg(cortex_m)]
7483
pub fn isb() {
7584
unsafe { inner::__isb() }
7685
}
@@ -83,6 +92,7 @@ pub fn isb() {
8392
/// * any explicit memory access made before this instruction is complete
8493
/// * all cache and branch predictor maintenance operations before this instruction complete
8594
#[inline]
95+
#[cortex_m_macros::asm_cfg(cortex_m)]
8696
pub fn dsb() {
8797
unsafe { inner::__dsb() }
8898
}
@@ -93,6 +103,7 @@ pub fn dsb() {
93103
/// instruction are observed before any explicit memory accesses that appear in program order
94104
/// after the `DMB` instruction.
95105
#[inline]
106+
#[cortex_m_macros::asm_cfg(cortex_m)]
96107
pub fn dmb() {
97108
unsafe { inner::__dmb() }
98109
}
@@ -103,7 +114,7 @@ pub fn dmb() {
103114
/// Returns a Test Target Response Payload (cf section D1.2.215 of
104115
/// Armv8-M Architecture Reference Manual).
105116
#[inline]
106-
#[cfg(armv8m)]
117+
#[cortex_m_macros::asm_cfg(armv8m)]
107118
// The __tt function does not dereference the pointer received.
108119
#[allow(clippy::not_unsafe_ptr_arg_deref)]
109120
pub fn tt(addr: *mut u32) -> u32 {
@@ -118,7 +129,7 @@ pub fn tt(addr: *mut u32) -> u32 {
118129
/// Returns a Test Target Response Payload (cf section D1.2.215 of
119130
/// Armv8-M Architecture Reference Manual).
120131
#[inline]
121-
#[cfg(armv8m)]
132+
#[cortex_m_macros::asm_cfg(armv8m)]
122133
// The __ttt function does not dereference the pointer received.
123134
#[allow(clippy::not_unsafe_ptr_arg_deref)]
124135
pub fn ttt(addr: *mut u32) -> u32 {
@@ -134,7 +145,7 @@ pub fn ttt(addr: *mut u32) -> u32 {
134145
/// Returns a Test Target Response Payload (cf section D1.2.215 of
135146
/// Armv8-M Architecture Reference Manual).
136147
#[inline]
137-
#[cfg(armv8m)]
148+
#[cortex_m_macros::asm_cfg(armv8m)]
138149
// The __tta function does not dereference the pointer received.
139150
#[allow(clippy::not_unsafe_ptr_arg_deref)]
140151
pub fn tta(addr: *mut u32) -> u32 {
@@ -150,7 +161,7 @@ pub fn tta(addr: *mut u32) -> u32 {
150161
/// Returns a Test Target Response Payload (cf section D1.2.215 of
151162
/// Armv8-M Architecture Reference Manual).
152163
#[inline]
153-
#[cfg(armv8m)]
164+
#[cortex_m_macros::asm_cfg(armv8m)]
154165
// The __ttat function does not dereference the pointer received.
155166
#[allow(clippy::not_unsafe_ptr_arg_deref)]
156167
pub fn ttat(addr: *mut u32) -> u32 {
@@ -163,7 +174,7 @@ pub fn ttat(addr: *mut u32) -> u32 {
163174
/// See section C2.4.26 of Armv8-M Architecture Reference Manual for details.
164175
/// Undefined if executed in Non-Secure state.
165176
#[inline]
166-
#[cfg(armv8m)]
177+
#[cortex_m_macros::asm_cfg(armv8m)]
167178
pub unsafe fn bx_ns(addr: u32) {
168179
unsafe { crate::asm::inner::__bxns(addr) };
169180
}
@@ -172,8 +183,12 @@ pub unsafe fn bx_ns(addr: u32) {
172183
///
173184
/// This method is used by cortex-m-semihosting to provide semihosting syscalls.
174185
#[inline]
175-
pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 {
176-
unsafe { inner::__sh_syscall(nr, arg) }
186+
#[cortex_m_macros::asm_cfg(cortex_m)]
187+
pub unsafe fn semihosting_syscall(mut nr: u32, arg: u32) -> u32 {
188+
unsafe {
189+
asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nomem, nostack, preserves_flags))
190+
};
191+
nr
177192
}
178193

179194
/// Switch to unprivileged mode using the Process Stack
@@ -192,8 +207,8 @@ pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 {
192207
/// * The size of the stack provided here must be large enough for your
193208
/// program - stack overflows are obviously UB. If your processor supports
194209
/// it, you may wish to set the `PSPLIM` register to guard against this.
195-
#[cfg(cortex_m)]
196210
#[inline(always)]
211+
#[cortex_m_macros::asm_cfg(cortex_m)]
197212
pub unsafe fn enter_unprivileged_psp(psp: *const u32, entry: extern "C" fn() -> !) -> ! {
198213
use crate::register::control::{Control, Npriv, Spsel};
199214
const CONTROL_FLAGS: u32 = {
@@ -234,8 +249,8 @@ pub unsafe fn enter_unprivileged_psp(psp: *const u32, entry: extern "C" fn() ->
234249
/// * The size of the stack provided here must be large enough for your
235250
/// program - stack overflows are obviously UB. If your processor supports
236251
/// it, you may wish to set the `PSPLIM` register to guard against this.
237-
#[cfg(cortex_m)]
238252
#[inline(always)]
253+
#[cortex_m_macros::asm_cfg(cortex_m)]
239254
pub unsafe fn enter_privileged_psp(psp: *const u32, entry: extern "C" fn() -> !) -> ! {
240255
use crate::register::control::{Control, Npriv, Spsel};
241256
const CONTROL_FLAGS: u32 = {
@@ -272,6 +287,7 @@ pub unsafe fn enter_privileged_psp(psp: *const u32, entry: extern "C" fn() -> !)
272287
/// `msp` and `rv` must point to valid stack memory and executable code,
273288
/// respectively.
274289
#[inline]
290+
#[cortex_m_macros::asm_cfg(cortex_m)]
275291
pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! {
276292
// Ensure thumb mode is set.
277293
let rv = (rv as u32) | 1;
@@ -292,6 +308,7 @@ pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! {
292308
/// table, with a valid stack pointer as the first word and
293309
/// a valid reset vector as the second word.
294310
#[inline]
311+
#[cortex_m_macros::asm_cfg(cortex_m)]
295312
pub unsafe fn bootload(vector_table: *const u32) -> ! {
296313
unsafe {
297314
let msp = core::ptr::read_volatile(vector_table);

0 commit comments

Comments
 (0)