| // 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 |