#![allow(unknown_lints)] #![allow(clippy::manual_is_multiple_of)]
use proc_macro2::{Span, TokenStream};
use syn::{
parse::{Parse, ParseStream},
Error, Ident, Result,
};
#[derive(Clone, Copy, Debug)]
pub enum RiscvArch {
Rv32I,
Rv32E,
Rv64I,
Rv64E,
}
impl Parse for RiscvArch {
fn parse(input: ParseStream) -> Result<Self> {
let ident: 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(Error::new(ident.span(), "Invalid RISC-V architecture")),
}
}
}
impl RiscvArch {
pub 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,
}
}
pub const fn width(&self) -> usize {
match self {
Self::Rv32I | Self::Rv32E => 4,
Self::Rv64I | Self::Rv64E => 8,
}
}
pub const fn store(&self) -> &str {
match self {
Self::Rv32I | Self::Rv32E => "sw",
Self::Rv64I | Self::Rv64E => "sd",
}
}
pub const fn load(&self) -> &str {
match self {
Self::Rv32I | Self::Rv32E => "lw",
Self::Rv64I | Self::Rv64E => "ld",
}
}
pub 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 | Self::Rv64E => {
vec!["ra", "t0", "t1", "t2", "a0", "a1", "a2", "a3", "a4", "a5"]
}
}
}
pub const fn byte_alignment(&self) -> usize {
match self {
Self::Rv32E | Self::Rv64E => 4,
_ => 16,
}
}
pub fn store_trap<T: FnMut(&str) -> bool>(&self, mut filter: T) -> String {
let width = self.width();
let store = self.store();
self.trap_frame()
.iter()
.enumerate()
.filter(|(_, ®)| !reg.starts_with('_') && filter(reg))
.map(|(i, reg)| format!("{store} {reg}, {i}*{width}(sp)"))
.collect::<Vec<_>>()
.join("\n ")
}
pub fn load_trap(&self) -> String {
let width = self.width();
let load = self.load();
self.trap_frame()
.iter()
.enumerate()
.filter(|(_, ®)| !reg.starts_with('_'))
.map(|(i, reg)| format!("{load} {reg}, {i}*{width}(sp)"))
.collect::<Vec<_>>()
.join("\n ")
}
pub fn default_start_trap(&self) -> TokenStream {
let width = self.width();
let trap_size = self.trap_frame().len();
let byte_alignment = self.byte_alignment();
if (trap_size * width) % byte_alignment != 0 {
return Error::new(Span::call_site(), "Trap frame size must be 16-byte aligned")
.to_compile_error();
}
let store = self.store_trap(|_| true);
let load = self.load_trap();
#[cfg(feature = "s-mode")]
let ret = "sret";
#[cfg(not(feature = "s-mode"))]
let ret = "mret";
let pre_default_start_trap = if cfg!(feature = "rvrt-pre-default-start-trap") {
r#"
j _pre_default_start_trap
.global _pre_default_start_trap_ret
_pre_default_start_trap_ret:"#
} else {
""
};
let vectored_trap = if cfg!(feature = "rt-v-trap") {
let store_start = self.store_trap(|reg| reg == "a0");
let store_continue = self.store_trap(|reg| reg != "a0");
format!(
r#"
.section .trap.continue, \"ax\"
.balign 4
.global _start_DefaultHandler_trap
_start_DefaultHandler_trap:
addi sp, sp, -{trap_size} * {width}
{store_start}
la a0, DefaultHandler
.global _continue_interrupt_trap
_continue_interrupt_trap:
{store_continue}
jalr ra, a0, 0
{load}
addi sp, sp, {trap_size} * {width}
{ret}"#
)
} else {
String::new()
};
format!(
r#"
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
core::arch::global_asm!(
"
.section .trap.start, \"ax\"
.balign 4 /* Alignment required for xtvec */
.global _default_start_trap
_default_start_trap:{pre_default_start_trap}
addi sp, sp, - {trap_size} * {width}
{store}
add a0, sp, zero
jal ra, _start_trap_rust
{load}
addi sp, sp, {trap_size} * {width}
{ret}{vectored_trap}
"
);
"#
)
.parse()
.unwrap()
}
pub fn start_interrupt_trap(&self, ident: &Ident) -> TokenStream {
let interrupt = ident.to_string();
let width = self.width();
let trap_size = self.trap_frame().len();
let store = self.store_trap(|r| r == "a0");
format!(
r#"
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
core::arch::global_asm!(
"
.section .trap.start.{interrupt}, \"ax\"
.balign 4
.global _start_{interrupt}_trap
_start_{interrupt}_trap:
addi sp, sp, -{trap_size} * {width}
{store}
la a0, {interrupt}
j _continue_interrupt_trap
"
);
"#
)
.parse()
.unwrap()
}
}