blob: ac541a3ab2c534c24c72556a6a8037297ce1e5e2 [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_divsqrt_multi #(
parameter fpnew_pkg::fmt_logic_t FpFmtConfig = '1,
// FPU configuration
parameter int unsigned NumPipeRegs = 0,
parameter fpnew_pkg::pipe_config_t PipeConfig = fpnew_pkg::AFTER,
parameter type TagType = logic,
parameter type AuxType = logic,
// Do not change
localparam int unsigned WIDTH = fpnew_pkg::max_fp_width(FpFmtConfig),
localparam int unsigned NUM_FORMATS = fpnew_pkg::NUM_FP_FORMATS
) (
input logic clk_i,
input logic rst_ni,
// Input signals
input logic [1:0][WIDTH-1:0] operands_i, // 2 operands
input logic [NUM_FORMATS-1:0][1:0] is_boxed_i, // 2 operands
input fpnew_pkg::roundmode_e rnd_mode_i,
input fpnew_pkg::operation_e op_i,
input fpnew_pkg::fp_format_e dst_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
// ----------
// Pipelines
localparam NUM_INP_REGS = (PipeConfig == fpnew_pkg::BEFORE)
? NumPipeRegs
: (PipeConfig == fpnew_pkg::DISTRIBUTED
? (NumPipeRegs / 2) // Last to get distributed regs
: 0); // no regs here otherwise
localparam NUM_OUT_REGS = (PipeConfig == fpnew_pkg::AFTER || PipeConfig == fpnew_pkg::INSIDE)
? NumPipeRegs
: (PipeConfig == fpnew_pkg::DISTRIBUTED
? ((NumPipeRegs + 1) / 2) // First to get distributed regs
: 0); // no regs here otherwise
// ---------------
// Input pipeline
// ---------------
// Selected pipeline output signals as non-arrays
logic [1:0][WIDTH-1:0] operands_q;
fpnew_pkg::roundmode_e rnd_mode_q;
fpnew_pkg::operation_e op_q;
fpnew_pkg::fp_format_e dst_fmt_q;
logic in_valid_q;
// 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;
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;
fpnew_pkg::fp_format_e [0:NUM_INP_REGS] inp_pipe_dst_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_rnd_mode_q[0] = rnd_mode_i;
assign inp_pipe_op_q[0] = op_i;
assign inp_pipe_dst_fmt_q[0] = dst_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_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_dst_fmt_q[i+1], inp_pipe_dst_fmt_q[i], reg_ena, fpnew_pkg::fp_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 rnd_mode_q = inp_pipe_rnd_mode_q[NUM_INP_REGS];
assign op_q = inp_pipe_op_q[NUM_INP_REGS];
assign dst_fmt_q = inp_pipe_dst_fmt_q[NUM_INP_REGS];
assign in_valid_q = inp_pipe_valid_q[NUM_INP_REGS];
// -----------------
// Input processing
// -----------------
logic [1:0] divsqrt_fmt;
logic [1:0][63:0] divsqrt_operands; // those are fixed to 64bit
logic input_is_fp8;
// Translate fpnew formats into divsqrt formats
always_comb begin : translate_fmt
unique case (dst_fmt_q)
fpnew_pkg::FP32: divsqrt_fmt = 2'b00;
fpnew_pkg::FP64: divsqrt_fmt = 2'b01;
fpnew_pkg::FP16: divsqrt_fmt = 2'b10;
fpnew_pkg::FP16ALT: divsqrt_fmt = 2'b11;
default: divsqrt_fmt = 2'b10; // maps also FP8 to FP16
endcase
// Only if FP8 is enabled
input_is_fp8 = FpFmtConfig[fpnew_pkg::FP8] & (dst_fmt_q == fpnew_pkg::FP8);
// If FP8 is supported, map it to an FP16 value
divsqrt_operands[0] = input_is_fp8 ? operands_q[0] << 8 : operands_q[0];
divsqrt_operands[1] = input_is_fp8 ? operands_q[1] << 8 : operands_q[1];
end
// ------------
// Control FSM
// ------------
logic in_ready; // input handshake with upstream
logic div_valid, sqrt_valid; // input signalling with unit
logic unit_ready, unit_done; // status signals from unit instance
logic op_starting; // high in the cycle a new operation starts
logic out_valid, out_ready; // output handshake with downstream
logic hold_result; // whether to put result into hold register
logic data_is_held; // data in hold register is valid
logic unit_busy; // valid data in flight
// FSM states
typedef enum logic [1:0] {IDLE, BUSY, HOLD} fsm_state_e;
fsm_state_e state_q, state_d;
// Upstream ready comes from sanitization FSM
assign inp_pipe_ready[NUM_INP_REGS] = in_ready;
// Valids are gated by the FSM ready. Invalid input ops run a sqrt to not lose illegal instr.
assign div_valid = in_valid_q & (op_q == fpnew_pkg::DIV) & in_ready & ~flush_i;
assign sqrt_valid = in_valid_q & (op_q != fpnew_pkg::DIV) & in_ready & ~flush_i;
assign op_starting = div_valid | sqrt_valid;
// FSM to safely apply and receive data from DIVSQRT unit
always_comb begin : flag_fsm
// Default assignments
in_ready = 1'b0;
out_valid = 1'b0;
hold_result = 1'b0;
data_is_held = 1'b0;
unit_busy = 1'b0;
state_d = state_q;
unique case (state_q)
// Waiting for work
IDLE: begin
in_ready = 1'b1; // we're ready
if (in_valid_q && unit_ready) begin // New work arrives
state_d = BUSY; // go into processing state
end
end
// Operation in progress
BUSY: begin
unit_busy = 1'b1; // data in flight
// If the unit is done with processing
if (unit_done) begin
out_valid = 1'b1; // try to commit result downstream
// If downstream accepts our result
if (out_ready) begin
state_d = IDLE; // we anticipate going back to idling..
if (in_valid_q && unit_ready) begin // ..unless new work comes in
in_ready = 1'b1; // we acknowledge the instruction
state_d = BUSY; // and stay busy with it
end
// Otherwise if downstream is not ready for the result
end else begin
hold_result = 1'b1; // activate the hold register
state_d = HOLD; // wait for the pipeline to take the data
end
end
end
// Waiting with valid result for downstream
HOLD: begin
unit_busy = 1'b1; // data in flight
data_is_held = 1'b1; // data in hold register is valid
out_valid = 1'b1; // try to commit result downstream
// If the result is accepted by downstream
if (out_ready) begin
state_d = IDLE; // go back to idle..
if (in_valid_q && unit_ready) begin // ..unless new work comes in
in_ready = 1'b1; // acknowledge the new transaction
state_d = BUSY; // will be busy with the next instruction
end
end
end
// fall into idle state otherwise
default: state_d = IDLE;
endcase
// Flushing overrides the other actions
if (flush_i) begin
unit_busy = 1'b0; // data is invalidated
out_valid = 1'b0; // cancel any valid data
state_d = IDLE; // go to default state
end
end
// FSM status register (asynch active low rst_ni)
`FF(state_q, state_d, IDLE)
// Hold additional information while the operation is in progress
logic result_is_fp8_q;
TagType result_tag_q;
AuxType result_aux_q;
// Fill the registers everytime a valid operation arrives (load FF, active low asynch rst)
`FFL(result_is_fp8_q, input_is_fp8, op_starting, '0)
`FFL(result_tag_q, inp_pipe_tag_q[NUM_INP_REGS], op_starting, '0)
`FFL(result_aux_q, inp_pipe_aux_q[NUM_INP_REGS], op_starting, '0)
// -----------------
// DIVSQRT instance
// -----------------
logic [63:0] unit_result;
logic [WIDTH-1:0] adjusted_result, held_result_q;
fpnew_pkg::status_t unit_status, held_status_q;
div_sqrt_top_mvp i_divsqrt_lei (
.Clk_CI ( clk_i ),
.Rst_RBI ( rst_ni ),
.Div_start_SI ( div_valid ),
.Sqrt_start_SI ( sqrt_valid ),
.Operand_a_DI ( divsqrt_operands[0] ),
.Operand_b_DI ( divsqrt_operands[1] ),
.RM_SI ( rnd_mode_q ),
.Precision_ctl_SI ( '0 ),
.Format_sel_SI ( divsqrt_fmt ),
.Kill_SI ( flush_i ),
.Result_DO ( unit_result ),
.Fflags_SO ( unit_status ),
.Ready_SO ( unit_ready ),
.Done_SO ( unit_done )
);
// Adjust result width and fix FP8
assign adjusted_result = result_is_fp8_q ? unit_result >> 8 : unit_result;
// The Hold register (load, no rst_ni)
`FFLNR(held_result_q, adjusted_result, hold_result, clk_i)
`FFLNR(held_status_q, unit_status, hold_result, clk_i)
// --------------
// Output Select
// --------------
logic [WIDTH-1:0] result_d;
fpnew_pkg::status_t status_d;
// Prioritize hold register data
assign result_d = data_is_held ? held_result_q : adjusted_result;
assign status_d = data_is_held ? held_status_q : unit_status;
// ----------------
// 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;
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_tag_q[0] = result_tag_q;
assign out_pipe_aux_q[0] = result_aux_q;
assign out_pipe_valid_q[0] = out_valid;
// Input stage: Propagate pipeline ready signal to inside pipe
assign out_ready = 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_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 = 1'b1; // always NaN-Box result
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, unit_busy, out_pipe_valid_q});
endmodule