diff --git a/arch/csr/fcsr.yaml b/arch/csr/fcsr.yaml index bc114273d..c6ccb689c 100644 --- a/arch/csr/fcsr.yaml +++ b/arch/csr/fcsr.yaml @@ -93,7 +93,7 @@ fcsr: length: 32 definedBy: F fields: - RMODE: + FRM: location: 7-5 description: | Rounding modes are encoded as follows: diff --git a/arch/csr/mstatus.yaml b/arch/csr/mstatus.yaml index c79799b67..456535713 100644 --- a/arch/csr/mstatus.yaml +++ b/arch/csr/mstatus.yaml @@ -323,14 +323,27 @@ mstatus: FS: location: 14-13 description: | - Floating point context status. + *Floating point context status* When 0, floating point instructions (from F and D extensions) are disabled, and cause `ILLEGAL INSTRUCTION` exceptions. - When a floating point register, or the fCSR register is written, FS obtains the value 3. - Values 1 and 2 are valid write values for software, but are not interpreted by hardware - other than to possibly enable a previously-disabled floating point unit. - type: RW-H + + Software may write values 1, 2, or 3 into `mstatus.FS` to indicate the state is + Initial, Clean, or Dirty, respectively. + + The hart may update `mstatus.FS` when floating point state is written. + If HW_MSTATUS_FS_DIRTY_UPDATE == "precise", then `mstatus.FS` is written with the value 3 + exactly when floating point state (f register or `fcsr`) is written and no other time. + If HW_MSTATUS_FS_DIRTY_UPDATE == "imprecise", then `mstatus.FS` is written with the value 3 + at unpredictable times by the hart. + If HW_MSTATUS_FS_DIRTY_UPDATE == "none", then the hart never writes `mstatus.FS`, other than + through software writes (_e.g._, via `csrrw`). + type(): | + if (!implemented?(ExtensionName::F) || (HW_MSTATUS_FS_DIRTY_UPDATE == "none")) { + return CsrFieldType::RW; + } else { + return CsrFieldType::RWH; + } definedBy: F reset_value: UNDEFINED_LEGAL MPP: @@ -509,3 +522,9 @@ mstatus: type: RW-H reset_value: UNDEFINED_LEGAL + sw_read(): | + if (implemented?(ExtensionName::F) && (HW_MSTATUS_FS_DIRTY_UPDATE == "imprecise")) { + unpredictable("The value of `mstatus.FS` is unpredictable"); + } else { + return $bits(CSR[mstatus]); + } \ No newline at end of file diff --git a/arch/ext/F.yaml b/arch/ext/F.yaml index 5d64b2ec4..45c409635 100644 --- a/arch/ext/F.yaml +++ b/arch/ext/F.yaml @@ -237,4 +237,18 @@ F: description: | Indicates whether or not the `F` extension can be disabled with the `misa.F` bit. schema: - type: boolean \ No newline at end of file + type: boolean + HW_MSTATUS_FS_DIRTY_UPDATE: + description: | + Indicates whether or not hardware will write to `mstatus.FS` + + Values are: + [separator="!"] + !=== + h! none ! Hardware never writes `mstatus.FS` + h! precise ! Hardware writes `mstatus.FS` to the Dirty (3) state precisely when F registers are modified + h! imprecise ! Hardware writes `mstatus.FS` imprecisely. This will result in a call to unpredictable() on any attempt to read `mstatus` or write FP state. + !=== + schema: + type: string + enum: ["none", "precise", "imprecise"] \ No newline at end of file diff --git a/arch/inst/F/fclass.s.yaml b/arch/inst/F/fclass.s.yaml new file mode 100644 index 000000000..a7ea807c2 --- /dev/null +++ b/arch/inst/F/fclass.s.yaml @@ -0,0 +1,71 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +fclass.s: + long_name: Single-precision floating-point classify + definedBy: F + assembly: rd, fs1 + description: | + The `fclass.s` instruction examines the value in floating-point register + _fs1_ and writes to integer register _rd_ a 10-bit mask that indicates + the class of the floating-point number. + The format of the mask is described in the table below. + The corresponding bit in _rd_ will be set if the property is true and + clear otherwise. + All other bits in _rd_ are cleared. + Note that exactly one bit in rd will be set. + `fclass.s` does not set the floating-point exception flags. + + .Format of result of `fclass` instruction. + [%autowidth,float="center",align="center",cols="^,<",options="header",] + |=== + |_rd_ bit |Meaning + |0 |_rs1_ is latexmath:[$-\infty$]. + |1 |_rs1_ is a negative normal number. + |2 |_rs1_ is a negative subnormal number. + |3 |_rs1_ is latexmath:[$-0$]. + |4 |_rs1_ is latexmath:[$+0$]. + |5 |_rs1_ is a positive subnormal number. + |6 |_rs1_ is a positive normal number. + |7 |_rs1_ is latexmath:[$+\infty$]. + |8 |_rs1_ is a signaling NaN. + |9 |_rs1_ is a quiet NaN. + |=== + encoding: + match: 111000000000-----001-----1010011 + variables: + - name: fs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + check_f_ok(); + + Bits<32> sp_value = f[fs1][31:0]; + + if (is_sp_neg_inf?(sp_value)) { + X[rd] = 1 << 0; + } else if (is_sp_neg_norm?(sp_value)) { + X[rd] = 1 << 1; + } else if (is_sp_neg_subnorm?(sp_value)) { + X[rd] = 1 << 2; + } else if (is_sp_neg_zero?(sp_value)) { + X[rd] = 1 << 3; + } else if (is_sp_pos_zero?(sp_value)) { + X[rd] = 1 << 4; + } else if (is_sp_pos_subnorm?(sp_value)) { + X[rd] = 1 << 5; + } else if (is_sp_pos_norm?(sp_value)) { + X[rd] = 1 << 6; + } else if (is_sp_pos_inf?(sp_value)) { + X[rd] = 1 << 7; + } else if (is_sp_signaling_nan?(sp_value)) { + X[rd] = 1 << 8; + } else { + assert(is_sp_quiet_nan?(sp_value), "Unexpected SP value"); + X[rd] = 1 << 9; + } \ No newline at end of file diff --git a/arch/inst/F/fcvt.s.w.yaml b/arch/inst/F/fcvt.s.w.yaml new file mode 100644 index 000000000..d3f479b50 --- /dev/null +++ b/arch/inst/F/fcvt.s.w.yaml @@ -0,0 +1,49 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +fcvt.s.w: + long_name: Convert signed 32-bit integer to single-precision float + definedBy: F + assembly: fd, xs1 + description: | + Converts a 32-bit signed integer in integer register _rs1_ into a floating-point number in + floating-point register _fd_. + + All floating-point to integer and integer to floating-point conversion instructions round + according to the _rm_ field. + A floating-point register can be initialized to floating-point positive zero using + `fcvt.s.w rd, x0`, which will never set any exception flags. + + All floating-point conversion instructions set the Inexact exception flag if the rounded + result differs from the operand value and the Invalid exception flag is not set. + + encoding: + match: 110100000000-------------1010011 + variables: + - name: rs1 + location: 19-15 + - name: rm + location: 14-12 + - name: fd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + check_f_ok(); + + Bits<32> int_value = X[rs1]; + + Bits<1> sign = int_value[31]; + + RoundingMode rouding_mode = rm_to_mode(rm, $encoding); + + if (! (int_value & 32'h7fff_ffff)) { + X[fd] = (sign == 1) ? packToF32UI(1, 0x9E, 0) : 0; + } else { + Bits<32> absA = (sign == 1) ? -int_value : int_value; + X[fd] = softfloat_normRoundPackToF32( sign, 0x9C, absA, rounding_mode ); + } + + mark_f_state_dirty(); diff --git a/arch/inst/F/fcvt.w.s.yaml b/arch/inst/F/fcvt.w.s.yaml new file mode 100644 index 000000000..8554e865d --- /dev/null +++ b/arch/inst/F/fcvt.w.s.yaml @@ -0,0 +1,79 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +fcvt.w.s: + long_name: Convert single-precision float to integer word to signed 32-bit integer + definedBy: F + assembly: rd, fs1 + description: | + Converts a floating-point number in floating-point register _fs1_ to a signed 32-bit integer indicates + integer register _rd_. + + For XLEN >32, `fcvt.w.s` sign-extends the 32-bit result to the destination register width. + + If the rounded result is not representable as a 32-bit signed integer, it is clipped to the + nearest value and the invalid flag is set. + + The range of valid inputs and behavior for invalid inputs are: + + [separator="!"] + !=== + ! ! Value + + h! Minimum valid input (after rounding) ! `-2^31` + h! Maximum valid input (after rounding) ! `2^31 - 1` + h! Output for out-of-range negative input ! `-2^31` + h! Output for `-∞` ! `-2^31` + h! Output for out-of-range positive input ! `2^31 - 1` + h! Output for `+∞` for `NaN` ! `2^31 - 1` + !=== + + All floating-point to integer and integer to floating-point conversion instructions round + according to the _rm_ field. + A floating-point register can be initialized to floating-point positive zero using + `fcvt.s.w rd, x0`, which will never set any exception flags. + + All floating-point conversion instructions set the Inexact exception flag if the rounded + result differs from the operand value and the Invalid exception flag is not set. + encoding: + match: 110000000000-------------1010011 + variables: + - name: fs1 + location: 19-15 + - name: rm + location: 14-12 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + check_f_ok(); + + Bits<32> sp_value = f[fs1][31:0]; + + Bits<1> sign = sp_value[31]; + Bits<8> exp = sp_value[30:23]; + Bits<23> sig = sp_value[22:0]; + + RoundingMode rounding_mode = rm_to_mode(rm, $encoding); + + if ( (exp == 0xff) && (sig != 0)) { + sign = 0; + set_fp_flag(FpFlag::NV); + X[rd] = SP_CANONICAL_NAN; + } else { + if (exp != 0) { + sig = sig | 0x00800000; + } + Bits<64> sig64 = sig << 32; + Bits<16> shift_dist = 0xAA - exp; + if (0 < shift_dist) { + sig64 = softfloat_shiftRightJam64(sig64, shift_dist ); + } + X[rd] = softfloat_roundToI32( sign, sig64, rounding_mode ); + } + + + \ No newline at end of file diff --git a/arch/inst/F/feq.s.yaml b/arch/inst/F/feq.s.yaml new file mode 100644 index 000000000..2d3dbeba1 --- /dev/null +++ b/arch/inst/F/feq.s.yaml @@ -0,0 +1,43 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +feq.s: + long_name: Single-precision floating-point equal + definedBy: F + assembly: rd, fs1, fs2 + description: | + Writes 1 to _rd_ if _fs1_ and _fs2_ are equal, and 0 otherwise. + + If either operand is NaN, the result is 0 (not equal). If either operand is a signaling NaN, the invalid flag is set. + + Positive zero is considered equal to negative zero. + encoding: + match: 1010000----------010-----1010011 + variables: + - name: fs2 + location: 24-20 + - name: fs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + check_f_ok(); + + Bits<32> sp_value_a = f[fs1][31:0]; + Bits<32> sp_value_b = f[fs1][31:0]; + + if (is_sp_nan?(sp_value_a) || is_sp_nan?(sp_value_b)) { + if (is_sp_signaling_nan?(sp_value_a) || is_sp_signaling_nan?(sp_value_b)) { + set_fp_flag(FpFlag::NV); + } + X[rd] = 0; + } else { + X[rd] = ( + (sp_value_a == sp_value_b) + || ((sp_value_a | sp_value_b)[30:0] == 0) # pos 0 is equal to neg zero + ) ? 1 : 0; + } diff --git a/arch/inst/F/fle.s.yaml b/arch/inst/F/fle.s.yaml new file mode 100644 index 000000000..6517a7a50 --- /dev/null +++ b/arch/inst/F/fle.s.yaml @@ -0,0 +1,47 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +fle.s: + long_name: Single-precision floating-point less than or equal + definedBy: F + assembly: rd, fs1, fs2 + description: | + Writes 1 to _rd_ if _fs1_ is less than or equal to _fs2_, and 0 otherwise. + + If either operand is NaN, the result is 0 (not equal). + If either operand is a NaN (signaling or quiet), the invalid flag is set. + + Positive zero and negative zero are considered equal. + + encoding: + match: 1010000----------000-----1010011 + variables: + - name: fs2 + location: 24-20 + - name: fs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + check_f_ok(); + + Bits<32> sp_value_a = f[fs1][31:0]; + Bits<32> sp_value_b = f[fs1][31:0]; + + if (is_sp_nan?(sp_value_a) || is_sp_nan?(sp_value_b)) { + set_fp_flag(FpFlag::NV); + X[rd] = 0; + } else { + Boolean sign_a = sp_value_a[31] == 1; + Boolean sign_b = sp_value_b[31] == 1; + + Boolean a_lt_b = + (sign_a != sign_b) + ? (sign_a || ((sp_value_a[30:0] | sp_value_b[30:0]) == 0)) # opposite sign, a is less than or equal to b if a is negative or both are zero + : ((sp_value_a == sp_value_b) || (sign_a != (sp_value_a < sp_value_b))); + X[rd] = a_lt_b ? 1 : 0; + } diff --git a/arch/inst/F/flt.s.yaml b/arch/inst/F/flt.s.yaml new file mode 100644 index 000000000..886bcb4a1 --- /dev/null +++ b/arch/inst/F/flt.s.yaml @@ -0,0 +1,45 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +flt.s: + long_name: Single-precision floating-point less than + definedBy: F + assembly: rd, fs1, fs2 + description: | + Writes 1 to _rd_ if _fs1_ is less than _fs2_, and 0 otherwise. + + If either operand is NaN, the result is 0 (not equal). + If either operand is a NaN (signaling or quiet), the invalid flag is set. + + encoding: + match: 1010000----------001-----1010011 + variables: + - name: fs2 + location: 24-20 + - name: fs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + check_f_ok(); + + Bits<32> sp_value_a = f[fs1][31:0]; + Bits<32> sp_value_b = f[fs1][31:0]; + + if (is_sp_nan?(sp_value_a) || is_sp_nan?(sp_value_b)) { + set_fp_flag(FpFlag::NV); + X[rd] = 0; + } else { + Boolean sign_a = sp_value_a[31] == 1; + Boolean sign_b = sp_value_b[31] == 1; + + Boolean a_lt_b = + (sign_a != sign_b) + ? (sign_a && ((sp_value_a[30:0] | sp_value_b[30:0]) != 0)) # opposite sign, a is negative. a is less than b as long as both are not zero + : ((sp_value_a != sp_value_b) && (sign_a != (sp_value_a < sp_value_b))); + X[rd] = a_lt_b ? 1 : 0; + } diff --git a/arch/inst/F/flw.yaml b/arch/inst/F/flw.yaml new file mode 100644 index 000000000..25e3f0437 --- /dev/null +++ b/arch/inst/F/flw.yaml @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +flw: + long_name: Single-precision floating-point load + description: | + The `flw` instruction loads a single-precision floating-point value from memory at address _rs1_ + _imm_ into floating-point register _rd_. + + `flw` does not modify the bits being transferred; in particular, the payloads of non-canonical NaNs are preserved. + + + definedBy: F + assembly: fd, imm(xs1) + encoding: + match: -----------------010-----0000111 + variables: + - name: imm + location: 31-20 + - name: rs1 + location: 19-15 + - name: fd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + check_f_ok(); + + XReg virtual_address = X[rs1] + $signed(imm); + + Bits<32> sp_value = read_memory<32>(virtual_address); + + if (implemented?(ExtensionName::D)) { + f[fd] = nan_box(sp_value); + } else { + f[fd] = sp_value; + } + + mark_f_state_dirty(); \ No newline at end of file diff --git a/arch/inst/F/fmv.w.x.yaml b/arch/inst/F/fmv.w.x.yaml index d25dddec6..2ab5aa45e 100644 --- a/arch/inst/F/fmv.w.x.yaml +++ b/arch/inst/F/fmv.w.x.yaml @@ -5,7 +5,7 @@ fmv.w.x: description: | Moves the single-precision value encoded in IEEE 754-2008 standard encoding from the lower 32 bits of integer register `rs1` to the floating-point - register `rd`. The bits are not modified in the transfer, and in particular, + register `fd`. The bits are not modified in the transfer, and in particular, the payloads of non-canonical NaNs are preserved. definedBy: F assembly: fd, xs1 @@ -14,12 +14,22 @@ fmv.w.x: variables: - name: rs1 location: 19-15 - - name: rd + - name: fd location: 11-7 access: s: always u: always vs: always vu: always - # operation(): | - # f[rd] = X[rs1][31:0]; + operation(): | + check_f_ok(); + + Bits<32> sp_value = X[rs1][31:0]; + + if (implemented?(ExtensionName::D)) { + f[fd] = nan_box(sp_value); + } else { + f[fd] = sp_value; + } + + mark_f_state_dirty(); diff --git a/arch/inst/F/fmv.x.w.yaml b/arch/inst/F/fmv.x.w.yaml new file mode 100644 index 000000000..cfa8eb03b --- /dev/null +++ b/arch/inst/F/fmv.x.w.yaml @@ -0,0 +1,29 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +fmv.x.w: + long_name: Move single-precision value from floating-point to integer register + definedBy: F + assembly: rd, fs1 + description: | + Moves the single-precision value in floating-point register rs1 represented in IEEE 754-2008 + encoding to the lower 32 bits of integer register rd. + The bits are not modified in the transfer, and in particular, the payloads of non-canonical + NaNs are preserved. + For RV64, the higher 32 bits of the destination register are filled with copies of the + floating-point number's sign bit. + encoding: + match: 111000000000-----000-----1010011 + variables: + - name: fs1 + location: 19-15 + - name: rd + location: 11-7 + access: + s: always + u: always + vs: always + vu: always + operation(): | + check_f_ok(); + + X[rd] = sext(f[fs1][31:0], 32); diff --git a/arch/inst/F/fsgnj.s.yaml b/arch/inst/F/fsgnj.s.yaml new file mode 100644 index 000000000..97666f3f3 --- /dev/null +++ b/arch/inst/F/fsgnj.s.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +fsgnj.s: + long_name: Single-precision sign inject + description: | + Writes _fd_ with sign bit of _fs2_ and the exponent and mantissa of _fs1_. + + Sign-injection instructions do not set floating-point exception flags, nor do they canonicalize NaNs. + definedBy: F + assembly: fd, fs1, fs2 + encoding: + match: 0010000----------000-----1010011 + variables: + - name: fs2 + location: 24-20 + - name: fs1 + location: 19-15 + - name: fd + location: 11-7 + pseudoinstructions: + when: fs1 == fs2 + to: fmv.s fd, fs1 + access: + s: always + u: always + vs: always + vu: always + operation(): | + check_f_ok(); + + Bits<32> sp_value = {f[fs2][31], f[fs1][30:0]}; + + if (implemented?(ExtensionName::D)) { + f[fd] = nan_box(sp_value); + } else { + f[fd] = sp_value; + } + + mark_f_state_dirty(); diff --git a/arch/inst/F/fsgnjn.s.yaml b/arch/inst/F/fsgnjn.s.yaml new file mode 100644 index 000000000..c5b1b2ed1 --- /dev/null +++ b/arch/inst/F/fsgnjn.s.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +fsgnjn.s: + long_name: Single-precision sign inject negate + description: | + Writes _fd_ with the opposite of the sign bit of _fs2_ and the exponent and mantissa of _fs1_. + + Sign-injection instructions do not set floating-point exception flags, nor do they canonicalize NaNs. + definedBy: F + assembly: fd, fs1, fs2 + encoding: + match: 0010000----------001-----1010011 + variables: + - name: fs2 + location: 24-20 + - name: fs1 + location: 19-15 + - name: fd + location: 11-7 + pseudoinstructions: + when: fs1 == fs2 + to: fneg.s fd, fs1 + access: + s: always + u: always + vs: always + vu: always + operation(): | + check_f_ok(); + + Bits<32> sp_value = {~f[fs2][31], f[fs1][30:0]}; + + if (implemented?(ExtensionName::D)) { + f[fd] = nan_box(sp_value); + } else { + f[fd] = sp_value; + } + + mark_f_state_dirty(); diff --git a/arch/inst/F/fsgnjx.s.yaml b/arch/inst/F/fsgnjx.s.yaml new file mode 100644 index 000000000..cf1cc64a6 --- /dev/null +++ b/arch/inst/F/fsgnjx.s.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +fsgnjx.s: + long_name: Single-precision sign inject exclusive or + description: | + Writes _fd_ with the xor of the sign bits of _fs2_ and _fs1_ and the exponent and mantissa of _fs1_. + + Sign-injection instructions do not set floating-point exception flags, nor do they canonicalize NaNs. + definedBy: F + assembly: fd, fs1, fs2 + encoding: + match: 0010000----------010-----1010011 + variables: + - name: fs2 + location: 24-20 + - name: fs1 + location: 19-15 + - name: fd + location: 11-7 + pseudoinstructions: + when: fs1 == fs2 + to: fabs.s fd, fs1 + access: + s: always + u: always + vs: always + vu: always + operation(): | + check_f_ok(); + + Bits<32> sp_value = {f[fs1][31] ^ f[fs2][31], f[fs1][30:0]}; + + if (implemented?(ExtensionName::D)) { + f[fd] = nan_box(sp_value); + } else { + f[fd] = sp_value; + } + + mark_f_state_dirty(); diff --git a/arch/inst/F/fsw.yaml b/arch/inst/F/fsw.yaml new file mode 100644 index 000000000..e9475d001 --- /dev/null +++ b/arch/inst/F/fsw.yaml @@ -0,0 +1,32 @@ +# yaml-language-server: $schema=../../../schemas/inst_schema.json + +fsw: + long_name: Single-precision floating-point store + description: | + The `fsw` instruction stores a single-precision floating-point value in _fs2_ to memory at address _rs1_ + _imm_. + + `fsw` does not modify the bits being transferred; in particular, the payloads of non-canonical NaNs are preserved. + + + definedBy: F + assembly: fs2, imm(xs1) + encoding: + match: -----------------010-----0100111 + variables: + - name: imm + location: 31-25|11-7 + - name: fs2 + location: 24-20 + - name: rs1 + location: 19-15 + access: + s: always + u: always + vs: always + vu: always + operation(): | + check_f_ok(); + + XReg virtual_address = X[rs1] + $signed(imm); + + write_memory<32>(virtual_address, f[fs2][31:0]); diff --git a/arch/isa/fp.idl b/arch/isa/fp.idl new file mode 100644 index 000000000..f8ad25f1d --- /dev/null +++ b/arch/isa/fp.idl @@ -0,0 +1,454 @@ +%version: 1.0 + +# Many functions in this file (and all prefixed with softfloat_*) are +# adapted from berkeley-softfloat-3 by John R. Hauser +# (https://github.com/ucb-bar/berkeley-softfloat-3) +# Files in berkely-softfloat-3 repository are licensed under BSD-3-clause. + +# floating point register file +U32 FLEN = implemented?(ExtensionName::D) ? 7'd64 : 7'd32; +Bits f[32] = [0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0]; + +# FP constants +Bits<32> SP_POS_INF = 32'b0_11111111_00000000000000000000000; +Bits<32> SP_NEG_INF = 32'b1_11111111_00000000000000000000000; +Bits<32> SP_POS_ZERO = 32'b0_00000000_00000000000000000000000; +Bits<32> SP_NEG_ZERO = 32'b1_00000000_00000000000000000000000; +Bits<32> SP_CANONICAL_NAN = 32'b0_11111111_10000000000000000000000; + +Bits<32> WORD_NEG_OVERFLOW = 32'h8000_0000; # minimum 32-bit integer +Bits<32> WORD_POS_OVERFLOW = 32'h7FFF_FFFF; # maximum 32-bit integer + +enum RoundingMode { + RNE 0b000 # Round to Nearest, ties to Even + RTZ 0b001 # Round toward Zero + RDN 0b010 # Round Down (towards -∞) + RUP 0b011 # Round Up (towards +∞) + RMM 0b100 # Round to Nearest, ties to Max Magnitude + DYN 0b111 # Dynamic; use rm field in instruction +} + +enum FpFlag { + NX 0b00001 # Inexact + UF 0b00010 # Underflow + OF 0b00100 # Overflow + DZ 0b01000 # Divide by zero + NV 0b10000 # Invalid Operation +} + +function set_fp_flag { + arguments + FpFlag flag + description { + Add +flag+ to the sticky flags bits in CSR[fcsr] + } + body { + if (flag == FpFlag::NX) { + CSR[fcsr].NX = 1; + } else if (flag == FpFlag::UF) { + CSR[fcsr].UF = 1; + } else if (flag == FpFlag::OF) { + CSR[fcsr].OF = 1; + } else if (flag == FpFlag::DZ) { + CSR[fcsr].DZ = 1; + } else if (flag == FpFlag::NV) { + CSR[fcsr].NV = 1; + } + } +} + +function rm_to_mode { + returns RoundingMode + arguments + Bits<3> rm, # rm field from an instruction encoding + Bits<32> encoding # instruction encoding, needed to raise an exception + description { + Convert +rm+ to a RoundingMode. + + +encoding+ is the full encoding of the instruction +rm+ comes from. + + Will raise an IllegalInstruction exception if rm is a + reserved encoding. + } + body { + if (rm == $bits(RoundingMode::RNE)) { + return RoundingMode::RNE; + } else if (rm == $bits(RoundingMode::RTZ)) { + return RoundingMode::RTZ; + } else if (rm == $bits(RoundingMode::RDN)) { + return RoundingMode::RDN; + } else if (rm == $bits(RoundingMode::RUP)) { + return RoundingMode::RUP; + } else if (rm == $bits(RoundingMode::RMM)) { + return RoundingMode::RMM; + } else if (rm == $bits(RoundingMode::DYN)) { + return CSR[fcsr].FRM; + } else { + raise(ExceptionCode::IllegalInstruction, encoding); + } + } +} + +function mark_f_state_dirty { + description { + Potentially updates `mstatus.FS` to the Dirty (3) state, depending on configuration settings. + } + body { + if (HW_MSTATUS_FS_DIRTY_UPDATE== "precise") { + CSR[mstatus].FS = 3; # set dirty state + } else if (HW_MSTATUS_FS_DIRTY_UPDATE == "imprecise") { + unpredictable("The hart may or may not update mstatus.FS now"); + } + } +} + +function nan_box { + returns Bits<64> + arguments Bits<32> sp_value + description { + Produces a properly NaN-boxed double-precision value from a single-precision value + by adding all 1's to the upper bits. + } + body { + return {{32,{1'b1}}, sp_value}; + } +} + +function check_f_ok { + description { + Checks if instructions from the `F` extension can be executed, and, if not, + raise an exception. + } + body { + if (MUTABLE_MISA_F && CSR[misa].F == 0) { + raise(ExceptionCode::IllegalInstruction, $encoding); + } + + if (CSR[mstatus].FS == 0) { + raise(ExceptionCode::IllegalInstruction, $encoding); + } + } +} + +function is_sp_neg_inf? { + returns Boolean + arguments Bits<32> sp_value + description { + Return true if +sp_value+ is negative infinity. + } + body { + return sp_value == SP_NEG_INF; + } +} + +function is_sp_pos_inf? { + returns Boolean + arguments Bits<32> sp_value + description { + Return true if +sp_value+ is positive infinity. + } + body { + return sp_value == SP_POS_INF; + } +} + +function is_sp_neg_norm? { + returns Boolean + arguments Bits<32> sp_value + description { + Returns true if +sp_value+ is a negative normal number. + } + body { + return + (sp_value[31] == 1) # negative + && (sp_value[30:23] != 0b11111111) # not inf/NaN + && !( # not subnornmal + (sp_value[30:23] == 0b00000000) + && sp_value[22:0] != 0 + ); + } +} + +function is_sp_pos_norm? { + returns Boolean + arguments Bits<32> sp_value + description { + Returns true if +sp_value+ is a positive normal number. + } + body { + return + (sp_value[31] == 0) # positive + && (sp_value[30:23] != 0b11111111) # not inf/NaN + && !( # not subnornmal + (sp_value[30:23] == 0b00000000) + && sp_value[22:0] != 0 + ); + } +} + +function is_sp_neg_subnorm? { + returns Boolean + arguments Bits<32> sp_value + description { + Returns true if +sp_value+ is a negative subnormal number. + } + body { + return + (sp_value[31] == 1) # negative + && (sp_value[30:23] == 0) # subnormal exponent + && (sp_value[22:0] != 0); # not zero + } +} + +function is_sp_pos_subnorm? { + returns Boolean + arguments Bits<32> sp_value + description { + Returns true if +sp_value+ is a positive subnormal number. + } + body { + return + (sp_value[31] == 0) # positive + && (sp_value[30:23] == 0) # subnormal exponent + && (sp_value[22:0] != 0); # not zero + } +} + +function is_sp_neg_zero? { + returns Boolean + arguments Bits<32> sp_value + description { + Returns true if +sp_value+ is negative zero. + } + body { + return sp_value == SP_NEG_ZERO; + } +} + +function is_sp_pos_zero? { + returns Boolean + arguments Bits<32> sp_value + description { + Returns true if +sp_value+ is positive zero. + } + body { + return sp_value == SP_POS_ZERO; + } +} + +function is_sp_nan? { + returns Boolean + arguments Bits<32> sp_value + description { + Returns true if +sp_value+ is a NaN (quiet or signaling) + } + body { + return + (sp_value[30:23] == 0b11111111) + && (sp_value[22:0] != 0); # signaling bit + } +} + +function is_sp_signaling_nan? { + returns Boolean + arguments Bits<32> sp_value + description { + Returns true if +sp_value+ is a signaling NaN + } + body { + return + (sp_value[30:23] == 0b11111111) + && (sp_value[22] == 0) # signaling bit + && (sp_value[21:0] != 0); # not infinity + } +} + +function is_sp_quiet_nan? { + returns Boolean + arguments Bits<32> sp_value + description { + Returns true if +sp_value+ is a quiet NaN + } + body { + return + (sp_value[30:23] == 0b11111111) + && (sp_value[22] == 1); # signaling bit + } +} + +function softfloat_shiftRightJam32 { + returns Bits<32> + arguments + Bits<32> a, + Bits<32> dist + description { + Shifts +a+ right by the number of bits given in +dist+, which must not + be zero. If any nonzero bits are shifted off, they are "jammed" into the + least-significant bit of the shifted value by setting the least-significant + bit to 1. This shifted-and-jammed value is returned. + The value of +dist+ can be arbitrarily large. In particular, if +dist+ is + greater than 32, the result will be either 0 or 1, depending on whether +a+ + is zero or nonzero. + } + body { + return (dist < 31) ? a>>dist | (((a<<(-dist & 31)) != 0) ? 1 : 0) : ((a != 0) ? 1 : 0); + } +} + +function softfloat_shiftRightJam64 { + returns Bits<64> + arguments + Bits<64> a, + Bits<32> dist + description { + Shifts +a+ right by the number of bits given in +dist+, which must not + be zero. If any nonzero bits are shifted off, they are "jammed" into the + least-significant bit of the shifted value by setting the least-significant + bit to 1. This shifted-and-jammed value is returned. + + The value of 'dist' can be arbitrarily large. In particular, if +dist+ is + greater than 64, the result will be either 0 or 1, depending on whether +a+ + is zero or nonzero. + } + body { + return (dist < 63) ? a>>dist | (((a<<(-dist & 63)) != 0) ? 1 : 0) : ((a != 0) ? 1 : 0); + } +} + +function softfloat_roundToI32 { + returns Bits<32> + arguments + Bits<1> sign, + Bits<64> sig, + RoundingMode roundingMode + description { + Round to unsigned 32-bit integer, using +rounding_mode+ + } + body { + Bits<16> roundIncrement = 0x800; + if ( + (roundingMode != RoundingMode::RMM) + && (roundingMode != RoundingMode::RNE) + ) { + roundIncrement = 0; + if ( + sign == 1 + ? (roundingMode == RoundingMode::RDN) + : (roundingMode == RoundingMode::RUP) + ) { + roundIncrement = 0xFFF; + } + } + Bits<16> roundBits = sig & 0xFFF; + sig = sig + roundIncrement; + if (sig & 0xFFFFF00000000000) { + set_fp_flag(FpFlag::NV); + return sign == 1 ? WORD_NEG_OVERFLOW : WORD_POS_OVERFLOW; + } + + Bits<32> sig32 = sig >> 12; + if ( + (roundBits == 0x800 && (roundingMode == RoundingMode::RNE)) + ) { + sig32 = sig32 & ~32'b1; + } + + Bits<32> z = (sign == 1) ? -sig32 : sig32; + if ((z != 0) && (($signed(z) < 0) != (sign == 1))) { + set_fp_flag(FpFlag::NV); + return sign == 1 ? WORD_NEG_OVERFLOW : WORD_POS_OVERFLOW; + } + + if (roundBits) { + set_fp_flag(FpFlag::NX); + } + return z; + } +} + +function packToF32UI { + returns Bits<32> + arguments + Bits<1> sign, + Bits<8> exp, + Bits<23> sig + description { + Pack components into a 32-bit value + } + body { + return {sign, exp, sig}; + } +} + +function softfloat_normRoundPackToF32 { + returns Bits<32> + arguments + Bits<1> sign, + Bits<8> exp, + Bits<23> sig, + RoudningMode mode + description { + + } + body { + Bits<8> shiftDist = count_leading_zeros<32>(sig) - 1; + exp = exp - shiftDist; + if ( (7 < shiftDist) && (exp < 0xFD) > 0) { + return packToF32UI(sign, (sig != 0) ? exp : 0, sig << (shiftDist - 7)); + } else { + return softfloat_roundPackToF32(sign, exp, sig << shiftDist, mode); + } + } +} + +function softfloat_roundPackToF32 { + returns Bits<32> # single precision value + arguments + Bits<1> sign, + Bits<8> exp, + Bits<23> sig, + RoundingMode mode + description { + Round FP value according to +mdode+ and then pack it in IEEE format. + } + body { + Bits<8> roundIncrement = 0x40; + if ( (mode != RoundingMode::RNE) && (mode != RoundingMode::RMM)) { + roundIncrement = + (mode == ((sign != 0) ? RoudningMode::RDN : RoudningMode::RUP)) + ? 0x7F + : 0; + } + Bits<8> roundBits = sig & 0x7f; + + if ( 0xFD <= exp ) { + if ($signed(exp) < 0) { + Boolean isTiny = + ($signed(exp) < -1) || (sig + roundIncrement < 0x80000000); + sig = softfloat_shiftRightJam32( sig, -exp ); + exp = 0; + roundBits = sig & 0x7F; + if (isTiny && roundBits) { + set_fp_flag(FpFlag::UF); + } + } else if (0xFD < $signed(exp) || (0x80000000 <= sig + roundIncrement)) { + set_fp_flag(FpFlag::OF); + set_fp_flag(FpFlag::NX); + return packToF32UI(sign, 0xFF, 0) - (roundIncrement == 0) ? 1 : 0; + } + } + + sig = (sig + roundIncrement) >> 7; + if (roundBits != 0) { + set_fp_flag(FpFlag::NX); + } + sig = sig & ~((roundBits ^ 0x40) & ((mode == RoundingMode::RNE) ? 1 : 0)); + if ( sig == 0 ) { + exp = 0; + } + return packToF32UI(sign, exp, sig); + } +} \ No newline at end of file diff --git a/arch/isa/globals.isa b/arch/isa/globals.isa index a70a24d89..5e222d022 100644 --- a/arch/isa/globals.isa +++ b/arch/isa/globals.isa @@ -2,6 +2,7 @@ include "builtin_functions.idl" include "util.idl" +include "fp.idl" # global state @@ -128,14 +129,6 @@ enum XRegWidth { # StoreAmoGuestPageFault 23 # } -enum RoundingMode { - RNE 0 # Round to nearest, ties to even - RTZ 1 # Round toward zero - RDN 2 # Round down (towards -inf) - RUP 3 # Round up (towards +inf) - RMM 4 # Round to nearest, ties to Max Magnitude -} - enum SatpMode { Bare 0 Sv32 1 @@ -175,13 +168,6 @@ bitfield (64) Sv39PageTableEntry { V 0 } -# floating point register file -U32 FLEN = implemented?(ExtensionName::D) ? 7'd64 : 7'd32; -Bits f[32] = [0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0]; - PrivilegeMode current_mode = PrivilegeMode::M; function mode { @@ -843,18 +829,6 @@ function pmp_match { } } -function nan_box { - returns Bits<64> - arguments Bits<32> sp_value - description { - Produces a properly NaN-boxed double-precision value from a single-precision value - by adding all 1's to the upper bits. - } - body { - return {{32,{1'b1}}, sp_value}; - } -} - function mpv { returns Bits<1> description { diff --git a/arch/isa/util.idl b/arch/isa/util.idl index 308a23305..739628973 100644 --- a/arch/isa/util.idl +++ b/arch/isa/util.idl @@ -69,6 +69,50 @@ function lowest_set_bit { } } +function bit_length { + returns XReg + arguments XReg value + description { + Returns the minimum number of bits needed to represent value. + + Only works on unsigned values. + + The value 0 returns 1. + } + body { + for (XReg i = 63; i > 0; i--) { + if (value[i] == 1) { + return i; + } + } + + # if we get here, the value is 0 or 1. either way, say we need one bit + return 1; + } +} + +function count_leading_zeros { + template U32 N + returns + Bits # Number of leading zeros in +vlaue+ + arguments + Bits value # value to count zero in + description { + Returns the number of leading 0 bits before the most-significant 1 bit of +value+, + or N if value is zero. + } + body { + for (U32 i=0; i < N; i++) { + if (value[N - 1 - i] == 1) { + return i; + } + } + + # fall-through; value must be zero + return N; + } +} + function sext { returns XReg arguments XReg value, XReg first_extended_bit @@ -94,28 +138,6 @@ function sext { } } -function bit_length { - returns XReg - arguments XReg value - description { - Returns the minimum number of bits needed to represent value. - - Only works on unsigned values. - - The value 0 returns 1. - } - body { - for (XReg i = 63; i > 0; i--) { - if (value[i] == 1) { - return i; - } - } - - # if we get here, the value is 0 or 1. either way, say we need one bit - return 1; - } -} - function is_naturally_aligned { template U32 M, U32 N returns Boolean diff --git a/backends/arch_gen/tasks.rake b/backends/arch_gen/tasks.rake index 0ce393aaf..887a662d6 100644 --- a/backends/arch_gen/tasks.rake +++ b/backends/arch_gen/tasks.rake @@ -72,7 +72,10 @@ rule %r{#{$root}/\.stamps/arch-gen-.*\.stamp} => proc { |tname| arch_files = Dir.glob($root / "arch" / "**" / "*.yaml") config_files = Dir.glob($root / "cfgs" / config_name / "arch_overlay" / "**" / "*.yaml") + - [($root / "cfgs" / config_name / "params.yaml").to_s] + [ + ($root / "cfgs" / config_name / "params.yaml").to_s, + ($root / "cfgs" / config_name / "implemented_exts.yaml").to_s, + ] [ "#{$root}/.stamps", "#{ARCH_GEN_DIR}/lib/arch_gen.rb", diff --git a/cfgs/generic_rv64/implemented_exts.yaml b/cfgs/generic_rv64/implemented_exts.yaml index f33d15710..d677571c9 100644 --- a/cfgs/generic_rv64/implemented_exts.yaml +++ b/cfgs/generic_rv64/implemented_exts.yaml @@ -5,7 +5,7 @@ implemented_extensions: - [B, 1.0] - [C, 2.2] - [D, 2.2] - # - [F, 2.2] + - [F, 2.2] - [I, 2.1] - [H, 1.0] - [M, 2.0] diff --git a/cfgs/generic_rv64/params.yaml b/cfgs/generic_rv64/params.yaml index 423eaadab..ea1634345 100644 --- a/cfgs/generic_rv64/params.yaml +++ b/cfgs/generic_rv64/params.yaml @@ -348,6 +348,8 @@ params: # the extension can be disabled in the `misa.F` bit. MUTABLE_MISA_F: false + HW_MSTATUS_FS_DIRTY_UPDATE: precise + # when the H extensions is supported, indicates whether or not # the extension can be disabled in the `misa.H` bit. MUTABLE_MISA_H: false diff --git a/lib/idl/ast.rb b/lib/idl/ast.rb index b9b1efd5f..6e06b91be 100644 --- a/lib/idl/ast.rb +++ b/lib/idl/ast.rb @@ -2874,7 +2874,7 @@ def type(symtab) # @!macro value_no_archdef def value(symtab) - internal_error "Must call type_check first" if symtab.get(@enum_class_name).nil? + internal_error "Must call type_check first: No enum class #{@enum_class_name}" if symtab.get(@enum_class_name).nil? symtab.get(@enum_class_name).value(@member_name) end @@ -4943,6 +4943,7 @@ def type(symtab) # @!macro value def value(symtab) + type_error "CSR[#{csr_name(symtab)}] has no field named #{@field_name}" if field_def(symtab).nil? value_error "'#{csr_name(symtab)}.#{field_name(symtab)}' is not RO" unless field_def(symtab).type(symtab.archdef) == "RO" field_def(symtab).reset_value(symtab.archdef) end diff --git a/lib/idl/idl.treetop b/lib/idl/idl.treetop index b1da639b6..fa6a0f2d6 100644 --- a/lib/idl/idl.treetop +++ b/lib/idl/idl.treetop @@ -68,13 +68,13 @@ grammar Idl '0b' [0-1] [0-1_]* 's' / '0' [0-7] [0-7_]* 's' / [1-9] [0-9]* 's' / - '0x' [0-9a-fA-F] [0-9a-fA-F]* 's' / + '0x' [0-9a-fA-F] [0-9a-fA-F_]* 's' / # c++ style: unsigned '0b' [0-1] [0-1_]* / '0' [0-7] [0-7_]* / [1-9] [0-9]* / - '0x' [0-9a-fA-F] [0-9a-fA-F]* / + '0x' [0-9a-fA-F] [0-9a-fA-F_]* / # special case: just a single 0 '0' 's'? diff --git a/lib/idl/type.rb b/lib/idl/type.rb index 474279272..9468d0c69 100644 --- a/lib/idl/type.rb +++ b/lib/idl/type.rb @@ -37,6 +37,8 @@ def default false when :array Array.new(@width, sub_type.default) + when :string + "" else raise "No default for #{@kind}" end