Skip to content

Commit

Permalink
Use environment variables for finding out the base ISA on riscv_rt_ma…
Browse files Browse the repository at this point in the history
…cros
  • Loading branch information
romancardenas committed Nov 27, 2024
1 parent 1e71338 commit fb14952
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 145 deletions.
18 changes: 16 additions & 2 deletions riscv-rt/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// NOTE: Adapted from cortex-m/build.rs

use riscv_target_parser::RiscvTarget;
use riscv_target_parser::{Extension, RiscvTarget, Width};
use std::{env, fs, io, path::PathBuf};

fn add_linker_script(arch_width: u32) -> io::Result<()> {
Expand Down Expand Up @@ -28,11 +28,25 @@ fn main() {
let cargo_flags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

if let Ok(target) = RiscvTarget::build(&target, &cargo_flags) {
let width = target.width();
let base = target.base_extension().expect("No base extension found");

// set environmet variable RISCV_RT_BASE_ISA to the width of the target
// this is used in riscv_rt_macros to determine the base ISA
let env_var = match (width, base) {
(Width::W32, Extension::I) => "rv32i",
(Width::W32, Extension::E) => "rv32e",
(Width::W64, Extension::I) => "rv64i",
(Width::W64, Extension::E) => "rv64e",
_ => panic!("Unsupported target"),
};
println!("cargo:rustc-env=RISCV_RT_BASE_ISA={env_var}");

for flag in target.rustc_flags() {
// Required until target_feature risc-v is stable and in-use
println!("cargo:rustc-check-cfg=cfg({flag})");
println!("cargo:rustc-cfg={flag}");
}
add_linker_script(target.width().into()).unwrap();
add_linker_script(width.into()).unwrap();
}
}
145 changes: 32 additions & 113 deletions riscv-rt/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
#![deny(warnings)]

extern crate core;
extern crate proc_macro;
extern crate proc_macro2;
extern crate quote;
extern crate syn;

use std::vec;

use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
use syn::{
Expand All @@ -18,8 +11,6 @@ use syn::{
FnArg, ItemFn, LitInt, LitStr, PatType, Path, ReturnType, Token, Type, Visibility,
};

use proc_macro::TokenStream;

/// Attribute to declare the entry point of the program
///
/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you
Expand Down Expand Up @@ -367,7 +358,7 @@ enum RiscvArch {
Rv64E,
}

impl syn::parse::Parse for RiscvArch {
impl Parse for RiscvArch {
fn parse(input: parse::ParseStream) -> syn::Result<Self> {
let ident: syn::Ident = input.parse()?;
match ident.to_string().as_str() {
Expand All @@ -381,6 +372,17 @@ impl syn::parse::Parse for RiscvArch {
}

impl RiscvArch {
fn try_from_env() -> Option<Self> {
let arch = std::env::var("RISCV_RT_BASE_ISA").ok()?;
match arch.as_str() {
"rv32i" => Some(Self::Rv32I),
"rv32e" => Some(Self::Rv32E),
"rv64i" => Some(Self::Rv64I),
"rv64e" => Some(Self::Rv64E),
_ => None,
}
}

fn width(&self) -> usize {
match self {
Self::Rv32I | Self::Rv32E => 4,
Expand Down Expand Up @@ -431,9 +433,9 @@ fn store_trap<T: FnMut(&str) -> bool>(arch: RiscvArch, mut filter: T) -> String
.iter()
.enumerate()
.filter(|(_, &reg)| !reg.starts_with('_') && filter(reg))
.map(|(i, reg)| format!(" {store} {reg}, {i}*{width}(sp)"))
.map(|(i, reg)| format!("{store} {reg}, {i}*{width}(sp)"))
.collect::<Vec<_>>()
.join("\n")
.join("\n ")
}

/// Generate the assembly instructions to load the trap frame.
Expand All @@ -445,18 +447,18 @@ fn load_trap(arch: RiscvArch) -> String {
.iter()
.enumerate()
.filter(|(_, &reg)| !reg.starts_with('_'))
.map(|(i, reg)| format!(" {load} {reg}, {i}*{width}(sp)"))
.map(|(i, reg)| format!("{load} {reg}, {i}*{width}(sp)"))
.collect::<Vec<_>>()
.join("\n")
.join("\n ")
}

/// Generates weak `_start_trap` function in assembly.
///
/// This implementation stores all registers in the trap frame and calls `_start_trap_rust`.
/// The trap frame is allocated on the stack and deallocated after the call.
#[proc_macro]
pub fn weak_start_trap(input: TokenStream) -> TokenStream {
let arch = parse_macro_input!(input as RiscvArch);
pub fn weak_start_trap(_input: TokenStream) -> TokenStream {
let arch = RiscvArch::try_from_env().unwrap();

let width = arch.width();
let trap_size = arch.trap_frame().len();
Expand All @@ -482,10 +484,10 @@ core::arch::global_asm!(
.weak _start_trap
_start_trap:
addi sp, sp, - {trap_size} * {width}
{store}
{store}
add a0, sp, zero
jal ra, _start_trap_rust
{load}
{load}
addi sp, sp, {trap_size} * {width}
{ret}
");"#
Expand All @@ -500,8 +502,8 @@ _start_trap:
/// The '_start_DefaultHandler_trap' function stores the trap frame partially (only register a0) and
/// jumps to the interrupt handler. The '_continue_interrupt_trap' function stores the trap frame
/// partially (all registers except a0), jumps to the interrupt handler, and restores the trap frame.
pub fn vectored_interrupt_trap(input: TokenStream) -> TokenStream {
let arch = parse_macro_input!(input as RiscvArch);
pub fn vectored_interrupt_trap(_input: TokenStream) -> TokenStream {
let arch = RiscvArch::try_from_env().unwrap();
let width = arch.width();
let trap_size = arch.trap_frame().len();
let store_start = store_trap(arch, |reg| reg == "a0");
Expand All @@ -522,15 +524,15 @@ core::arch::global_asm!(
.global _start_DefaultHandler_trap
_start_DefaultHandler_trap:
addi sp, sp, -{trap_size} * {width} // allocate space for trap frame
{store_start} // store trap partially (only register a0)
{store_start} // store trap partially (only register a0)
la a0, DefaultHandler // load interrupt handler address into a0
.align 4
.global _continue_interrupt_trap
_continue_interrupt_trap:
{store_continue} // store trap partially (all registers except a0)
{store_continue} // store trap partially (all registers except a0)
jalr ra, a0, 0 // jump to corresponding interrupt handler (address stored in a0)
{load} // restore trap frame
{load} // restore trap frame
addi sp, sp, {trap_size} * {width} // deallocate space for trap frame
{ret} // return from interrupt
");"#
Expand Down Expand Up @@ -643,93 +645,11 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
/// loop{};
/// }
/// ```
pub fn core_interrupt_rv32i(args: TokenStream, input: TokenStream) -> TokenStream {
let arch = match () {
#[cfg(feature = "v-trap")]
() => Some(RiscvArch::Rv32I),
#[cfg(not(feature = "v-trap"))]
() => None,
};
trap(args, input, RiscvPacItem::CoreInterrupt, arch)
}

#[proc_macro_attribute]
/// 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.
///
/// # Example
///
/// ``` ignore,no_run
/// #[riscv_rt::core_interrupt(riscv::interrupt::Interrupt::SupervisorSoft)]
/// fn supervisor_soft() -> ! {
/// loop{};
/// }
/// ```
pub fn core_interrupt_rv32e(args: TokenStream, input: TokenStream) -> TokenStream {
let arch = match () {
#[cfg(feature = "v-trap")]
() => Some(RiscvArch::Rv32E),
#[cfg(not(feature = "v-trap"))]
() => None,
};
trap(args, input, RiscvPacItem::CoreInterrupt, arch)
}

#[proc_macro_attribute]
/// 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.
///
/// # Example
///
/// ``` ignore,no_run
/// #[riscv_rt::core_interrupt(riscv::interrupt::Interrupt::SupervisorSoft)]
/// fn supervisor_soft() -> ! {
/// loop{};
/// }
/// ```
pub fn core_interrupt_rv64i(args: TokenStream, input: TokenStream) -> TokenStream {
let arch = match () {
#[cfg(feature = "v-trap")]
() => Some(RiscvArch::Rv64I),
#[cfg(not(feature = "v-trap"))]
() => None,
};
trap(args, input, RiscvPacItem::CoreInterrupt, arch)
}

#[proc_macro_attribute]
/// 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.
///
/// # Example
///
/// ``` ignore,no_run
/// #[riscv_rt::core_interrupt(riscv::interrupt::Interrupt::SupervisorSoft)]
/// fn supervisor_soft() -> ! {
/// loop{};
/// }
/// ```
pub fn core_interrupt_rv64e(args: TokenStream, input: TokenStream) -> TokenStream {
let arch = match () {
#[cfg(feature = "v-trap")]
() => Some(RiscvArch::Rv64E),
#[cfg(not(feature = "v-trap"))]
() => None,
pub fn core_interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
let arch = if cfg!(feature = "v-trap") {
RiscvArch::try_from_env()
} else {
None
};
trap(args, input, RiscvPacItem::CoreInterrupt, arch)
}
Expand Down Expand Up @@ -790,7 +710,6 @@ fn trap(
Some(arch) => {
let trap = start_interrupt_trap(int_ident, arch);
quote! {
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
#trap
}
}
Expand Down Expand Up @@ -828,7 +747,7 @@ core::arch::global_asm!(
.global _start_{interrupt}_trap
_start_{interrupt}_trap:
addi sp, sp, -{trap_size} * {width} // allocate space for trap frame
{store} // store trap partially (only register a0)
{store} // store trap partially (only register a0)
la a0, {interrupt} // load interrupt handler address into a0
j _continue_interrupt_trap // jump to common part of interrupt trap
");"#
Expand Down
19 changes: 3 additions & 16 deletions riscv-rt/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,23 +278,10 @@ _pre_init_trap:
j _pre_init_trap",
);

#[cfg(all(target_arch = "riscv32", riscvi))]
riscv_rt_macros::weak_start_trap!(rv32i);
#[cfg(all(target_arch = "riscv32", riscve))]
riscv_rt_macros::weak_start_trap!(rv32e);
#[cfg(all(target_arch = "riscv64", riscvi))]
riscv_rt_macros::weak_start_trap!(rv64i);
#[cfg(all(target_arch = "riscv64", riscve))]
riscv_rt_macros::weak_start_trap!(rv64e);
riscv_rt_macros::weak_start_trap!();

#[cfg(all(target_arch = "riscv32", riscvi, feature = "v-trap"))]
riscv_rt_macros::vectored_interrupt_trap!(rv32i);
#[cfg(all(target_arch = "riscv32", riscve, feature = "v-trap"))]
riscv_rt_macros::vectored_interrupt_trap!(rv32e);
#[cfg(all(target_arch = "riscv64", riscvi, feature = "v-trap"))]
riscv_rt_macros::vectored_interrupt_trap!(rv64i);
#[cfg(all(target_arch = "riscv64", riscve, feature = "v-trap"))]
riscv_rt_macros::vectored_interrupt_trap!(rv64e);
#[cfg(feature = "v-trap")]
riscv_rt_macros::vectored_interrupt_trap!();

#[rustfmt::skip]
global_asm!(
Expand Down
15 changes: 1 addition & 14 deletions riscv-rt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,21 +547,8 @@ use riscv::register::scause as xcause;
#[cfg(not(feature = "s-mode"))]
use riscv::register::mcause as xcause;

pub use riscv_rt_macros::{entry, exception, external_interrupt, pre_init};

pub use riscv_pac::*;

#[cfg(all(target_arch = "riscv32", riscve))]
pub use riscv_rt_macros::core_interrupt_rv32e as core_interrupt;
#[cfg(all(target_arch = "riscv32", riscvi))]
pub use riscv_rt_macros::core_interrupt_rv32i as core_interrupt;
#[cfg(all(target_arch = "riscv64", riscve))]
pub use riscv_rt_macros::core_interrupt_rv64e as core_interrupt;
#[cfg(any(
all(target_arch = "riscv64", riscvi),
not(any(target_arch = "riscv32", target_arch = "riscv64"))
))]
pub use riscv_rt_macros::core_interrupt_rv64i as core_interrupt;
pub use riscv_rt_macros::{core_interrupt, entry, exception, external_interrupt, pre_init};

/// We export this static with an informative name so that if an application attempts to link
/// two copies of riscv-rt together, linking will fail. We also declare a links key in
Expand Down

0 comments on commit fb14952

Please sign in to comment.