blob: 2d258cf2be9ff2ab568b5d89e212309ffa7757ca [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>
package fpnew_pkg;
// ---------
// FP TYPES
// ---------
// | Enumerator | Format | Width | EXP_BITS | MAN_BITS
// |:----------:|------------------|-------:|:--------:|:--------:
// | FP32 | IEEE binary32 | 32 bit | 8 | 23
// | FP64 | IEEE binary64 | 64 bit | 11 | 52
// | FP16 | IEEE binary16 | 16 bit | 5 | 10
// | FP8 | binary8 | 8 bit | 5 | 2
// | FP16ALT | binary16alt | 16 bit | 8 | 7
// *NOTE:* Add new formats only at the end of the enumeration for backwards compatibilty!
// Encoding for a format
typedef struct packed {
int unsigned exp_bits;
int unsigned man_bits;
} fp_encoding_t;
localparam int unsigned NUM_FP_FORMATS = 5; // change me to add formats
localparam int unsigned FP_FORMAT_BITS = $clog2(NUM_FP_FORMATS);
// FP formats
typedef enum logic [FP_FORMAT_BITS-1:0] {
FP32 = 'd0,
FP64 = 'd1,
FP16 = 'd2,
FP8 = 'd3,
FP16ALT = 'd4
// add new formats here
} fp_format_e;
// Encodings for supported FP formats
localparam fp_encoding_t [0:NUM_FP_FORMATS-1] FP_ENCODINGS = '{
'{8, 23}, // IEEE binary32 (single)
'{11, 52}, // IEEE binary64 (double)
'{5, 10}, // IEEE binary16 (half)
'{5, 2}, // custom binary8
'{8, 7} // custom binary16alt
// add new formats here
};
typedef logic [0:NUM_FP_FORMATS-1] fmt_logic_t; // Logic indexed by FP format (for masks)
typedef logic [0:NUM_FP_FORMATS-1][31:0] fmt_unsigned_t; // Unsigned indexed by FP format
localparam fmt_logic_t CPK_FORMATS = 5'b11000; // FP32 and FP64 can provide CPK only
// ---------
// INT TYPES
// ---------
// | Enumerator | Width |
// |:----------:|-------:|
// | INT8 | 8 bit |
// | INT16 | 16 bit |
// | INT32 | 32 bit |
// | INT64 | 64 bit |
// *NOTE:* Add new formats only at the end of the enumeration for backwards compatibilty!
localparam int unsigned NUM_INT_FORMATS = 4; // change me to add formats
localparam int unsigned INT_FORMAT_BITS = $clog2(NUM_INT_FORMATS);
// Int formats
typedef enum logic [INT_FORMAT_BITS-1:0] {
INT8,
INT16,
INT32,
INT64
// add new formats here
} int_format_e;
// Returns the width of an INT format by index
function automatic int unsigned int_width(int_format_e ifmt);
unique case (ifmt)
INT8: return 8;
INT16: return 16;
INT32: return 32;
INT64: return 64;
// default: begin
// pragma translate_off
// $fatal(1, "Invalid INT format supplied");
// pragma translate_on
// just return any integer to avoid any latches
// hopefully this error is caught by simulation
//return INT8;
//end
endcase
endfunction
typedef logic [0:NUM_INT_FORMATS-1] ifmt_logic_t; // Logic indexed by INT format (for masks)
// --------------
// FP OPERATIONS
// --------------
localparam int unsigned NUM_OPGROUPS = 4;
// Each FP operation belongs to an operation group
typedef enum logic [1:0] {
ADDMUL, DIVSQRT, NONCOMP, CONV
} opgroup_e;
localparam int unsigned OP_BITS = 4;
typedef enum logic [OP_BITS-1:0] {
FMADD, FNMSUB, ADD, MUL, // ADDMUL operation group
DIV, SQRT, // DIVSQRT operation group
SGNJ, MINMAX, CMP, CLASSIFY, // NONCOMP operation group
F2F, F2I, I2F, CPKAB, CPKCD // CONV operation group
} operation_e;
// -------------------
// RISC-V FP-SPECIFIC
// -------------------
// Rounding modes
typedef enum logic [2:0] {
RNE = 3'b000,
RTZ = 3'b001,
RDN = 3'b010,
RUP = 3'b011,
RMM = 3'b100,
DYN = 3'b111
} roundmode_e;
// Status flags
typedef struct packed {
logic NV; // Invalid
logic DZ; // Divide by zero
logic OF; // Overflow
logic UF; // Underflow
logic NX; // Inexact
} status_t;
// Information about a floating point value
typedef struct packed {
logic is_normal; // is the value normal
logic is_subnormal; // is the value subnormal
logic is_zero; // is the value zero
logic is_inf; // is the value infinity
logic is_nan; // is the value NaN
logic is_signalling; // is the value a signalling NaN
logic is_quiet; // is the value a quiet NaN
logic is_boxed; // is the value properly NaN-boxed (RISC-V specific)
} fp_info_t;
// Classification mask
typedef enum logic [9:0] {
NEGINF = 10'b00_0000_0001,
NEGNORM = 10'b00_0000_0010,
NEGSUBNORM = 10'b00_0000_0100,
NEGZERO = 10'b00_0000_1000,
POSZERO = 10'b00_0001_0000,
POSSUBNORM = 10'b00_0010_0000,
POSNORM = 10'b00_0100_0000,
POSINF = 10'b00_1000_0000,
SNAN = 10'b01_0000_0000,
QNAN = 10'b10_0000_0000
} classmask_e;
// ------------------
// FPU configuration
// ------------------
// Pipelining registers can be inserted (at elaboration time) into operational units
typedef enum logic [1:0] {
BEFORE, // registers are inserted at the inputs of the unit
AFTER, // registers are inserted at the outputs of the unit
INSIDE, // registers are inserted at predetermined (suboptimal) locations in the unit
DISTRIBUTED // registers are evenly distributed, INSIDE >= AFTER >= BEFORE
} pipe_config_t;
// Arithmetic units can be arranged in parallel (per format), merged (multi-format) or not at all.
typedef enum logic [1:0] {
DISABLED, // arithmetic units are not generated
PARALLEL, // arithmetic units are generated in prallel slices, one for each format
MERGED // arithmetic units are contained within a merged unit holding multiple formats
} unit_type_t;
// Array of unit types indexed by format
typedef unit_type_t [0:NUM_FP_FORMATS-1] fmt_unit_types_t;
// Array of format-specific unit types by opgroup
typedef fmt_unit_types_t [0:NUM_OPGROUPS-1] opgrp_fmt_unit_types_t;
// same with unsigned
typedef fmt_unsigned_t [0:NUM_OPGROUPS-1] opgrp_fmt_unsigned_t;
// FPU configuration: features
typedef struct packed {
int unsigned Width;
logic EnableVectors;
logic EnableNanBox;
fmt_logic_t FpFmtMask;
ifmt_logic_t IntFmtMask;
} fpu_features_t;
localparam fpu_features_t RV64D = '{
Width: 64,
EnableVectors: 1'b0,
EnableNanBox: 1'b1,
FpFmtMask: 5'b11000,
IntFmtMask: 4'b0011
};
localparam fpu_features_t RV32D = '{
Width: 64,
EnableVectors: 1'b1,
EnableNanBox: 1'b1,
FpFmtMask: 5'b11000,
IntFmtMask: 4'b0010
};
localparam fpu_features_t RV32F = '{
Width: 32,
EnableVectors: 1'b0,
EnableNanBox: 1'b1,
FpFmtMask: 5'b10000,
IntFmtMask: 4'b0010
};
localparam fpu_features_t RV64D_Xsflt = '{
Width: 64,
EnableVectors: 1'b1,
EnableNanBox: 1'b1,
FpFmtMask: 5'b11111,
IntFmtMask: 4'b1111
};
localparam fpu_features_t RV32F_Xsflt = '{
Width: 32,
EnableVectors: 1'b1,
EnableNanBox: 1'b1,
FpFmtMask: 5'b10111,
IntFmtMask: 4'b1110
};
localparam fpu_features_t RV32F_Xf16alt_Xfvec = '{
Width: 32,
EnableVectors: 1'b1,
EnableNanBox: 1'b1,
FpFmtMask: 5'b10001,
IntFmtMask: 4'b0110
};
// FPU configuraion: implementation
typedef struct packed {
opgrp_fmt_unsigned_t PipeRegs;
opgrp_fmt_unit_types_t UnitTypes;
pipe_config_t PipeConfig;
} fpu_implementation_t;
localparam fpu_implementation_t DEFAULT_NOREGS = '{
PipeRegs: '{default: 0},
UnitTypes: '{'{default: PARALLEL}, // ADDMUL
'{default: MERGED}, // DIVSQRT
'{default: PARALLEL}, // NONCOMP
'{default: MERGED}}, // CONV
PipeConfig: BEFORE
};
localparam fpu_implementation_t DEFAULT_SNITCH = '{
PipeRegs: '{default: 1},
UnitTypes: '{'{default: PARALLEL}, // ADDMUL
'{default: DISABLED}, // DIVSQRT
'{default: PARALLEL}, // NONCOMP
'{default: MERGED}}, // CONV
PipeConfig: BEFORE
};
// -----------------------
// Synthesis optimization
// -----------------------
localparam logic DONT_CARE = 1'b1; // the value to assign as don't care
// -------------------------
// General helper functions
// -------------------------
function automatic int minimum(int a, int b);
return (a < b) ? a : b;
endfunction
function automatic int maximum(int a, int b);
return (a > b) ? a : b;
endfunction
// -------------------------------------------
// Helper functions for FP formats and values
// -------------------------------------------
// Returns the width of a FP format
function automatic int unsigned fp_width(fp_format_e fmt);
return FP_ENCODINGS[fmt].exp_bits + FP_ENCODINGS[fmt].man_bits + 1;
endfunction
// Returns the widest FP format present
function automatic int unsigned max_fp_width(fmt_logic_t cfg);
automatic int unsigned res = 0;
for (int unsigned i = 0; i < NUM_FP_FORMATS; i++)
if (cfg[i])
res = unsigned'(maximum(res, fp_width(fp_format_e'(i))));
return res;
endfunction
// Returns the narrowest FP format present
function automatic int unsigned min_fp_width(fmt_logic_t cfg);
automatic int unsigned res = max_fp_width(cfg);
for (int unsigned i = 0; i < NUM_FP_FORMATS; i++)
if (cfg[i])
res = unsigned'(minimum(res, fp_width(fp_format_e'(i))));
return res;
endfunction
// Returns the number of expoent bits for a format
function automatic int unsigned exp_bits(fp_format_e fmt);
return FP_ENCODINGS[fmt].exp_bits;
endfunction
// Returns the number of mantissa bits for a format
function automatic int unsigned man_bits(fp_format_e fmt);
return FP_ENCODINGS[fmt].man_bits;
endfunction
// Returns the bias value for a given format (as per IEEE 754-2008)
function automatic int unsigned bias(fp_format_e fmt);
return unsigned'(2**(FP_ENCODINGS[fmt].exp_bits-1)-1); // symmetrical bias
endfunction
function automatic fp_encoding_t super_format(fmt_logic_t cfg);
automatic fp_encoding_t res;
res = '0;
for (int unsigned fmt = 0; fmt < NUM_FP_FORMATS; fmt++)
if (cfg[fmt]) begin // only active format
res.exp_bits = unsigned'(maximum(res.exp_bits, exp_bits(fp_format_e'(fmt))));
res.man_bits = unsigned'(maximum(res.man_bits, man_bits(fp_format_e'(fmt))));
end
return res;
endfunction
// -------------------------------------------
// Helper functions for INT formats and values
// -------------------------------------------
// Returns the widest INT format present
function automatic int unsigned max_int_width(ifmt_logic_t cfg);
automatic int unsigned res = 0;
for (int ifmt = 0; ifmt < NUM_INT_FORMATS; ifmt++) begin
if (cfg[ifmt]) res = maximum(res, int_width(int_format_e'(ifmt)));
end
return res;
endfunction
// --------------------------------------------------
// Helper functions for operations and FPU structure
// --------------------------------------------------
// Returns the operation group of the given operation
function automatic opgroup_e get_opgroup(operation_e op);
unique case (op)
FMADD, FNMSUB, ADD, MUL: return ADDMUL;
DIV, SQRT: return DIVSQRT;
SGNJ, MINMAX, CMP, CLASSIFY: return NONCOMP;
F2F, F2I, I2F, CPKAB, CPKCD: return CONV;
default: return NONCOMP;
endcase
endfunction
// Returns the number of operands by operation group
function automatic int unsigned num_operands(opgroup_e grp);
unique case (grp)
ADDMUL: return 3;
DIVSQRT: return 2;
NONCOMP: return 2;
CONV: return 3; // vectorial casts use 3 operands
default: return 0;
endcase
endfunction
// Returns the number of lanes according to width, format and vectors
function automatic int unsigned num_lanes(int unsigned width, fp_format_e fmt, logic vec);
return vec ? width / fp_width(fmt) : 1; // if no vectors, only one lane
endfunction
// Returns the maximum number of lanes in the FPU according to width, format config and vectors
function automatic int unsigned max_num_lanes(int unsigned width, fmt_logic_t cfg, logic vec);
return vec ? width / min_fp_width(cfg) : 1; // if no vectors, only one lane
endfunction
// Returns a mask of active FP formats that are present in lane lane_no of a multiformat slice
function automatic fmt_logic_t get_lane_formats(int unsigned width,
fmt_logic_t cfg,
int unsigned lane_no);
automatic fmt_logic_t res;
for (int unsigned fmt = 0; fmt < NUM_FP_FORMATS; fmt++)
// Mask active formats with the number of lanes for that format
res[fmt] = cfg[fmt] & (width / fp_width(fp_format_e'(fmt)) > lane_no);
return res;
endfunction
// Returns a mask of active INT formats that are present in lane lane_no of a multiformat slice
function automatic ifmt_logic_t get_lane_int_formats(int unsigned width,
fmt_logic_t cfg,
ifmt_logic_t icfg,
int unsigned lane_no);
automatic ifmt_logic_t res;
automatic fmt_logic_t lanefmts;
res = '0;
lanefmts = get_lane_formats(width, cfg, lane_no);
for (int unsigned ifmt = 0; ifmt < NUM_INT_FORMATS; ifmt++)
for (int unsigned fmt = 0; fmt < NUM_FP_FORMATS; fmt++)
// Mask active int formats with the width of the float formats
if ((fp_width(fp_format_e'(fmt)) == int_width(int_format_e'(ifmt))))
res[ifmt] |= icfg[ifmt] && lanefmts[fmt];
return res;
endfunction
// Returns a mask of active FP formats that are present in lane lane_no of a CONV slice
function automatic fmt_logic_t get_conv_lane_formats(int unsigned width,
fmt_logic_t cfg,
int unsigned lane_no);
automatic fmt_logic_t res;
for (int unsigned fmt = 0; fmt < NUM_FP_FORMATS; fmt++)
// Mask active formats with the number of lanes for that format, CPK at least twice
res[fmt] = cfg[fmt] && ((width / fp_width(fp_format_e'(fmt)) > lane_no) ||
(CPK_FORMATS[fmt] && (lane_no < 2)));
return res;
endfunction
// Returns a mask of active INT formats that are present in lane lane_no of a CONV slice
function automatic ifmt_logic_t get_conv_lane_int_formats(int unsigned width,
fmt_logic_t cfg,
ifmt_logic_t icfg,
int unsigned lane_no);
automatic ifmt_logic_t res;
automatic fmt_logic_t lanefmts;
res = '0;
lanefmts = get_conv_lane_formats(width, cfg, lane_no);
for (int unsigned ifmt = 0; ifmt < NUM_INT_FORMATS; ifmt++)
for (int unsigned fmt = 0; fmt < NUM_FP_FORMATS; fmt++)
// Mask active int formats with the width of the float formats
res[ifmt] |= icfg[ifmt] && lanefmts[fmt] &&
(fp_width(fp_format_e'(fmt)) == int_width(int_format_e'(ifmt)));
return res;
endfunction
// Return whether any active format is set as MERGED
function automatic logic any_enabled_multi(fmt_unit_types_t types, fmt_logic_t cfg);
for (int unsigned i = 0; i < NUM_FP_FORMATS; i++)
if (cfg[i] && types[i] == MERGED)
return 1'b1;
return 1'b0;
endfunction
// Return whether the given format is the first active one set as MERGED
function automatic logic is_first_enabled_multi(fp_format_e fmt,
fmt_unit_types_t types,
fmt_logic_t cfg);
for (int unsigned i = 0; i < NUM_FP_FORMATS; i++) begin
if (cfg[i] && types[i] == MERGED) return (fp_format_e'(i) == fmt);
end
return 1'b0;
endfunction
// Returns the first format that is active and is set as MERGED
function automatic fp_format_e get_first_enabled_multi(fmt_unit_types_t types, fmt_logic_t cfg);
for (int unsigned i = 0; i < NUM_FP_FORMATS; i++)
if (cfg[i] && types[i] == MERGED)
return fp_format_e'(i);
return fp_format_e'(0);
endfunction
// Returns the largest number of regs that is active and is set as MERGED
function automatic int unsigned get_num_regs_multi(fmt_unsigned_t regs,
fmt_unit_types_t types,
fmt_logic_t cfg);
automatic int unsigned res = 0;
for (int unsigned i = 0; i < NUM_FP_FORMATS; i++) begin
if (cfg[i] && types[i] == MERGED) res = maximum(res, regs[i]);
end
return res;
endfunction
endpackage