Skip to content
Closed
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
1 change: 1 addition & 0 deletions riscv-macros/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Added

- `entry` macro for the `riscv-rt` crate (migrated from `riscv-rt-macros`).
- `post_init` macro for the `riscv-rt` crate (migrated from `riscv-rt-macros`).

## v0.4.0 - 2025-12-19
Expand Down
1 change: 1 addition & 0 deletions riscv-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ proc-macro = true
rt = []
rt-v-trap = ["rt"]
riscv-rt = ["rt", "syn/extra-traits", "syn/full"]
u-boot = []

[dependencies]
proc-macro2 = "1.0"
Expand Down
45 changes: 45 additions & 0 deletions riscv-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,48 @@ pub fn pac_enum(attr: TokenStream, item: TokenStream) -> TokenStream {
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)
}
33 changes: 32 additions & 1 deletion riscv-macros/src/riscv_rt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,19 @@ use syn::{

/// Enum representing the supported runtime function attributes
pub enum Fn {
Entry,
PostInit,
}

impl Fn {
/// Convenience method to generate the token stream for the `entry` attribute
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
match Self::Entry.check_args_empty(args) {
Ok(_) => Self::Entry.quote_fn(input),
Err(e) => e.to_compile_error().into(),
}
}

/// Convenience method to generate the token stream for the `post_init` attribute
pub fn post_init(args: TokenStream, input: TokenStream) -> TokenStream {
match Self::PostInit.check_args_empty(args) {
Expand Down Expand Up @@ -83,6 +92,7 @@ impl Fn {
const fn attr_name(&self) -> &'static str {
// Use this match to specify attribute names for different functions in the future
match self {
Self::Entry => "entry",
Self::PostInit => "post_init",
}
}
Expand All @@ -91,6 +101,10 @@ impl Fn {
const fn expected_signature(&self) -> &'static str {
// Use this match to specify expected signatures for different functions in the future
match self {
#[cfg(not(feature = "u-boot"))]
Self::Entry => "[unsafe] fn([usize[, usize[, usize]]]) -> !",
#[cfg(feature = "u-boot")]
Self::Entry => "[unsafe] fn([c_int[, *const *const c_char]]) -> !",
Self::PostInit => "[unsafe] fn([usize])",
}
}
Expand All @@ -99,6 +113,10 @@ impl Fn {
fn check_inputs(&self, inputs: &Punctuated<FnArg, Comma>) -> Result<()> {
// Use this match to specify expected input arguments for different functions in the future
match self {
#[cfg(not(feature = "u-boot"))]
Self::Entry => self.check_fn_args(inputs, &["usize", "usize", "usize"]),
#[cfg(feature = "u-boot")]
Self::Entry => self.check_fn_args(inputs, &["c_int", "*const *const c_char"]),
Self::PostInit => self.check_fn_args(inputs, &["usize"]),
}
}
Expand All @@ -107,6 +125,7 @@ impl Fn {
fn check_output(&self, output: &ReturnType) -> Result<()> {
// Use this match to specify expected output types for different functions in the future
match self {
Self::Entry => check_output_never(output),
Self::PostInit => check_output_empty(output),
}
}
Expand All @@ -115,6 +134,7 @@ impl Fn {
fn export_name(&self, _f: &ItemFn) -> Option<TokenStream2> {
// Use this match to specify export names for different functions in the future
let export_name = match self {
Self::Entry => Some("main".to_string()),
Self::PostInit => Some("__post_init".to_string()),
};

Expand All @@ -129,7 +149,7 @@ impl Fn {
fn link_section(&self, _f: &ItemFn) -> Option<TokenStream2> {
// Use this match to specify section names for different functions in the future
let section_name: Option<String> = match self {
Self::PostInit => None,
Self::Entry | Self::PostInit => None,
};

section_name.map(|section| quote! {
Expand Down Expand Up @@ -236,3 +256,14 @@ fn check_output_empty(output: &ReturnType) -> Result<()> {
},
}
}

/// Make sure the output type is `!` (never)
fn check_output_never(output: &ReturnType) -> Result<()> {
match output {
ReturnType::Type(_, ty) => match **ty {
Type::Never(_) => Ok(()),
_ => Err(Error::new(ty.span(), "return type must be !")),
},
ReturnType::Default => Err(Error::new(output.span(), "return type must be !")),
}
}
1 change: 1 addition & 0 deletions riscv-rt/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Changed

- Using `entry` from `riscv-macros` instead of `riscv-rt-macros`.
- Using `post_init` from `riscv-macros` instead of `riscv-rt-macros`.

## v0.17.1 - 2026-01-13
Expand Down
2 changes: 1 addition & 1 deletion riscv-rt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ post-init = []
s-mode = ["riscv-rt-macros/s-mode"]
single-hart = []
v-trap = ["riscv-rt-macros/v-trap", "riscv/rt-v-trap", "riscv-macros/rt-v-trap"]
u-boot = ["riscv-rt-macros/u-boot", "single-hart"]
u-boot = ["riscv-rt-macros/u-boot", "single-hart", "riscv-macros/u-boot"]
no-interrupts = []
no-exceptions = []
no-mhartid = ["single-hart"]
Expand Down
3 changes: 2 additions & 1 deletion riscv-rt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,8 @@ use riscv::register::{
mtvec::{self as xtvec, Mtvec as Xtvec, TrapMode},
};

pub use riscv_rt_macros::{core_interrupt, entry, exception, external_interrupt};
pub use riscv_macros::entry;
pub use riscv_rt_macros::{core_interrupt, exception, external_interrupt};
pub use riscv_types::*;

#[cfg(feature = "post-init")]
Expand Down
4 changes: 4 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/fail_arg_count.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[riscv_rt::entry]
fn entry_point(_hart_id: usize, _dtb: usize, _other: usize, _one_more: usize) {}

fn main() {}
5 changes: 5 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/fail_arg_count.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: `#[entry]` function has too many input arguments
--> tests/riscv-rt/entry/fail_arg_count.rs:2:61
|
2 | fn entry_point(_hart_id: usize, _dtb: usize, _other: usize, _one_more: usize) {}
| ^^^^^^^^^
4 changes: 4 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/fail_arg_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[riscv_rt::entry]
fn entry_point(_hart_id: String) {}

fn main() {}
5 changes: 5 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/fail_arg_type.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: argument type must be usize
--> tests/riscv-rt/entry/fail_arg_type.rs:2:26
|
2 | fn entry_point(_hart_id: String) {}
| ^^^^^^
4 changes: 4 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/fail_async.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[riscv_rt::entry]
async fn entry_point() {}

fn main() {}
5 changes: 5 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/fail_async.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: `#[entry]` function signature must be `[unsafe] fn([usize[, usize[, usize]]]) -> !`
--> tests/riscv-rt/entry/fail_async.rs:2:1
|
2 | async fn entry_point() {}
| ^^^^^
4 changes: 4 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/fail_public.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[riscv_rt::entry]
pub fn entry_point() -> ! {}

fn main() {}
5 changes: 5 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/fail_public.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: `#[entry]` function must be private
--> tests/riscv-rt/entry/fail_public.rs:2:1
|
2 | pub fn entry_point() -> ! {}
| ^^^
4 changes: 4 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/fail_return_empty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[riscv_rt::entry]
fn entry_point() {}

fn main() {}
7 changes: 7 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/fail_return_empty.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: return type must be !
--> tests/riscv-rt/entry/fail_return_empty.rs:1:1
|
1 | #[riscv_rt::entry]
| ^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `riscv_rt::entry` (in Nightly builds, run with -Z macro-backtrace for more info)
4 changes: 4 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/fail_return_tuple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[riscv_rt::entry]
fn entry_point() -> () {}

fn main() {}
5 changes: 5 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/fail_return_tuple.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: return type must be !
--> tests/riscv-rt/entry/fail_return_tuple.rs:2:21
|
2 | fn entry_point() -> () {}
| ^^
6 changes: 6 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/pass_safe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#[riscv_rt::entry]
fn entry_point(_hart_id: usize) -> ! {
loop {}
}

fn main() {} // TEST OK
6 changes: 6 additions & 0 deletions tests-trybuild/tests/riscv-rt/entry/pass_unsafe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#[riscv_rt::entry]
unsafe fn entry_point(_hart_id: usize) -> ! {
loop {}
}

fn main() {} // TEST OK