Skip to content

Commit 904d37e

Browse files
committed
Add asm_wrapper 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.
1 parent f22f532 commit 904d37e

9 files changed

Lines changed: 160 additions & 201 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" }
2223

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

cortex-m/macros/.zed/settings.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"lsp": {
3+
"rust-analyzer": {
4+
"initialization_options": {
5+
"cargo": {
6+
// "cfgs": ["test"],
7+
// "features": [
8+
// // blah
9+
// // "inline-asm",
10+
// // "cm7-r0p1",
11+
// ],
12+
// "target": "thumbv8m.main-none-eabihf",
13+
// "target": "thumbv6m-none-eabi",
14+
// "target": "thumbv7em-none-eabi",
15+
// "target": "thumbv7em-none-eabihf",
16+
// "target": "thumbv7m-none-eabi",
17+
// "target": "thumbv8m.base-none-eabi",
18+
// "target": "thumbv8m.main-none-eabi"
19+
},
20+
},
21+
},
22+
},
23+
}

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

cortex-m/src/asm.rs

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,24 @@
55

66
#![allow(missing_docs)]
77

8-
#[cfg_attr(any(armv6m, armv7m, armv7em, armv8m), path = "asm/inner.rs")]
9-
#[cfg_attr(
10-
all(not(armv6m), not(armv7m), not(armv7em), not(armv8m)),
11-
path = "asm/inner_mock.rs"
12-
)]
8+
#[cfg(any(armv6m, armv7m, armv7em, armv8m))]
9+
use core::arch::asm;
10+
11+
// #[cfg_attr(any(armv6m, armv7m, armv7em, armv8m), path = "asm/inner.rs")]
12+
// #[cfg_attr(
13+
// all(not(armv6m), not(armv7m), not(armv7em), not(armv8m)),
14+
// path = "asm/inner_mock.rs"
15+
// )]
1316
pub mod inner;
1417

1518
/// Puts the processor in Debug state. Debuggers can pick this up as a "breakpoint".
1619
///
1720
/// **NOTE** calling `bkpt` when the processor is not connected to a debugger will cause an
1821
/// exception.
1922
#[inline(always)]
23+
#[cortex_m_macros::asm_wrapper(any(armv6m, armv7m, armv7em, armv8m))]
2024
pub fn bkpt() {
21-
unsafe { inner::__bkpt() };
25+
unsafe { asm!("bkpt", options(nomem, nostack, preserves_flags)) };
2226
}
2327

