From 02fd8a0f570336dd0483c3de72a4200a573358b6 Mon Sep 17 00:00:00 2001 From: mini-ninja-64 Date: Wed, 6 Dec 2023 19:53:14 +0000 Subject: [PATCH 1/8] Add basic FPU initialization --- riscv-rt/src/lib.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index fcc77f9f..55ba5d29 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -377,6 +377,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"] @@ -512,7 +518,25 @@ 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 + for i in 0..32 { + if cfg!(all(riscv32, riscvd)) { + // rv32 targets with double precision floating point can use fmvp.d.x + // to combine 2 32 bit registers to fill the 64 bit floating point + // register + core::arch::asm!("fmvp.d.x f{}, x0, x0", i); + } else if cfg!(riscvd) { + core::arch::asm!("fmv.d.x f{}, x0", i); + } else { + core::arch::asm!("fmv.w.x f{}, x0", i); + } + } + } _setup_interrupts(); From a36d4771450753c9064ac850881867023971b5f5 Mon Sep 17 00:00:00 2001 From: mini-ninja-64 Date: Thu, 7 Dec 2023 22:29:36 +0000 Subject: [PATCH 2/8] Add feature-flag detection --- riscv-rt/build.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/riscv-rt/build.rs b/riscv-rt/build.rs index 3fbbd397..3e47c693 100644 --- a/riscv-rt/build.rs +++ b/riscv-rt/build.rs @@ -50,11 +50,23 @@ fn main() { let target = env::var("TARGET").unwrap(); let _name = env::var("CARGO_PKG_NAME").unwrap(); + // This is required until target_feature risc-v work is stable and in-use (rust 1.75.0) + let cargo_flags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(); + let cargo_flags = cargo_flags + .split(0x1fu8 as char) + .filter(|arg| !arg.is_empty()); + + let target_features = cargo_flags + .filter(|k| k.starts_with("target-feature=")).flat_map(|str| { + let flags = str.split('=').collect::>()[1]; + flags.split(',') + }); + // set configuration flags depending on the target if target.starts_with("riscv") { println!("cargo:rustc-cfg=riscv"); - let (bits, extensions) = parse_target(&target); + let (bits, mut extensions) = parse_target(&target); // generate the linker script and expose the ISA width let arch_width = match bits { @@ -70,6 +82,15 @@ fn main() { }; add_linker_script(arch_width).unwrap(); + target_features.for_each(|feature| { + let chars = feature.chars().collect::>(); + if chars[0] == '+' { + extensions.insert(chars[1]); + } else if chars[0] == '-' { + extensions.remove(&chars[1]); + } + }); + // expose the ISA extensions for ext in &extensions { println!("cargo:rustc-cfg=riscv{}", ext); From d60e71d570300dde38a6849bcfc0fa74065c1c34 Mon Sep 17 00:00:00 2001 From: mini-ninja-64 Date: Thu, 7 Dec 2023 22:29:58 +0000 Subject: [PATCH 3/8] Add loop_asm macro --- riscv-rt/macros/src/lib.rs | 32 +++++++++++++++++++++++++++++++- riscv-rt/src/lib.rs | 20 +++++++++----------- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index f63d8ceb..3c20e615 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -9,7 +9,7 @@ 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, PathArguments, ReturnType, Type, Visibility, LitStr, LitInt}; use proc_macro::TokenStream; @@ -205,3 +205,33 @@ 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(), + }) + } +} + +#[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 55ba5d29..0617d075 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -524,17 +524,15 @@ pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! { core::arch::asm!("fscsr x0"); // Zero out fcsr register csrrw x0, fcsr, x0 // Zero out floating point registers - for i in 0..32 { - if cfg!(all(riscv32, riscvd)) { - // rv32 targets with double precision floating point can use fmvp.d.x - // to combine 2 32 bit registers to fill the 64 bit floating point - // register - core::arch::asm!("fmvp.d.x f{}, x0, x0", i); - } else if cfg!(riscvd) { - core::arch::asm!("fmv.d.x f{}, x0", i); - } else { - core::arch::asm!("fmv.w.x f{}, x0", i); - } + if cfg!(all(target_arch = "riscv32", riscvd)) { + // rv32 targets with double precision floating point can use fmvp.d.x + // to combine 2 32 bit registers to fill the 64 bit floating point + // register + riscv_rt_macros::loop_asm!("fmvp.d.x f{}, x0, x0", 32); + } else if cfg!(riscvd) { + riscv_rt_macros::loop_asm!("fmv.d.x f{}, x0", 32); + } else { + riscv_rt_macros::loop_asm!("fmv.w.x f{}, x0", 32); } } From 6a6c1cdba13921daadb656a0cf2c7588ae914d61 Mon Sep 17 00:00:00 2001 From: mini-ninja-64 Date: Sun, 10 Dec 2023 22:51:55 +0000 Subject: [PATCH 4/8] Refactor target_feature parsing --- riscv-rt/build.rs | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/riscv-rt/build.rs b/riscv-rt/build.rs index 3e47c693..0fa56377 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,30 +43,39 @@ fn parse_target(target: &str) -> (u32, HashSet) { extensions.insert('d'); } - (bits, extensions) -} - -fn main() { - let target = env::var("TARGET").unwrap(); - let _name = env::var("CARGO_PKG_NAME").unwrap(); - - // This is required until target_feature risc-v work is stable and in-use (rust 1.75.0) - let cargo_flags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(); let cargo_flags = cargo_flags .split(0x1fu8 as char) .filter(|arg| !arg.is_empty()); - - let target_features = cargo_flags + + 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, mut 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 { @@ -82,15 +91,6 @@ fn main() { }; add_linker_script(arch_width).unwrap(); - target_features.for_each(|feature| { - let chars = feature.chars().collect::>(); - if chars[0] == '+' { - extensions.insert(chars[1]); - } else if chars[0] == '-' { - extensions.remove(&chars[1]); - } - }); - // expose the ISA extensions for ext in &extensions { println!("cargo:rustc-cfg=riscv{}", ext); From ed7799a19d2aaad3b7682fb98e90f9c4d9e38ca1 Mon Sep 17 00:00:00 2001 From: mini-ninja-64 Date: Mon, 11 Dec 2023 00:44:13 +0000 Subject: [PATCH 5/8] Add missing changelog entry --- riscv-rt/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/riscv-rt/CHANGELOG.md b/riscv-rt/CHANGELOG.md index f4bebca6..dea846ee 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 - New GitHub workflow for checking invalid labels in PRs - New GitHub workflow for checking modifications on CHANGELOG.md - New GitHub workflow for checking clippy lints in PRs From 511ebcda12b0bb21dcf61825600234274e52d3f6 Mon Sep 17 00:00:00 2001 From: mini-ninja-64 Date: Mon, 11 Dec 2023 00:45:00 +0000 Subject: [PATCH 6/8] Add documentation for loop_asm! --- riscv-rt/macros/src/lib.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index 3c20e615..bbf57eb0 100644 --- a/riscv-rt/macros/src/lib.rs +++ b/riscv-rt/macros/src/lib.rs @@ -224,6 +224,25 @@ impl Parse for AsmLoopArgs { } } +/// 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); From 7a2f9050db9304559396eb429103592dcf27f6b6 Mon Sep 17 00:00:00 2001 From: mini-ninja-64 Date: Mon, 11 Dec 2023 18:00:46 +0000 Subject: [PATCH 7/8] Run cargo format --- riscv-rt/build.rs | 19 +++++++++++++------ riscv-rt/macros/src/lib.rs | 23 +++++++++++++++-------- riscv-rt/src/lib.rs | 4 ++-- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/riscv-rt/build.rs b/riscv-rt/build.rs index 0fa56377..b0302bb6 100644 --- a/riscv-rt/build.rs +++ b/riscv-rt/build.rs @@ -46,18 +46,25 @@ fn parse_target(target: &str, cargo_flags: &str) -> (u32, HashSet) { 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| { + .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"); } + '+' => { + extensions.insert(chars[1]); + } + '-' => { + extensions.remove(&chars[1]); + } + _ => { + panic!("Unsupported target feature operation"); + } } }); @@ -73,7 +80,7 @@ fn main() { if target.starts_with("riscv") { println!("cargo:rustc-cfg=riscv"); - // This is required until target_arch & target_feature risc-v work is + // 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); diff --git a/riscv-rt/macros/src/lib.rs b/riscv-rt/macros/src/lib.rs index bbf57eb0..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::{self, Parse}, spanned::Spanned, FnArg, ItemFn, PathArguments, ReturnType, Type, Visibility, LitStr, LitInt}; +use syn::{ + parse::{self, Parse}, + spanned::Spanned, + FnArg, ItemFn, LitInt, LitStr, PathArguments, ReturnType, Type, Visibility, +}; use proc_macro::TokenStream; @@ -230,9 +234,9 @@ impl Parse for AsmLoopArgs { /// 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 +/// 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 @@ -247,10 +251,13 @@ impl Parse for AsmLoopArgs { 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"); + 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 0617d075..341b4b5b 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -525,8 +525,8 @@ pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! { // Zero out floating point registers if cfg!(all(target_arch = "riscv32", riscvd)) { - // rv32 targets with double precision floating point can use fmvp.d.x - // to combine 2 32 bit registers to fill the 64 bit floating point + // rv32 targets with double precision floating point can use fmvp.d.x + // to combine 2 32 bit registers to fill the 64 bit floating point // register riscv_rt_macros::loop_asm!("fmvp.d.x f{}, x0, x0", 32); } else if cfg!(riscvd) { From 49b55884eb061a547956e5a04e99ef70777ddfe4 Mon Sep 17 00:00:00 2001 From: mini-ninja-64 Date: Mon, 11 Dec 2023 19:17:40 +0000 Subject: [PATCH 8/8] Replace if with conditional compilation - Replace fmvp.d.x usage with fcvt.d.w as it does not require zfa --- riscv-rt/src/lib.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/riscv-rt/src/lib.rs b/riscv-rt/src/lib.rs index 341b4b5b..37748c3d 100644 --- a/riscv-rt/src/lib.rs +++ b/riscv-rt/src/lib.rs @@ -524,16 +524,14 @@ pub unsafe extern "C" fn start_rust(a0: usize, a1: usize, a2: usize) -> ! { core::arch::asm!("fscsr x0"); // Zero out fcsr register csrrw x0, fcsr, x0 // Zero out floating point registers - if cfg!(all(target_arch = "riscv32", riscvd)) { - // rv32 targets with double precision floating point can use fmvp.d.x - // to combine 2 32 bit registers to fill the 64 bit floating point - // register - riscv_rt_macros::loop_asm!("fmvp.d.x f{}, x0, x0", 32); - } else if cfg!(riscvd) { - riscv_rt_macros::loop_asm!("fmv.d.x f{}, x0", 32); - } else { - riscv_rt_macros::loop_asm!("fmv.w.x f{}, x0", 32); - } + #[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();