blob: fda2a57f3889c8699bf217b60f4a64790393f229 [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_opgroup_fmt_slice #(
parameter fpnew_pkg::opgroup_e OpGroup = fpnew_pkg::ADDMUL,
parameter fpnew_pkg::fp_format_e FpFormat = fpnew_pkg::fp_format_e'(0),
// FPU configuration
parameter int unsigned Width = 32,
parameter logic EnableVectors = 1'b1,
parameter int unsigned NumPipeRegs = 0,
parameter fpnew_pkg::pipe_config_t PipeConfig = fpnew_pkg::BEFORE,
parameter type TagType = logic,
// Do not change
localparam int unsigned NUM_OPERANDS = fpnew_pkg::num_operands(OpGroup)
) (
input logic clk_i,
input logic rst_ni,
// Input signals
input logic [NUM_OPERANDS-1:0][Width-1:0] operands_i,
input logic [NUM_OPERANDS-1:0] is_boxed_i,
input fpnew_pkg::roundmode_e rnd_mode_i,
input fpnew_pkg::operation_e op_i,
input logic op_mod_i,
input logic vectorial_op_i,
input TagType tag_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 handshake
output logic out_valid_o,
input logic out_ready_i,
// Indication of valid data in flight
output logic busy_o
);
localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(FpFormat);
localparam int unsigned NUM_LANES = fpnew_pkg::num_lanes(Width, FpFormat, EnableVectors);
logic [NUM_LANES-1:0] lane_in_ready, lane_out_valid; // Handshake signals for the lanes
logic vectorial_op;
logic [NUM_LANES*FP_WIDTH-1:0] slice_result;
logic [Width-1:0] slice_regular_result, slice_class_result, slice_vec_class_result;
fpnew_pkg::status_t [NUM_LANES-1:0] lane_status;
logic [NUM_LANES-1:0] lane_ext_bit; // only the first one is actually used
fpnew_pkg::classmask_e [NUM_LANES-1:0] lane_class_mask;
TagType [NUM_LANES-1:0] lane_tags; // only the first one is actually used
logic [NUM_LANES-1:0] lane_vectorial, lane_busy, lane_is_class; // dito
logic result_is_vector, result_is_class;
// -----------
// Input Side
// -----------
assign in_ready_o = lane_in_ready[0]; // Upstream ready is given by first lane
assign vectorial_op = vectorial_op_i & EnableVectors; // only do vectorial stuff if enabled
// ---------------
// Generate Lanes
// ---------------
for (genvar lane = 0; lane < int'(NUM_LANES); lane++) begin : gen_num_lanes
logic [FP_WIDTH-1:0] local_result; // lane-local results
logic local_sign;
// Generate instances only if needed, lane 0 always generated
if ((lane == 0) || EnableVectors) begin : active_lane
logic in_valid, out_valid, out_ready; // lane-local handshake
logic [NUM_OPERANDS-1:0][FP_WIDTH-1:0] local_operands; // lane-local operands
logic [FP_WIDTH-1:0] op_result; // lane-local results
fpnew_pkg::status_t op_status;
assign in_valid = in_valid_i & ((lane == 0) | vectorial_op); // upper lanes only for vectors
// Slice out the operands for this lane
always_comb begin : prepare_input
for (int i = 0; i < int'(NUM_OPERANDS); i++) begin
local_operands[i] = operands_i[i][(unsigned'(lane)+1)*FP_WIDTH-1:unsigned'(lane)*FP_WIDTH];
end
end
// Instantiate the operation from the selected opgroup
if (OpGroup == fpnew_pkg::ADDMUL) begin : lane_instance
fpnew_fma #(
.FpFormat ( FpFormat ),
.NumPipeRegs ( NumPipeRegs ),
.PipeConfig ( PipeConfig ),
.TagType ( TagType ),
.AuxType ( logic )
) i_fma (
.clk_i,
.rst_ni,
.operands_i ( local_operands ),
.is_boxed_i ( is_boxed_i[NUM_OPERANDS-1:0] ),
.rnd_mode_i,
.op_i,
.op_mod_i,
.tag_i,
.aux_i ( vectorial_op ), // Remember whether operation was vectorial
.in_valid_i ( in_valid ),
.in_ready_o ( lane_in_ready[lane] ),
.flush_i,
.result_o ( op_result ),
.status_o ( op_status ),
.extension_bit_o ( lane_ext_bit[lane] ),
.tag_o ( lane_tags[lane] ),
.aux_o ( lane_vectorial[lane] ),
.out_valid_o ( out_valid ),
.out_ready_i ( out_ready ),
.busy_o ( lane_busy[lane] )
);
assign lane_is_class[lane] = 1'b0;
assign lane_class_mask[lane] = fpnew_pkg::NEGINF;
end else if (OpGroup == fpnew_pkg::DIVSQRT) begin : lane_instance
// fpnew_divsqrt #(
// .FpFormat (FpFormat),
// .NumPipeRegs(NumPipeRegs),
// .PipeConfig (PipeConfig),
// .TagType (TagType),
// .AuxType (logic)
// ) i_divsqrt (
// .clk_i,
// .rst_ni,
// .operands_i ( local_operands ),
// .is_boxed_i ( is_boxed_i[NUM_OPERANDS-1:0] ),
// .rnd_mode_i,
// .op_i,
// .op_mod_i,
// .tag_i,
// .aux_i ( vectorial_op ), // Remember whether operation was vectorial
// .in_valid_i ( in_valid ),
// .in_ready_o ( lane_in_ready[lane] ),
// .flush_i,
// .result_o ( op_result ),
// .status_o ( op_status ),
// .extension_bit_o ( lane_ext_bit[lane] ),
// .tag_o ( lane_tags[lane] ),
// .aux_o ( lane_vectorial[lane] ),
// .out_valid_o ( out_valid ),
// .out_ready_i ( out_ready ),
// .busy_o ( lane_busy[lane] )
// );
// assign lane_is_class[lane] = 1'b0;
end else if (OpGroup == fpnew_pkg::NONCOMP) begin : lane_instance
fpnew_noncomp #(
.FpFormat (FpFormat),
.NumPipeRegs(NumPipeRegs),
.PipeConfig (PipeConfig),
.TagType (TagType),
.AuxType (logic)
) i_noncomp (
.clk_i,
.rst_ni,
.operands_i ( local_operands ),
.is_boxed_i ( is_boxed_i[NUM_OPERANDS-1:0] ),
.rnd_mode_i,
.op_i,
.op_mod_i,
.tag_i,
.aux_i ( vectorial_op ), // Remember whether operation was vectorial
.in_valid_i ( in_valid ),
.in_ready_o ( lane_in_ready[lane] ),
.flush_i,
.result_o ( op_result ),
.status_o ( op_status ),
.extension_bit_o ( lane_ext_bit[lane] ),
.class_mask_o ( lane_class_mask[lane] ),
.is_class_o ( lane_is_class[lane] ),
.tag_o ( lane_tags[lane] ),
.aux_o ( lane_vectorial[lane] ),
.out_valid_o ( out_valid ),
.out_ready_i ( out_ready ),
.busy_o ( lane_busy[lane] )
);
end // ADD OTHER OPTIONS HERE
// Handshakes are only done if the lane is actually used
assign out_ready = out_ready_i & ((lane == 0) | result_is_vector);
assign lane_out_valid[lane] = out_valid & ((lane == 0) | result_is_vector);
// Properly NaN-box or sign-extend the slice result if not in use
assign local_result = lane_out_valid[lane] ? op_result : '{default: lane_ext_bit[0]};
assign lane_status[lane] = lane_out_valid[lane] ? op_status : '0;
// Otherwise generate constant sign-extension
end else begin
assign lane_out_valid[lane] = 1'b0; // unused lane
assign lane_in_ready[lane] = 1'b0; // unused lane
assign local_result = '{default: lane_ext_bit[0]}; // sign-extend/nan box
assign lane_status[lane] = '0;
assign lane_busy[lane] = 1'b0;
assign lane_is_class[lane] = 1'b0;
end
// Insert lane result into slice result
assign slice_result[(unsigned'(lane)+1)*FP_WIDTH-1:unsigned'(lane)*FP_WIDTH] = local_result;
// Create Classification results
if ((lane+1)*8 <= Width) begin : vectorial_class // vectorial class blocks are 8bits in size
assign local_sign = (lane_class_mask[lane] == fpnew_pkg::NEGINF ||
lane_class_mask[lane] == fpnew_pkg::NEGNORM ||
lane_class_mask[lane] == fpnew_pkg::NEGSUBNORM ||
lane_class_mask[lane] == fpnew_pkg::NEGZERO);
// Write the current block segment
assign slice_vec_class_result[(lane+1)*8-1:lane*8] = {
local_sign, // BIT 7
~local_sign, // BIT 6
lane_class_mask[lane] == fpnew_pkg::QNAN, // BIT 5
lane_class_mask[lane] == fpnew_pkg::SNAN, // BIT 4
lane_class_mask[lane] == fpnew_pkg::POSZERO
|| lane_class_mask[lane] == fpnew_pkg::NEGZERO, // BIT 3
lane_class_mask[lane] == fpnew_pkg::POSSUBNORM
|| lane_class_mask[lane] == fpnew_pkg::NEGSUBNORM, // BIT 2
lane_class_mask[lane] == fpnew_pkg::POSNORM
|| lane_class_mask[lane] == fpnew_pkg::NEGNORM, // BIT 1
lane_class_mask[lane] == fpnew_pkg::POSINF
|| lane_class_mask[lane] == fpnew_pkg::NEGINF // BIT 0
};
end
end
// ------------
// Output Side
// ------------
assign result_is_vector = lane_vectorial[0];
assign result_is_class = lane_is_class[0];
assign slice_regular_result = $signed({extension_bit_o, slice_result});
localparam int unsigned CLASS_VEC_BITS = (NUM_LANES*8 > Width) ? 8 * (Width / 8) : NUM_LANES*8;
// Pad out unused vec_class bits
if (CLASS_VEC_BITS < Width) begin : pad_vectorial_class
assign slice_vec_class_result[Width-1:CLASS_VEC_BITS] = '0;
end
// localparam logic [Width-1:0] CLASS_VEC_MASK = 2**CLASS_VEC_BITS - 1;
assign slice_class_result = result_is_vector ? slice_vec_class_result : lane_class_mask[0];
// Select the proper result
assign result_o = result_is_class ? slice_class_result : slice_regular_result;
assign extension_bit_o = lane_ext_bit[0]; // upper lanes unused
assign tag_o = lane_tags[0]; // upper lanes unused
assign busy_o = (| lane_busy);
assign out_valid_o = lane_out_valid[0]; // upper lanes unused
// Collapse the lane status
always_comb begin : output_processing
// Collapse the status
automatic fpnew_pkg::status_t temp_status;
temp_status = '0;
for (int i = 0; i < int'(NUM_LANES); i++)
temp_status |= lane_status[i];
status_o = temp_status;
end
endmodule