diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index 99175de4..ecbb8a5d 100644 --- a/riscv-rt/CHANGELOG.md +++ b/riscv-rt/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Add FPU initialization - Static array for vectored-like handling of exceptions - New GitHub workflow for checking invalid labels in PRs - New GitHub workflow for checking modifications on CHANGELOG.md diff --git a/riscv-rt/build.rs b/riscv-rt/build.rs index 3fbbd397..b0302bb6 100644 --- a/riscv-rt/build.rs +++ b/riscv-rt/build.rs @@ -18,7 +18,7 @@ fn add_linker_script(arch_width: u32) -> io::Result<()> { } /// Parse the target RISC-V architecture and returns its bit width and the extension set -fn parse_target(target: &str) -> (u32, HashSet) { +fn parse_target(target: &str, cargo_flags: &str) -> (u32, HashSet) { // isolate bit width and extensions from the rest of the target information let arch = target .trim_start_matches("riscv") @@ -43,18 +43,46 @@ fn parse_target(target: &str) -> (u32, HashSet) { extensions.insert('d'); } + let cargo_flags = cargo_flags + .split(0x1fu8 as char) + .filter(|arg| !arg.is_empty()); + + cargo_flags + .filter(|k| k.starts_with("target-feature=")) + .flat_map(|str| { + let flags = str.split('=').collect::>()[1]; + flags.split(',') + }) + .for_each(|feature| { + let chars = feature.chars().collect::>(); + match chars[0] { + '+' => { + extensions.insert(chars[1]); + } + '-' => { + extensions.remove(&chars[1]); + } + _ => { + panic!("Unsupported target feature operation"); + } + } + }); + (bits, extensions) } fn main() { let target = env::var("TARGET").unwrap(); + let cargo_flags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(); let _name = env::var("CARGO_PKG_NAME").unwrap(); // set configuration flags depending on the target if target.starts_with("riscv") { println!("cargo:rustc-cfg=riscv"); - let (bits, extensions) = parse_target(&target); + // This is required until target_arch & target_feature risc-v work is + // stable and in-use (rust 1.75.0) + let (bits, extensions) = parse_target(&target, &cargo_flags); // generate the linker script and expose the ISA width let arch_width = match bits { diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index f63d8ceb..b48cc111 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -9,7 +9,11 @@ extern crate proc_macro2; extern crate syn; use proc_macro2::Span; -use syn::{parse, spanned::Spanned, FnArg, ItemFn, PathArguments, ReturnType, Type, Visibility}; +use syn::{ + parse::{self, Parse}, + spanned::Spanned, + FnArg, ItemFn, LitInt, LitStr, PathArguments, ReturnType, Type, Visibility, +}; use proc_macro::TokenStream; @@ -205,3 +209,55 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { ) .into() } + +struct AsmLoopArgs { + asm_template: String, + count: usize, +} + +impl Parse for AsmLoopArgs { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let template: LitStr = input.parse().unwrap(); + _ = input.parse::().unwrap(); + let count: LitInt = input.parse().unwrap(); + + Ok(Self { + asm_template: template.value(), + count: count.base10_parse().unwrap(), + }) + } +} + +/// Loops an asm expression n times. +/// +/// `loop_asm!` takes 2 arguments, the first is a string literal and the second is a number literal +/// See [the formatting syntax documentation in `std::fmt`](../std/fmt/index.html) +/// for details. +/// +/// Argument 1 is an assembly expression, all "{}" in this assembly expression will be replaced with the +/// current loop index. +/// +/// Argument 2 is the number of loops to do with the provided expression. +/// +/// # Examples +/// +/// ``` +/// # use riscv_rt_macros::loop_asm; +/// unsafe { +/// loop_asm!("fmv.w.x f{}, x0", 32); // => core::arch::asm!("fmv.w.x f0, x0") ... core::arch::asm!("fmv.w.x f31, x0") +/// } +/// ``` +#[proc_macro] +pub fn loop_asm(input: TokenStream) -> TokenStream { + let args = parse_macro_input!(input as AsmLoopArgs); + + let tokens = (0..args.count) + .map(|i| { + let i = i.to_string(); + let asm = args.asm_template.replace("{}", &i); + format!("core::arch::asm!(\"{}\");", asm) + }) + .collect::>() + .join("\n"); + tokens.parse().unwrap() +} diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index 6e4ba835..a7a900eb 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -415,6 +415,12 @@ use riscv::register::{mcause as xcause, mtvec as xtvec, mtvec::TrapMode as xTrap #[cfg(all(not(feature = "single-hart"), not(feature = "s-mode")))] use riscv::register::mhartid; +#[cfg(all(feature = "s-mode", any(riscvf, riscvd)))] +use riscv::register::sstatus as xstatus; + +#[cfg(all(not(feature = "s-mode"), any(riscvf, riscvd)))] +use riscv::register::mstatus as xstatus; + pub use riscv_rt_macros::{entry, pre_init}; #[export_name = "error: riscv-rt appears more than once in the dependency graph"] @@ -550,7 +556,21 @@ pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! { compiler_fence(Ordering::SeqCst); } - // TODO: Enable FPU when available + #[cfg(any(riscvf, riscvd))] + { + xstatus::set_fs(xstatus::FS::Initial); // Enable fpu in xstatus + core::arch::asm!("fscsr x0"); // Zero out fcsr register csrrw x0, fcsr, x0 + + // Zero out floating point registers + #[cfg(all(target_arch = "riscv32", riscvd))] + riscv_rt_macros::loop_asm!("fcvt.d.w f{}, x0", 32); + + #[cfg(all(target_arch = "riscv64", riscvd))] + riscv_rt_macros::loop_asm!("fmv.d.x f{}, x0", 32); + + #[cfg(not(riscvd))] + riscv_rt_macros::loop_asm!("fmv.w.x f{}, x0", 32); + } _setup_interrupts();