Skip to content

Commit

Permalink
Use conditional dependencies in riscv-rt depending on base extension
Browse files Browse the repository at this point in the history
  • Loading branch information
romancardenas committed Nov 19, 2024
1 parent 289206a commit 1e71338
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 114 deletions.
2 changes: 1 addition & 1 deletion riscv-rt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ riscv-target-parser = { path = "../riscv-target-parser", version = "0.1.0" }
[dependencies]
riscv = { path = "../riscv", version = "0.12.0" }
riscv-pac = { path = "../riscv-pac", version = "0.2.0" }
riscv-rt-macros = { path = "macros", version = "0.2.2" }
riscv-rt-macros = { path = "macros", version = "0.3.0" }

[dev-dependencies]
panic-halt = "1.0.0"
Expand Down
9 changes: 1 addition & 8 deletions riscv-rt/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,11 @@ fn main() {
let cargo_flags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

if let Ok(target) = RiscvTarget::build(&target, &cargo_flags) {
let width = target.width();
if matches!(width, riscv_target_parser::Width::W128) {
panic!("Unsupported RISC-V target: {width}");
}
if target.base_extension().is_none() {
panic!("Unsupported RISC-V target: no base extension");
}
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(width.into()).unwrap();
add_linker_script(target.width().into()).unwrap();
}
}
2 changes: 1 addition & 1 deletion riscv-rt/macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ keywords = ["riscv", "runtime", "startup"]
license = "MIT OR Apache-2.0"
name = "riscv-rt-macros"
repository = "https://github.com/rust-embedded/riscv"
version = "0.2.2"
version = "0.3.0"
edition = "2021"

[lib]
Expand Down
210 changes: 122 additions & 88 deletions riscv-rt/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ extern crate proc_macro2;
extern crate quote;
extern crate syn;

use std::vec;

