Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

riscv-rt: Implement FPU initialization #166

Merged
merged 9 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions riscv-rt/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 30 additions & 2 deletions riscv-rt/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<char>) {
fn parse_target(target: &str, cargo_flags: &str) -> (u32, HashSet<char>) {
// isolate bit width and extensions from the rest of the target information
let arch = target
.trim_start_matches("riscv")
Expand All @@ -43,18 +43,46 @@ fn parse_target(target: &str) -> (u32, HashSet<char>) {
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::<Vec<&str>>()[1];
flags.split(',')
})
.for_each(|feature| {
let chars = feature.chars().collect::<Vec<char>>();
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 {
Expand Down
58 changes: 57 additions & 1 deletion riscv-rt/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<Self> {
let template: LitStr = input.parse().unwrap();
_ = input.parse::<Token![,]>().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::<Vec<String>>()
.join("\n");
tokens.parse().unwrap()
}
22 changes: 21 additions & 1 deletion riscv-rt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down Expand Up @@ -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();

Expand Down