2428
/// Blocks the program for *at least* `cycles` CPU cycles.
@@ -36,12 +40,14 @@ pub fn bkpt() {
3640
/// initialization of peripherals if and only if accurate timing is not essential. In any other case
3741
/// please use a more accurate method to produce a delay.
3842
#[inline]
43+
#[cortex_m_macros::asm_wrapper(any(armv6m, armv7m, armv7em, armv8m))]
3944
pub fn delay(cycles: u32) {
4045
unsafe { inner::__delay(cycles) };
4146
}
4247

4348
/// A no-operation. Useful to prevent delay loops from being optimized away.
4449
#[inline]
50+
#[cortex_m_macros::asm_wrapper(any(armv6m, armv7m, armv7em, armv8m))]
4551
pub fn nop() {
4652
unsafe { inner::__nop() };
4753
}
@@ -50,24 +56,28 @@ pub fn nop() {
5056
///
5157
/// Can be used as a stable alternative to `core::intrinsics::abort`.
5258
#[inline]
59+
#[cortex_m_macros::asm_wrapper(any(armv6m, armv7m, armv7em, armv8m))]
5360
pub fn udf() -> ! {
5461
unsafe { inner::__udf() }
5562
}
5663

5764
/// Wait For Event
5865
#[inline]
66+
#[cortex_m_macros::asm_wrapper(any(armv6m, armv7m, armv7em, armv8m))]
5967
pub fn wfe() {
6068
unsafe { inner::__wfe() }
6169
}
6270

6371
/// Wait For Interrupt
6472
#[inline]
73+
#[cortex_m_macros::asm_wrapper(any(armv6m, armv7m, armv7em, armv8m))]
6574
pub fn wfi() {
6675
unsafe { inner::__wfi() }
6776
}
6877

6978
/// Send Event
7079
#[inline]
80+
#[cortex_m_macros::asm_wrapper(any(armv6m, armv7m, armv7em, armv8m))]
7181
pub fn sev() {
7282
unsafe { inner::__sev() }
7383
}
@@ -77,6 +87,7 @@ pub fn sev() {
7787
/// Flushes the pipeline in the processor, so that all instructions following the `ISB` are fetched
7888
/// from cache or memory, after the instruction has been completed.
7989
#[inline]
90+
#[cortex_m_macros::asm_wrapper(any(armv6m, armv7m, armv7em, armv8m))]
8091
pub fn isb() {
8192
unsafe { inner::__isb() }
8293
}
@@ -89,6 +100,7 @@ pub fn isb() {
89100
/// * any explicit memory access made before this instruction is complete
90101
/// * all cache and branch predictor maintenance operations before this instruction complete
91102
#[inline]
103+
#[cortex_m_macros::asm_wrapper(any(armv6m, armv7m, armv7em, armv8m))]
92104
pub fn dsb() {
93105
unsafe { inner::__dsb() }
94106
}
@@ -99,6 +111,7 @@ pub fn dsb() {
99111
/// instruction are observed before any explicit memory accesses that appear in program order
100112
/// after the `DMB` instruction.
101113
#[inline]
114+
#[cortex_m_macros::asm_wrapper(any(armv6m, armv7m, armv7em, armv8m))]
102115
pub fn dmb() {
103116
unsafe { inner::__dmb() }
104117
}
@@ -109,7 +122,7 @@ pub fn dmb() {
109122
/// Returns a Test Target Response Payload (cf section D1.2.215 of
110123
/// Armv8-M Architecture Reference Manual).
111124
#[inline]
112-
#[cfg(armv8m)]
125+
#[cortex_m_macros::asm_wrapper(armv8m)]
113126
// The __tt function does not dereference the pointer received.
114127
#[allow(clippy::not_unsafe_ptr_arg_deref)]
115128
pub fn tt(addr: *mut u32) -> u32 {
@@ -124,7 +137,7 @@ pub fn tt(addr: *mut u32) -> u32 {
124137
/// Returns a Test Target Response Payload (cf section D1.2.215 of
125138
/// Armv8-M Architecture Reference Manual).
126139
#[inline]
127-
#[cfg(armv8m)]
140+
#[cortex_m_macros::asm_wrapper(armv8m)]
128141
// The __ttt function does not dereference the pointer received.
129142
#[allow(clippy::not_unsafe_ptr_arg_deref)]
130143
pub fn ttt(addr: *mut u32) -> u32 {
@@ -140,7 +153,7 @@ pub fn ttt(addr: *mut u32) -> u32 {
140153
/// Returns a Test Target Response Payload (cf section D1.2.215 of
141154
/// Armv8-M Architecture Reference Manual).
142155
#[inline]
143-
#[cfg(armv8m)]
156+
#[cortex_m_macros::asm_wrapper(armv8m)]
144157
// The __tta function does not dereference the pointer received.
145158
#[allow(clippy::not_unsafe_ptr_arg_deref)]
146159
pub fn tta(addr: *mut u32) -> u32 {
@@ -156,7 +169,7 @@ pub fn tta(addr: *mut u32) -> u32 {
156169
/// Returns a Test Target Response Payload (cf section D1.2.215 of
157170
/// Armv8-M Architecture Reference Manual).
158171
#[inline]
159-
#[cfg(armv8m)]
172+
#[cortex_m_macros::asm_wrapper(armv8m)]
160173
// The __ttat function does not dereference the pointer received.
161174
#[allow(clippy::not_unsafe_ptr_arg_deref)]
162175
pub fn ttat(addr: *mut u32) -> u32 {
@@ -169,7 +182,7 @@ pub fn ttat(addr: *mut u32) -> u32 {
169182
/// See section C2.4.26 of Armv8-M Architecture Reference Manual for details.
170183
/// Undefined if executed in Non-Secure state.
171184
#[inline]
172-
#[cfg(armv8m)]
185+
#[cortex_m_macros::asm_wrapper(armv8m)]
173186
pub unsafe fn bx_ns(addr: u32) {
174187
unsafe { crate::asm::inner::__bxns(addr) };
175188
}
@@ -178,8 +191,13 @@ pub unsafe fn bx_ns(addr: u32) {
178191
///
179192
/// This method is used by cortex-m-semihosting to provide semihosting syscalls.
180193
#[inline]
194+
#[cortex_m_macros::asm_wrapper(any(armv6m, armv7m, armv7em, armv8m))]
181195
pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 {
182-
unsafe { inner::__sh_syscall(nr, arg) }
196+
let mut nr = nr;
197+
unsafe {
198+
asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nomem, nostack, preserves_flags))
199+
};
200+
nr
183201
}
184202

185203
/// Switch to unprivileged mode using the Process Stack
@@ -198,8 +216,8 @@ pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 {
198216
/// * The size of the stack provided here must be large enough for your
199217
/// program - stack overflows are obviously UB. If your processor supports
200218
/// it, you may wish to set the `PSPLIM` register to guard against this.
201-
#[cfg(cortex_m)]
202219
#[inline(always)]
220+
#[cortex_m_macros::asm_wrapper(cortex_m)]
203221
pub unsafe fn enter_unprivileged_psp(psp: *const u32, entry: extern "C" fn() -> !) -> ! {
204222
use crate::register::control::{Control, Npriv, Spsel};
205223
const CONTROL_FLAGS: u32 = {
@@ -240,8 +258,8 @@ pub unsafe fn enter_unprivileged_psp(psp: *const u32, entry: extern "C" fn() ->
240258
/// * The size of the stack provided here must be large enough for your
241259
/// program - stack overflows are obviously UB. If your processor supports
242260
/// it, you may wish to set the `PSPLIM` register to guard against this.
243-
#[cfg(cortex_m)]
244261
#[inline(always)]
262+
#[cortex_m_macros::asm_wrapper(cortex_m)]
245263
pub unsafe fn enter_privileged_psp(psp: *const u32, entry: extern "C" fn() -> !) -> ! {
246264
use crate::register::control::{Control, Npriv, Spsel};
247265
const CONTROL_FLAGS: u32 = {
@@ -278,6 +296,7 @@ pub unsafe fn enter_privileged_psp(psp: *const u32, entry: extern "C" fn() -> !)
278296
/// `msp` and `rv` must point to valid stack memory and executable code,
279297
/// respectively.
280298
#[inline]
299+
#[cortex_m_macros::asm_wrapper(any(armv6m, armv7m, armv7em, armv8m))]
281300
pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! {
282301
// Ensure thumb mode is set.
283302
let rv = (rv as u32) | 1;
@@ -298,6 +317,7 @@ pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! {
298317
/// table, with a valid stack pointer as the first word and
299318
/// a valid reset vector as the second word.
300319
#[inline]
320+
#[cortex_m_macros::asm_wrapper(any(armv6m, armv7m, armv7em, armv8m))]
301321
pub unsafe fn bootload(vector_table: *const u32) -> ! {
302322
unsafe {
303323
let msp = core::ptr::read_volatile(vector_table);

0 commit comments

Comments
 (0)