use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
use syn::{
Expand Down Expand Up @@ -359,56 +361,60 @@ pub fn loop_global_asm(input: TokenStream) -> TokenStream {

#[derive(Clone, Copy, Debug)]
enum RiscvArch {
Rv32,
Rv64,
Rv32I,
Rv32E,
Rv64I,
Rv64E,
}

impl syn::parse::Parse for RiscvArch {
fn parse(input: parse::ParseStream) -> syn::Result<Self> {
let ident: syn::Ident = input.parse()?;
match ident.to_string().as_str() {
"rv32i" => Ok(Self::Rv32I),
"rv32e" => Ok(Self::Rv32E),
"rv64i" => Ok(Self::Rv64I),
"rv64e" => Ok(Self::Rv64E),
_ => Err(syn::Error::new(ident.span(), "Invalid RISC-V architecture")),
}
}
}

impl RiscvArch {
fn width(&self) -> usize {
match self {
Self::Rv32 => 4,
Self::Rv64 => 8,
Self::Rv32I | Self::Rv32E => 4,
Self::Rv64I | Self::Rv64E => 8,
}
}

fn store(&self) -> &str {
match self {
Self::Rv32 => "sw",
Self::Rv64 => "sd",
Self::Rv32I | Self::Rv32E => "sw",
Self::Rv64I | Self::Rv64E => "sd",
}
}

fn load(&self) -> &str {
match self {
Self::Rv32 => "lw",
Self::Rv64 => "ld",
Self::Rv32I | Self::Rv32E => "lw",
Self::Rv64I | Self::Rv64E => "ld",
}
}
}

/// Size of the trap frame (in number of registers)
const TRAP_SIZE: usize = 16;

#[rustfmt::skip]
/// List of the register names to be stored in the trap frame
const TRAP_FRAME: [&str; TRAP_SIZE] = [
"ra",
"t0",
"t1",
"t2",
"t3",
"t4",
"t5",
"t6",
"a0",
"a1",
"a2",
"a3",
"a4",
"a5",
"a6",
"a7",
];
fn trap_frame(&self) -> Vec<&str> {
match self {
Self::Rv32I | Self::Rv64I => vec![
"ra", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "a0", "a1", "a2", "a3", "a4", "a5",
"a6", "a7",
],
Self::Rv32E => vec![
"ra", "t0", "t1", "t2", "a0", "a1", "a2", "a3", "a4", "a5", "_r0", "_r1",
],
Self::Rv64E => vec!["ra", "t0", "t1", "t2", "a0", "a1", "a2", "a3", "a4", "a5"],
}
}
}

/// Generate the assembly instructions to store the trap frame.
///
Expand All @@ -421,11 +427,11 @@ const TRAP_FRAME: [&str; TRAP_SIZE] = [
fn store_trap<T: FnMut(&str) -> bool>(arch: RiscvArch, mut filter: T) -> String {
let width = arch.width();
let store = arch.store();
TRAP_FRAME
arch.trap_frame()
.iter()
.enumerate()
.filter(|(_, &reg)| filter(reg))
.map(|(i, reg)| format!("{store} {reg}, {i}*{width}(sp)"))
.filter(|(_, &reg)| !reg.starts_with('_') && filter(reg))
.map(|(i, reg)| format!(" {store} {reg}, {i}*{width}(sp)"))
.collect::<Vec<_>>()
.join("\n")
}
Expand All @@ -435,43 +441,27 @@ fn store_trap<T: FnMut(&str) -> bool>(arch: RiscvArch, mut filter: T) -> String
fn load_trap(arch: RiscvArch) -> String {
let width = arch.width();
let load = arch.load();
TRAP_FRAME
arch.trap_frame()
.iter()
.enumerate()
.map(|(i, reg)| format!("{load} {reg}, {i}*{width}(sp)"))
.filter(|(_, &reg)| !reg.starts_with('_'))
.map(|(i, reg)| format!(" {load} {reg}, {i}*{width}(sp)"))
.collect::<Vec<_>>()
.join("\n")
}

/// Generates weak `_start_trap` function in assembly for RISCV-32 targets.
///
/// 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_riscv32(_input: TokenStream) -> TokenStream {
weak_start_trap(RiscvArch::Rv32)
}

/// Generates weak `_start_trap` function in assembly for RISCV-64 targets.
/// 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_riscv64(_input: TokenStream) -> TokenStream {
weak_start_trap(RiscvArch::Rv64)
}
pub fn weak_start_trap(input: TokenStream) -> TokenStream {
let arch = parse_macro_input!(input as RiscvArch);

/// 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.
///
/// The `arch` parameter is used to determine the width of the registers.
/// The macro also ensures that the trap frame size is 16-byte aligned.
fn weak_start_trap(arch: RiscvArch) -> TokenStream {
let width = arch.width();
let trap_size = arch.trap_frame().len();
// ensure we do not break that sp is 16-byte aligned
if (TRAP_SIZE * width) % 16 != 0 {
if (trap_size * width) % 16 != 0 {
return parse::Error::new(Span::call_site(), "Trap frame size must be 16-byte aligned")
.to_compile_error()
.into();
Expand All @@ -491,40 +481,29 @@ core::arch::global_asm!(
.align {width}
.weak _start_trap
_start_trap:
addi sp, sp, - {TRAP_SIZE} * {width}
{store}
addi sp, sp, - {trap_size} * {width}
{store}
add a0, sp, zero
jal ra, _start_trap_rust
{load}
addi sp, sp, {TRAP_SIZE} * {width}
{load}
addi sp, sp, {trap_size} * {width}
{ret}
");"#
)
.parse()
.unwrap()
}

/// Generates vectored interrupt trap functions in assembly for RISCV-32 targets.
#[cfg(feature = "v-trap")]
#[proc_macro]
pub fn vectored_interrupt_trap_riscv32(_input: TokenStream) -> TokenStream {
vectored_interrupt_trap(RiscvArch::Rv32)
}

/// Generates vectored interrupt trap functions in assembly for RISCV-64 targets.
#[cfg(feature = "v-trap")]
#[proc_macro]
pub fn vectored_interrupt_trap_riscv64(_input: TokenStream) -> TokenStream {
vectored_interrupt_trap(RiscvArch::Rv64)
}

#[cfg(feature = "v-trap")]
/// Generates global '_start_DefaultHandler_trap' and '_continue_interrupt_trap' functions in assembly.
/// 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.
fn vectored_interrupt_trap(arch: RiscvArch) -> TokenStream {
pub fn vectored_interrupt_trap(input: TokenStream) -> TokenStream {
let arch = parse_macro_input!(input as RiscvArch);
let width = arch.width();
let trap_size = arch.trap_frame().len();
let store_start = store_trap(arch, |reg| reg == "a0");
let store_continue = store_trap(arch, |reg| reg != "a0");
let load = load_trap(arch);
Expand All @@ -542,17 +521,17 @@ core::arch::global_asm!(
.align 4
.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)
addi sp, sp, -{trap_size} * {width} // allocate space for trap frame
{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
addi sp, sp, {TRAP_SIZE} * {width} // deallocate space for 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 @@ -664,10 +643,64 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
/// loop{};
/// }
/// ```
pub fn core_interrupt_riscv32(args: TokenStream, input: TokenStream) -> TokenStream {
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::Rv32),
() => Some(RiscvArch::Rv64I),
#[cfg(not(feature = "v-trap"))]
() => None,
};
Expand All @@ -691,10 +724,10 @@ pub fn core_interrupt_riscv32(args: TokenStream, input: TokenStream) -> TokenStr
/// loop{};
/// }
/// ```
pub fn core_interrupt_riscv64(args: TokenStream, input: TokenStream) -> TokenStream {
pub fn core_interrupt_rv64e(args: TokenStream, input: TokenStream) -> TokenStream {
let arch = match () {
#[cfg(feature = "v-trap")]
() => Some(RiscvArch::Rv64),
() => Some(RiscvArch::Rv64E),
#[cfg(not(feature = "v-trap"))]
() => None,
};
Expand Down Expand Up @@ -784,6 +817,7 @@ fn trap(
fn start_interrupt_trap(ident: &syn::Ident, arch: RiscvArch) -> proc_macro2::TokenStream {
let interrupt = ident.to_string();
let width = arch.width();
let trap_size = arch.trap_frame().len();
let store = store_trap(arch, |r| r == "a0");

let instructions = format!(
Expand All @@ -793,8 +827,8 @@ core::arch::global_asm!(
.align 2
.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)
addi sp, sp, -{trap_size} * {width} // allocate space for trap frame
{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
Loading

0 comments on commit 1e71338

Please sign in to comment.