blob: acddd4881bd045a285bb92a4de3e96ed75739255 [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>
module fpnew_noncomp #(
parameter fpnew_pkg::fp_format_e FpFormat = fpnew_pkg::fp_format_e'(0),
parameter int unsigned NumPipeRegs = 0,
parameter fpnew_pkg::pipe_config_t PipeConfig = fpnew_pkg::BEFORE,
parameter type TagType = logic,
parameter type AuxType = logic,
localparam int unsigned WIDTH = fpnew_pkg::fp_width(FpFormat) // do not change
) (
input logic clk_i,
input logic rst_ni,
// Input signals
input logic [1:0][WIDTH-1:0] operands_i, // 2 operands
input logic [1:0] is_boxed_i, // 2 operands
input fpnew_pkg::roundmode_e rnd_mode_i,
input fpnew_pkg::operation_e op_i,
input logic op_mod_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 fpnew_pkg::classmask_e class_mask_o,
output logic is_class_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 EXP_BITS = fpnew_pkg::exp_bits(FpFormat);
localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(FpFormat);
// Pipelines
localparam NUM_INP_REGS = (PipeConfig == fpnew_pkg::BEFORE || PipeConfig == fpnew_pkg::INSIDE)
? NumPipeRegs
: (PipeConfig == fpnew_pkg::DISTRIBUTED
? ((NumPipeRegs + 1) / 2) // First to get distributed regs
: 0); // no regs here otherwise
localparam NUM_OUT_REGS = PipeConfig == fpnew_pkg::AFTER
? NumPipeRegs
: (PipeConfig == fpnew_pkg::DISTRIBUTED
? (NumPipeRegs / 2) // Last to get distributed regs
: 0); // no regs here otherwise
// ----------------
// Type definition
// ----------------
typedef struct packed {
logic sign;
logic [EXP_BITS-1:0] exponent;
logic [MAN_BITS-1:0] mantissa;
} fp_t;
// ---------------
// Input pipeline
// ---------------
// Input pipeline signals, index i holds signal after i register stages
logic [0:NUM_INP_REGS][1:0][WIDTH-1:0] inp_pipe_operands_q;
logic [0:NUM_INP_REGS][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;
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_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_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
// ---------------------
// Input classification
// ---------------------
fpnew_pkg::fp_info_t [1:0] info_q;
// Classify input
fpnew_classifier #(
.FpFormat ( FpFormat ),
.NumOperands ( 2 )
) i_class_a (
.operands_i ( inp_pipe_operands_q[NUM_INP_REGS] ),
.is_boxed_i ( inp_pipe_is_boxed_q[NUM_INP_REGS] ),
.info_o ( info_q )
);
fp_t operand_a, operand_b;
fpnew_pkg::fp_info_t info_a, info_b;
// Packing-order-agnostic assignments
assign operand_a = inp_pipe_operands_q[NUM_INP_REGS][0];
assign operand_b = inp_pipe_operands_q[NUM_INP_REGS][1];
assign info_a = info_q[0];
assign info_b = info_q[1];
logic any_operand_inf;
logic any_operand_nan;
logic signalling_nan;
// Reduction for special case handling
assign any_operand_inf = (| {info_a.is_inf, info_b.is_inf});
assign any_operand_nan = (| {info_a.is_nan, info_b.is_nan});
assign signalling_nan = (| {info_a.is_signalling, info_b.is_signalling});
logic operands_equal, operand_a_smaller;
// Equality checks for zeroes too
assign operands_equal = (operand_a == operand_b) || (info_a.is_zero && info_b.is_zero);
// Invert result if non-zero signs involved (unsigned comparison)
assign operand_a_smaller = (operand_a < operand_b) ^ (operand_a.sign || operand_b.sign);
// ---------------
// Sign Injection
// ---------------
fp_t sgnj_result;
fpnew_pkg::status_t sgnj_status;
logic sgnj_extension_bit;
// Sign Injection - operation is encoded in rnd_mode_q:
// RNE = SGNJ, RTZ = SGNJN, RDN = SGNJX, RUP = Passthrough (no NaN-box check)
always_comb begin : sign_injections
logic sign_a, sign_b; // internal signs
// Default assignment
sgnj_result = operand_a; // result based on operand a
// NaN-boxing check will treat invalid inputs as canonical NaNs
if (!info_a.is_boxed) sgnj_result = '{sign: 1'b0, exponent: '1, mantissa: 2**(MAN_BITS-1)};
// Internal signs are treated as positive in case of non-NaN-boxed values
sign_a = operand_a.sign & info_a.is_boxed;
sign_b = operand_b.sign & info_b.is_boxed;
// Do the sign injection based on rm field
unique case (inp_pipe_rnd_mode_q[NUM_INP_REGS])
fpnew_pkg::RNE: sgnj_result.sign = sign_b; // SGNJ
fpnew_pkg::RTZ: sgnj_result.sign = ~sign_b; // SGNJN
fpnew_pkg::RDN: sgnj_result.sign = sign_a ^ sign_b; // SGNJX
fpnew_pkg::RUP: sgnj_result = operand_a; // passthrough
default: sgnj_result = '{default: fpnew_pkg::DONT_CARE}; // don't care
endcase
end
assign sgnj_status = '0; // sign injections never raise exceptions
// op_mod_q enables integer sign-extension of result (for storing to integer regfile)
assign sgnj_extension_bit = inp_pipe_op_mod_q[NUM_INP_REGS] ? sgnj_result.sign : 1'b1;
// ------------------
// Minimum / Maximum
// ------------------
fp_t minmax_result;
fpnew_pkg::status_t minmax_status;
logic minmax_extension_bit;
// Minimum/Maximum - operation is encoded in rnd_mode_q:
// RNE = MIN, RTZ = MAX
always_comb begin : min_max
// Default assignment
minmax_status = '0;
// Min/Max use quiet comparisons - only sNaN are invalid
minmax_status.NV = signalling_nan;
// Both NaN inputs cause a NaN output
if (info_a.is_nan && info_b.is_nan)
minmax_result = '{sign: 1'b0, exponent: '1, mantissa: 2**(MAN_BITS-1)}; // canonical qNaN
// If one operand is NaN, the non-NaN operand is returned
else if (info_a.is_nan) minmax_result = operand_b;
else if (info_b.is_nan) minmax_result = operand_a;
// Otherwise decide according to the operation
else begin
unique case (inp_pipe_rnd_mode_q[NUM_INP_REGS])
fpnew_pkg::RNE: minmax_result = operand_a_smaller ? operand_a : operand_b; // MIN
fpnew_pkg::RTZ: minmax_result = operand_a_smaller ? operand_b : operand_a; // MAX
default: minmax_result = '{default: fpnew_pkg::DONT_CARE}; // don't care
endcase
end
end
assign minmax_extension_bit = 1'b1; // NaN-box as result is always a float value
// ------------
// Comparisons
// ------------
fp_t cmp_result;
fpnew_pkg::status_t cmp_status;
logic cmp_extension_bit;
// Comparisons - operation is encoded in rnd_mode_q:
// RNE = LE, RTZ = LT, RDN = EQ
// op_mod_q inverts boolean outputs
always_comb begin : comparisons
// Default assignment
cmp_result = '0; // false
cmp_status = '0; // no flags
// Signalling NaNs always compare as false and are illegal
if (signalling_nan) cmp_status.NV = 1'b1; // invalid operation
// Otherwise do comparisons
else begin
unique case (inp_pipe_rnd_mode_q[NUM_INP_REGS])
fpnew_pkg::RNE: begin // Less than or equal
if (any_operand_nan) cmp_status.NV = 1'b1; // Signalling comparison: NaNs are invalid
else cmp_result = (operand_a_smaller | operands_equal) ^ inp_pipe_op_mod_q[NUM_INP_REGS];
end
fpnew_pkg::RTZ: begin // Less than
if (any_operand_nan) cmp_status.NV = 1'b1; // Signalling comparison: NaNs are invalid
else cmp_result = (operand_a_smaller & ~operands_equal) ^ inp_pipe_op_mod_q[NUM_INP_REGS];
end
fpnew_pkg::RDN: begin // Equal
if (any_operand_nan) cmp_result = inp_pipe_op_mod_q[NUM_INP_REGS]; // NaN always not equal
else cmp_result = operands_equal ^ inp_pipe_op_mod_q[NUM_INP_REGS];
end
default: cmp_result = '{default: fpnew_pkg::DONT_CARE}; // don't care
endcase
end
end
assign cmp_extension_bit = 1'b0; // Comparisons always produce booleans in integer registers
// ---------------
// Classification
// ---------------
fpnew_pkg::status_t class_status;
logic class_extension_bit;
fpnew_pkg::classmask_e class_mask_d; // the result is actually here
// Classification - always return the classification mask on the dedicated port
always_comb begin : classify
if (info_a.is_normal) begin
class_mask_d = operand_a.sign ? fpnew_pkg::NEGNORM : fpnew_pkg::POSNORM;
end else if (info_a.is_subnormal) begin
class_mask_d = operand_a.sign ? fpnew_pkg::NEGSUBNORM : fpnew_pkg::POSSUBNORM;
end else if (info_a.is_zero) begin
class_mask_d = operand_a.sign ? fpnew_pkg::NEGZERO : fpnew_pkg::POSZERO;
end else if (info_a.is_inf) begin
class_mask_d = operand_a.sign ? fpnew_pkg::NEGINF : fpnew_pkg::POSINF;
end else if (info_a.is_nan) begin
class_mask_d = info_a.is_signalling ? fpnew_pkg::SNAN : fpnew_pkg::QNAN;
end else begin
class_mask_d = fpnew_pkg::QNAN; // default value
end
end
assign class_status = '0; // classification does not set flags
assign class_extension_bit = 1'b0; // classification always produces results in integer registers
// -----------------
// Result selection
// -----------------
fp_t result_d;
fpnew_pkg::status_t status_d;
logic extension_bit_d;
logic is_class_d;
// Select result
always_comb begin : select_result
unique case (inp_pipe_op_q[NUM_INP_REGS])
fpnew_pkg::SGNJ: begin
result_d = sgnj_result;
status_d = sgnj_status;
extension_bit_d = sgnj_extension_bit;
end
fpnew_pkg::MINMAX: begin
result_d = minmax_result;
status_d = minmax_status;
extension_bit_d = minmax_extension_bit;
end
fpnew_pkg::CMP: begin
result_d = cmp_result;
status_d = cmp_status;
extension_bit_d = cmp_extension_bit;
end
fpnew_pkg::CLASSIFY: begin
result_d = '{default: fpnew_pkg::DONT_CARE}; // unused
status_d = class_status;
extension_bit_d = class_extension_bit;
end
default: begin
result_d = '{default: fpnew_pkg::DONT_CARE}; // dont care
status_d = '{default: fpnew_pkg::DONT_CARE}; // dont care
extension_bit_d = fpnew_pkg::DONT_CARE; // dont care
end
endcase
end
assign is_class_d = (inp_pipe_op_q[NUM_INP_REGS] == fpnew_pkg::CLASSIFY);
// ----------------
// Output Pipeline
// ----------------
// Output pipeline signals, index i holds signal after i register stages
fp_t [0:NUM_OUT_REGS] out_pipe_result_q;
fpnew_pkg::status_t [0:NUM_OUT_REGS] out_pipe_status_q;
logic [0:NUM_OUT_REGS] out_pipe_extension_bit_q;
fpnew_pkg::classmask_e [0:NUM_OUT_REGS] out_pipe_class_mask_q;
logic [0:NUM_OUT_REGS] out_pipe_is_class_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_extension_bit_q[0] = extension_bit_d;
assign out_pipe_class_mask_q[0] = class_mask_d;
assign out_pipe_is_class_q[0] = is_class_d;
assign out_pipe_tag_q[0] = inp_pipe_tag_q[NUM_INP_REGS];
assign out_pipe_aux_q[0] = inp_pipe_aux_q[NUM_INP_REGS];
assign out_pipe_valid_q[0] = inp_pipe_valid_q[NUM_INP_REGS];
// Input stage: Propagate pipeline ready signal to inside pipe
assign inp_pipe_ready[NUM_INP_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_extension_bit_q[i+1], out_pipe_extension_bit_q[i], reg_ena, '0)
`FFL(out_pipe_class_mask_q[i+1], out_pipe_class_mask_q[i], reg_ena, fpnew_pkg::QNAN)
`FFL(out_pipe_is_class_q[i+1], out_pipe_is_class_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_extension_bit_q[NUM_OUT_REGS];
assign class_mask_o = out_pipe_class_mask_q[NUM_OUT_REGS];
assign is_class_o = out_pipe_is_class_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, out_pipe_valid_q});
endmodule