diff --git a/src/risc_v/Cargo.lock b/src/risc_v/Cargo.lock index 051490359265629f9590fd212f560ddd7984b774..b43b7574b0fe36e36f669c1f6997ca29cb6ca20c 100644 --- a/src/risc_v/Cargo.lock +++ b/src/risc_v/Cargo.lock @@ -824,6 +824,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ieee-apsqrt" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4328941c554aaeea28ca420cd1f10e932fa38874faf8c75e3ed184c64c5c6cec" +dependencies = [ + "rustc_apfloat", +] + [[package]] name = "indenter" version = "0.3.3" @@ -1448,6 +1457,7 @@ dependencies = [ "derive_more", "goblin", "hex", + "ieee-apsqrt", "kernel-loader", "lazy_static", "num_enum", diff --git a/src/risc_v/Cargo.toml b/src/risc_v/Cargo.toml index e9a92dae1ab64d6e3f57ba7781ae401357e1d0e6..e370e992b84fe974d5fc7fe4b6a35ada3ea24ea6 100644 --- a/src/risc_v/Cargo.toml +++ b/src/risc_v/Cargo.toml @@ -12,3 +12,4 @@ exclude = ["jstz", "dummy_kernel", "tests"] [workspace.dependencies] rustc_apfloat = "0.2.0" +ieee-apsqrt = "0.1.1" diff --git a/src/risc_v/interpreter/Cargo.toml b/src/risc_v/interpreter/Cargo.toml index fc091a04f8e2b5488193607d0a827a647fbb2a8b..b5add93c8b399c05a1c2cc6677d2c36d2c2a359f 100644 --- a/src/risc_v/interpreter/Cargo.toml +++ b/src/risc_v/interpreter/Cargo.toml @@ -21,6 +21,9 @@ path = "../kernel_loader" [dependencies.rustc_apfloat] workspace = true +[dependencies.ieee-apsqrt] +workspace = true + [dev-dependencies] goblin = "0.7.1" hex = "0.4.3" diff --git a/src/risc_v/interpreter/src/interpreter/float.rs b/src/risc_v/interpreter/src/interpreter/float.rs index ba2bea9f5b39938c1a47b98e6f464fdfa2c0f5b4..0bcbb5ceab3f1e64fdc324a6e8fa403c9b1af154 100644 --- a/src/risc_v/interpreter/src/interpreter/float.rs +++ b/src/risc_v/interpreter/src/interpreter/float.rs @@ -14,7 +14,7 @@ use crate::{ state_backend as backend, traps::Exception, }; -use rustc_apfloat::{ieee::Double, ieee::Single, Float, Round, Status, StatusAnd}; +use rustc_apfloat::{Float, Round, Status, StatusAnd}; use std::ops::Neg; pub trait FloatExt: Float + Into + Copy + Neg + From { @@ -32,18 +32,6 @@ pub trait FloatExt: Float + Into + Copy + Neg + From { } } -impl FloatExt for Single { - fn canonical_nan() -> Self { - Self::from_bits(0x7fc00000_u32 as u128) - } -} - -impl FloatExt for Double { - fn canonical_nan() -> Self { - Self::from_bits(0x7ff8000000000000_u64 as u128) - } -} - impl HartState where M: backend::Manager, @@ -292,7 +280,7 @@ where /// /// Writes all the bits of `rs1`, except for the sign bit, to `rd`. /// The sign bit is taken from `rs2`. - pub fn run_fsgnj(&mut self, rs1: FRegister, rs2: FRegister, rd: FRegister) { + pub(super) fn run_fsgnj(&mut self, rs1: FRegister, rs2: FRegister, rd: FRegister) { self.f_sign_injection::(rs1, rs2, rd, |_x, y| y); } @@ -300,7 +288,12 @@ where /// /// Writes all the bits of `rs1`, except for the sign bit, to `rd`. /// The sign bit is taken from the negative of `rs2`. - pub fn run_fsgnjn(&mut self, rs1: FRegister, rs2: FRegister, rd: FRegister) { + pub(super) fn run_fsgnjn( + &mut self, + rs1: FRegister, + rs2: FRegister, + rd: FRegister, + ) { self.f_sign_injection::(rs1, rs2, rd, |_x, y| !y); } @@ -308,7 +301,12 @@ where /// /// Writes all the bits of `rs1`, except for the sign bit, to `rd`. /// The sign bit is taken from the bitwise XOR of the sign bits from `rs1` & `rs2`. - pub fn run_fsgnjx(&mut self, rs1: FRegister, rs2: FRegister, rd: FRegister) { + pub(super) fn run_fsgnjx( + &mut self, + rs1: FRegister, + rs2: FRegister, + rd: FRegister, + ) { self.f_sign_injection::(rs1, rs2, rd, |x, y| x ^ y); } @@ -362,7 +360,7 @@ where Ok(()) } - fn f_rounding_mode(&self, rm: InstrRoundingMode) -> Result { + pub(super) fn f_rounding_mode(&self, rm: InstrRoundingMode) -> Result { let rm = match rm { InstrRoundingMode::Static(rm) => rm, InstrRoundingMode::Dynamic => self.csregisters.read(CSRegister::frm).try_into()?, @@ -486,7 +484,7 @@ impl CSRegisters { self.set_bits(CSRegister::fflags, 1 << mask as usize); } - fn set_exception_flag_status(&mut self, status: Status) { + pub(super) fn set_exception_flag_status(&mut self, status: Status) { let bits = status_to_bits(status); self.set_bits(CSRegister::fflags, bits as u64); } diff --git a/src/risc_v/interpreter/src/interpreter/rv64d.rs b/src/risc_v/interpreter/src/interpreter/rv64d.rs index 073260dac0fcb7f42738a41b971caf3c6e31ae54..45d9ede6a95642534f1dd6d21420bdb8567d6496 100644 --- a/src/risc_v/interpreter/src/interpreter/rv64d.rs +++ b/src/risc_v/interpreter/src/interpreter/rv64d.rs @@ -6,6 +6,7 @@ //! //! Chapter 12 - "D" Standard Extension for Double-Precision Floating-Point +use super::float::FloatExt; use crate::{ machine_state::{ bus::main_memory::MainMemoryLayout, @@ -17,8 +18,7 @@ use crate::{ state_backend as backend, traps::Exception, }; - -use rustc_apfloat::{ieee::Double, Float}; +use rustc_apfloat::{ieee::Double, Float, Status, StatusAnd}; impl From for FValue { fn from(f: Double) -> Self { @@ -33,6 +33,14 @@ impl From for Double { } } +const CANONICAL_NAN_BITS: u64 = 0x7ff8000000000000; + +impl FloatExt for Double { + fn canonical_nan() -> Self { + Self::from_bits(CANONICAL_NAN_BITS as u128) + } +} + impl HartState where M: backend::Manager, @@ -117,6 +125,28 @@ where self.run_fdiv::(rs1, rs2, rm, rd) } + /// `FSQRT.D` R-type instruction. + pub fn run_fsqrt_d( + &mut self, + rs1: FRegister, + rm: InstrRoundingMode, + rd: FRegister, + ) -> Result<(), Exception> { + let rval: u64 = self.fregisters.read(rs1).into(); + + let rm = self.f_rounding_mode(rm)?; + + let (StatusAnd { status, value }, _iterations) = ieee_apsqrt::sqrt_accurate(rval, rm); + + if status != Status::OK { + self.csregisters.set_exception_flag_status(status); + } + + self.fregisters.write(rd, value.into()); + + Ok(()) + } + /// `FMIN.D` R-type instruction. /// /// See [Self::run_fmin]. diff --git a/src/risc_v/interpreter/src/interpreter/rv64f.rs b/src/risc_v/interpreter/src/interpreter/rv64f.rs index bb20cbd6d3d041960210a93ec7fb7f08fe94f985..35e9be7b72496a0a471c48a61283246d928533eb 100644 --- a/src/risc_v/interpreter/src/interpreter/rv64f.rs +++ b/src/risc_v/interpreter/src/interpreter/rv64f.rs @@ -18,7 +18,7 @@ use crate::{ state_backend as backend, traps::Exception, }; -use rustc_apfloat::{ieee::Single, Float}; +use rustc_apfloat::{ieee::Single, Float, Status, StatusAnd}; impl From for FValue { fn from(f: Single) -> Self { @@ -41,6 +41,14 @@ impl From for Single { } } +const CANONICAL_NAN_BITS: u32 = 0x7fc00000; + +impl FloatExt for Single { + fn canonical_nan() -> Self { + Self::from_bits(CANONICAL_NAN_BITS as u128) + } +} + impl HartState where M: backend::Manager, @@ -125,6 +133,29 @@ where self.run_fdiv::(rs1, rs2, rm, rd) } + /// `FSQRT.S` R-type instruction. + pub fn run_fsqrt_s( + &mut self, + rs1: FRegister, + rm: InstrRoundingMode, + rd: FRegister, + ) -> Result<(), Exception> { + let rval = self.fregisters.read(rs1); + let rm = self.f_rounding_mode(rm)?; + + let rval = fvalue_to_f32_bits(rval); + + let (StatusAnd { status, value }, _iterations) = ieee_apsqrt::sqrt_accurate(rval, rm); + + if status != Status::OK { + self.csregisters.set_exception_flag_status(status); + } + + self.fregisters.write(rd, f32_to_fvalue(value)); + + Ok(()) + } + /// `FMIN.S` R-type instruction. /// /// See [Self::run_fmin]. @@ -283,6 +314,18 @@ fn f32_to_fvalue(val: u32) -> FValue { (val as u64 | 0xffffffff00000000).into() } +fn fvalue_to_f32_bits(f: FValue) -> u32 { + let val: u64 = f.into(); + + // Check value correctly NaN boxed: + // all upper bits must be set to 1 + if val >> 32 != 0xffffffff { + CANONICAL_NAN_BITS + } else { + val as u32 + } +} + #[cfg(test)] mod tests { use super::f32_to_fvalue; diff --git a/src/risc_v/interpreter/src/machine_state.rs b/src/risc_v/interpreter/src/machine_state.rs index b6c4a0eb324cbd928153fd3eb4b03f15e7bc3d7e..1744e6ab45538ce6b0964874c8d9a6b314a0913e 100644 --- a/src/risc_v/interpreter/src/machine_state.rs +++ b/src/risc_v/interpreter/src/machine_state.rs @@ -320,6 +320,7 @@ impl MachineState Instr::Fsubs(args) => run_f_r_instr!(self, instr, args, run_fsub_s, rs2, rm), Instr::Fmuls(args) => run_f_r_instr!(self, instr, args, run_fmul_s, rs2, rm), Instr::Fdivs(args) => run_f_r_instr!(self, instr, args, run_fdiv_s, rs2, rm), + Instr::Fsqrts(args) => run_f_r_instr!(self, instr, args, run_fsqrt_s, rm), Instr::Fmins(args) => run_f_r_instr!(self, instr, args, run_fmin_s), Instr::Fmaxs(args) => run_f_r_instr!(self, instr, args, run_fmax_s), Instr::Fmadds(args) => run_f_r_instr!(self, instr, args, run_fmadd_s, rs2, rs3, rm), @@ -343,6 +344,7 @@ impl MachineState Instr::Fsubd(args) => run_f_r_instr!(self, instr, args, run_fsub_d, rs2, rm), Instr::Fmuld(args) => run_f_r_instr!(self, instr, args, run_fmul_d, rs2, rm), Instr::Fdivd(args) => run_f_r_instr!(self, instr, args, run_fdiv_d, rs2, rm), + Instr::Fsqrtd(args) => run_f_r_instr!(self, instr, args, run_fsqrt_d, rm), Instr::Fmind(args) => run_f_r_instr!(self, instr, args, run_fmin_d), Instr::Fmaxd(args) => run_f_r_instr!(self, instr, args, run_fmax_d), Instr::Fmaddd(args) => run_f_r_instr!(self, instr, args, run_fmadd_d, rs2, rs3, rm), diff --git a/src/risc_v/interpreter/src/parser.rs b/src/risc_v/interpreter/src/parser.rs index db688a49664fcd4bf56fe0c116d20fd6795edafc..b70ba349d127e0220436aa48b734eba9ebc52fe0 100644 --- a/src/risc_v/interpreter/src/parser.rs +++ b/src/risc_v/interpreter/src/parser.rs @@ -267,6 +267,20 @@ macro_rules! f_r_instr { }; } +macro_rules! f_r_rm_1_instr { + ($enum_variant:ident, $instr:expr, $rm:expr) => { + if let Some(rounding) = InstrRoundingMode::from_rm($rm) { + $enum_variant(instruction::FR1ArgWithRounding { + rd: rd_f($instr), + rs1: rs1_f($instr), + rm: rounding, + }) + } else { + Unknown { instr: $instr } + } + }; +} + macro_rules! f_r_rm_2_instr { ($enum_variant:ident, $instr:expr, $rs2_bits:expr, $rm:expr) => {{ if let Some(rounding) = InstrRoundingMode::from_rm($rm) { @@ -393,6 +407,7 @@ const F5_3: u32 = 0b0_0011; const F5_4: u32 = 0b0_0100; const F5_5: u32 = 0b0_0101; const F5_8: u32 = 0b0_1000; +const F5_11: u32 = 0b0_1011; const F5_12: u32 = 0b0_1100; const F5_16: u32 = 0b1_0000; const F5_20: u32 = 0b1_0100; @@ -664,6 +679,7 @@ fn parse_uncompressed_instruction(instr: u32) -> Instr { (F5_4, RM_2, rs2_bits) => f_r_instr!(Fsgnjxs, instr, rs2_bits), (F5_5, RM_MIN, rs2_bits) => f_r_instr!(Fmins, instr, rs2_bits), (F5_5, RM_MAX, rs2_bits) => f_r_instr!(Fmaxs, instr, rs2_bits), + (F5_11, rounding, RS2_0) => f_r_rm_1_instr!(Fsqrts, instr, rounding), (F5_20, RM_EQ, rs2_bits) => f_cmp_instr!(Feqs, instr, rs2_bits), (F5_20, RM_LE, rs2_bits) => f_cmp_instr!(Fles, instr, rs2_bits), (F5_20, RM_LT, rs2_bits) => f_cmp_instr!(Flts, instr, rs2_bits), @@ -699,6 +715,7 @@ fn parse_uncompressed_instruction(instr: u32) -> Instr { (F5_4, RM_2, rs2_bits) => f_r_instr!(Fsgnjxd, instr, rs2_bits), (F5_5, RM_MIN, rs2_bits) => f_r_instr!(Fmind, instr, rs2_bits), (F5_5, RM_MAX, rs2_bits) => f_r_instr!(Fmaxd, instr, rs2_bits), + (F5_11, rounding, RS2_0) => f_r_rm_1_instr!(Fsqrtd, instr, rounding), (F5_20, RM_EQ, rs2_bits) => f_cmp_instr!(Feqd, instr, rs2_bits), (F5_20, RM_LE, rs2_bits) => f_cmp_instr!(Fled, instr, rs2_bits), (F5_20, RM_LT, rs2_bits) => f_cmp_instr!(Fltd, instr, rs2_bits), diff --git a/src/risc_v/interpreter/src/parser/instruction.rs b/src/risc_v/interpreter/src/parser/instruction.rs index af57234ccf3da2f74e4cf57a8ec9b3d0876fbb09..f806efb9b0559ebef318b2b5ba6f4a09c9a1446f 100644 --- a/src/risc_v/interpreter/src/parser/instruction.rs +++ b/src/risc_v/interpreter/src/parser/instruction.rs @@ -112,6 +112,15 @@ impl InstrRoundingMode { } } +/// Floating-point R-type instruction, containing +/// rounding mode, and one input argument. +#[derive(Debug, PartialEq, Clone, Copy)] +pub struct FR1ArgWithRounding { + pub rs1: FRegister, + pub rm: InstrRoundingMode, + pub rd: FRegister, +} + /// Floating-point R-type instruction, containing /// rounding mode, and two input arguments. #[derive(Debug, PartialEq, Clone, Copy)] @@ -263,6 +272,7 @@ pub enum Instr { Fsubs(FR2ArgsWithRounding), Fmuls(FR2ArgsWithRounding), Fdivs(FR2ArgsWithRounding), + Fsqrts(FR1ArgWithRounding), Fmins(FRArgs), Fmaxs(FRArgs), Fmadds(FR3ArgsWithRounding), @@ -286,6 +296,7 @@ pub enum Instr { Fsubd(FR2ArgsWithRounding), Fmuld(FR2ArgsWithRounding), Fdivd(FR2ArgsWithRounding), + Fsqrtd(FR1ArgWithRounding), Fmind(FRArgs), Fmaxd(FRArgs), Fmaddd(FR3ArgsWithRounding), @@ -420,6 +431,7 @@ impl Instr { | Fsubs(_) | Fmuls(_) | Fdivs(_) + | Fsqrts(_) | Fmins(_) | Fmaxs(_) | Fmadds(_) @@ -441,6 +453,7 @@ impl Instr { | Fsubd(_) | Fmuld(_) | Fdivd(_) + | Fsqrtd(_) | Fmind(_) | Fmaxd(_) | Fmaddd(_) @@ -475,6 +488,12 @@ macro_rules! r_instr { }; } +macro_rules! r2_instr { + ($f:expr, $op:expr, $args:expr) => { + write!($f, "{} {},{}", $op, $args.rd, $args.rs1) + }; +} + macro_rules! r4_instr { ($f:expr, $op:expr, $args:expr) => { write!( @@ -720,6 +739,7 @@ impl fmt::Display for Instr { Fsubs(args) => r_instr!(f, "fsub.s", args), Fmuls(args) => r_instr!(f, "fmul.s", args), Fdivs(args) => r_instr!(f, "fdiv.s", args), + Fsqrts(args) => r2_instr!(f, "fsqrt.s", args), Fmins(args) => r_instr!(f, "fmin.s", args), Fmaxs(args) => r_instr!(f, "fmax.s", args), Fmadds(args) => r4_instr!(f, "fmadd.s", args), @@ -743,6 +763,7 @@ impl fmt::Display for Instr { Fsubd(args) => r_instr!(f, "fsub.d", args), Fmuld(args) => r_instr!(f, "fmul.d", args), Fdivd(args) => r_instr!(f, "fdiv.d", args), + Fsqrtd(args) => r2_instr!(f, "fsqrt.d", args), Fmind(args) => r_instr!(f, "fmin.d", args), Fmaxd(args) => r_instr!(f, "fmax.d", args), Fmaddd(args) => r4_instr!(f, "fmadd.d", args), diff --git a/src/risc_v/interpreter/tests/test_suite_interpreter.rs b/src/risc_v/interpreter/tests/test_suite_interpreter.rs index 673d663779ed2f89ceead7a5f0780728a0a4d84b..1b219452dab94ad72a0395af6370cae0c79527fc 100644 --- a/src/risc_v/interpreter/tests/test_suite_interpreter.rs +++ b/src/risc_v/interpreter/tests/test_suite_interpreter.rs @@ -130,13 +130,13 @@ test_case!(test_suite_rv64ud_p_fclass, "rv64ud-p-fclass"); test_case!(test_suite_rv64ud_p_fcmp, "rv64ud-p-fcmp"); test_case!(#[ignore], test_suite_rv64ud_p_fcvt, "rv64ud-p-fcvt"); test_case!(#[ignore], test_suite_rv64ud_p_fcvt_w, "rv64ud-p-fcvt_w"); -test_case!(#[ignore], test_suite_rv64ud_p_fdiv, "rv64ud-p-fdiv"); +test_case!(test_suite_rv64ud_p_fdiv, "rv64ud-p-fdiv"); test_case!(test_suite_rv64ud_p_fmadd, "rv64ud-p-fmadd"); test_case!(test_suite_rv64ud_p_fmin, "rv64ud-p-fmin"); test_case!(test_suite_rv64ud_p_ldst, "rv64ud-p-ldst"); test_case!(test_suite_rv64ud_p_move, "rv64ud-p-move"); test_case!(#[ignore], test_suite_rv64ud_p_recoding, "rv64ud-p-recoding"); -test_case!(#[ignore], test_suite_rv64ud_p_structural, "rv64ud-p-structural"); +test_case!(test_suite_rv64ud_p_structural, "rv64ud-p-structural"); test_case!(#[ignore], test_suite_rv64ud_v_fadd, "rv64ud-v-fadd"); test_case!(#[ignore], test_suite_rv64ud_v_fclass, "rv64ud-v-fclass"); @@ -156,7 +156,7 @@ test_case!(test_suite_rv64uf_p_fclass, "rv64uf-p-fclass"); test_case!(test_suite_rv64uf_p_fcmp, "rv64uf-p-fcmp"); test_case!(#[ignore], test_suite_rv64uf_p_fcvt, "rv64uf-p-fcvt"); test_case!(#[ignore], test_suite_rv64uf_p_fcvt_w, "rv64uf-p-fcvt_w"); -test_case!(#[ignore], test_suite_rv64uf_p_fdiv, "rv64uf-p-fdiv"); +test_case!(test_suite_rv64uf_p_fdiv, "rv64uf-p-fdiv"); test_case!(test_suite_rv64uf_p_fmadd, "rv64uf-p-fmadd"); test_case!(test_suite_rv64uf_p_fmin, "rv64uf-p-fmin"); test_case!(test_suite_rv64uf_p_ldst, "rv64uf-p-ldst");