Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion riscv-macros/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
## v0.4.1 - Unreleased

### Added

- New `exception`, `core_interrupt`, and `external_interrupt` macros for trap handlers.
- New `entry` macro for the Rust entry point (for `riscv-rt`).
- New `rvrt-u-boot` feature to adapt `entry` macro for U-Boot.
- New `post_init` macro for Rust routines that must be executed before main.
- New `rvrt_llvm_arch_patch` and `rvrt_default_start_trap` for generating assembly code
required by the `riscv-rt` crate.
- New `riscv-rt` feature to opt-in `riscv-rt`-related macros.
- New `rvrt-pre-default-start-trap` feature to opt-in assembly injection at the
beginning of `_default_start_trap`.
- New `s-mode` feature to adapt macros to S-Mode execution.

## v0.4.0 - 2025-12-19

Expand Down
8 changes: 6 additions & 2 deletions riscv-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ keywords = ["riscv", "register", "peripheral"]
license = "MIT OR Apache-2.0"
name = "riscv-macros"
repository = "https://github.com/rust-embedded/riscv"
version = "0.4.0"
version = "0.4.1"
edition = "2021"

[lib]
Expand All @@ -18,8 +18,12 @@ proc-macro = true
[features]
rt = []
rt-v-trap = ["rt"]
riscv-rt = ["rt", "syn/extra-traits", "syn/full"]
rvrt-pre-default-start-trap = ["riscv-rt"]
rvrt-u-boot = ["riscv-rt"]
s-mode = []

[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0" }
syn = "2.0"
185 changes: 185 additions & 0 deletions riscv-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use syn::{parse_macro_input, DeriveInput};

mod riscv;

#[cfg(feature = "riscv-rt")]
mod riscv_rt;

/// Attribute-like macro that implements the traits of the `riscv-types` crate for a given enum.
///
/// As these traits are unsafe, the macro must be called with the `unsafe` keyword followed by the trait name.
Expand Down Expand Up @@ -59,3 +62,185 @@ pub fn pac_enum(attr: TokenStream, item: TokenStream) -> TokenStream {
}
.into()
}

/// Attribute to mark which function will be called before jumping to the entry point.
/// You must enable the `post-init` feature in the `riscv-rt` crate to use this macro.
///
/// In contrast with `__pre_init`, this function is called after the static variables
/// are initialized, so it is safe to access them. It is also safe to run Rust code.
///
/// The function must have the signature of `[unsafe] fn([usize])`, where the argument
/// corresponds to the hart ID of the current hart. This is useful for multi-hart systems
/// to perform hart-specific initialization.
///
/// # IMPORTANT
///
/// This attribute can appear at most *once* in the dependency graph.
///
/// # Examples
///
/// ```
/// #[riscv_macros::post_init]
/// unsafe fn before_main(hart_id: usize) {
/// // do something here
/// }
/// ```
#[cfg(feature = "riscv-rt")]
#[proc_macro_attribute]
pub fn post_init(args: TokenStream, input: TokenStream) -> TokenStream {
riscv_rt::Fn::post_init(args, input)
}

/// Attribute to declare the entry point of the program
///
/// The specified function will be called by the reset handler *after* RAM has been initialized.
/// If present, the FPU will also be enabled before the function is called.
///
/// # Signature
///
/// ## Regular Usage
///
/// The type of the specified function must be `[unsafe] fn([usize[, usize[, usize]]]) -> !` (never ending function).
/// The optional arguments correspond to the values passed in registers `a0`, `a1`, and `a2`.
/// The first argument holds the hart ID of the current hart, which is useful for multi-hart systems.
/// The other two arguments are currently unused and reserved for future use.
///
/// ## With U-Boot
///
/// This runtime supports being booted by U-Boot. In this case, the entry point function
/// must have the signature `[unsafe] fn([c_int[, *const *const c_char]]) -> !`, where the first argument
/// corresponds to the `argc` parameter and the second argument corresponds to the `argv` parameter passed by U-Boot.
///
/// Remember to enable the `u-boot` feature in the `riscv-rt` crate to use this functionality.
///
/// # IMPORTANT
///
/// This attribute can appear at most *once* in the dependency graph.
///
/// The entry point will be called by the reset handler. The program can't reference to the entry
/// point, much less invoke it.
///
/// # Examples
///
/// ``` no_run
/// #[riscv_macros::entry]
/// fn main() -> ! {
/// loop {
/// /* .. */
/// }
/// }
/// ```
#[cfg(feature = "riscv-rt")]
#[proc_macro_attribute]
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
riscv_rt::Fn::entry(args, input)
}

