
`include "wally-config.vh"

module fcvt (
    input logic             XSgnE,          // input's sign
    input logic [`NE-1:0]   XExpE,          // input's exponent
    input logic [`NF:0]     XManE,          // input's fraction
    input logic [`XLEN-1:0] ForwardedSrcAE, // integer input - from IEU
    input logic [2:0]       FOpCtrlE,       // choose which opperation (look below for values)
    input logic             FWriteIntE,     // is fp->int (since it's writting to the integer register)
    input logic             XZeroE,         // is the input zero
    input logic             XDenormE,   // is the input denormalized
    input logic [`FMTBITS-1:0] FmtE,        // the input's precision (11=quad 01=double 00=single 10=half)
    output logic [`NE:0]           CvtCalcExpE,    // the calculated expoent
	output logic [`LOGCVTLEN-1:0] CvtShiftAmtE,  // how much to shift by
    output logic                   CvtResDenormUfE,// does the result underflow or is denormalized
    output logic                   CvtResSgnE,     // the result's sign
    output logic                   IntZeroE,      // is the integer zero?
    output logic [`CVTLEN-1:0]      CvtLzcInE      // 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

    // (FF) fp  -> fp coversion signals
    // (IF) int -> fp coversion signals
    // (FI) fp  -> int coversion signals


    logic [`FMTBITS-1:0]    OutFmt;     // format of the output
    logic [`XLEN-1:0]       PosInt;     // the positive integer input
    logic [`XLEN-1:0]       TrimInt;    // integer trimmed to the correct size
    logic [`NE-2:0]         NewBias;    // the bias of the final result
    logic [`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                   ToInt;      // is the opperation an fp->int conversion?
    logic [`LOGCVTLEN-1:0] ZeroCnt; // output from the LZC


    // seperate OpCtrl for code readability
    assign Signed = FOpCtrlE[0];
    assign Int64 =  FOpCtrlE[1];
    assign IntToFp =   FOpCtrlE[2];
    assign ToInt =  FWriteIntE;

    // choose the ouptut format depending on the opperation
    //      - fp -> fp: OpCtrl contains the percision of the output
    //      - int -> fp: FmtE contains the percision of the output
    if (`FPSIZES == 2) 
        assign OutFmt = IntToFp ? FmtE : (FOpCtrlE[1:0] == `FMT); 
    else if (`FPSIZES == 3 | `FPSIZES == 4) 
        assign OutFmt = IntToFp ? FmtE : FOpCtrlE[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 = CvtResSgnE ? -ForwardedSrcAE : ForwardedSrcAE;
    assign TrimInt = {{`XLEN-32{Int64}}, {32{1'b1}}} & PosInt;
    assign IntZeroE = ~|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 CvtLzcInE = IntToFp ? {TrimInt, {`CVTLEN-`XLEN{1'b0}}} :
                             {XManE[`NF-1:0], {`CVTLEN-`NF{1'b0}}};
    
    lzc #(`CVTLEN) lzc (.num(CvtLzcInE), .ZeroCnt);

    ///////////////////////////////////////////////////////////////////////////
    // 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 denorm input)
    //      denormalized/undeflowed result fp -> fp:
    //          - shift left by NF-1+CalcExp - to shift till the biased expoenent is 0
    //      ??? -> fp: 
    //          - shift left by ZeroCnt+1 - to shift till the result is normalized
    //              - only shift fp -> fp if the intital value is denormalized
    //                  - 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?
    assign CvtShiftAmtE = ToInt ? CvtCalcExpE[`LOGCVTLEN-1:0]&{`LOGCVTLEN{~CvtCalcExpE[`NE]}} :
                    CvtResDenormUfE&~IntToFp ? (`LOGCVTLEN)'(`NF-1)+CvtCalcExpE[`LOGCVTLEN-1:0] : 
                              (ZeroCnt+1)&{`LOGCVTLEN{XDenormE|IntToFp}};
    
    ///////////////////////////////////////////////////////////////////////////
    // exp calculations
    ///////////////////////////////////////////////////////////////////////////


    // *** possible optimizaations:
        //  - if subtracting exp by bias only the msb needs a full adder, the rest can be HA - dunno how to implement this for synth
        //  - Smaller exp -> Larger Exp can be calculated with: *** can use in Other units??? FMA??? insert this thing in later
        //          Exp if in range: {~Exp[SNE-1], Exp[SNE-2:0]}
        //          Exp in range if: Exp[SNE-1] = 1 & Exp[LNE-2:SNE] = 1111... & Exp[LNE-1] = 0 | Exp[SNE-1] = 0 & Exp[LNE-2:SNE] = 000... & Exp[LNE-1] = 1
        //                     i.e.: &Exp[LNE-2:SNE-1] xor Exp[LNE-1]
        //          Too big if:      Exp[LNE-1] = 1
        //          Too small if:    none of the above

    // Select the bias of the output
    //      fp -> int : select 1
    //      ??? -> fp : pick the new bias depending on the output format 
    if (`FPSIZES == 1) begin
        assign NewBias = ToInt ? (`NE-1)'(1) : (`NE-1)'(`BIAS); 

    end else if (`FPSIZES == 2) begin
        assign NewBias = ToInt ? (`NE-1)'(1) : OutFmt ? (`NE-1)'(`BIAS) : (`NE-1)'(`BIAS1); 

    end else if (`FPSIZES == 3) begin
        logic [`NE-2:0] NewBiasToFp;
        always_comb
            case (OutFmt)
                `FMT: NewBiasToFp =  (`NE-1)'(`BIAS);
                `FMT1: NewBiasToFp = (`NE-1)'(`BIAS1);
                `FMT2: NewBiasToFp = (`NE-1)'(`BIAS2);
                default: NewBiasToFp = {`NE-1{1'bx}};
            endcase
        assign NewBias = ToInt ? (`NE-1)'(1) : NewBiasToFp; 

    end else if (`FPSIZES == 4) begin        
        logic [`NE-2:0] NewBiasToFp;
        always_comb
            case (OutFmt)
                2'h3: NewBiasToFp =  (`NE-1)'(`Q_BIAS);
                2'h1: NewBiasToFp =  (`NE-1)'(`D_BIAS);
                2'h0: NewBiasToFp =  (`NE-1)'(`S_BIAS);
                2'h2: NewBiasToFp =  (`NE-1)'(`H_BIAS);
            endcase
        assign NewBias = ToInt ? (`NE-1)'(1) : NewBiasToFp; 
    end
    // select the old exponent
    //      int -> fp : largest bias + XLEN
    //      fp -> ??? : XExp
    assign OldExp = IntToFp ? (`NE)'(`BIAS)+(`NE)'(`XLEN) : XExpE;
    
    // calculate CalcExp
    //      fp -> fp : 
    //          - XExp - Largest bias + new bias - (ZeroCnt+1)
    //                                          only do ^ if the input was denormalized
    //              - convert the expoenent to the final preciaion (Exp - oldBias + newBias)
    //              - correct the expoent when there is a normalization shift ( + ZeroCnt+1) 
    //      fp -> int : XExp - Largest Bias + 1 - (ZeroCnt+1)
    //          |  `XLEN  zeros |     Mantissa      | 0's if nessisary | << CalcExp
    //          process:
    //              - start
    //                  |  `XLEN  zeros     |     Mantissa      | 0's if nessisary |
    //
    //              - shift left 1 (1)
    //                  | `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 denormalized then we dont shift... so the  "- (ZeroCnt+1)" is just leftovers from other options
    //      int -> fp : largest bias +  XLEN - Largest bias + new bias - 1 - ZeroCnt = XLEN + NewBias - 1 - ZeroCnt
    //              Process:
    //                  - shifted right by XLEN (XLEN)
    //                  - shift left to normilize (-1-ZeroCnt)
    //                  - newBias to make the biased exponent
    //          
    assign CvtCalcExpE = {1'b0, OldExp} - (`NE+1)'(`BIAS) + {2'b0, NewBias} - {{`NE{1'b0}}, XDenormE|IntToFp} - {{`NE-`LOGCVTLEN+1{1'b0}}, (ZeroCnt&{`LOGCVTLEN{XDenormE|IntToFp}})};
    // 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 CvtResDenormUfE = (~|CvtCalcExpE | CvtCalcExpE[`NE])&~XZeroE&~IntToFp;

    
    ///////////////////////////////////////////////////////////////////////////
    // 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
    assign CvtResSgnE = IntToFp ? Int64 ? ForwardedSrcAE[`XLEN-1]&Signed : ForwardedSrcAE[31]&Signed : XSgnE;

endmodule

