Skip to content

Commit 187453b

Browse files
Merge pull request #389 from romancardenas/riscv-macros
`riscv-macros`: add all the macros required by `riscv-rt`
2 parents 97443b4 + aa8c389 commit 187453b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1242
-205
lines changed

riscv-macros/CHANGELOG.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,20 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

8-
## [Unreleased]
8+
## v0.4.1 - Unreleased
9+
10+
### Added
11+
12+
- New `exception`, `core_interrupt`, and `external_interrupt` macros for trap handlers.
13+
- New `entry` macro for the Rust entry point (for `riscv-rt`).
14+
- New `rvrt-u-boot` feature to adapt `entry` macro for U-Boot.
15+
- New `post_init` macro for Rust routines that must be executed before main.
16+
- New `rvrt_llvm_arch_patch` and `rvrt_default_start_trap` for generating assembly code
17+
required by the `riscv-rt` crate.
18+
- New `riscv-rt` feature to opt-in `riscv-rt`-related macros.
19+
- New `rvrt-pre-default-start-trap` feature to opt-in assembly injection at the
20+
beginning of `_default_start_trap`.
21+
- New `s-mode` feature to adapt macros to S-Mode execution.
922

1023
## v0.4.0 - 2025-12-19
1124

riscv-macros/Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ keywords = ["riscv", "register", "peripheral"]
99
license = "MIT OR Apache-2.0"
1010
name = "riscv-macros"
1111
repository = "https://github.com/rust-embedded/riscv"
12-
version = "0.4.0"
12+
version = "0.4.1"
1313
edition = "2021"
1414

1515
[lib]
@@ -18,8 +18,12 @@ proc-macro = true
1818
[features]
1919
rt = []
2020
rt-v-trap = ["rt"]
21+
riscv-rt = ["rt", "syn/extra-traits", "syn/full"]
22+
rvrt-pre-default-start-trap = ["riscv-rt"]
23+
rvrt-u-boot = ["riscv-rt"]
24+
s-mode = []
2125

2226
[dependencies]
2327
proc-macro2 = "1.0"
2428
quote = "1.0"
25-
syn = { version = "2.0" }
29+
syn = "2.0"

riscv-macros/src/lib.rs

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ use syn::{parse_macro_input, DeriveInput};
44

55
mod riscv;
66

