blob: f275553e8e466eee5b6e2bf4b45e32a2703d0899 [file] [log] [blame]
// Copyright 2019 ETH Zurich and University of Bologna.
//
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
// Author: Stefan Mach <smach@iis.ee.ethz.ch>
`include "registers.svh"
module fpnew_cast_multi #(
parameter fpnew_pkg::fmt_logic_t FpFmtConfig = '1,
parameter fpnew_pkg::ifmt_logic_t IntFmtConfig = '1,
// FPU configuration
parameter int unsigned NumPipeRegs = 0,
parameter fpnew_pkg::pipe_config_t PipeConfig = fpnew_pkg::BEFORE,
parameter type TagType = logic,
parameter type AuxType = logic,
// Do not change
localparam int unsigned WIDTH = fpnew_pkg::maximum(fpnew_pkg::max_fp_width(FpFmtConfig),
fpnew_pkg::max_int_width(IntFmtConfig)),
localparam int unsigned NUM_FORMATS = fpnew_pkg::NUM_FP_FORMATS
) (
input logic clk_i,
input logic rst_ni,
// Input signals
input logic [WIDTH-1:0] operands_i, // 1 operand
input logic [NUM_FORMATS-1:0] is_boxed_i, // 1 operand
input fpnew_pkg::roundmode_e rnd_mode_i,
input fpnew_pkg::operation_e op_i,
input logic op_mod_i,
input fpnew_pkg::fp_format_e src_fmt_i,
input fpnew_pkg::fp_format_e dst_fmt_i,
input fpnew_pkg::int_format_e int_fmt_i,
input TagType tag_i,
input AuxType aux_i,
// Input Handshake
input logic in_valid_i,
output logic in_ready_o,
input logic flush_i,
// Output signals
output logic [WIDTH-1:0] result_o,
output fpnew_pkg::status_t status_o,
output logic extension_bit_o,
output TagType tag_o,
output AuxType aux_o,
// Output handshake
output logic out_valid_o,
input logic out_ready_i,
// Indication of valid data in flight
output logic busy_o
);
// ----------
// Constants
// ----------
localparam int unsigned NUM_INT_FORMATS = fpnew_pkg::NUM_INT_FORMATS;
localparam int unsigned MAX_INT_WIDTH = fpnew_pkg::max_int_width(IntFmtConfig);
localparam fpnew_pkg::fp_encoding_t SUPER_FORMAT = fpnew_pkg::super_format(FpFmtConfig);
localparam int unsigned SUPER_EXP_BITS = SUPER_FORMAT.exp_bits;
localparam int unsigned SUPER_MAN_BITS = SUPER_FORMAT.man_bits;
localparam int unsigned SUPER_BIAS = 2**(SUPER_EXP_BITS - 1) - 1;
// The internal mantissa includes normal bit or an entire integer
localparam int unsigned INT_MAN_WIDTH = fpnew_pkg::maximum(SUPER_MAN_BITS + 1, MAX_INT_WIDTH);
// If needed, there will be a LZC for renormalization
localparam int unsigned LZC_RESULT_WIDTH = $clog2(INT_MAN_WIDTH);
// The internal exponent must be able to represent the smallest denormal input value as signed
// or the number of bits in an integer
localparam int unsigned INT_EXP_WIDTH = fpnew_pkg::maximum($clog2(MAX_INT_WIDTH),
fpnew_pkg::maximum(SUPER_EXP_BITS, $clog2(SUPER_BIAS + SUPER_MAN_BITS))) + 1;
// Pipelines
localparam NUM_INP_REGS = PipeConfig == fpnew_pkg::BEFORE
? NumPipeRegs
: (PipeConfig == fpnew_pkg::DISTRIBUTED
? ((NumPipeRegs + 1) / 3) // Second to get distributed regs
: 0); // no regs here otherwise
localparam NUM_MID_REGS = PipeConfig == fpnew_pkg::INSIDE
? NumPipeRegs
: (PipeConfig == fpnew_pkg::DISTRIBUTED
? ((NumPipeRegs + 2) / 3) // First to get distributed regs
: 0); // no regs here otherwise
localparam NUM_OUT_REGS = PipeConfig == fpnew_pkg::AFTER
? NumPipeRegs
: (PipeConfig == fpnew_pkg::DISTRIBUTED
? (NumPipeRegs / 3) // Last to get distributed regs
: 0); // no regs here otherwise
// ---------------
// Input pipeline
// ---------------
// Selected pipeline output signals as non-arrays
logic [WIDTH-1:0] operands_q;
logic [NUM_FORMATS-1:0] is_boxed_q;
logic op_mod_q;
fpnew_pkg::fp_format_e src_fmt_q;
fpnew_pkg::fp_format_e dst_fmt_q;
fpnew_pkg::int_format_e int_fmt_q;
// Input pipeline signals, index i holds signal after i register stages
logic [0:NUM_INP_REGS][WIDTH-1:0] inp_pipe_operands_q;
logic [0:NUM_INP_REGS][NUM_FORMATS-1:0] inp_pipe_is_boxed_q;
fpnew_pkg::roundmode_e [0:NUM_INP_REGS] inp_pipe_rnd_mode_q;
fpnew_pkg::operation_e [0:NUM_INP_REGS] inp_pipe_op_q;
logic [0:NUM_INP_REGS] inp_pipe_op_mod_q;
fpnew_pkg::fp_format_e [0:NUM_INP_REGS] inp_pipe_src_fmt_q;
fpnew_pkg::fp_format_e [0:NUM_INP_REGS] inp_pipe_dst_fmt_q;
fpnew_pkg::int_format_e [0:NUM_INP_REGS] inp_pipe_int_fmt_q;
TagType [0:NUM_INP_REGS] inp_pipe_tag_q;
AuxType [0:NUM_INP_REGS] inp_pipe_aux_q;
logic [0:NUM_INP_REGS] inp_pipe_valid_q;
// Ready signal is combinatorial for all stages
logic [0:NUM_INP_REGS] inp_pipe_ready;
// Input stage: First element of pipeline is taken from inputs
assign inp_pipe_operands_q[0] = operands_i;
assign inp_pipe_is_boxed_q[0] = is_boxed_i;
assign inp_pipe_rnd_mode_q[0] = rnd_mode_i;
assign inp_pipe_op_q[0] = op_i;
assign inp_pipe_op_mod_q[0] = op_mod_i;
assign inp_pipe_src_fmt_q[0] = src_fmt_i;
assign inp_pipe_dst_fmt_q[0] = dst_fmt_i;
assign inp_pipe_int_fmt_q[0] = int_fmt_i;
assign inp_pipe_tag_q[0] = tag_i;
assign inp_pipe_aux_q[0] = aux_i;
assign inp_pipe_valid_q[0] = in_valid_i;
// Input stage: Propagate pipeline ready signal to updtream circuitry
assign in_ready_o = inp_pipe_ready[0];
// Generate the register stages
for (genvar i = 0; i < NUM_INP_REGS; i++) begin : gen_input_pipeline
// Internal register enable for this stage
logic reg_ena;
// Determine the ready signal of the current stage - advance the pipeline:
// 1. if the next stage is ready for our data
// 2. if the next stage only holds a bubble (not valid) -> we can pop it
assign inp_pipe_ready[i] = inp_pipe_ready[i+1] | ~inp_pipe_valid_q[i+1];
// Valid: enabled by ready signal, synchronous clear with the flush signal
`FFLARNC(inp_pipe_valid_q[i+1], inp_pipe_valid_q[i], inp_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni)
// Enable register if pipleine ready and a valid data item is present
assign reg_ena = inp_pipe_ready[i] & inp_pipe_valid_q[i];
// Generate the pipeline registers within the stages, use enable-registers
`FFL(inp_pipe_operands_q[i+1], inp_pipe_operands_q[i], reg_ena, '0)
`FFL(inp_pipe_is_boxed_q[i+1], inp_pipe_is_boxed_q[i], reg_ena, '0)
`FFL(inp_pipe_rnd_mode_q[i+1], inp_pipe_rnd_mode_q[i], reg_ena, fpnew_pkg::RNE)
`FFL(inp_pipe_op_q[i+1], inp_pipe_op_q[i], reg_ena, fpnew_pkg::FMADD)
`FFL(inp_pipe_op_mod_q[i+1], inp_pipe_op_mod_q[i], reg_ena, '0)
`FFL(inp_pipe_src_fmt_q[i+1], inp_pipe_src_fmt_q[i], reg_ena, fpnew_pkg::fp_format_e'(0))
`FFL(inp_pipe_dst_fmt_q[i+1], inp_pipe_dst_fmt_q[i], reg_ena, fpnew_pkg::fp_format_e'(0))
`FFL(inp_pipe_int_fmt_q[i+1], inp_pipe_int_fmt_q[i], reg_ena, fpnew_pkg::int_format_e'(0))
`FFL(inp_pipe_tag_q[i+1], inp_pipe_tag_q[i], reg_ena, TagType'('0))
`FFL(inp_pipe_aux_q[i+1], inp_pipe_aux_q[i], reg_ena, AuxType'('0))
end
// Output stage: assign selected pipe outputs to signals for later use
assign operands_q = inp_pipe_operands_q[NUM_INP_REGS];
assign is_boxed_q = inp_pipe_is_boxed_q[NUM_INP_REGS];
assign op_mod_q = inp_pipe_op_mod_q[NUM_INP_REGS];
assign src_fmt_q = inp_pipe_src_fmt_q[NUM_INP_REGS];
assign dst_fmt_q = inp_pipe_dst_fmt_q[NUM_INP_REGS];
assign int_fmt_q = inp_pipe_int_fmt_q[NUM_INP_REGS];
// -----------------
// Input processing
// -----------------
logic src_is_int, dst_is_int; // if 0, it's a float
assign src_is_int = (inp_pipe_op_q[NUM_INP_REGS] == fpnew_pkg::I2F);
assign dst_is_int = (inp_pipe_op_q[NUM_INP_REGS] == fpnew_pkg::F2I);
logic [INT_MAN_WIDTH-1:0] encoded_mant; // input mantissa with implicit bit
logic [NUM_FORMATS-1:0] fmt_sign;
logic signed [NUM_FORMATS-1:0][INT_EXP_WIDTH-1:0] fmt_exponent;
logic [NUM_FORMATS-1:0][INT_MAN_WIDTH-1:0] fmt_mantissa;
logic signed [NUM_FORMATS-1:0][INT_EXP_WIDTH-1:0] fmt_shift_compensation; // for LZC
fpnew_pkg::fp_info_t [NUM_FORMATS-1:0] info;
logic [NUM_INT_FORMATS-1:0][INT_MAN_WIDTH-1:0] ifmt_input_val;
logic int_sign;
logic [INT_MAN_WIDTH-1:0] int_value, int_mantissa;
// FP Input initialization
for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : fmt_init_inputs
// Set up some constants
localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(fpnew_pkg::fp_format_e'(fmt));
localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(fpnew_pkg::fp_format_e'(fmt));
localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(fpnew_pkg::fp_format_e'(fmt));
if (FpFmtConfig[fmt]) begin : active_format
// Classify input
fpnew_classifier #(
.FpFormat ( fpnew_pkg::fp_format_e'(fmt) ),
.NumOperands ( 1 )
) i_fpnew_classifier (
.operands_i ( operands_q[FP_WIDTH-1:0] ),
.is_boxed_i ( is_boxed_q[fmt] ),
.info_o ( info[fmt] )
);
assign fmt_sign[fmt] = operands_q[FP_WIDTH-1];
assign fmt_exponent[fmt] = signed'({1'b0, operands_q[MAN_BITS+:EXP_BITS]});
assign fmt_mantissa[fmt] = {info[fmt].is_normal, operands_q[MAN_BITS-1:0]}; // zero pad
// Compensation for the difference in mantissa widths used for leading-zero count
assign fmt_shift_compensation[fmt] = signed'(INT_MAN_WIDTH - 1 - MAN_BITS);
end else begin : inactive_format
assign info[fmt] = '{default: fpnew_pkg::DONT_CARE}; // format disabled
assign fmt_sign[fmt] = fpnew_pkg::DONT_CARE; // format disabled
assign fmt_exponent[fmt] = '{default: fpnew_pkg::DONT_CARE}; // format disabled
assign fmt_mantissa[fmt] = '{default: fpnew_pkg::DONT_CARE}; // format disabled
assign fmt_shift_compensation[fmt] = '{default: fpnew_pkg::DONT_CARE}; // format disabled
end
end
// Sign-extend INT input
for (genvar ifmt = 0; ifmt < int'(NUM_INT_FORMATS); ifmt++) begin : gen_sign_extend_int
// Set up some constants
localparam int unsigned INT_WIDTH = fpnew_pkg::int_width(fpnew_pkg::int_format_e'(ifmt));
if (IntFmtConfig[ifmt]) begin : active_format // only active formats
always_comb begin : sign_ext_input
// sign-extend value only if it's signed
ifmt_input_val[ifmt] = '{default: operands_q[INT_WIDTH-1] & ~op_mod_q};
ifmt_input_val[ifmt][INT_WIDTH-1:0] = operands_q[INT_WIDTH-1:0];
end
end else begin : inactive_format
assign ifmt_input_val[ifmt] = '{default: fpnew_pkg::DONT_CARE}; // format disabled
end
end
// Construct input mantissa from integer
assign int_value = ifmt_input_val[int_fmt_q];
assign int_sign = int_value[INT_MAN_WIDTH-1] & ~op_mod_q; // only signed ints are negative
assign int_mantissa = int_sign ? unsigned'(-int_value) : int_value; // get magnitude of negative
// select mantissa with source format
assign encoded_mant = src_is_int ? int_mantissa : fmt_mantissa[src_fmt_q];
// --------------
// Normalization
// --------------
logic signed [INT_EXP_WIDTH-1:0] src_bias; // src format bias
logic signed [INT_EXP_WIDTH-1:0] src_exp; // src format exponent (biased)
logic signed [INT_EXP_WIDTH-1:0] src_subnormal; // src is subnormal
logic signed [INT_EXP_WIDTH-1:0] src_offset; // src offset within mantissa
assign src_bias = signed'(fpnew_pkg::bias(src_fmt_q));
assign src_exp = fmt_exponent[src_fmt_q];
assign src_subnormal = signed'({1'b0, info[src_fmt_q].is_subnormal});
assign src_offset = fmt_shift_compensation[src_fmt_q];
logic input_sign; // input sign
logic signed [INT_EXP_WIDTH-1:0] input_exp; // unbiased true exponent
logic [INT_MAN_WIDTH-1:0] input_mant; // normalized input mantissa
logic mant_is_zero; // for integer zeroes
logic signed [INT_EXP_WIDTH-1:0] fp_input_exp;
logic signed [INT_EXP_WIDTH-1:0] int_input_exp;
// Input mantissa needs to be normalized
logic [LZC_RESULT_WIDTH-1:0] renorm_shamt; // renormalization shift amount
logic [LZC_RESULT_WIDTH:0] renorm_shamt_sgn; // signed form for calculations
// Leading-zero counter is needed for renormalization
lzc #(
.WIDTH ( INT_MAN_WIDTH ),
.MODE ( 1 ) // MODE = 1 counts leading zeroes
) i_lzc (
.in_i ( encoded_mant ),
.cnt_o ( renorm_shamt ),
.empty_o ( mant_is_zero )
);
assign renorm_shamt_sgn = signed'({1'b0, renorm_shamt});
// Get the sign from the proper source
assign input_sign = src_is_int ? int_sign : fmt_sign[src_fmt_q];
// Realign input mantissa, append zeroes if destination is wider
assign input_mant = encoded_mant << renorm_shamt;
// Unbias exponent and compensate for shift
assign fp_input_exp = signed'(src_exp + src_subnormal - src_bias -
renorm_shamt_sgn + src_offset); // compensate for shift
assign int_input_exp = signed'(INT_MAN_WIDTH - 1 - renorm_shamt_sgn);
assign input_exp = src_is_int ? int_input_exp : fp_input_exp;
logic signed [INT_EXP_WIDTH-1:0] destination_exp; // re-biased exponent for destination
// Rebias the exponent
assign destination_exp = input_exp + signed'(fpnew_pkg::bias(dst_fmt_q));
// ---------------
// Internal pipeline
// ---------------
// Pipeline output signals as non-arrays
logic input_sign_q;
logic signed [INT_EXP_WIDTH-1:0] input_exp_q;
logic [INT_MAN_WIDTH-1:0] input_mant_q;
logic signed [INT_EXP_WIDTH-1:0] destination_exp_q;
logic src_is_int_q;
logic dst_is_int_q;
fpnew_pkg::fp_info_t info_q;
logic mant_is_zero_q;
logic op_mod_q2;
fpnew_pkg::roundmode_e rnd_mode_q;
fpnew_pkg::fp_format_e src_fmt_q2;
fpnew_pkg::fp_format_e dst_fmt_q2;
fpnew_pkg::int_format_e int_fmt_q2;
// Internal pipeline signals, index i holds signal after i register stages
logic [0:NUM_MID_REGS] mid_pipe_input_sign_q;
logic signed [0:NUM_MID_REGS][INT_EXP_WIDTH-1:0] mid_pipe_input_exp_q;
logic [0:NUM_MID_REGS][INT_MAN_WIDTH-1:0] mid_pipe_input_mant_q;
logic signed [0:NUM_MID_REGS][INT_EXP_WIDTH-1:0] mid_pipe_dest_exp_q;
logic [0:NUM_MID_REGS] mid_pipe_src_is_int_q;
logic [0:NUM_MID_REGS] mid_pipe_dst_is_int_q;
fpnew_pkg::fp_info_t [0:NUM_MID_REGS] mid_pipe_info_q;
logic [0:NUM_MID_REGS] mid_pipe_mant_zero_q;
logic [0:NUM_MID_REGS] mid_pipe_op_mod_q;
fpnew_pkg::roundmode_e [0:NUM_MID_REGS] mid_pipe_rnd_mode_q;
fpnew_pkg::fp_format_e [0:NUM_MID_REGS] mid_pipe_src_fmt_q;
fpnew_pkg::fp_format_e [0:NUM_MID_REGS] mid_pipe_dst_fmt_q;
fpnew_pkg::int_format_e [0:NUM_MID_REGS] mid_pipe_int_fmt_q;
TagType [0:NUM_MID_REGS] mid_pipe_tag_q;
AuxType [0:NUM_MID_REGS] mid_pipe_aux_q;
logic [0:NUM_MID_REGS] mid_pipe_valid_q;
// Ready signal is combinatorial for all stages
logic [0:NUM_MID_REGS] mid_pipe_ready;
// Input stage: First element of pipeline is taken from upstream logic
assign mid_pipe_input_sign_q[0] = input_sign;
assign mid_pipe_input_exp_q[0] = input_exp;
assign mid_pipe_input_mant_q[0] = input_mant;
assign mid_pipe_dest_exp_q[0] = destination_exp;
assign mid_pipe_src_is_int_q[0] = src_is_int;
assign mid_pipe_dst_is_int_q[0] = dst_is_int;
assign mid_pipe_info_q[0] = info[src_fmt_q];
assign mid_pipe_mant_zero_q[0] = mant_is_zero;
assign mid_pipe_op_mod_q[0] = op_mod_q;
assign mid_pipe_rnd_mode_q[0] = inp_pipe_rnd_mode_q[NUM_INP_REGS];
assign mid_pipe_src_fmt_q[0] = src_fmt_q;
assign mid_pipe_dst_fmt_q[0] = dst_fmt_q;
assign mid_pipe_int_fmt_q[0] = int_fmt_q;
assign mid_pipe_tag_q[0] = inp_pipe_tag_q[NUM_INP_REGS];
assign mid_pipe_aux_q[0] = inp_pipe_aux_q[NUM_INP_REGS];
assign mid_pipe_valid_q[0] = inp_pipe_valid_q[NUM_INP_REGS];
// Input stage: Propagate pipeline ready signal to input pipe
assign inp_pipe_ready[NUM_INP_REGS] = mid_pipe_ready[0];
// Generate the register stages
for (genvar i = 0; i < NUM_MID_REGS; i++) begin : gen_inside_pipeline
// Internal register enable for this stage
logic reg_ena;
// Determine the ready signal of the current stage - advance the pipeline:
// 1. if the next stage is ready for our data
// 2. if the next stage only holds a bubble (not valid) -> we can pop it
assign mid_pipe_ready[i] = mid_pipe_ready[i+1] | ~mid_pipe_valid_q[i+1];
// Valid: enabled by ready signal, synchronous clear with the flush signal
`FFLARNC(mid_pipe_valid_q[i+1], mid_pipe_valid_q[i], mid_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni)
// Enable register if pipleine ready and a valid data item is present
assign reg_ena = mid_pipe_ready[i] & mid_pipe_valid_q[i];
// Generate the pipeline registers within the stages, use enable-registers
`FFL(mid_pipe_input_sign_q[i+1], mid_pipe_input_sign_q[i], reg_ena, '0)
`FFL(mid_pipe_input_exp_q[i+1], mid_pipe_input_exp_q[i], reg_ena, '0)
`FFL(mid_pipe_input_mant_q[i+1], mid_pipe_input_mant_q[i], reg_ena, '0)
`FFL(mid_pipe_dest_exp_q[i+1], mid_pipe_dest_exp_q[i], reg_ena, '0)
`FFL(mid_pipe_src_is_int_q[i+1], mid_pipe_src_is_int_q[i], reg_ena, '0)
`FFL(mid_pipe_dst_is_int_q[i+1], mid_pipe_dst_is_int_q[i], reg_ena, '0)
`FFL(mid_pipe_info_q[i+1], mid_pipe_info_q[i], reg_ena, '0)
`FFL(mid_pipe_mant_zero_q[i+1], mid_pipe_mant_zero_q[i], reg_ena, '0)
`FFL(mid_pipe_op_mod_q[i+1], mid_pipe_op_mod_q[i], reg_ena, '0)
`FFL(mid_pipe_rnd_mode_q[i+1], mid_pipe_rnd_mode_q[i], reg_ena, fpnew_pkg::RNE)
`FFL(mid_pipe_src_fmt_q[i+1], mid_pipe_src_fmt_q[i], reg_ena, fpnew_pkg::fp_format_e'(0))
`FFL(mid_pipe_dst_fmt_q[i+1], mid_pipe_dst_fmt_q[i], reg_ena, fpnew_pkg::fp_format_e'(0))
`FFL(mid_pipe_int_fmt_q[i+1], mid_pipe_int_fmt_q[i], reg_ena, fpnew_pkg::int_format_e'(0))
`FFL(mid_pipe_tag_q[i+1], mid_pipe_tag_q[i], reg_ena, TagType'('0))
`FFL(mid_pipe_aux_q[i+1], mid_pipe_aux_q[i], reg_ena, AuxType'('0))
end
// Output stage: assign selected pipe outputs to signals for later use
assign input_sign_q = mid_pipe_input_sign_q[NUM_MID_REGS];
assign input_exp_q = mid_pipe_input_exp_q[NUM_MID_REGS];
assign input_mant_q = mid_pipe_input_mant_q[NUM_MID_REGS];
assign destination_exp_q = mid_pipe_dest_exp_q[NUM_MID_REGS];
assign src_is_int_q = mid_pipe_src_is_int_q[NUM_MID_REGS];
assign dst_is_int_q = mid_pipe_dst_is_int_q[NUM_MID_REGS];
assign info_q = mid_pipe_info_q[NUM_MID_REGS];
assign mant_is_zero_q = mid_pipe_mant_zero_q[NUM_MID_REGS];
assign op_mod_q2 = mid_pipe_op_mod_q[NUM_MID_REGS];
assign rnd_mode_q = mid_pipe_rnd_mode_q[NUM_MID_REGS];
assign src_fmt_q2 = mid_pipe_src_fmt_q[NUM_MID_REGS];
assign dst_fmt_q2 = mid_pipe_dst_fmt_q[NUM_MID_REGS];
assign int_fmt_q2 = mid_pipe_int_fmt_q[NUM_MID_REGS];
// --------
// Casting
// --------
logic [INT_EXP_WIDTH-1:0] final_exp; // after eventual adjustments
logic [2*INT_MAN_WIDTH:0] preshift_mant; // mantissa before final shift
logic [2*INT_MAN_WIDTH:0] destination_mant; // mantissa from shifter, with rnd bit
logic [SUPER_MAN_BITS-1:0] final_mant; // mantissa after adjustments
logic [MAX_INT_WIDTH-1:0] final_int; // integer shifted in position
logic [$clog2(INT_MAN_WIDTH+1)-1:0] denorm_shamt; // shift amount for denormalization
logic [1:0] fp_round_sticky_bits, int_round_sticky_bits, round_sticky_bits;
logic of_before_round, uf_before_round;
// Perform adjustments to mantissa and exponent
always_comb begin : cast_value
// Default assignment
final_exp = unsigned'(destination_exp_q); // take exponent as is, only look at lower bits
preshift_mant = '0; // initialize mantissa container with zeroes
denorm_shamt = SUPER_MAN_BITS - fpnew_pkg::man_bits(dst_fmt_q2); // right of mantissa
of_before_round = 1'b0;
uf_before_round = 1'b0;
// Place mantissa to the left of the shifter
preshift_mant = input_mant_q << (INT_MAN_WIDTH + 1);
// Handle INT casts
if (dst_is_int_q) begin
// By default right shift mantissa to be an integer
denorm_shamt = unsigned'(MAX_INT_WIDTH - 1 - input_exp_q);
// overflow: when converting to unsigned the range is larger by one
if (input_exp_q >= signed'(fpnew_pkg::int_width(int_fmt_q2) - 1 + op_mod_q2)) begin
denorm_shamt = '0; // prevent shifting
of_before_round = 1'b1;
// underflow
end else if (input_exp_q < -1) begin
denorm_shamt = MAX_INT_WIDTH + 1; // all bits go to the sticky
uf_before_round = 1'b1;
end
// Handle FP over-/underflows
end else begin
// Overflow or infinities (for proper rounding)
if ((destination_exp_q >= signed'(2**fpnew_pkg::exp_bits(dst_fmt_q2))-1) ||
(~src_is_int_q && info_q.is_inf)) begin
final_exp = unsigned'(2**fpnew_pkg::exp_bits(dst_fmt_q2)-2); // largest normal value
preshift_mant = '1; // largest normal value and RS bits set
of_before_round = 1'b1;
// Denormalize underflowing values
end else if (destination_exp_q < 1 &&
destination_exp_q >= -signed'(fpnew_pkg::man_bits(dst_fmt_q2))) begin
final_exp = '0; // denormal result
denorm_shamt = unsigned'(denorm_shamt + 1 - destination_exp_q); // adjust right shifting
uf_before_round = 1'b1;
// Limit the shift to retain sticky bits
end else if (destination_exp_q < -signed'(fpnew_pkg::man_bits(dst_fmt_q2))) begin
final_exp = '0; // denormal result
denorm_shamt = unsigned'(denorm_shamt + 2 + fpnew_pkg::man_bits(dst_fmt_q2)); // to sticky
uf_before_round = 1'b1;
end
end
end
localparam NUM_FP_STICKY = 2 * INT_MAN_WIDTH - SUPER_MAN_BITS - 1; // removed mantissa, 1. and R
localparam NUM_INT_STICKY = 2 * INT_MAN_WIDTH - MAX_INT_WIDTH; // removed int and R
// Mantissa adjustment shift
assign destination_mant = preshift_mant >> denorm_shamt;
// Extract final mantissa and round bit, discard the normal bit (for FP)
assign {final_mant, fp_round_sticky_bits[1]} =
destination_mant[2*INT_MAN_WIDTH-1-:SUPER_MAN_BITS+1];
assign {final_int, int_round_sticky_bits[1]} = destination_mant[2*INT_MAN_WIDTH-:MAX_INT_WIDTH+1];
// Collapse sticky bits
assign fp_round_sticky_bits[0] = (| {destination_mant[NUM_FP_STICKY-1:0]});
assign int_round_sticky_bits[0] = (| {destination_mant[NUM_INT_STICKY-1:0]});
// select RS bits for destination operation
assign round_sticky_bits = dst_is_int_q ? int_round_sticky_bits : fp_round_sticky_bits;
// ----------------------------
// Rounding and classification
// ----------------------------
logic [WIDTH-1:0] pre_round_abs; // absolute value of result before rnd
logic of_after_round; // overflow
logic uf_after_round; // underflow
logic [NUM_FORMATS-1:0][WIDTH-1:0] fmt_pre_round_abs; // per format
logic [NUM_FORMATS-1:0] fmt_of_after_round;
logic [NUM_FORMATS-1:0] fmt_uf_after_round;
logic [NUM_INT_FORMATS-1:0][WIDTH-1:0] ifmt_pre_round_abs; // per format
logic rounded_sign;
logic [WIDTH-1:0] rounded_abs; // absolute value of result after rounding
logic result_true_zero;
logic [WIDTH-1:0] rounded_int_res; // after possible inversion
logic rounded_int_res_zero; // after rounding
// Pack exponent and mantissa into proper rounding form
for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : gen_res_assemble
// Set up some constants
localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(fpnew_pkg::fp_format_e'(fmt));
localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(fpnew_pkg::fp_format_e'(fmt));
if (FpFmtConfig[fmt]) begin : active_format
always_comb begin : assemble_result
fmt_pre_round_abs[fmt] = {final_exp[EXP_BITS-1:0], final_mant[MAN_BITS-1:0]}; // 0-extend
end
end else begin : inactive_format
assign fmt_pre_round_abs[fmt] = '{default: fpnew_pkg::DONT_CARE};
end
end
// Sign-extend integer result
for (genvar ifmt = 0; ifmt < int'(NUM_INT_FORMATS); ifmt++) begin : gen_int_res_sign_ext
// Set up some constants
localparam int unsigned INT_WIDTH = fpnew_pkg::int_width(fpnew_pkg::int_format_e'(ifmt));
if (IntFmtConfig[ifmt]) begin : active_format
always_comb begin : assemble_result
// sign-extend reusult
ifmt_pre_round_abs[ifmt] = '{default: final_int[INT_WIDTH-1]};
ifmt_pre_round_abs[ifmt][INT_WIDTH-1:0] = final_int[INT_WIDTH-1:0];
end
end else begin : inactive_format
assign ifmt_pre_round_abs[ifmt] = '{default: fpnew_pkg::DONT_CARE};
end
end
// Select output with destination format and operation
assign pre_round_abs = dst_is_int_q ? ifmt_pre_round_abs[int_fmt_q2] : fmt_pre_round_abs[dst_fmt_q2];
fpnew_rounding #(
.AbsWidth ( WIDTH )
) i_fpnew_rounding (
.abs_value_i ( pre_round_abs ),
.sign_i ( input_sign_q ), // source format
.round_sticky_bits_i ( round_sticky_bits ),
.rnd_mode_i ( rnd_mode_q ),
.effective_subtraction_i ( 1'b0 ), // no operation happened
.abs_rounded_o ( rounded_abs ),
.sign_o ( rounded_sign ),
.exact_zero_o ( result_true_zero )
);
logic [NUM_FORMATS-1:0][WIDTH-1:0] fmt_result;
// Detect overflows and inject sign
for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : gen_sign_inject
// Set up some constants
localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(fpnew_pkg::fp_format_e'(fmt));
localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(fpnew_pkg::fp_format_e'(fmt));
localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(fpnew_pkg::fp_format_e'(fmt));
if (FpFmtConfig[fmt]) begin : active_format
always_comb begin : post_process
// detect of / uf
fmt_uf_after_round[fmt] = rounded_abs[EXP_BITS+MAN_BITS-1:MAN_BITS] == '0; // denormal
fmt_of_after_round[fmt] = rounded_abs[EXP_BITS+MAN_BITS-1:MAN_BITS] == '1; // inf exp.
// Assemble regular result, nan box short ones. Int zeroes need to be detected`
fmt_result[fmt] = '1;
fmt_result[fmt][FP_WIDTH-1:0] = src_is_int_q & mant_is_zero_q
? '0
: {rounded_sign, rounded_abs[EXP_BITS+MAN_BITS-1:0]};
end
end else begin : inactive_format
assign fmt_uf_after_round[fmt] = fpnew_pkg::DONT_CARE;
assign fmt_of_after_round[fmt] = fpnew_pkg::DONT_CARE;
assign fmt_result[fmt] = '{default: fpnew_pkg::DONT_CARE};
end
end
// Classification after rounding select by destination format
assign uf_after_round = fmt_uf_after_round[dst_fmt_q2];
assign of_after_round = fmt_of_after_round[dst_fmt_q2];
// Negative integer result needs to be brought into two's complement
assign rounded_int_res = rounded_sign ? unsigned'(-rounded_abs) : rounded_abs;
assign rounded_int_res_zero = (rounded_int_res == '0);
// -------------------------
// FP Special case handling
// -------------------------
logic [WIDTH-1:0] fp_special_result;
fpnew_pkg::status_t fp_special_status;
logic fp_result_is_special;
logic [NUM_FORMATS-1:0][WIDTH-1:0] fmt_special_result;
// Special result construction
for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : gen_special_results
// Set up some constants
localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(fpnew_pkg::fp_format_e'(fmt));
localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(fpnew_pkg::fp_format_e'(fmt));
localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(fpnew_pkg::fp_format_e'(fmt));
localparam logic [EXP_BITS-1:0] QNAN_EXPONENT = '1;
localparam logic [MAN_BITS-1:0] QNAN_MANTISSA = 2**(MAN_BITS-1);
if (FpFmtConfig[fmt]) begin : active_format
always_comb begin : special_results
logic [FP_WIDTH-1:0] special_res;
special_res = info_q.is_zero
? input_sign_q << FP_WIDTH-1 // signed zero
: {1'b0, QNAN_EXPONENT, QNAN_MANTISSA}; // qNaN
// Initialize special result with ones (NaN-box)
fmt_special_result[fmt] = '1;
fmt_special_result[fmt][FP_WIDTH-1:0] = special_res;
end
end else begin : inactive_format
assign fmt_special_result[fmt] = '{default: fpnew_pkg::DONT_CARE};
end
end
// Detect special case from source format, I2F casts don't produce a special result
assign fp_result_is_special = ~src_is_int_q & (info_q.is_zero |
info_q.is_nan |
~info_q.is_boxed);
// Signalling input NaNs raise invalid flag, otherwise no flags set
assign fp_special_status = '{NV: info_q.is_signalling, default: 1'b0};
// Assemble result according to destination format
assign fp_special_result = fmt_special_result[dst_fmt_q2]; // destination format
// --------------------------
// INT Special case handling
// --------------------------
logic [WIDTH-1:0] int_special_result;
fpnew_pkg::status_t int_special_status;
logic int_result_is_special;
logic [NUM_INT_FORMATS-1:0][WIDTH-1:0] ifmt_special_result;
// Special result construction
for (genvar ifmt = 0; ifmt < int'(NUM_INT_FORMATS); ifmt++) begin : gen_special_results_int
// Set up some constants
localparam int unsigned INT_WIDTH = fpnew_pkg::int_width(fpnew_pkg::int_format_e'(ifmt));
if (IntFmtConfig[ifmt]) begin : active_format
always_comb begin : special_results
automatic logic [INT_WIDTH-1:0] special_res;
// Default is overflow to positive max, which is 2**INT_WIDTH-1 or 2**(INT_WIDTH-1)-1
special_res[INT_WIDTH-2:0] = '1; // alone yields 2**(INT_WIDTH-1)-1
special_res[INT_WIDTH-1] = op_mod_q2; // for unsigned casts yields 2**INT_WIDTH-1
// Negative special case (except for nans) tie to -max or 0
if (input_sign_q && !info_q.is_nan)
special_res = ~special_res;
// Initialize special result with sign-extension
ifmt_special_result[ifmt] = '{default: special_res[INT_WIDTH-1]};
ifmt_special_result[ifmt][INT_WIDTH-1:0] = special_res;
end
end else begin : inactive_format
assign ifmt_special_result[ifmt] = '{default: fpnew_pkg::DONT_CARE};
end
end
// Detect special case from source format (inf, nan, overflow, nan-boxing or negative unsigned)
assign int_result_is_special = info_q.is_nan | info_q.is_inf |
of_before_round | ~info_q.is_boxed |
(input_sign_q & op_mod_q2 & ~rounded_int_res_zero);
// All integer special cases are invalid
assign int_special_status = '{NV: 1'b1, default: 1'b0};
// Assemble result according to destination format
assign int_special_result = ifmt_special_result[int_fmt_q2]; // destination format
// -----------------
// Result selection
// -----------------
fpnew_pkg::status_t int_regular_status, fp_regular_status;
logic [WIDTH-1:0] fp_result, int_result;
fpnew_pkg::status_t fp_status, int_status;
assign fp_regular_status.NV = src_is_int_q & (of_before_round | of_after_round); // overflow is invalid for I2F casts
assign fp_regular_status.DZ = 1'b0; // no divisions
assign fp_regular_status.OF = ~src_is_int_q & (~info_q.is_inf & (of_before_round | of_after_round)); // inf casts no OF
assign fp_regular_status.UF = uf_after_round & fp_regular_status.NX;
assign fp_regular_status.NX = src_is_int_q ? (| fp_round_sticky_bits) // overflow is invalid in i2f
: (| fp_round_sticky_bits) | (~info_q.is_inf & (of_before_round | of_after_round));
assign int_regular_status = '{NX: (| int_round_sticky_bits), default: 1'b0};
assign fp_result = fp_result_is_special ? fp_special_result : fmt_result[dst_fmt_q2];
assign fp_status = fp_result_is_special ? fp_special_status : fp_regular_status;
assign int_result = int_result_is_special ? int_special_result : rounded_int_res;
assign int_status = int_result_is_special ? int_special_status : int_regular_status;
// Final results for output pipeline
logic [WIDTH-1:0] result_d;
fpnew_pkg::status_t status_d;
logic extension_bit;
// Select output depending on special case detection
assign result_d = dst_is_int_q ? int_result : fp_result;
assign status_d = dst_is_int_q ? int_status : fp_status;
// MSB of int result decides extension, otherwise NaN box
assign extension_bit = dst_is_int_q ? int_result[WIDTH-1] : 1'b1;
// ----------------
// Output Pipeline
// ----------------
// Output pipeline signals, index i holds signal after i register stages
logic [0:NUM_OUT_REGS][WIDTH-1:0] out_pipe_result_q;
fpnew_pkg::status_t [0:NUM_OUT_REGS] out_pipe_status_q;
logic [0:NUM_OUT_REGS] out_pipe_ext_bit_q;
TagType [0:NUM_OUT_REGS] out_pipe_tag_q;
AuxType [0:NUM_OUT_REGS] out_pipe_aux_q;
logic [0:NUM_OUT_REGS] out_pipe_valid_q;
// Ready signal is combinatorial for all stages
logic [0:NUM_OUT_REGS] out_pipe_ready;
// Input stage: First element of pipeline is taken from inputs
assign out_pipe_result_q[0] = result_d;
assign out_pipe_status_q[0] = status_d;
assign out_pipe_ext_bit_q[0] = extension_bit;
assign out_pipe_tag_q[0] = mid_pipe_tag_q[NUM_MID_REGS];
assign out_pipe_aux_q[0] = mid_pipe_aux_q[NUM_MID_REGS];
assign out_pipe_valid_q[0] = mid_pipe_valid_q[NUM_MID_REGS];
// Input stage: Propagate pipeline ready signal to inside pipe
assign mid_pipe_ready[NUM_MID_REGS] = out_pipe_ready[0];
// Generate the register stages
for (genvar i = 0; i < NUM_OUT_REGS; i++) begin : gen_output_pipeline
// Internal register enable for this stage
logic reg_ena;
// Determine the ready signal of the current stage - advance the pipeline:
// 1. if the next stage is ready for our data
// 2. if the next stage only holds a bubble (not valid) -> we can pop it
assign out_pipe_ready[i] = out_pipe_ready[i+1] | ~out_pipe_valid_q[i+1];
// Valid: enabled by ready signal, synchronous clear with the flush signal
`FFLARNC(out_pipe_valid_q[i+1], out_pipe_valid_q[i], out_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni)
// Enable register if pipleine ready and a valid data item is present
assign reg_ena = out_pipe_ready[i] & out_pipe_valid_q[i];
// Generate the pipeline registers within the stages, use enable-registers
`FFL(out_pipe_result_q[i+1], out_pipe_result_q[i], reg_ena, '0)
`FFL(out_pipe_status_q[i+1], out_pipe_status_q[i], reg_ena, '0)
`FFL(out_pipe_ext_bit_q[i+1], out_pipe_ext_bit_q[i], reg_ena, '0)
`FFL(out_pipe_tag_q[i+1], out_pipe_tag_q[i], reg_ena, TagType'('0))
`FFL(out_pipe_aux_q[i+1], out_pipe_aux_q[i], reg_ena, AuxType'('0))
end
// Output stage: Ready travels backwards from output side, driven by downstream circuitry
assign out_pipe_ready[NUM_OUT_REGS] = out_ready_i;
// Output stage: assign module outputs
assign result_o = out_pipe_result_q[NUM_OUT_REGS];
assign status_o = out_pipe_status_q[NUM_OUT_REGS];
assign extension_bit_o = out_pipe_ext_bit_q[NUM_OUT_REGS];
assign tag_o = out_pipe_tag_q[NUM_OUT_REGS];
assign aux_o = out_pipe_aux_q[NUM_OUT_REGS];
assign out_valid_o = out_pipe_valid_q[NUM_OUT_REGS];
assign busy_o = (| {inp_pipe_valid_q, mid_pipe_valid_q, out_pipe_valid_q});
endmodule