cvw/src/fpu/fcvt.sv
Harshini Srinath 610ac81a71 Update fcvt.sv
Program clean up
2023-06-11 16:05:14 -07:00

225 lines
11 KiB
Systemverilog

///////////////////////////////////////////
// fcvt.sv
//
// Written: me@KatherineParry.com
// Modified: 7/5/2022
//
// Purpose: Floating point conversions of configurable size
//
// Documentation: RISC-V System on Chip Design Chapter 13
//
// Int component of the Wally configurable RISC-V project.
//
// Copyright (C) 2021-23 Harvey Mudd College & Oklahoma State University
//
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
//
// Licensed under the Solderpad Hardware License v 2.1 (the “License”); you may not use this file
// except in compliance with the License, or, at your option, the Apache License version 2.0. You
// may obtain a copy of the License at
//
// https://solderpad.org/licenses/SHL-2.1/
//
// Unless required by applicable law or agreed to in writing, any work distributed under the
// 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.
////////////////////////////////////////////////////////////////////////////////////////////////
module fcvt import cvw::*; #(parameter cvw_t P) (
input logic Xs, // input's sign
input logic [P.NE-1:0] Xe, // input's exponent
input logic [P.NF:0] Xm, // input's fraction
input logic [P.XLEN-1:0] Int, // integer input - from IEU
input logic [2:0] OpCtrl, // choose which opperation (look below for values)
input logic ToInt, // is fp->int (since it's writting to the integer register)
input logic XZero, // is the input zero
input logic [P.FMTBITS-1:0] Fmt, // the input's precision (11=quad 01=double 00=single 10=half)
output logic [P.NE:0] Ce, // the calculated expoent
output logic [P.LOGCVTLEN-1:0] ShiftAmt, // how much to shift by
output logic ResSubnormUf, // does the result underflow or is subnormal
output logic Cs, // the result's sign
output logic IntZero, // is the integer zero?
output logic [P.CVTLEN-1:0] LzcIn // input to the Leading Zero Counter (priority encoder)
);
// OpCtrls:
// fp->fp conversions: {0, output precision} - only one of the operations writes to the int register
// half - 10
// single - 00
// double - 01
// quad - 11
// int<->fp conversions: {is int->fp?, is the integer 64-bit?, is the integer signed?}
// bit 2 bit 1 bit 0
// for example: signed long -> single floating point has the OpCode 101
logic [P.FMTBITS-1:0] OutFmt; // format of the output
logic [P.XLEN-1:0] PosInt; // the positive integer input
logic [P.XLEN-1:0] TrimInt; // integer trimmed to the correct size
logic [P.NE-2:0] NewBias; // the bias of the final result
logic [P.NE-1:0] OldExp; // the old exponent
logic Signed; // is the opperation with a signed integer?
logic Int64; // is the integer 64 bits?
logic IntToFp; // is the opperation an int->fp conversion?
logic [P.CVTLEN:0] LzcInFull; // input to the Leading Zero Counter (priority encoder)
logic [P.LOGCVTLEN-1:0] LeadingZeros; // output from the LZC
// seperate OpCtrl for code readability
assign Signed = OpCtrl[0];
assign Int64 = OpCtrl[1];
assign IntToFp = OpCtrl[2];
// choose the ouptut format depending on the opperation
// - fp -> fp: OpCtrl contains the percision of the output
// - int -> fp: Fmt contains the percision of the output
if (P.FPSIZES == 2)
assign OutFmt = IntToFp ? Fmt : (OpCtrl[1:0] == P.FMT);
else if (P.FPSIZES == 3 | P.FPSIZES == 4)
assign OutFmt = IntToFp ? Fmt : OpCtrl[1:0];
///////////////////////////////////////////////////////////////////////////
// negation
///////////////////////////////////////////////////////////////////////////
// 1) negate the input if the input is a negitive singed integer
// 2) trim the input to the proper size (kill the 32 most significant zeroes if needed)
assign PosInt = Cs ? -Int : Int;
assign TrimInt = {{P.XLEN-32{Int64}}, {32{1'b1}}} & PosInt;
assign IntZero = ~|TrimInt;
///////////////////////////////////////////////////////////////////////////
// lzc
///////////////////////////////////////////////////////////////////////////
// choose the input to the leading zero counter i.e. priority encoder
// int -> fp : | positive integer | 00000... (if needed) |
// fp -> fp : | fraction | 00000... (if needed) |
assign LzcInFull = IntToFp ? {TrimInt, {P.CVTLEN-P.XLEN+1{1'b0}}} :
{Xm, {P.CVTLEN-P.NF{1'b0}}};
// used as shifter input in postprocessor
assign LzcIn = LzcInFull[P.CVTLEN-1:0];
lzc #(P.CVTLEN+1) lzc (.num(LzcInFull), .ZeroCnt(LeadingZeros));
///////////////////////////////////////////////////////////////////////////
// exp calculations
///////////////////////////////////////////////////////////////////////////
// Select the bias of the output
// fp -> int : select 1
// ??? -> fp : pick the new bias depending on the output format
if (P.FPSIZES == 1) begin
assign NewBias = ToInt ? (P.NE-1)'(1) : (P.NE-1)'(P.BIAS);
end else if (P.FPSIZES == 2) begin
logic [P.NE-2:0] NewBiasToFp;
assign NewBiasToFp = OutFmt ? (P.NE-1)'(P.BIAS) : (P.NE-1)'(P.BIAS1);
assign NewBias = ToInt ? (P.NE-1)'(1) : NewBiasToFp;
end else if (P.FPSIZES == 3) begin
logic [P.NE-2:0] NewBiasToFp;
always_comb
case (OutFmt)
P.FMT: NewBiasToFp = (P.NE-1)'(P.BIAS);
P.FMT1: NewBiasToFp = (P.NE-1)'(P.BIAS1);
P.FMT2: NewBiasToFp = (P.NE-1)'(P.BIAS2);
default: NewBiasToFp = {P.NE-1{1'bx}};
endcase
assign NewBias = ToInt ? (P.NE-1)'(1) : NewBiasToFp;
end else if (P.FPSIZES == 4) begin
logic [P.NE-2:0] NewBiasToFp;
always_comb
case (OutFmt)
2'h3: NewBiasToFp = (P.NE-1)'(P.Q_BIAS);
2'h1: NewBiasToFp = (P.NE-1)'(P.D_BIAS);
2'h0: NewBiasToFp = (P.NE-1)'(P.S_BIAS);
2'h2: NewBiasToFp = (P.NE-1)'(P.H_BIAS);
endcase
assign NewBias = ToInt ? (P.NE-1)'(1) : NewBiasToFp;
end
// select the old exponent
// int -> fp : largest bias + XLEN-1
// fp -> ??? : XExp
assign OldExp = IntToFp ? (P.NE)'(P.BIAS)+(P.NE)'(P.XLEN-1) : Xe;
// calculate CalcExp
// fp -> fp :
// - XExp - Largest bias + new bias - (LeadingZeros+1)
// only do ^ if the input was subnormal
// - convert the expoenent to the final preciaion (Exp - oldBias + newBias)
// - correct the expoent when there is a normalization shift ( + LeadingZeros+1)
// - the plus 1 is built into the leading zeros by counting the leading zeroes in the mantissa rather than the fraction
// fp -> int : XExp - Largest Bias + 1 - (LeadingZeros+1)
// | P.XLEN zeros | Mantissa | 0's if nessisary | << CalcExp
// process:
// - start
// | P.XLEN zeros | Mantissa | 0's if nessisary |
//
// - shift left 1 (1)
// | P.XLEN-1 zeros |bit| frac | 0's if nessisary |
// . <- binary point
//
// - shift left till unbiased exponent is 0 (XExp - Largest Bias)
// | 0's | Mantissa | 0's if nessisary |
// | keep |
//
// - if the input is subnormal then we dont shift... so the "- LeadingZeros" is just leftovers from other options
// int -> fp : largest bias + XLEN-1 - Largest bias + new bias - LeadingZeros = XLEN-1 + NewBias - LeadingZeros
// Process:
// |XLEN|.0000
// - shifted right by XLEN (XLEN)
// 000000.|XLEN|
// - shift left to normilize (-LeadingZeros)
// 000000.1...
// - shift left 1 to normalize
// 000001.stuff
// - newBias to make the biased exponent
//
// oldexp - biasold - LeadingZeros + newbias
assign Ce = {1'b0, OldExp} - (P.NE+1)'(P.BIAS) - {{P.NE-P.LOGCVTLEN+1{1'b0}}, (LeadingZeros)} + {2'b0, NewBias};
// find if the result is dnormal or underflows
// - if Calculated expoenent is 0 or negitive (and the input/result is not exactaly 0)
// - can't underflow an integer to Fp conversion
assign ResSubnormUf = (~|Ce | Ce[P.NE])&~XZero&~IntToFp;
///////////////////////////////////////////////////////////////////////////
// shifter
///////////////////////////////////////////////////////////////////////////
// kill the shift if it's negitive
// select the amount to shift by
// fp -> int:
// - shift left by CalcExp - essentially shifting until the unbiased exponent = 0
// - don't shift if supposed to shift right (underflowed or Subnorm input)
// subnormal/undeflowed result fp -> fp:
// - shift left by NF-1+CalcExp - to shift till the biased expoenent is 0
// ??? -> fp:
// - shift left by LeadingZeros - to shift till the result is normalized
// - only shift fp -> fp if the intital value is subnormal
// - this is a problem because the input to the lzc was the fraction rather than the mantissa
// - rather have a few and-gates than an extra bit in the priority encoder??? *** is this true?
always_comb
if(ToInt) ShiftAmt = Ce[P.LOGCVTLEN-1:0]&{P.LOGCVTLEN{~Ce[P.NE]}};
else if (ResSubnormUf) ShiftAmt = (P.LOGCVTLEN)'(P.NF-1)+Ce[P.LOGCVTLEN-1:0];
else ShiftAmt = LeadingZeros;
///////////////////////////////////////////////////////////////////////////
// sign
///////////////////////////////////////////////////////////////////////////
// determine the sign of the result
// - if int -> fp
// - if 64-bit : check the msb of the 64-bit integer input and if it's signed
// - if 32-bit : check the msb of the 32-bit integer input and if it's signed
// - otherwise: the floating point input's sign
always_comb
if(IntToFp)
if(Int64) Cs = Int[P.XLEN-1]&Signed;
else Cs = Int[31]&Signed;
else Cs = Xs;
endmodule