7+
#[cfg(feature = "riscv-rt")]
8+
mod riscv_rt;
9+
710
/// Attribute-like macro that implements the traits of the `riscv-types` crate for a given enum.
811
///
912
/// As these traits are unsafe, the macro must be called with the `unsafe` keyword followed by the trait name.
@@ -59,3 +62,185 @@ pub fn pac_enum(attr: TokenStream, item: TokenStream) -> TokenStream {
5962
}
6063
.into()
6164
}
65+
66+
/// Attribute to mark which function will be called before jumping to the entry point.
67+
/// You must enable the `post-init` feature in the `riscv-rt` crate to use this macro.
68+
///
69+
/// In contrast with `__pre_init`, this function is called after the static variables
70+
/// are initialized, so it is safe to access them. It is also safe to run Rust code.
71+
///
72+
/// The function must have the signature of `[unsafe] fn([usize])`, where the argument
73+
/// corresponds to the hart ID of the current hart. This is useful for multi-hart systems
74+
/// to perform hart-specific initialization.
75+
///
76+
/// # IMPORTANT
77+
///
78+
/// This attribute can appear at most *once* in the dependency graph.
79+
///
80+
/// # Examples
81+
///
82+
/// ```
83+
/// #[riscv_macros::post_init]
84+
/// unsafe fn before_main(hart_id: usize) {
85+
/// // do something here
86+
/// }
87+
/// ```
88+
#[cfg(feature = "riscv-rt")]
89+
#[proc_macro_attribute]
90+
pub fn post_init(args: TokenStream, input: TokenStream) -> TokenStream {
91+
riscv_rt::Fn::post_init(args, input)
92+
}
93+
94+
/// Attribute to declare the entry point of the program
95+
///
96+
/// The specified function will be called by the reset handler *after* RAM has been initialized.
97+
/// If present, the FPU will also be enabled before the function is called.
98+
///
99+
/// # Signature
100+
///
101+
/// ## Regular Usage
102+
///
103+
/// The type of the specified function must be `[unsafe] fn([usize[, usize[, usize]]]) -> !` (never ending function).
104+
/// The optional arguments correspond to the values passed in registers `a0`, `a1`, and `a2`.
105+
/// The first argument holds the hart ID of the current hart, which is useful for multi-hart systems.
106+
/// The other two arguments are currently unused and reserved for future use.
107+
///
108+
/// ## With U-Boot
109+
///
110+
/// This runtime supports being booted by U-Boot. In this case, the entry point function
111+
/// must have the signature `[unsafe] fn([c_int[, *const *const c_char]]) -> !`, where the first argument
112+
/// corresponds to the `argc` parameter and the second argument corresponds to the `argv` parameter passed by U-Boot.
113+
///
114+
/// Remember to enable the `u-boot` feature in the `riscv-rt` crate to use this functionality.
115+
///
116+
/// # IMPORTANT
117+
///
118+
/// This attribute can appear at most *once* in the dependency graph.
119+
///
120+
/// The entry point will be called by the reset handler. The program can't reference to the entry
121+
/// point, much less invoke it.
122+
///
123+
/// # Examples
124+
///
125+
/// ``` no_run
126+
/// #[riscv_macros::entry]
127+
/// fn main() -> ! {
128+
/// loop {
129+
/// /* .. */
130+
/// }
131+
/// }
132+
/// ```
133+
#[cfg(feature = "riscv-rt")]
134+
#[proc_macro_attribute]
135+
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
136+
riscv_rt::Fn::entry(args, input)
137+
}
138+
139+
/// Attribute to declare an exception handler.
140+
///
141+
/// The function must have the signature `[unsafe] fn([&[mut] riscv_rt::TrapFrame]) [-> !]`.
142+
///
143+
/// The argument of the macro must be a path to a variant of an enum that implements the `riscv_rt::ExceptionNumber` trait.
144+
///
145+
/// # Example
146+
///
147+
/// ``` ignore,no_run
148+
/// #[riscv_rt::exception(riscv::interrupt::Exception::LoadMisaligned)]
149+
/// fn load_misaligned(trap_frame: &mut riscv_rt::TrapFrame) -> ! {
150+
/// loop{};
151+
/// }
152+
/// ```
153+
#[cfg(feature = "riscv-rt")]
154+
#[proc_macro_attribute]
155+
pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
156+
riscv_rt::Fn::trap(args, input, riscv_rt::TrapType::Exception)
157+
}
158+
159+
/// Attribute to declare a core interrupt handler.
160+
///
161+
/// The function must have the signature `[unsafe] fn() [-> !]`.
162+
///
163+
/// The argument of the macro must be a path to a variant of an enum that implements the `riscv_rt::CoreInterruptNumber` trait.
164+
///
165+
/// If the `v-trap` feature is enabled, this macro generates the corresponding interrupt trap handler in assembly.
166+
/// This feature relies on the `RISCV_RT_BASE_ISA` environment variable being set to one of
167+
/// `rv32i`, `rv32e`, `rv64i`, or `rv64e`. Otherwise, this will **panic**.
168+
///
169+
/// # Example
170+
///
171+
/// ``` ignore,no_run
172+
/// #[riscv_rt::core_interrupt(riscv::interrupt::Interrupt::SupervisorSoft)]
173+
/// fn supervisor_soft() -> ! {
174+
/// loop{};
175+
/// }
176+
/// ```
177+
#[cfg(feature = "riscv-rt")]
178+
#[proc_macro_attribute]
179+
pub fn core_interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
180+
riscv_rt::Fn::trap(args, input, riscv_rt::TrapType::CoreInterrupt)
181+
}
182+
183+
/// Attribute to declare an external interrupt handler.
184+
///
185+
/// The function must have the signature `[unsafe] fn() [-> !]`.
186+
///
187+
/// The argument of the macro must be a path to a variant of an enum that implements the `riscv_rt::ExternalInterruptNumber` trait.
188+
///
189+
/// # Example
190+
///
191+
/// ``` ignore,no_run
192+
/// #[riscv_rt::external_interrupt(e310x::interrupt::Interrupt::GPIO0)]
193+
/// fn gpio0() -> ! {
194+
/// loop{};
195+
/// }
196+
/// ```
197+
#[cfg(feature = "riscv-rt")]
198+
#[proc_macro_attribute]
199+
pub fn external_interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
200+
riscv_rt::Fn::trap(args, input, riscv_rt::TrapType::ExternalInterrupt)
201+
}
202+
203+
/// Temporary patch macro to deal with LLVM bug.
204+
///
205+
/// # Note
206+
///
207+
/// This macro is intended to be used internally by the `riscv-rt` crate. Do not use it directly in your code.
208+
#[cfg(feature = "riscv-rt")]
209+
#[proc_macro]
210+
pub fn rvrt_llvm_arch_patch(_input: TokenStream) -> TokenStream {
211+
let q = if let Ok(arch) = std::env::var("RISCV_RT_LLVM_ARCH_PATCH") {
212+
let patch = format!(".attribute arch,\"{arch}\"");
213+
quote! { core::arch::global_asm!{#patch} }
214+
} else {
215+
quote!(compile_error!("RISCV_RT_LLVM_ARCH_PATCH is not set"))
216+
};
217+
q.into()
218+
}
219+
220+
/// Generates assembly code required for the default handling of traps.
221+
///
222+
/// The main routine generated is `_default_start_trap`. If no `_start_trap` function
223+
/// is defined, the linker will use this function as the default trap entry point.
224+
///
225+
/// If the `pre-default-start-trap` feature is enabled, the generated code will also
226+
/// include a call to a user-defined function `_pre_default_start_trap` at the beginning
227+
/// of the `_default_start_trap` routine.
228+
///
229+
/// If the `rt-v-trap` feature is enabled, the macro will also include the assembly code
230+
/// for the `_start_DefaultInterrupt_trap` and `_continue_interrupt_trap` routines, which
231+
/// are required for handling core interrupts in vectored trap mode.
232+
///
233+
/// # Note
234+
///
235+
/// This macro is intended to be used internally by the `riscv-rt` crate. Do not use it directly in your code.
236+
#[cfg(feature = "riscv-rt")]
237+
#[proc_macro]
238+
pub fn rvrt_default_start_trap(_input: TokenStream) -> TokenStream {
239+
match riscv_rt::asm::RiscvArch::try_from_env() {
240+
Some(arch) => arch.default_start_trap().into(),
241+
None => quote! {
242+
compile_error!("RISCV_RT_BASE_ISA environment variable is not set or is invalid");
243+
}
244+
.into(),
245+
}
246+
}

0 commit comments

Comments
 (0)