/// Attribute to declare an exception handler.
///
/// The function must have the signature `[unsafe] fn([&[mut] riscv_rt::TrapFrame]) [-> !]`.
///
/// The argument of the macro must be a path to a variant of an enum that implements the `riscv_rt::ExceptionNumber` trait.
///
/// # Example
///
/// ``` ignore,no_run
/// #[riscv_rt::exception(riscv::interrupt::Exception::LoadMisaligned)]
/// fn load_misaligned(trap_frame: &mut riscv_rt::TrapFrame) -> ! {
/// loop{};
/// }
/// ```
#[cfg(feature = "riscv-rt")]
#[proc_macro_attribute]
pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
riscv_rt::Fn::trap(args, input, riscv_rt::TrapType::Exception)
}

/// Attribute to declare a core interrupt handler.
///
/// The function must have the signature `[unsafe] fn() [-> !]`.
///
/// The argument of the macro must be a path to a variant of an enum that implements the `riscv_rt::CoreInterruptNumber` trait.
///
/// If the `v-trap` feature is enabled, this macro generates the corresponding interrupt trap handler in assembly.
/// This feature relies on the `RISCV_RT_BASE_ISA` environment variable being set to one of
/// `rv32i`, `rv32e`, `rv64i`, or `rv64e`. Otherwise, this will **panic**.
///
/// # Example
///
/// ``` ignore,no_run
/// #[riscv_rt::core_interrupt(riscv::interrupt::Interrupt::SupervisorSoft)]
/// fn supervisor_soft() -> ! {
/// loop{};
/// }
/// ```
#[cfg(feature = "riscv-rt")]
#[proc_macro_attribute]
pub fn core_interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
riscv_rt::Fn::trap(args, input, riscv_rt::TrapType::CoreInterrupt)
}

/// Attribute to declare an external interrupt handler.
///
/// The function must have the signature `[unsafe] fn() [-> !]`.
///
/// The argument of the macro must be a path to a variant of an enum that implements the `riscv_rt::ExternalInterruptNumber` trait.
///
/// # Example
///
/// ``` ignore,no_run
/// #[riscv_rt::external_interrupt(e310x::interrupt::Interrupt::GPIO0)]
/// fn gpio0() -> ! {
/// loop{};
/// }
/// ```
#[cfg(feature = "riscv-rt")]
#[proc_macro_attribute]
pub fn external_interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
riscv_rt::Fn::trap(args, input, riscv_rt::TrapType::ExternalInterrupt)
}

/// Temporary patch macro to deal with LLVM bug.
///
/// # Note
///
/// This macro is intended to be used internally by the `riscv-rt` crate. Do not use it directly in your code.
#[cfg(feature = "riscv-rt")]
#[proc_macro]
pub fn rvrt_llvm_arch_patch(_input: TokenStream) -> TokenStream {
let q = if let Ok(arch) = std::env::var("RISCV_RT_LLVM_ARCH_PATCH") {
let patch = format!(".attribute arch,\"{arch}\"");
quote! { core::arch::global_asm!{#patch} }
} else {
quote!(compile_error!("RISCV_RT_LLVM_ARCH_PATCH is not set"))
};
q.into()
}

/// Generates assembly code required for the default handling of traps.
///
/// The main routine generated is `_default_start_trap`. If no `_start_trap` function
/// is defined, the linker will use this function as the default trap entry point.
///
/// If the `pre-default-start-trap` feature is enabled, the generated code will also
/// include a call to a user-defined function `_pre_default_start_trap` at the beginning
/// of the `_default_start_trap` routine.
///
/// If the `rt-v-trap` feature is enabled, the macro will also include the assembly code
/// for the `_start_DefaultInterrupt_trap` and `_continue_interrupt_trap` routines, which
/// are required for handling core interrupts in vectored trap mode.
///
/// # Note
///
/// This macro is intended to be used internally by the `riscv-rt` crate. Do not use it directly in your code.
#[cfg(feature = "riscv-rt")]
#[proc_macro]
pub fn rvrt_default_start_trap(_input: TokenStream) -> TokenStream {
match riscv_rt::asm::RiscvArch::try_from_env() {
Some(arch) => arch.default_start_trap().into(),
None => quote! {
compile_error!("RISCV_RT_BASE_ISA environment variable is not set or is invalid");
}
.into(),
}
}
Loading