aboutsummaryrefslogtreecommitdiff
path: root/rv64cpu/rtl/core/fpu
diff options
context:
space:
mode:
Diffstat (limited to 'rv64cpu/rtl/core/fpu')
-rw-r--r--rv64cpu/rtl/core/fpu/fpu.sv2392
1 files changed, 2392 insertions, 0 deletions
diff --git a/rv64cpu/rtl/core/fpu/fpu.sv b/rv64cpu/rtl/core/fpu/fpu.sv
new file mode 100644
index 0000000..9e9494d
--- /dev/null
+++ b/rv64cpu/rtl/core/fpu/fpu.sv
@@ -0,0 +1,2392 @@
+// Richard CPU
+// Copyright (C) 2026 Richard Qin
+
+// File path: rtl/core/fpu/fpu_unit.sv
+// Description: RV64F/RV64D floating-point execution unit.
+
+module fpu
+ import rv64_pkg::*;
+(
+ input fp_op_t fp_op,
+ input fp_fmt_t fp_fmt,
+ input logic [2:0] rm,
+ input logic [2:0] funct3,
+ input logic [4:0] rs2_addr,
+ input logic [XLEN-1:0] fs1_data,
+ input logic [XLEN-1:0] fs2_data,
+ input logic [XLEN-1:0] fs3_data,
+ input logic [XLEN-1:0] int_rs1_data,
+
+ output logic [XLEN-1:0] fp_result,
+ output logic [XLEN-1:0] int_result,
+ output logic [4:0] fflags,
+ output logic illegal_instr
+);
+
+ localparam logic [31:0] CANONICAL_NAN_S = 32'h7FC0_0000;
+ localparam logic [63:0] CANONICAL_NAN_D = 64'h7FF8_0000_0000_0000;
+
+ logic [31:0] s1_raw;
+ logic [31:0] s2_raw;
+ logic [31:0] s3_raw;
+ logic [31:0] s1;
+ logic [31:0] s2;
+ logic [31:0] s3;
+ logic [63:0] d1;
+ logic [63:0] d2;
+ logic [63:0] d3;
+
+ assign s1_raw = fs1_data[31:0];
+ assign s2_raw = fs2_data[31:0];
+ assign s3_raw = fs3_data[31:0];
+ assign s1 = (fs1_data[63:32] == 32'hFFFF_FFFF) ? s1_raw : CANONICAL_NAN_S;
+ assign s2 = (fs2_data[63:32] == 32'hFFFF_FFFF) ? s2_raw : CANONICAL_NAN_S;
+ assign s3 = (fs3_data[63:32] == 32'hFFFF_FFFF) ? s3_raw : CANONICAL_NAN_S;
+ assign d1 = fs1_data;
+ assign d2 = fs2_data;
+ assign d3 = fs3_data;
+
+ function automatic logic is_nan_s(input logic [31:0] value);
+ return (&value[30:23]) && (|value[22:0]);
+ endfunction
+
+ function automatic logic is_snan_s(input logic [31:0] value);
+ return is_nan_s(value) && (value[22] == 1'b0);
+ endfunction
+
+ function automatic logic is_nan_d(input logic [63:0] value);
+ return (&value[62:52]) && (|value[51:0]);
+ endfunction
+
+ function automatic logic is_snan_d(input logic [63:0] value);
+ return is_nan_d(value) && (value[51] == 1'b0);
+ endfunction
+
+ function automatic logic [9:0] class_s(input logic [31:0] value);
+ logic sign;
+ logic [7:0] exp;
+ logic [22:0] frac;
+ sign = value[31];
+ exp = value[30:23];
+ frac = value[22:0];
+ class_s = '0;
+ if (&exp && (frac != '0)) begin
+ class_s[is_snan_s(value) ? 8 : 9] = 1'b1;
+ end else if (sign && (&exp) && (frac == '0)) begin
+ class_s[0] = 1'b1;
+ end else if (sign && (exp != '0)) begin
+ class_s[1] = 1'b1;
+ end else if (sign && (exp == '0) && (frac != '0)) begin
+ class_s[2] = 1'b1;
+ end else if (sign && (exp == '0) && (frac == '0)) begin
+ class_s[3] = 1'b1;
+ end else if (!sign && (exp == '0) && (frac == '0)) begin
+ class_s[4] = 1'b1;
+ end else if (!sign && (exp == '0) && (frac != '0)) begin
+ class_s[5] = 1'b1;
+ end else if (!sign && (exp != '0) && !(&exp)) begin
+ class_s[6] = 1'b1;
+ end else if (!sign && (&exp) && (frac == '0)) begin
+ class_s[7] = 1'b1;
+ end
+ endfunction
+
+ function automatic logic [9:0] class_d(input logic [63:0] value);
+ logic sign;
+ logic [10:0] exp;
+ logic [51:0] frac;
+ sign = value[63];
+ exp = value[62:52];
+ frac = value[51:0];
+ class_d = '0;
+ if (&exp && (frac != '0)) begin
+ class_d[is_snan_d(value) ? 8 : 9] = 1'b1;
+ end else if (sign && (&exp) && (frac == '0)) begin
+ class_d[0] = 1'b1;
+ end else if (sign && (exp != '0)) begin
+ class_d[1] = 1'b1;
+ end else if (sign && (exp == '0) && (frac != '0)) begin
+ class_d[2] = 1'b1;
+ end else if (sign && (exp == '0) && (frac == '0)) begin
+ class_d[3] = 1'b1;
+ end else if (!sign && (exp == '0) && (frac == '0)) begin
+ class_d[4] = 1'b1;
+ end else if (!sign && (exp == '0) && (frac != '0)) begin
+ class_d[5] = 1'b1;
+ end else if (!sign && (exp != '0) && !(&exp)) begin
+ class_d[6] = 1'b1;
+ end else if (!sign && (&exp) && (frac == '0)) begin
+ class_d[7] = 1'b1;
+ end
+ endfunction
+
+ function automatic logic less_s(input logic [31:0] lhs, input logic [31:0] rhs);
+ logic lhs_neg;
+ logic rhs_neg;
+ lhs_neg = lhs[31];
+ rhs_neg = rhs[31];
+ if ((lhs[30:0] == '0) && (rhs[30:0] == '0)) begin
+ return 1'b0;
+ end
+ if (lhs_neg != rhs_neg) begin
+ return lhs_neg;
+ end
+ return lhs_neg ? (lhs[30:0] > rhs[30:0]) : (lhs[30:0] < rhs[30:0]);
+ endfunction
+
+ function automatic logic less_d(input logic [63:0] lhs, input logic [63:0] rhs);
+ logic lhs_neg;
+ logic rhs_neg;
+ lhs_neg = lhs[63];
+ rhs_neg = rhs[63];
+ if ((lhs[62:0] == '0) && (rhs[62:0] == '0)) begin
+ return 1'b0;
+ end
+ if (lhs_neg != rhs_neg) begin
+ return lhs_neg;
+ end
+ return lhs_neg ? (lhs[62:0] > rhs[62:0]) : (lhs[62:0] < rhs[62:0]);
+ endfunction
+
+ function automatic logic equal_s(input logic [31:0] lhs, input logic [31:0] rhs);
+ if ((lhs[30:0] == '0) && (rhs[30:0] == '0)) begin
+ return 1'b1;
+ end
+ return lhs == rhs;
+ endfunction
+
+ function automatic logic equal_d(input logic [63:0] lhs, input logic [63:0] rhs);
+ if ((lhs[62:0] == '0) && (rhs[62:0] == '0)) begin
+ return 1'b1;
+ end
+ return lhs == rhs;
+ endfunction
+
+ function automatic logic [2:0] effective_rm(input logic [2:0] rm_value);
+ effective_rm = (rm_value <= 3'b100) ? rm_value : 3'b000;
+ endfunction
+
+ function automatic logic round_increment(
+ input logic [2:0] round_mode,
+ input logic sign,
+ input logic [127:0] quotient,
+ input logic [127:0] remainder,
+ input logic [127:0] half
+ );
+ logic greater_than_half;
+ logic equal_to_half;
+
+ greater_than_half = remainder > half;
+ equal_to_half = remainder == half;
+
+ unique case (effective_rm(round_mode))
+ 3'b000: round_increment = greater_than_half || (equal_to_half && quotient[0]); // RNE
+ 3'b001: round_increment = 1'b0; // RTZ
+ 3'b010: round_increment = sign && (remainder != '0); // RDN
+ 3'b011: round_increment = !sign && (remainder != '0); // RUP
+ 3'b100: round_increment = greater_than_half || equal_to_half; // RMM
+ default: round_increment = 1'b0;
+ endcase
+ endfunction
+
+ function automatic logic [63:0] invalid_integer_result(
+ input logic is_signed,
+ input logic is_long,
+ input logic sign,
+ input logic is_nan
+ );
+ if (is_signed) begin
+ if (sign && !is_nan) begin
+ invalid_integer_result = is_long ? 64'h8000_0000_0000_0000 : 64'hFFFF_FFFF_8000_0000;
+ end else begin
+ invalid_integer_result = is_long ? 64'h7FFF_FFFF_FFFF_FFFF : 64'h0000_0000_7FFF_FFFF;
+ end
+ end else begin
+ if (sign && !is_nan) begin
+ invalid_integer_result = '0;
+ end else begin
+ invalid_integer_result = 64'hFFFF_FFFF_FFFF_FFFF;
+ end
+ end
+ endfunction
+
+ function automatic logic [63:0] cvt_s_to_int(
+ input logic [31:0] value,
+ input logic [1:0] int_fmt,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic sign;
+ logic [7:0] exp;
+ logic [22:0] frac;
+ logic [23:0] significand;
+ logic signed [12:0] unbiased_exp;
+ logic is_signed;
+ logic is_long;
+ logic invalid;
+ logic inexact;
+ logic [127:0] magnitude;
+ logic [127:0] quotient;
+ logic [127:0] remainder;
+ logic [127:0] divisor;
+ logic [127:0] half;
+ logic [127:0] rounded_magnitude;
+ logic [63:0] positive_limit;
+ logic [63:0] negative_limit;
+ logic [63:0] unsigned_limit;
+ logic [63:0] integer_value;
+ int shift;
+
+ sign = value[31];
+ exp = value[30:23];
+ frac = value[22:0];
+ significand = (exp == '0) ? {1'b0, frac} : {1'b1, frac};
+ unbiased_exp = $signed({5'b0, exp}) - 13'sd127;
+ is_signed = (int_fmt == 2'd0) || (int_fmt == 2'd2);
+ is_long = (int_fmt == 2'd2) || (int_fmt == 2'd3);
+ invalid = 1'b0;
+ inexact = 1'b0;
+ magnitude = '0;
+ quotient = '0;
+ remainder = '0;
+ divisor = '0;
+ half = '0;
+ rounded_magnitude = '0;
+ positive_limit = is_long ? 64'h7FFF_FFFF_FFFF_FFFF : 64'h0000_0000_7FFF_FFFF;
+ negative_limit = is_long ? 64'h8000_0000_0000_0000 : 64'h0000_0000_8000_0000;
+ unsigned_limit = is_long ? 64'hFFFF_FFFF_FFFF_FFFF : 64'h0000_0000_FFFF_FFFF;
+ integer_value = '0;
+ flags = '0;
+
+ if (is_nan_s(value) || (&exp)) begin
+ invalid = 1'b1;
+ end else if (significand == '0) begin
+ rounded_magnitude = '0;
+ end else begin
+ if (unbiased_exp >= 13'sd23) begin
+ shift = int'(unbiased_exp) - 23;
+ if (shift >= 104) begin
+ invalid = 1'b1;
+ end else begin
+ rounded_magnitude = {104'b0, significand} << shift;
+ end
+ end else begin
+ shift = 23 - int'(unbiased_exp);
+ if (shift > 24) begin
+ quotient = '0;
+ remainder = {104'b0, significand};
+ divisor = '0;
+ half = '0;
+ inexact = remainder != '0;
+ unique case (effective_rm(round_mode))
+ 3'b010: rounded_magnitude = (sign && inexact) ? 128'd1 : 128'd0;
+ 3'b011: rounded_magnitude = (!sign && inexact) ? 128'd1 : 128'd0;
+ default: rounded_magnitude = '0;
+ endcase
+ end else begin
+ divisor = 128'd1 << shift;
+ quotient = {104'b0, significand} >> shift;
+ remainder = {104'b0, significand} & (divisor - 128'd1);
+ half = divisor >> 1;
+ inexact = (remainder != '0);
+ rounded_magnitude = quotient +
+ (round_increment(round_mode, sign, quotient, remainder, half) ? 128'd1 : 128'd0);
+ end
+ end
+ end
+
+ if (!invalid) begin
+ if (is_signed) begin
+ if ((!sign && (rounded_magnitude > {64'b0, positive_limit})) ||
+ (sign && (rounded_magnitude > {64'b0, negative_limit}))) begin
+ invalid = 1'b1;
+ end
+ end else begin
+ if ((sign && (rounded_magnitude != '0)) ||
+ (rounded_magnitude > {64'b0, unsigned_limit})) begin
+ invalid = 1'b1;
+ end
+ end
+ end
+
+ if (invalid) begin
+ flags[4] = 1'b1;
+ return invalid_integer_result(is_signed, is_long, sign, is_nan_s(value));
+ end
+
+ if (inexact) begin
+ flags[0] = 1'b1;
+ end
+
+ integer_value = rounded_magnitude[63:0];
+ if (sign) begin
+ integer_value = (~integer_value) + 64'd1;
+ end
+
+ if (!is_long) begin
+ return {{32{integer_value[31]}}, integer_value[31:0]};
+ end
+
+ return integer_value;
+ endfunction
+
+ function automatic logic [63:0] cvt_d_to_int(
+ input logic [63:0] value,
+ input logic [1:0] int_fmt,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic sign;
+ logic [10:0] exp;
+ logic [51:0] frac;
+ logic [52:0] significand;
+ logic signed [12:0] unbiased_exp;
+ logic is_signed;
+ logic is_long;
+ logic invalid;
+ logic inexact;
+ logic [127:0] quotient;
+ logic [127:0] remainder;
+ logic [127:0] divisor;
+ logic [127:0] half;
+ logic [127:0] rounded_magnitude;
+ logic [63:0] positive_limit;
+ logic [63:0] negative_limit;
+ logic [63:0] unsigned_limit;
+ logic [63:0] integer_value;
+ int shift;
+
+ sign = value[63];
+ exp = value[62:52];
+ frac = value[51:0];
+ significand = (exp == '0) ? {1'b0, frac} : {1'b1, frac};
+ unbiased_exp = $signed({2'b0, exp}) - 13'sd1023;
+ is_signed = (int_fmt == 2'd0) || (int_fmt == 2'd2);
+ is_long = (int_fmt == 2'd2) || (int_fmt == 2'd3);
+ invalid = 1'b0;
+ inexact = 1'b0;
+ quotient = '0;
+ remainder = '0;
+ divisor = '0;
+ half = '0;
+ rounded_magnitude = '0;
+ positive_limit = is_long ? 64'h7FFF_FFFF_FFFF_FFFF : 64'h0000_0000_7FFF_FFFF;
+ negative_limit = is_long ? 64'h8000_0000_0000_0000 : 64'h0000_0000_8000_0000;
+ unsigned_limit = is_long ? 64'hFFFF_FFFF_FFFF_FFFF : 64'h0000_0000_FFFF_FFFF;
+ integer_value = '0;
+ flags = '0;
+
+ if (is_nan_d(value) || (&exp)) begin
+ invalid = 1'b1;
+ end else if (significand == '0) begin
+ rounded_magnitude = '0;
+ end else begin
+ if (unbiased_exp >= 13'sd52) begin
+ shift = int'(unbiased_exp) - 52;
+ if (shift >= 75) begin
+ invalid = 1'b1;
+ end else begin
+ rounded_magnitude = {75'b0, significand} << shift;
+ end
+ end else begin
+ shift = 52 - int'(unbiased_exp);
+ if (shift > 53) begin
+ quotient = '0;
+ remainder = {75'b0, significand};
+ inexact = remainder != '0;
+ unique case (effective_rm(round_mode))
+ 3'b010: rounded_magnitude = (sign && inexact) ? 128'd1 : 128'd0;
+ 3'b011: rounded_magnitude = (!sign && inexact) ? 128'd1 : 128'd0;
+ default: rounded_magnitude = '0;
+ endcase
+ end else begin
+ divisor = 128'd1 << shift;
+ quotient = {75'b0, significand} >> shift;
+ remainder = {75'b0, significand} & (divisor - 128'd1);
+ half = divisor >> 1;
+ inexact = (remainder != '0);
+ rounded_magnitude = quotient +
+ (round_increment(round_mode, sign, quotient, remainder, half) ? 128'd1 : 128'd0);
+ end
+ end
+ end
+
+ if (!invalid) begin
+ if (is_signed) begin
+ if ((!sign && (rounded_magnitude > {64'b0, positive_limit})) ||
+ (sign && (rounded_magnitude > {64'b0, negative_limit}))) begin
+ invalid = 1'b1;
+ end
+ end else begin
+ if ((sign && (rounded_magnitude != '0)) ||
+ (rounded_magnitude > {64'b0, unsigned_limit})) begin
+ invalid = 1'b1;
+ end
+ end
+ end
+
+ if (invalid) begin
+ flags[4] = 1'b1;
+ return invalid_integer_result(is_signed, is_long, sign, is_nan_d(value));
+ end
+
+ if (inexact) begin
+ flags[0] = 1'b1;
+ end
+
+ integer_value = rounded_magnitude[63:0];
+ if (sign) begin
+ integer_value = (~integer_value) + 64'd1;
+ end
+
+ if (!is_long) begin
+ return {{32{integer_value[31]}}, integer_value[31:0]};
+ end
+
+ return integer_value;
+ endfunction
+
+ function automatic int highest_bit_64(input logic [63:0] value);
+ highest_bit_64 = -1;
+ for (int bit_idx = 0; bit_idx < 64; bit_idx++) begin
+ if (value[bit_idx]) begin
+ highest_bit_64 = bit_idx;
+ end
+ end
+ endfunction
+
+ function automatic int highest_bit_53(input logic [52:0] value);
+ highest_bit_53 = -1;
+ for (int bit_idx = 0; bit_idx < 53; bit_idx++) begin
+ if (value[bit_idx]) begin
+ highest_bit_53 = bit_idx;
+ end
+ end
+ endfunction
+
+ function automatic logic [31:0] overflow_s_result(input logic sign, input logic [2:0] round_mode);
+ unique case (effective_rm(round_mode))
+ 3'b001: overflow_s_result = {sign, 8'hFE, 23'h7F_FFFF};
+ 3'b010: overflow_s_result = sign ? 32'hFF80_0000 : 32'h7F7F_FFFF;
+ 3'b011: overflow_s_result = sign ? 32'hFF7F_FFFF : 32'h7F80_0000;
+ default: overflow_s_result = {sign, 8'hFF, 23'b0};
+ endcase
+ endfunction
+
+ function automatic logic [63:0] overflow_d_result(input logic sign, input logic [2:0] round_mode);
+ unique case (effective_rm(round_mode))
+ 3'b001: overflow_d_result = {sign, 11'h7FE, 52'hF_FFFF_FFFF_FFFF};
+ 3'b010: overflow_d_result = sign ? 64'hFFF0_0000_0000_0000 : 64'h7FEF_FFFF_FFFF_FFFF;
+ 3'b011: overflow_d_result = sign ? 64'hFFEF_FFFF_FFFF_FFFF : 64'h7FF0_0000_0000_0000;
+ default: overflow_d_result = {sign, 11'h7FF, 52'b0};
+ endcase
+ endfunction
+
+ function automatic logic [27:0] shift_right_jam_28(
+ input logic [27:0] value,
+ input int shift
+ );
+ logic sticky;
+
+ if (shift <= 0) begin
+ return value;
+ end
+ if (shift >= 28) begin
+ return (value != '0) ? 28'd1 : 28'd0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 28; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ return (value >> shift) | (sticky ? 28'd1 : 28'd0);
+ endfunction
+
+ function automatic logic shifted_out_28(
+ input logic [27:0] value,
+ input int shift
+ );
+ logic sticky;
+
+ if (shift <= 0) begin
+ return 1'b0;
+ end
+ if (shift >= 28) begin
+ return value != '0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 28; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ return sticky;
+ endfunction
+
+ function automatic logic [27:0] shift_right_jam_48(
+ input logic [47:0] value,
+ input int shift
+ );
+ logic sticky;
+ logic [27:0] shifted;
+
+ if (shift <= 0) begin
+ return value[27:0];
+ end
+ if (shift >= 48) begin
+ return (value != '0) ? 28'd1 : 28'd0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 48; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ shifted = 28'((value >> shift));
+ return shifted | (sticky ? 28'd1 : 28'd0);
+ endfunction
+
+ function automatic logic [56:0] shift_right_jam_57(
+ input logic [56:0] value,
+ input int shift
+ );
+ logic sticky;
+
+ if (shift <= 0) begin
+ return value;
+ end
+ if (shift >= 57) begin
+ return (value != '0) ? 57'd1 : 57'd0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 57; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ return (value >> shift) | (sticky ? 57'd1 : 57'd0);
+ endfunction
+
+ function automatic logic [64:0] shift_right_jam_65(
+ input logic [64:0] value,
+ input int shift
+ );
+ logic sticky;
+
+ if (shift <= 0) begin
+ return value;
+ end
+ if (shift >= 65) begin
+ return (value != '0) ? 65'd1 : 65'd0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 65; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ return (value >> shift) | (sticky ? 65'd1 : 65'd0);
+ endfunction
+
+ function automatic logic [127:0] shift_right_jam_128(
+ input logic [127:0] value,
+ input int shift
+ );
+ logic sticky;
+
+ if (shift <= 0) begin
+ return value;
+ end
+ if (shift >= 128) begin
+ return (value != '0) ? 128'd1 : 128'd0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 128; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ return (value >> shift) | (sticky ? 128'd1 : 128'd0);
+ endfunction
+
+ function automatic logic [56:0] shift_right_jam_128_to_57(
+ input logic [127:0] value,
+ input int shift
+ );
+ logic sticky;
+ logic [56:0] shifted;
+
+ if (shift <= 0) begin
+ return value[56:0];
+ end
+ if (shift >= 128) begin
+ return (value != '0) ? 57'd1 : 57'd0;
+ end
+
+ sticky = 1'b0;
+ for (int bit_idx = 0; bit_idx < 128; bit_idx++) begin
+ if (bit_idx < shift) begin
+ sticky = sticky | value[bit_idx];
+ end
+ end
+ shifted = 57'(value >> shift);
+ return shifted | (sticky ? 57'd1 : 57'd0);
+ endfunction
+
+ function automatic logic [31:0] round_pack_s(
+ input logic sign,
+ input logic signed [10:0] unbiased_exp,
+ input logic [27:0] significand,
+ input logic inexact_before_round,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic signed [10:0] result_unbiased_exp;
+ logic [27:0] result_sig;
+ logic round_any;
+ logic round_up;
+ logic [24:0] rounded_sig;
+ logic [7:0] result_exp;
+ logic [22:0] result_frac;
+ logic tiny_before_round;
+ int subnormal_shift;
+
+ flags = '0;
+ result_unbiased_exp = unbiased_exp;
+ result_sig = significand;
+ result_sig[0] = result_sig[0] | inexact_before_round;
+ tiny_before_round = 1'b0;
+
+ if (result_unbiased_exp < -11'sd126) begin
+ tiny_before_round = 1'b1;
+ subnormal_shift = -126 - int'(result_unbiased_exp);
+ result_sig = shift_right_jam_28(result_sig, subnormal_shift);
+ result_unbiased_exp = -11'sd126;
+ end
+
+ round_any = |result_sig[2:0];
+ unique case (effective_rm(round_mode))
+ 3'b000: round_up = result_sig[2] && (result_sig[1] || result_sig[0] || result_sig[3]);
+ 3'b001: round_up = 1'b0;
+ 3'b010: round_up = sign && round_any;
+ 3'b011: round_up = !sign && round_any;
+ 3'b100: round_up = result_sig[2];
+ default: round_up = 1'b0;
+ endcase
+
+ rounded_sig = {1'b0, result_sig[26:3]} + (round_up ? 25'd1 : 25'd0);
+ if (round_any) begin
+ flags[0] = 1'b1;
+ end
+ if (tiny_before_round && flags[0]) begin
+ flags[1] = 1'b1;
+ end
+
+ if (rounded_sig[24]) begin
+ rounded_sig = rounded_sig >> 1;
+ result_unbiased_exp = result_unbiased_exp + 11'sd1;
+ end
+
+ if (result_unbiased_exp > 11'sd127) begin
+ flags[2] = 1'b1;
+ flags[0] = 1'b1;
+ return overflow_s_result(sign, round_mode);
+ end
+
+ if ((result_unbiased_exp == -11'sd126) && !rounded_sig[23]) begin
+ result_exp = 8'b0;
+ result_frac = rounded_sig[22:0];
+ if (flags[0]) begin
+ flags[1] = 1'b1;
+ end
+ end else begin
+ result_exp = 8'(result_unbiased_exp + 11'sd127);
+ result_frac = rounded_sig[22:0];
+ end
+
+ return {sign, result_exp, result_frac};
+ endfunction
+
+ function automatic logic [63:0] round_pack_d(
+ input logic sign,
+ input logic signed [12:0] unbiased_exp,
+ input logic [56:0] significand,
+ input logic inexact_before_round,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic signed [12:0] result_unbiased_exp;
+ logic [56:0] result_sig;
+ logic round_any;
+ logic round_up;
+ logic [53:0] rounded_sig;
+ logic [10:0] result_exp;
+ logic [51:0] result_frac;
+ logic tiny_before_round;
+ int subnormal_shift;
+
+ flags = '0;
+ result_unbiased_exp = unbiased_exp;
+ result_sig = significand;
+ result_sig[0] = result_sig[0] | inexact_before_round;
+ tiny_before_round = 1'b0;
+
+ if (result_unbiased_exp < -13'sd1022) begin
+ tiny_before_round = 1'b1;
+ subnormal_shift = -1022 - int'(result_unbiased_exp);
+ result_sig = shift_right_jam_57(result_sig, subnormal_shift);
+ result_unbiased_exp = -13'sd1022;
+ end
+
+ round_any = |result_sig[2:0];
+ unique case (effective_rm(round_mode))
+ 3'b000: round_up = result_sig[2] && (result_sig[1] || result_sig[0] || result_sig[3]);
+ 3'b001: round_up = 1'b0;
+ 3'b010: round_up = sign && round_any;
+ 3'b011: round_up = !sign && round_any;
+ 3'b100: round_up = result_sig[2];
+ default: round_up = 1'b0;
+ endcase
+
+ rounded_sig = {1'b0, result_sig[55:3]} + (round_up ? 54'd1 : 54'd0);
+ if (round_any) begin
+ flags[0] = 1'b1;
+ end
+ if (tiny_before_round && flags[0]) begin
+ flags[1] = 1'b1;
+ end
+
+ if (rounded_sig[53]) begin
+ rounded_sig = rounded_sig >> 1;
+ result_unbiased_exp = result_unbiased_exp + 13'sd1;
+ end
+
+ if (result_unbiased_exp > 13'sd1023) begin
+ flags[2] = 1'b1;
+ flags[0] = 1'b1;
+ return overflow_d_result(sign, round_mode);
+ end
+
+ if ((result_unbiased_exp == -13'sd1022) && !rounded_sig[52]) begin
+ result_exp = 11'b0;
+ result_frac = rounded_sig[51:0];
+ if (flags[0]) begin
+ flags[1] = 1'b1;
+ end
+ end else begin
+ result_exp = 11'(result_unbiased_exp + 13'sd1023);
+ result_frac = rounded_sig[51:0];
+ end
+
+ return {sign, result_exp, result_frac};
+ endfunction
+
+ function automatic logic [31:0] fp_addsub_s(
+ input logic [31:0] lhs,
+ input logic [31:0] rhs,
+ input logic subtract,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic lhs_sign;
+ logic rhs_sign;
+ logic result_sign;
+ logic [7:0] lhs_exp;
+ logic [7:0] rhs_exp;
+ logic [22:0] lhs_frac;
+ logic [22:0] rhs_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic signed [10:0] lhs_unbiased_exp;
+ logic signed [10:0] rhs_unbiased_exp;
+ logic signed [10:0] result_unbiased_exp;
+ logic [27:0] lhs_sig;
+ logic [27:0] rhs_sig;
+ logic [27:0] result_sig;
+ logic [27:0] larger_sig;
+ logic [27:0] smaller_sig;
+ logic signed [10:0] larger_exp;
+ logic signs_match;
+ logic alignment_inexact;
+ int exp_diff;
+
+ flags = '0;
+ lhs_sign = lhs[31];
+ rhs_sign = rhs[31] ^ subtract;
+ lhs_exp = lhs[30:23];
+ rhs_exp = rhs[30:23];
+ lhs_frac = lhs[22:0];
+ rhs_frac = rhs[22:0];
+ lhs_is_zero = (lhs[30:0] == '0);
+ rhs_is_zero = (rhs[30:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+ signs_match = lhs_sign == rhs_sign;
+ alignment_inexact = 1'b0;
+
+ if (is_nan_s(lhs) || is_nan_s(rhs)) begin
+ flags[4] = is_snan_s(lhs) || is_snan_s(rhs);
+ return CANONICAL_NAN_S;
+ end
+
+ if (lhs_is_inf || rhs_is_inf) begin
+ if (lhs_is_inf && rhs_is_inf && !signs_match) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_S;
+ end
+ return lhs_is_inf ? {lhs_sign, 8'hFF, 23'b0} : {rhs_sign, 8'hFF, 23'b0};
+ end
+
+ if (lhs_is_zero && rhs_is_zero) begin
+ result_sign = signs_match ? lhs_sign : (effective_rm(round_mode) == 3'b010);
+ return {result_sign, 31'b0};
+ end
+ if (lhs_is_zero) begin
+ return {rhs_sign, rhs[30:0]};
+ end
+ if (rhs_is_zero) begin
+ return lhs;
+ end
+
+ lhs_unbiased_exp = (lhs_exp == '0) ? -11'sd126 : ($signed({3'b0, lhs_exp}) - 11'sd127);
+ rhs_unbiased_exp = (rhs_exp == '0) ? -11'sd126 : ($signed({3'b0, rhs_exp}) - 11'sd127);
+ lhs_sig = {1'b0, (lhs_exp == '0) ? 1'b0 : 1'b1, lhs_frac, 3'b000};
+ rhs_sig = {1'b0, (rhs_exp == '0) ? 1'b0 : 1'b1, rhs_frac, 3'b000};
+
+ if ((lhs_unbiased_exp > rhs_unbiased_exp) ||
+ ((lhs_unbiased_exp == rhs_unbiased_exp) && (lhs_sig >= rhs_sig))) begin
+ exp_diff = int'(lhs_unbiased_exp) - int'(rhs_unbiased_exp);
+ larger_sig = lhs_sig;
+ alignment_inexact = shifted_out_28(rhs_sig, exp_diff);
+ smaller_sig = shift_right_jam_28(rhs_sig, exp_diff);
+ larger_exp = lhs_unbiased_exp;
+ result_sign = lhs_sign;
+ end else begin
+ exp_diff = int'(rhs_unbiased_exp) - int'(lhs_unbiased_exp);
+ larger_sig = rhs_sig;
+ alignment_inexact = shifted_out_28(lhs_sig, exp_diff);
+ smaller_sig = shift_right_jam_28(lhs_sig, exp_diff);
+ larger_exp = rhs_unbiased_exp;
+ result_sign = rhs_sign;
+ end
+
+ result_unbiased_exp = larger_exp;
+ if (signs_match) begin
+ result_sig = larger_sig + smaller_sig;
+ if (result_sig[27]) begin
+ result_sig = shift_right_jam_28(result_sig, 1);
+ result_unbiased_exp = result_unbiased_exp + 11'sd1;
+ end
+ end else begin
+ result_sig = larger_sig - smaller_sig;
+ if (result_sig == '0) begin
+ result_sign = effective_rm(round_mode) == 3'b010;
+ return {result_sign, 31'b0};
+ end
+ while (!result_sig[26] && (result_unbiased_exp > -11'sd126)) begin
+ result_sig = result_sig << 1;
+ result_unbiased_exp = result_unbiased_exp - 11'sd1;
+ end
+ end
+
+ return round_pack_s(result_sign, result_unbiased_exp, result_sig, alignment_inexact, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] fp_addsub_d(
+ input logic [63:0] lhs,
+ input logic [63:0] rhs,
+ input logic subtract,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic lhs_sign;
+ logic rhs_sign;
+ logic result_sign;
+ logic [10:0] lhs_exp;
+ logic [10:0] rhs_exp;
+ logic [51:0] lhs_frac;
+ logic [51:0] rhs_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic signed [12:0] lhs_unbiased_exp;
+ logic signed [12:0] rhs_unbiased_exp;
+ logic signed [12:0] result_unbiased_exp;
+ logic [56:0] lhs_sig;
+ logic [56:0] rhs_sig;
+ logic [56:0] result_sig;
+ logic [56:0] larger_sig;
+ logic [56:0] smaller_sig;
+ logic signed [12:0] larger_exp;
+ logic signs_match;
+ int exp_diff;
+
+ flags = '0;
+ lhs_sign = lhs[63];
+ rhs_sign = rhs[63] ^ subtract;
+ lhs_exp = lhs[62:52];
+ rhs_exp = rhs[62:52];
+ lhs_frac = lhs[51:0];
+ rhs_frac = rhs[51:0];
+ lhs_is_zero = (lhs[62:0] == '0);
+ rhs_is_zero = (rhs[62:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+ signs_match = lhs_sign == rhs_sign;
+
+ if (is_nan_d(lhs) || is_nan_d(rhs)) begin
+ flags[4] = is_snan_d(lhs) || is_snan_d(rhs);
+ return CANONICAL_NAN_D;
+ end
+
+ if (lhs_is_inf || rhs_is_inf) begin
+ if (lhs_is_inf && rhs_is_inf && !signs_match) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_D;
+ end
+ return lhs_is_inf ? {lhs_sign, 11'h7FF, 52'b0} : {rhs_sign, 11'h7FF, 52'b0};
+ end
+
+ if (lhs_is_zero && rhs_is_zero) begin
+ result_sign = signs_match ? lhs_sign : (effective_rm(round_mode) == 3'b010);
+ return {result_sign, 63'b0};
+ end
+ if (lhs_is_zero) begin
+ return {rhs_sign, rhs[62:0]};
+ end
+ if (rhs_is_zero) begin
+ return lhs;
+ end
+
+ lhs_unbiased_exp = (lhs_exp == '0) ? -13'sd1022 : ($signed({2'b0, lhs_exp}) - 13'sd1023);
+ rhs_unbiased_exp = (rhs_exp == '0) ? -13'sd1022 : ($signed({2'b0, rhs_exp}) - 13'sd1023);
+ lhs_sig = {1'b0, (lhs_exp == '0) ? 1'b0 : 1'b1, lhs_frac, 3'b000};
+ rhs_sig = {1'b0, (rhs_exp == '0) ? 1'b0 : 1'b1, rhs_frac, 3'b000};
+
+ if ((lhs_unbiased_exp > rhs_unbiased_exp) ||
+ ((lhs_unbiased_exp == rhs_unbiased_exp) && (lhs_sig >= rhs_sig))) begin
+ exp_diff = int'(lhs_unbiased_exp) - int'(rhs_unbiased_exp);
+ larger_sig = lhs_sig;
+ smaller_sig = shift_right_jam_57(rhs_sig, exp_diff);
+ larger_exp = lhs_unbiased_exp;
+ result_sign = lhs_sign;
+ end else begin
+ exp_diff = int'(rhs_unbiased_exp) - int'(lhs_unbiased_exp);
+ larger_sig = rhs_sig;
+ smaller_sig = shift_right_jam_57(lhs_sig, exp_diff);
+ larger_exp = rhs_unbiased_exp;
+ result_sign = rhs_sign;
+ end
+
+ result_unbiased_exp = larger_exp;
+ if (signs_match) begin
+ result_sig = larger_sig + smaller_sig;
+ if (result_sig[56]) begin
+ result_sig = shift_right_jam_57(result_sig, 1);
+ result_unbiased_exp = result_unbiased_exp + 13'sd1;
+ end
+ end else begin
+ result_sig = larger_sig - smaller_sig;
+ if (result_sig == '0) begin
+ result_sign = effective_rm(round_mode) == 3'b010;
+ return {result_sign, 63'b0};
+ end
+ while (!result_sig[55] && (result_unbiased_exp > -13'sd1022)) begin
+ result_sig = result_sig << 1;
+ result_unbiased_exp = result_unbiased_exp - 13'sd1;
+ end
+ end
+
+ return round_pack_d(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] fp_mul_d(
+ input logic [63:0] lhs,
+ input logic [63:0] rhs,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic result_sign;
+ logic [10:0] lhs_exp;
+ logic [10:0] rhs_exp;
+ logic [51:0] lhs_frac;
+ logic [51:0] rhs_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic signed [12:0] lhs_unbiased_exp;
+ logic signed [12:0] rhs_unbiased_exp;
+ logic signed [12:0] result_unbiased_exp;
+ logic [52:0] lhs_sig;
+ logic [52:0] rhs_sig;
+ logic [105:0] product;
+ logic [56:0] result_sig;
+ int lhs_msb;
+ int rhs_msb;
+
+ flags = '0;
+ result_sign = lhs[63] ^ rhs[63];
+ lhs_exp = lhs[62:52];
+ rhs_exp = rhs[62:52];
+ lhs_frac = lhs[51:0];
+ rhs_frac = rhs[51:0];
+ lhs_is_zero = (lhs[62:0] == '0);
+ rhs_is_zero = (rhs[62:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+
+ if (is_nan_d(lhs) || is_nan_d(rhs)) begin
+ flags[4] = is_snan_d(lhs) || is_snan_d(rhs);
+ return CANONICAL_NAN_D;
+ end
+
+ if ((lhs_is_inf && rhs_is_zero) || (rhs_is_inf && lhs_is_zero)) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_D;
+ end
+
+ if (lhs_is_inf || rhs_is_inf) begin
+ return {result_sign, 11'h7FF, 52'b0};
+ end
+
+ if (lhs_is_zero || rhs_is_zero) begin
+ return {result_sign, 63'b0};
+ end
+
+ if (lhs_exp == '0) begin
+ lhs_msb = highest_bit_53({1'b0, lhs_frac});
+ lhs_unbiased_exp = 13'(lhs_msb - 1074);
+ lhs_sig = {1'b0, lhs_frac} << (52 - lhs_msb);
+ end else begin
+ lhs_unbiased_exp = $signed({2'b0, lhs_exp}) - 13'sd1023;
+ lhs_sig = {1'b1, lhs_frac};
+ end
+
+ if (rhs_exp == '0) begin
+ rhs_msb = highest_bit_53({1'b0, rhs_frac});
+ rhs_unbiased_exp = 13'(rhs_msb - 1074);
+ rhs_sig = {1'b0, rhs_frac} << (52 - rhs_msb);
+ end else begin
+ rhs_unbiased_exp = $signed({2'b0, rhs_exp}) - 13'sd1023;
+ rhs_sig = {1'b1, rhs_frac};
+ end
+
+ product = lhs_sig * rhs_sig;
+ if (product[105]) begin
+ result_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp + 13'sd1;
+ result_sig = shift_right_jam_128_to_57({22'b0, product}, 50);
+ end else begin
+ result_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp;
+ result_sig = shift_right_jam_128_to_57({22'b0, product}, 49);
+ end
+
+ return round_pack_d(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [31:0] fp_mul_s(
+ input logic [31:0] lhs,
+ input logic [31:0] rhs,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic result_sign;
+ logic [7:0] lhs_exp;
+ logic [7:0] rhs_exp;
+ logic [22:0] lhs_frac;
+ logic [22:0] rhs_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic signed [10:0] lhs_unbiased_exp;
+ logic signed [10:0] rhs_unbiased_exp;
+ logic signed [10:0] result_unbiased_exp;
+ logic [23:0] lhs_sig;
+ logic [23:0] rhs_sig;
+ logic [47:0] product;
+ logic [27:0] result_sig;
+ int lhs_msb;
+ int rhs_msb;
+
+ flags = '0;
+ result_sign = lhs[31] ^ rhs[31];
+ lhs_exp = lhs[30:23];
+ rhs_exp = rhs[30:23];
+ lhs_frac = lhs[22:0];
+ rhs_frac = rhs[22:0];
+ lhs_is_zero = (lhs[30:0] == '0);
+ rhs_is_zero = (rhs[30:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+
+ if (is_nan_s(lhs) || is_nan_s(rhs)) begin
+ flags[4] = is_snan_s(lhs) || is_snan_s(rhs);
+ return CANONICAL_NAN_S;
+ end
+
+ if ((lhs_is_inf && rhs_is_zero) || (rhs_is_inf && lhs_is_zero)) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_S;
+ end
+
+ if (lhs_is_inf || rhs_is_inf) begin
+ return {result_sign, 8'hFF, 23'b0};
+ end
+
+ if (lhs_is_zero || rhs_is_zero) begin
+ return {result_sign, 31'b0};
+ end
+
+ if (lhs_exp == '0) begin
+ lhs_msb = highest_bit_64({41'b0, lhs_frac});
+ lhs_unbiased_exp = 11'(lhs_msb - 149);
+ lhs_sig = {1'b0, lhs_frac} << (23 - lhs_msb);
+ end else begin
+ lhs_unbiased_exp = $signed({3'b0, lhs_exp}) - 11'sd127;
+ lhs_sig = {1'b1, lhs_frac};
+ end
+
+ if (rhs_exp == '0) begin
+ rhs_msb = highest_bit_64({41'b0, rhs_frac});
+ rhs_unbiased_exp = 11'(rhs_msb - 149);
+ rhs_sig = {1'b0, rhs_frac} << (23 - rhs_msb);
+ end else begin
+ rhs_unbiased_exp = $signed({3'b0, rhs_exp}) - 11'sd127;
+ rhs_sig = {1'b1, rhs_frac};
+ end
+
+ product = lhs_sig * rhs_sig;
+ if (product[47]) begin
+ result_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp + 11'sd1;
+ result_sig = shift_right_jam_48(product, 21);
+ end else begin
+ result_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp;
+ result_sig = shift_right_jam_48(product, 20);
+ end
+
+ return round_pack_s(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [31:0] fp_div_s(
+ input logic [31:0] lhs,
+ input logic [31:0] rhs,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic result_sign;
+ logic [7:0] lhs_exp;
+ logic [7:0] rhs_exp;
+ logic [22:0] lhs_frac;
+ logic [22:0] rhs_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic signed [10:0] lhs_unbiased_exp;
+ logic signed [10:0] rhs_unbiased_exp;
+ logic signed [10:0] result_unbiased_exp;
+ logic [23:0] lhs_sig;
+ logic [23:0] rhs_sig;
+ logic [63:0] dividend;
+ logic [63:0] quotient;
+ logic [23:0] remainder;
+ logic [27:0] result_sig;
+ int lhs_msb;
+ int rhs_msb;
+
+ flags = '0;
+ result_sign = lhs[31] ^ rhs[31];
+ lhs_exp = lhs[30:23];
+ rhs_exp = rhs[30:23];
+ lhs_frac = lhs[22:0];
+ rhs_frac = rhs[22:0];
+ lhs_is_zero = (lhs[30:0] == '0);
+ rhs_is_zero = (rhs[30:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+
+ if (is_nan_s(lhs) || is_nan_s(rhs)) begin
+ flags[4] = is_snan_s(lhs) || is_snan_s(rhs);
+ return CANONICAL_NAN_S;
+ end
+
+ if ((lhs_is_zero && rhs_is_zero) || (lhs_is_inf && rhs_is_inf)) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_S;
+ end
+
+ if (lhs_is_inf || rhs_is_zero) begin
+ if (rhs_is_zero && !lhs_is_zero && !lhs_is_inf) begin
+ flags[3] = 1'b1;
+ end
+ return {result_sign, 8'hFF, 23'b0};
+ end
+
+ if (lhs_is_zero || rhs_is_inf) begin
+ return {result_sign, 31'b0};
+ end
+
+ if (lhs_exp == '0) begin
+ lhs_msb = highest_bit_64({41'b0, lhs_frac});
+ lhs_unbiased_exp = 11'(lhs_msb - 149);
+ lhs_sig = {1'b0, lhs_frac} << (23 - lhs_msb);
+ end else begin
+ lhs_unbiased_exp = $signed({3'b0, lhs_exp}) - 11'sd127;
+ lhs_sig = {1'b1, lhs_frac};
+ end
+
+ if (rhs_exp == '0) begin
+ rhs_msb = highest_bit_64({41'b0, rhs_frac});
+ rhs_unbiased_exp = 11'(rhs_msb - 149);
+ rhs_sig = {1'b0, rhs_frac} << (23 - rhs_msb);
+ end else begin
+ rhs_unbiased_exp = $signed({3'b0, rhs_exp}) - 11'sd127;
+ rhs_sig = {1'b1, rhs_frac};
+ end
+
+ if (lhs_sig >= rhs_sig) begin
+ result_unbiased_exp = lhs_unbiased_exp - rhs_unbiased_exp;
+ dividend = {40'b0, lhs_sig} << 26;
+ end else begin
+ result_unbiased_exp = lhs_unbiased_exp - rhs_unbiased_exp - 11'sd1;
+ dividend = {40'b0, lhs_sig} << 27;
+ end
+
+ quotient = dividend / {40'b0, rhs_sig};
+ remainder = 24'(dividend % {40'b0, rhs_sig});
+ result_sig = 28'(quotient);
+ if (remainder != '0) begin
+ result_sig[0] = 1'b1;
+ end
+
+ return round_pack_s(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] fp_div_d(
+ input logic [63:0] lhs,
+ input logic [63:0] rhs,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic result_sign;
+ logic [10:0] lhs_exp;
+ logic [10:0] rhs_exp;
+ logic [51:0] lhs_frac;
+ logic [51:0] rhs_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic signed [12:0] lhs_unbiased_exp;
+ logic signed [12:0] rhs_unbiased_exp;
+ logic signed [12:0] result_unbiased_exp;
+ logic [52:0] lhs_sig;
+ logic [52:0] rhs_sig;
+ logic [127:0] dividend;
+ logic [127:0] quotient;
+ logic [52:0] remainder;
+ logic [56:0] result_sig;
+ int lhs_msb;
+ int rhs_msb;
+
+ flags = '0;
+ result_sign = lhs[63] ^ rhs[63];
+ lhs_exp = lhs[62:52];
+ rhs_exp = rhs[62:52];
+ lhs_frac = lhs[51:0];
+ rhs_frac = rhs[51:0];
+ lhs_is_zero = (lhs[62:0] == '0);
+ rhs_is_zero = (rhs[62:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+
+ if (is_nan_d(lhs) || is_nan_d(rhs)) begin
+ flags[4] = is_snan_d(lhs) || is_snan_d(rhs);
+ return CANONICAL_NAN_D;
+ end
+
+ if ((lhs_is_zero && rhs_is_zero) || (lhs_is_inf && rhs_is_inf)) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_D;
+ end
+
+ if (lhs_is_inf || rhs_is_zero) begin
+ if (rhs_is_zero && !lhs_is_zero && !lhs_is_inf) begin
+ flags[3] = 1'b1;
+ end
+ return {result_sign, 11'h7FF, 52'b0};
+ end
+
+ if (lhs_is_zero || rhs_is_inf) begin
+ return {result_sign, 63'b0};
+ end
+
+ if (lhs_exp == '0) begin
+ lhs_msb = highest_bit_53({1'b0, lhs_frac});
+ lhs_unbiased_exp = 13'(lhs_msb - 1074);
+ lhs_sig = {1'b0, lhs_frac} << (52 - lhs_msb);
+ end else begin
+ lhs_unbiased_exp = $signed({2'b0, lhs_exp}) - 13'sd1023;
+ lhs_sig = {1'b1, lhs_frac};
+ end
+
+ if (rhs_exp == '0) begin
+ rhs_msb = highest_bit_53({1'b0, rhs_frac});
+ rhs_unbiased_exp = 13'(rhs_msb - 1074);
+ rhs_sig = {1'b0, rhs_frac} << (52 - rhs_msb);
+ end else begin
+ rhs_unbiased_exp = $signed({2'b0, rhs_exp}) - 13'sd1023;
+ rhs_sig = {1'b1, rhs_frac};
+ end
+
+ if (lhs_sig >= rhs_sig) begin
+ result_unbiased_exp = lhs_unbiased_exp - rhs_unbiased_exp;
+ dividend = {75'b0, lhs_sig} << 55;
+ end else begin
+ result_unbiased_exp = lhs_unbiased_exp - rhs_unbiased_exp - 13'sd1;
+ dividend = {75'b0, lhs_sig} << 56;
+ end
+
+ quotient = dividend / {75'b0, rhs_sig};
+ remainder = 53'(dividend % {75'b0, rhs_sig});
+ result_sig = 57'(quotient);
+ if (remainder != '0) begin
+ result_sig[0] = 1'b1;
+ end
+
+ return round_pack_d(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [31:0] fp_sqrt_s(
+ input logic [31:0] value,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic sign;
+ logic [7:0] exp;
+ logic [22:0] frac;
+ logic signed [10:0] unbiased_exp;
+ logic signed [10:0] result_unbiased_exp;
+ logic [23:0] sig;
+ logic [63:0] radicand;
+ logic [63:0] trial;
+ logic [63:0] root;
+ logic [63:0] remainder;
+ logic [27:0] result_sig;
+ int sig_msb;
+
+ flags = '0;
+ sign = value[31];
+ exp = value[30:23];
+ frac = value[22:0];
+
+ if (is_nan_s(value)) begin
+ flags[4] = is_snan_s(value);
+ return CANONICAL_NAN_S;
+ end
+
+ if ((&exp) && (frac == '0)) begin
+ if (sign) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_S;
+ end
+ return value;
+ end
+
+ if (value[30:0] == '0) begin
+ return value;
+ end
+
+ if (sign) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_S;
+ end
+
+ if (exp == '0) begin
+ sig_msb = highest_bit_64({41'b0, frac});
+ unbiased_exp = 11'(sig_msb - 149);
+ sig = {1'b0, frac} << (23 - sig_msb);
+ end else begin
+ unbiased_exp = $signed({3'b0, exp}) - 11'sd127;
+ sig = {1'b1, frac};
+ end
+
+ if (unbiased_exp[0]) begin
+ radicand = {39'b0, sig, 1'b0} << 29;
+ result_unbiased_exp = (unbiased_exp - 11'sd1) >>> 1;
+ end else begin
+ radicand = {40'b0, sig} << 29;
+ result_unbiased_exp = unbiased_exp >>> 1;
+ end
+
+ root = '0;
+ for (int bit_idx = 26; bit_idx >= 0; bit_idx--) begin
+ trial = root | (64'd1 << bit_idx);
+ if ((trial * trial) <= radicand) begin
+ root = trial;
+ end
+ end
+
+ remainder = radicand - (root * root);
+ result_sig = 28'(root);
+ if (remainder != '0) begin
+ result_sig[0] = 1'b1;
+ end
+
+ return round_pack_s(1'b0, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] fp_sqrt_d(
+ input logic [63:0] value,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic sign;
+ logic [10:0] exp;
+ logic [51:0] frac;
+ logic signed [12:0] unbiased_exp;
+ logic signed [12:0] result_unbiased_exp;
+ logic [52:0] sig;
+ logic [127:0] radicand;
+ logic [127:0] trial;
+ logic [127:0] root;
+ logic [127:0] remainder;
+ logic [56:0] result_sig;
+ int sig_msb;
+
+ flags = '0;
+ sign = value[63];
+ exp = value[62:52];
+ frac = value[51:0];
+
+ if (is_nan_d(value)) begin
+ flags[4] = is_snan_d(value);
+ return CANONICAL_NAN_D;
+ end
+
+ if ((&exp) && (frac == '0)) begin
+ if (sign) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_D;
+ end
+ return value;
+ end
+
+ if (value[62:0] == '0) begin
+ return value;
+ end
+
+ if (sign) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_D;
+ end
+
+ if (exp == '0) begin
+ sig_msb = highest_bit_53({1'b0, frac});
+ unbiased_exp = 13'(sig_msb - 1074);
+ sig = {1'b0, frac} << (52 - sig_msb);
+ end else begin
+ unbiased_exp = $signed({2'b0, exp}) - 13'sd1023;
+ sig = {1'b1, frac};
+ end
+
+ if (unbiased_exp[0]) begin
+ radicand = {74'b0, sig, 1'b0} << 58;
+ result_unbiased_exp = (unbiased_exp - 13'sd1) >>> 1;
+ end else begin
+ radicand = {75'b0, sig} << 58;
+ result_unbiased_exp = unbiased_exp >>> 1;
+ end
+
+ root = '0;
+ for (int bit_idx = 55; bit_idx >= 0; bit_idx--) begin
+ trial = root | (128'd1 << bit_idx);
+ if ((trial * trial) <= radicand) begin
+ root = trial;
+ end
+ end
+
+ remainder = radicand - (root * root);
+ result_sig = 57'(root);
+ if (remainder != '0) begin
+ result_sig[0] = 1'b1;
+ end
+
+ return round_pack_d(1'b0, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [31:0] fp_fma_s(
+ input logic [31:0] mul_lhs,
+ input logic [31:0] mul_rhs,
+ input logic [31:0] addend,
+ input logic negate_product,
+ input logic negate_addend,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic lhs_sign;
+ logic rhs_sign;
+ logic addend_sign;
+ logic product_sign;
+ logic result_sign;
+ logic [7:0] lhs_exp;
+ logic [7:0] rhs_exp;
+ logic [7:0] addend_exp;
+ logic [22:0] lhs_frac;
+ logic [22:0] rhs_frac;
+ logic [22:0] addend_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic addend_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic addend_is_inf;
+ logic invalid_multiply;
+ logic signed [10:0] lhs_unbiased_exp;
+ logic signed [10:0] rhs_unbiased_exp;
+ logic signed [10:0] addend_unbiased_exp;
+ logic signed [10:0] product_unbiased_exp;
+ logic signed [10:0] common_unbiased_exp;
+ logic signed [10:0] result_unbiased_exp;
+ logic [23:0] lhs_sig;
+ logic [23:0] rhs_sig;
+ logic [23:0] addend_sig;
+ logic [47:0] product;
+ logic [47:0] normalized_product;
+ logic [64:0] product_ext;
+ logic [64:0] addend_ext;
+ logic [64:0] aligned_product;
+ logic [64:0] aligned_addend;
+ logic [64:0] result_ext;
+ logic [64:0] rounded_source;
+ logic [27:0] result_sig;
+ int lhs_msb;
+ int rhs_msb;
+ int addend_msb;
+ int exp_diff;
+
+ flags = '0;
+ lhs_sign = mul_lhs[31];
+ rhs_sign = mul_rhs[31];
+ addend_sign = addend[31] ^ negate_addend;
+ product_sign = lhs_sign ^ rhs_sign ^ negate_product;
+ lhs_exp = mul_lhs[30:23];
+ rhs_exp = mul_rhs[30:23];
+ addend_exp = addend[30:23];
+ lhs_frac = mul_lhs[22:0];
+ rhs_frac = mul_rhs[22:0];
+ addend_frac = addend[22:0];
+ lhs_is_zero = (mul_lhs[30:0] == '0);
+ rhs_is_zero = (mul_rhs[30:0] == '0);
+ addend_is_zero = (addend[30:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+ addend_is_inf = (&addend_exp) && (addend_frac == '0);
+ invalid_multiply = (lhs_is_inf && rhs_is_zero) || (rhs_is_inf && lhs_is_zero);
+
+ if (is_nan_s(mul_lhs) || is_nan_s(mul_rhs) || is_nan_s(addend) || invalid_multiply) begin
+ flags[4] = invalid_multiply || is_snan_s(mul_lhs) || is_snan_s(mul_rhs) || is_snan_s(addend);
+ return CANONICAL_NAN_S;
+ end
+
+ if (lhs_is_inf || rhs_is_inf) begin
+ if (addend_is_inf && (product_sign != addend_sign)) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_S;
+ end
+ return {product_sign, 8'hFF, 23'b0};
+ end
+
+ if (addend_is_inf) begin
+ return {addend_sign, 8'hFF, 23'b0};
+ end
+
+ if (lhs_is_zero || rhs_is_zero) begin
+ if (addend_is_zero) begin
+ result_sign = (product_sign == addend_sign) ? product_sign : (effective_rm(round_mode) == 3'b010);
+ return {result_sign, 31'b0};
+ end
+ return {addend_sign, addend[30:0]};
+ end
+
+ if (addend_is_zero) begin
+ return fp_mul_s({mul_lhs[31] ^ negate_product, mul_lhs[30:0]}, mul_rhs, round_mode, flags);
+ end
+
+ if (lhs_exp == '0) begin
+ lhs_msb = highest_bit_64({41'b0, lhs_frac});
+ lhs_unbiased_exp = 11'(lhs_msb - 149);
+ lhs_sig = {1'b0, lhs_frac} << (23 - lhs_msb);
+ end else begin
+ lhs_unbiased_exp = $signed({3'b0, lhs_exp}) - 11'sd127;
+ lhs_sig = {1'b1, lhs_frac};
+ end
+
+ if (rhs_exp == '0) begin
+ rhs_msb = highest_bit_64({41'b0, rhs_frac});
+ rhs_unbiased_exp = 11'(rhs_msb - 149);
+ rhs_sig = {1'b0, rhs_frac} << (23 - rhs_msb);
+ end else begin
+ rhs_unbiased_exp = $signed({3'b0, rhs_exp}) - 11'sd127;
+ rhs_sig = {1'b1, rhs_frac};
+ end
+
+ if (addend_exp == '0) begin
+ addend_msb = highest_bit_64({41'b0, addend_frac});
+ addend_unbiased_exp = 11'(addend_msb - 149);
+ addend_sig = {1'b0, addend_frac} << (23 - addend_msb);
+ end else begin
+ addend_unbiased_exp = $signed({3'b0, addend_exp}) - 11'sd127;
+ addend_sig = {1'b1, addend_frac};
+ end
+
+ product = lhs_sig * rhs_sig;
+ if (product[47]) begin
+ product_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp + 11'sd1;
+ normalized_product = product;
+ end else begin
+ product_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp;
+ normalized_product = product << 1;
+ end
+
+ product_ext = {4'b0, normalized_product, 13'b0};
+ addend_ext = {4'b0, addend_sig, 37'b0};
+
+ if (product_unbiased_exp >= addend_unbiased_exp) begin
+ common_unbiased_exp = product_unbiased_exp;
+ exp_diff = int'(product_unbiased_exp) - int'(addend_unbiased_exp);
+ aligned_product = product_ext;
+ aligned_addend = shift_right_jam_65(addend_ext, exp_diff);
+ end else begin
+ common_unbiased_exp = addend_unbiased_exp;
+ exp_diff = int'(addend_unbiased_exp) - int'(product_unbiased_exp);
+ aligned_product = shift_right_jam_65(product_ext, exp_diff);
+ aligned_addend = addend_ext;
+ end
+
+ result_unbiased_exp = common_unbiased_exp;
+ if (product_sign == addend_sign) begin
+ result_ext = aligned_product + aligned_addend;
+ result_sign = product_sign;
+ if (result_ext[61]) begin
+ result_ext = shift_right_jam_65(result_ext, 1);
+ result_unbiased_exp = result_unbiased_exp + 11'sd1;
+ end
+ end else if (aligned_product >= aligned_addend) begin
+ result_ext = aligned_product - aligned_addend;
+ result_sign = product_sign;
+ end else begin
+ result_ext = aligned_addend - aligned_product;
+ result_sign = addend_sign;
+ end
+
+ if (result_ext == '0) begin
+ result_sign = effective_rm(round_mode) == 3'b010;
+ return {result_sign, 31'b0};
+ end
+
+ while (!result_ext[60] && (result_unbiased_exp > -11'sd126)) begin
+ result_ext = result_ext << 1;
+ result_unbiased_exp = result_unbiased_exp - 11'sd1;
+ end
+
+ rounded_source = shift_right_jam_65(result_ext, 34);
+ result_sig = rounded_source[27:0];
+ return round_pack_s(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] fp_fma_d(
+ input logic [63:0] mul_lhs,
+ input logic [63:0] mul_rhs,
+ input logic [63:0] addend,
+ input logic negate_product,
+ input logic negate_addend,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic lhs_sign;
+ logic rhs_sign;
+ logic addend_sign;
+ logic product_sign;
+ logic result_sign;
+ logic [10:0] lhs_exp;
+ logic [10:0] rhs_exp;
+ logic [10:0] addend_exp;
+ logic [51:0] lhs_frac;
+ logic [51:0] rhs_frac;
+ logic [51:0] addend_frac;
+ logic lhs_is_zero;
+ logic rhs_is_zero;
+ logic addend_is_zero;
+ logic lhs_is_inf;
+ logic rhs_is_inf;
+ logic addend_is_inf;
+ logic invalid_multiply;
+ logic signed [12:0] lhs_unbiased_exp;
+ logic signed [12:0] rhs_unbiased_exp;
+ logic signed [12:0] addend_unbiased_exp;
+ logic signed [12:0] product_unbiased_exp;
+ logic signed [12:0] common_unbiased_exp;
+ logic signed [12:0] result_unbiased_exp;
+ logic [52:0] lhs_sig;
+ logic [52:0] rhs_sig;
+ logic [52:0] addend_sig;
+ logic [105:0] product;
+ logic [105:0] normalized_product;
+ logic [127:0] product_ext;
+ logic [127:0] addend_ext;
+ logic [127:0] aligned_product;
+ logic [127:0] aligned_addend;
+ logic [127:0] result_ext;
+ logic [127:0] rounded_source;
+ logic [56:0] result_sig;
+ int lhs_msb;
+ int rhs_msb;
+ int addend_msb;
+ int exp_diff;
+
+ flags = '0;
+ lhs_sign = mul_lhs[63];
+ rhs_sign = mul_rhs[63];
+ addend_sign = addend[63] ^ negate_addend;
+ product_sign = lhs_sign ^ rhs_sign ^ negate_product;
+ lhs_exp = mul_lhs[62:52];
+ rhs_exp = mul_rhs[62:52];
+ addend_exp = addend[62:52];
+ lhs_frac = mul_lhs[51:0];
+ rhs_frac = mul_rhs[51:0];
+ addend_frac = addend[51:0];
+ lhs_is_zero = (mul_lhs[62:0] == '0);
+ rhs_is_zero = (mul_rhs[62:0] == '0);
+ addend_is_zero = (addend[62:0] == '0);
+ lhs_is_inf = (&lhs_exp) && (lhs_frac == '0);
+ rhs_is_inf = (&rhs_exp) && (rhs_frac == '0);
+ addend_is_inf = (&addend_exp) && (addend_frac == '0);
+ invalid_multiply = (lhs_is_inf && rhs_is_zero) || (rhs_is_inf && lhs_is_zero);
+
+ if (is_nan_d(mul_lhs) || is_nan_d(mul_rhs) || is_nan_d(addend) || invalid_multiply) begin
+ flags[4] = invalid_multiply || is_snan_d(mul_lhs) || is_snan_d(mul_rhs) || is_snan_d(addend);
+ return CANONICAL_NAN_D;
+ end
+
+ if (lhs_is_inf || rhs_is_inf) begin
+ if (addend_is_inf && (product_sign != addend_sign)) begin
+ flags[4] = 1'b1;
+ return CANONICAL_NAN_D;
+ end
+ return {product_sign, 11'h7FF, 52'b0};
+ end
+
+ if (addend_is_inf) begin
+ return {addend_sign, 11'h7FF, 52'b0};
+ end
+
+ if (lhs_is_zero || rhs_is_zero) begin
+ if (addend_is_zero) begin
+ result_sign = (product_sign == addend_sign) ? product_sign : (effective_rm(round_mode) == 3'b010);
+ return {result_sign, 63'b0};
+ end
+ return {addend_sign, addend[62:0]};
+ end
+
+ if (addend_is_zero) begin
+ return fp_mul_d({mul_lhs[63] ^ negate_product, mul_lhs[62:0]}, mul_rhs, round_mode, flags);
+ end
+
+ if (lhs_exp == '0) begin
+ lhs_msb = highest_bit_53({1'b0, lhs_frac});
+ lhs_unbiased_exp = 13'(lhs_msb - 1074);
+ lhs_sig = {1'b0, lhs_frac} << (52 - lhs_msb);
+ end else begin
+ lhs_unbiased_exp = $signed({2'b0, lhs_exp}) - 13'sd1023;
+ lhs_sig = {1'b1, lhs_frac};
+ end
+
+ if (rhs_exp == '0) begin
+ rhs_msb = highest_bit_53({1'b0, rhs_frac});
+ rhs_unbiased_exp = 13'(rhs_msb - 1074);
+ rhs_sig = {1'b0, rhs_frac} << (52 - rhs_msb);
+ end else begin
+ rhs_unbiased_exp = $signed({2'b0, rhs_exp}) - 13'sd1023;
+ rhs_sig = {1'b1, rhs_frac};
+ end
+
+ if (addend_exp == '0) begin
+ addend_msb = highest_bit_53({1'b0, addend_frac});
+ addend_unbiased_exp = 13'(addend_msb - 1074);
+ addend_sig = {1'b0, addend_frac} << (52 - addend_msb);
+ end else begin
+ addend_unbiased_exp = $signed({2'b0, addend_exp}) - 13'sd1023;
+ addend_sig = {1'b1, addend_frac};
+ end
+
+ product = lhs_sig * rhs_sig;
+ if (product[105]) begin
+ product_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp + 13'sd1;
+ normalized_product = product;
+ end else begin
+ product_unbiased_exp = lhs_unbiased_exp + rhs_unbiased_exp;
+ normalized_product = product << 1;
+ end
+
+ product_ext = {9'b0, normalized_product, 13'b0};
+ addend_ext = {9'b0, addend_sig, 66'b0};
+
+ if (product_unbiased_exp >= addend_unbiased_exp) begin
+ common_unbiased_exp = product_unbiased_exp;
+ exp_diff = int'(product_unbiased_exp) - int'(addend_unbiased_exp);
+ aligned_product = product_ext;
+ aligned_addend = shift_right_jam_128(addend_ext, exp_diff);
+ end else begin
+ common_unbiased_exp = addend_unbiased_exp;
+ exp_diff = int'(addend_unbiased_exp) - int'(product_unbiased_exp);
+ aligned_product = shift_right_jam_128(product_ext, exp_diff);
+ aligned_addend = addend_ext;
+ end
+
+ result_unbiased_exp = common_unbiased_exp;
+ if (product_sign == addend_sign) begin
+ result_ext = aligned_product + aligned_addend;
+ result_sign = product_sign;
+ if (result_ext[119]) begin
+ result_ext = shift_right_jam_128(result_ext, 1);
+ result_unbiased_exp = result_unbiased_exp + 13'sd1;
+ end
+ end else if (aligned_product >= aligned_addend) begin
+ result_ext = aligned_product - aligned_addend;
+ result_sign = product_sign;
+ end else begin
+ result_ext = aligned_addend - aligned_product;
+ result_sign = addend_sign;
+ end
+
+ if (result_ext == '0) begin
+ result_sign = effective_rm(round_mode) == 3'b010;
+ return {result_sign, 63'b0};
+ end
+
+ while (!result_ext[118] && (result_unbiased_exp > -13'sd1022)) begin
+ result_ext = result_ext << 1;
+ result_unbiased_exp = result_unbiased_exp - 13'sd1;
+ end
+
+ rounded_source = shift_right_jam_128(result_ext, 63);
+ result_sig = rounded_source[56:0];
+ return round_pack_d(result_sign, result_unbiased_exp, result_sig, 1'b0, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] pack_uint_to_s(
+ input logic sign,
+ input logic [63:0] magnitude,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ int msb_index;
+ int shift;
+ logic [127:0] quotient;
+ logic [127:0] remainder;
+ logic [127:0] divisor;
+ logic [127:0] half;
+ logic [24:0] rounded_sig;
+ logic [7:0] exp_field;
+ logic [31:0] result_s;
+
+ flags = '0;
+ if (magnitude == '0) begin
+ return {32'hFFFF_FFFF, sign, 31'b0};
+ end
+
+ msb_index = highest_bit_64(magnitude);
+ exp_field = 8'(msb_index + 127);
+ if (msb_index <= 23) begin
+ rounded_sig = {1'b0, magnitude[23:0] << (23 - msb_index)};
+ end else begin
+ shift = msb_index - 23;
+ divisor = 128'd1 << shift;
+ quotient = {64'b0, magnitude} >> shift;
+ remainder = {64'b0, magnitude} & (divisor - 128'd1);
+ half = divisor >> 1;
+ rounded_sig = {1'b0, quotient[23:0]} +
+ (round_increment(round_mode, sign, quotient, remainder, half) ? 25'd1 : 25'd0);
+ if (remainder != '0) begin
+ flags[0] = 1'b1;
+ end
+ if (rounded_sig[24]) begin
+ rounded_sig = rounded_sig >> 1;
+ exp_field = exp_field + 8'd1;
+ end
+ end
+
+ result_s = {sign, exp_field, rounded_sig[22:0]};
+ return {32'hFFFF_FFFF, result_s};
+ endfunction
+
+ function automatic logic [63:0] pack_uint_to_d(
+ input logic sign,
+ input logic [63:0] magnitude,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ int msb_index;
+ int shift;
+ logic [127:0] quotient;
+ logic [127:0] remainder;
+ logic [127:0] divisor;
+ logic [127:0] half;
+ logic [53:0] rounded_sig;
+ logic [10:0] exp_field;
+
+ flags = '0;
+ if (magnitude == '0) begin
+ return {sign, 63'b0};
+ end
+
+ msb_index = highest_bit_64(magnitude);
+ exp_field = 11'(msb_index + 1023);
+ if (msb_index <= 52) begin
+ rounded_sig = {1'b0, magnitude[52:0] << (52 - msb_index)};
+ end else begin
+ shift = msb_index - 52;
+ divisor = 128'd1 << shift;
+ quotient = {64'b0, magnitude} >> shift;
+ remainder = {64'b0, magnitude} & (divisor - 128'd1);
+ half = divisor >> 1;
+ rounded_sig = {1'b0, quotient[52:0]} +
+ (round_increment(round_mode, sign, quotient, remainder, half) ? 54'd1 : 54'd0);
+ if (remainder != '0) begin
+ flags[0] = 1'b1;
+ end
+ if (rounded_sig[53]) begin
+ rounded_sig = rounded_sig >> 1;
+ exp_field = exp_field + 11'd1;
+ end
+ end
+
+ return {sign, exp_field, rounded_sig[51:0]};
+ endfunction
+
+ function automatic logic [63:0] cvt_int_to_fp(
+ input logic [63:0] int_value,
+ input logic [1:0] int_fmt,
+ input logic to_double,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic is_signed;
+ logic is_long;
+ logic sign;
+ logic [63:0] source_value;
+ logic [63:0] magnitude;
+
+ is_signed = (int_fmt == 2'd0) || (int_fmt == 2'd2);
+ is_long = (int_fmt == 2'd2) || (int_fmt == 2'd3);
+ source_value = is_long ? int_value :
+ (is_signed ? {{32{int_value[31]}}, int_value[31:0]} : {32'b0, int_value[31:0]});
+ sign = is_signed && source_value[63];
+ magnitude = sign ? ((~source_value) + 64'd1) : source_value;
+
+ if (to_double) begin
+ return pack_uint_to_d(sign, magnitude, round_mode, flags);
+ end
+
+ return pack_uint_to_s(sign, magnitude, round_mode, flags);
+ endfunction
+
+ function automatic logic [63:0] cvt_s_to_d(
+ input logic [31:0] value,
+ output logic [4:0] flags
+ );
+ logic sign;
+ logic [7:0] exp;
+ logic [22:0] frac;
+ int frac_msb;
+ logic [10:0] exp_d;
+ logic [51:0] frac_d;
+ logic [22:0] subnormal_tail_s;
+
+ flags = '0;
+ sign = value[31];
+ exp = value[30:23];
+ frac = value[22:0];
+
+ if (is_nan_s(value)) begin
+ flags[4] = is_snan_s(value);
+ return CANONICAL_NAN_D;
+ end
+ if (&exp) begin
+ return {sign, 11'h7FF, 52'b0};
+ end
+ if ((exp == '0) && (frac == '0)) begin
+ return {sign, 63'b0};
+ end
+ if (exp == '0) begin
+ frac_msb = -1;
+ for (int bit_idx = 0; bit_idx < 23; bit_idx++) begin
+ if (frac[bit_idx]) begin
+ frac_msb = bit_idx;
+ end
+ end
+ exp_d = 11'(frac_msb - 149 + 1023);
+ subnormal_tail_s = frac ^ (23'(1) << frac_msb);
+ frac_d = {29'b0, subnormal_tail_s} << (52 - frac_msb);
+ return {sign, exp_d, frac_d};
+ end
+
+ exp_d = 11'(int'(exp) - 127 + 1023);
+ frac_d = {frac, 29'b0};
+ return {sign, exp_d, frac_d};
+ endfunction
+
+ function automatic logic [63:0] cvt_d_to_s(
+ input logic [63:0] value,
+ input logic [2:0] round_mode,
+ output logic [4:0] flags
+ );
+ logic sign;
+ logic [10:0] exp;
+ logic [51:0] frac;
+ logic [52:0] significand;
+ logic signed [13:0] unbiased_exp;
+ int frac_msb;
+ int shift;
+ logic [127:0] quotient;
+ logic [127:0] remainder;
+ logic [127:0] divisor;
+ logic [127:0] half;
+ logic [24:0] rounded_sig;
+ logic [7:0] exp_s;
+ logic [31:0] result_s;
+
+ flags = '0;
+ sign = value[63];
+ exp = value[62:52];
+ frac = value[51:0];
+
+ if (is_nan_d(value)) begin
+ flags[4] = is_snan_d(value);
+ return {32'hFFFF_FFFF, CANONICAL_NAN_S};
+ end
+ if (&exp) begin
+ return {32'hFFFF_FFFF, sign, 8'hFF, 23'b0};
+ end
+ if ((exp == '0) && (frac == '0)) begin
+ return {32'hFFFF_FFFF, sign, 31'b0};
+ end
+
+ if (exp == '0) begin
+ frac_msb = highest_bit_53({1'b0, frac});
+ unbiased_exp = 14'(frac_msb - 1074);
+ significand = {1'b0, frac} << (52 - frac_msb);
+ end else begin
+ unbiased_exp = $signed({3'b0, exp}) - 14'sd1023;
+ significand = {1'b1, frac};
+ end
+
+ if (unbiased_exp > 14'sd127) begin
+ flags[2] = 1'b1;
+ flags[0] = 1'b1;
+ return {32'hFFFF_FFFF, overflow_s_result(sign, round_mode)};
+ end
+
+ if (unbiased_exp >= -14'sd126) begin
+ exp_s = 8'(unbiased_exp + 14'sd127);
+ shift = 29;
+ divisor = 128'd1 << shift;
+ quotient = {75'b0, significand} >> shift;
+ remainder = {75'b0, significand} & (divisor - 128'd1);
+ half = divisor >> 1;
+ rounded_sig = {1'b0, quotient[23:0]} +
+ (round_increment(round_mode, sign, quotient, remainder, half) ? 25'd1 : 25'd0);
+ if (remainder != '0) begin
+ flags[0] = 1'b1;
+ end
+ if (rounded_sig[24]) begin
+ rounded_sig = rounded_sig >> 1;
+ exp_s = exp_s + 8'd1;
+ if (exp_s == 8'hFF) begin
+ flags[2] = 1'b1;
+ flags[0] = 1'b1;
+ return {32'hFFFF_FFFF, overflow_s_result(sign, round_mode)};
+ end
+ end
+ result_s = {sign, exp_s, rounded_sig[22:0]};
+ return {32'hFFFF_FFFF, result_s};
+ end
+
+ shift = 29 + ((-126) - int'(unbiased_exp));
+ if (shift >= 128) begin
+ quotient = '0;
+ remainder = {75'b0, significand};
+ rounded_sig = '0;
+ if (remainder != '0) begin
+ flags[1] = 1'b1;
+ flags[0] = 1'b1;
+ unique case (effective_rm(round_mode))
+ 3'b010: rounded_sig = sign ? 25'd1 : 25'd0;
+ 3'b011: rounded_sig = !sign ? 25'd1 : 25'd0;
+ default: rounded_sig = '0;
+ endcase
+ end
+ end else begin
+ divisor = 128'd1 << shift;
+ quotient = {75'b0, significand} >> shift;
+ remainder = {75'b0, significand} & (divisor - 128'd1);
+ half = divisor >> 1;
+ rounded_sig = {1'b0, quotient[23:0]} +
+ (round_increment(round_mode, sign, quotient, remainder, half) ? 25'd1 : 25'd0);
+ if (remainder != '0) begin
+ flags[1] = 1'b1;
+ flags[0] = 1'b1;
+ end
+ end
+
+ if (rounded_sig[23]) begin
+ result_s = {sign, 8'd1, 23'b0};
+ end else begin
+ result_s = {sign, 8'b0, rounded_sig[22:0]};
+ end
+ return {32'hFFFF_FFFF, result_s};
+ endfunction
+
+ always_comb begin
+ logic invalid_s;
+ logic invalid_d;
+ logic cmp_value;
+ logic [31:0] result_s;
+ logic [63:0] result_d;
+ logic [63:0] result_int;
+ logic [4:0] result_flags;
+
+ fp_result = '0;
+ int_result = '0;
+ fflags = '0;
+ illegal_instr = 1'b0;
+ invalid_s = is_nan_s(s1) || is_nan_s(s2);
+ invalid_d = is_nan_d(d1) || is_nan_d(d2);
+ cmp_value = 1'b0;
+ result_s = '0;
+ result_d = '0;
+ result_int = '0;
+ result_flags = '0;
+
+ unique case (fp_op)
+ FP_OP_NONE: begin
+ fp_result = '0;
+ int_result = '0;
+ fflags = '0;
+ end
+
+ FP_OP_ADD, FP_OP_SUB: begin
+ if (fp_fmt == FP_FMT_S) begin
+ result_s = fp_addsub_s(s1, s2, fp_op == FP_OP_SUB, rm, result_flags);
+ fp_result = {32'hFFFF_FFFF, result_s};
+ fflags = result_flags;
+ end else begin
+ result_d = fp_addsub_d(d1, d2, fp_op == FP_OP_SUB, rm, result_flags);
+ fp_result = result_d;
+ fflags = result_flags;
+ end
+ end
+
+ FP_OP_MUL: begin
+ if (fp_fmt == FP_FMT_S) begin
+ result_s = fp_mul_s(s1, s2, rm, result_flags);
+ fp_result = {32'hFFFF_FFFF, result_s};
+ fflags = result_flags;
+ end else begin
+ result_d = fp_mul_d(d1, d2, rm, result_flags);
+ fp_result = result_d;
+ fflags = result_flags;
+ end
+ end
+
+ FP_OP_DIV: begin
+ if (fp_fmt == FP_FMT_S) begin
+ result_s = fp_div_s(s1, s2, rm, result_flags);
+ fp_result = {32'hFFFF_FFFF, result_s};
+ fflags = result_flags;
+ end else begin
+ result_d = fp_div_d(d1, d2, rm, result_flags);
+ fp_result = result_d;
+ fflags = result_flags;
+ end
+ end
+
+ FP_OP_SQRT: begin
+ if (fp_fmt == FP_FMT_S) begin
+ result_s = fp_sqrt_s(s1, rm, result_flags);
+ fp_result = {32'hFFFF_FFFF, result_s};
+ fflags = result_flags;
+ end else begin
+ result_d = fp_sqrt_d(d1, rm, result_flags);
+ fp_result = result_d;
+ fflags = result_flags;
+ end
+ end
+
+ FP_OP_MADD, FP_OP_MSUB, FP_OP_NMSUB, FP_OP_NMADD: begin
+ if (fp_fmt == FP_FMT_S) begin
+ unique case (fp_op)
+ FP_OP_MADD: result_s = fp_fma_s(s1, s2, s3, 1'b0, 1'b0, rm, result_flags);
+ FP_OP_MSUB: result_s = fp_fma_s(s1, s2, s3, 1'b0, 1'b1, rm, result_flags);
+ FP_OP_NMSUB: result_s = fp_fma_s(s1, s2, s3, 1'b1, 1'b0, rm, result_flags);
+ default: result_s = fp_fma_s(s1, s2, s3, 1'b1, 1'b1, rm, result_flags);
+ endcase
+ fp_result = {32'hFFFF_FFFF, result_s};
+ fflags = result_flags;
+ end else begin
+ unique case (fp_op)
+ FP_OP_MADD: result_d = fp_fma_d(d1, d2, d3, 1'b0, 1'b0, rm, result_flags);
+ FP_OP_MSUB: result_d = fp_fma_d(d1, d2, d3, 1'b0, 1'b1, rm, result_flags);
+ FP_OP_NMSUB: result_d = fp_fma_d(d1, d2, d3, 1'b1, 1'b0, rm, result_flags);
+ default: result_d = fp_fma_d(d1, d2, d3, 1'b1, 1'b1, rm, result_flags);
+ endcase
+ fp_result = result_d;
+ fflags = result_flags;
+ end
+ end
+
+ FP_OP_SGNJ, FP_OP_SGNJN, FP_OP_SGNJX: begin
+ if (fp_fmt == FP_FMT_S) begin
+ unique case (fp_op)
+ FP_OP_SGNJ: fp_result = {32'hFFFF_FFFF, s2[31], s1[30:0]};
+ FP_OP_SGNJN: fp_result = {32'hFFFF_FFFF, ~s2[31], s1[30:0]};
+ default: fp_result = {32'hFFFF_FFFF, s1[31] ^ s2[31], s1[30:0]};
+ endcase
+ end else begin
+ unique case (fp_op)
+ FP_OP_SGNJ: fp_result = {d2[63], d1[62:0]};
+ FP_OP_SGNJN: fp_result = {~d2[63], d1[62:0]};
+ default: fp_result = {d1[63] ^ d2[63], d1[62:0]};
+ endcase
+ end
+ end
+
+ FP_OP_MV_X2F: begin
+ fp_result = (fp_fmt == FP_FMT_S) ? {32'hFFFF_FFFF, int_rs1_data[31:0]} : int_rs1_data;
+ end
+
+ FP_OP_MV_F2X: begin
+ int_result = (fp_fmt == FP_FMT_S) ? {{32{s1_raw[31]}}, s1_raw} : d1;
+ end
+
+ FP_OP_CLASS: begin
+ int_result = (fp_fmt == FP_FMT_S) ? {54'b0, class_s(s1)} : {54'b0, class_d(d1)};
+ end
+
+ FP_OP_EQ, FP_OP_LT, FP_OP_LE: begin
+ if (fp_fmt == FP_FMT_S) begin
+ if (invalid_s) begin
+ fflags[4] = is_snan_s(s1) || is_snan_s(s2) || (fp_op != FP_OP_EQ);
+ cmp_value = 1'b0;
+ end else begin
+ cmp_value = (fp_op == FP_OP_EQ) ? equal_s(s1, s2) :
+ (fp_op == FP_OP_LT) ? less_s(s1, s2) :
+ (less_s(s1, s2) || equal_s(s1, s2));
+ end
+ end else begin
+ if (invalid_d) begin
+ fflags[4] = is_snan_d(d1) || is_snan_d(d2) || (fp_op != FP_OP_EQ);
+ cmp_value = 1'b0;
+ end else begin
+ cmp_value = (fp_op == FP_OP_EQ) ? equal_d(d1, d2) :
+ (fp_op == FP_OP_LT) ? less_d(d1, d2) :
+ (less_d(d1, d2) || equal_d(d1, d2));
+ end
+ end
+ int_result = {63'b0, cmp_value};
+ end
+
+ FP_OP_MIN, FP_OP_MAX: begin
+ if (fp_fmt == FP_FMT_S) begin
+ if (is_nan_s(s1) && is_nan_s(s2)) begin
+ fp_result = {32'hFFFF_FFFF, CANONICAL_NAN_S};
+ fflags[4] = is_snan_s(s1) || is_snan_s(s2);
+ end else if (is_nan_s(s1)) begin
+ fp_result = {32'hFFFF_FFFF, s2};
+ fflags[4] = is_snan_s(s1);
+ end else if (is_nan_s(s2)) begin
+ fp_result = {32'hFFFF_FFFF, s1};
+ fflags[4] = is_snan_s(s2);
+ end else if ((s1[30:0] == '0) && (s2[30:0] == '0)) begin
+ fp_result = {32'hFFFF_FFFF, (fp_op == FP_OP_MIN) ? {s1[31] | s2[31], 31'b0} :
+ {s1[31] & s2[31], 31'b0}};
+ end else if (fp_op == FP_OP_MIN) begin
+ fp_result = {32'hFFFF_FFFF, less_s(s1, s2) ? s1 : s2};
+ end else begin
+ fp_result = {32'hFFFF_FFFF, less_s(s1, s2) ? s2 : s1};
+ end
+ end else begin
+ if (is_nan_d(d1) && is_nan_d(d2)) begin
+ fp_result = CANONICAL_NAN_D;
+ fflags[4] = is_snan_d(d1) || is_snan_d(d2);
+ end else if (is_nan_d(d1)) begin
+ fp_result = d2;
+ fflags[4] = is_snan_d(d1);
+ end else if (is_nan_d(d2)) begin
+ fp_result = d1;
+ fflags[4] = is_snan_d(d2);
+ end else if ((d1[62:0] == '0) && (d2[62:0] == '0)) begin
+ fp_result = (fp_op == FP_OP_MIN) ? {d1[63] | d2[63], 63'b0} :
+ {d1[63] & d2[63], 63'b0};
+ end else if (fp_op == FP_OP_MIN) begin
+ fp_result = less_d(d1, d2) ? d1 : d2;
+ end else begin
+ fp_result = less_d(d1, d2) ? d2 : d1;
+ end
+ end
+ end
+
+ FP_OP_CVT_F2I: begin
+ if (fp_fmt == FP_FMT_S) begin
+ result_int = cvt_s_to_int(s1, rs2_addr[1:0], rm, result_flags);
+ end else begin
+ result_int = cvt_d_to_int(d1, rs2_addr[1:0], rm, result_flags);
+ end
+ int_result = result_int;
+ fflags = result_flags;
+ end
+
+ FP_OP_CVT_I2F: begin
+ result_d = cvt_int_to_fp(int_rs1_data, rs2_addr[1:0], fp_fmt == FP_FMT_D, rm, result_flags);
+ fp_result = result_d;
+ fflags = result_flags;
+ end
+
+ FP_OP_CVT_F2F: begin
+ if (fp_fmt == FP_FMT_D) begin
+ result_d = cvt_s_to_d(s1, result_flags);
+ fp_result = result_d;
+ if (is_nan_s(s1)) begin
+ result_flags[4] = is_snan_s(s1);
+ end
+ end else begin
+ result_d = cvt_d_to_s(d1, rm, result_flags);
+ fp_result = result_d;
+ if (is_nan_d(d1)) begin
+ result_flags[4] = is_snan_d(d1);
+ end
+ end
+ fflags = result_flags;
+ end
+
+ default: begin
+ fp_result = (fp_fmt == FP_FMT_S) ? {32'hFFFF_FFFF, CANONICAL_NAN_S} : CANONICAL_NAN_D;
+ int_result = '0;
+ fflags[4] = 1'b1;
+ end
+ endcase
+ end
+
+endmodule