`include "wally-config.vh"

module unpackinput ( 
    input logic  [`FLEN-1:0]        In,    // inputs from register file
    input logic  [`FMTBITS-1:0]     FmtE,       // format signal 00 - single 01 - double 11 - quad 10 - half
    output logic                    Sgn,    // sign bits of XYZ
    output logic [`NE-1:0]          Exp,    // exponents of XYZ (converted to largest supported precision)
    output logic [`NF:0]            Man,    // mantissas of XYZ (converted to largest supported precision)
    output logic                    NaN,    // is XYZ a NaN
    output logic                    SNaN, // is XYZ a signaling NaN
    output logic                    Zero,         // is XYZ zero
    output logic                    Inf,            // is XYZ infinity
    output logic                    ExpNonZero,            // is the exponent not zero
    output logic                    FracZero,            // is the fraction zero
    output logic                    ExpMax                       // does In have the maximum exponent (NaN or Inf)
);
 
    logic [`NF-1:0] Frac; //Fraction of XYZ
    logic           ExpZero;
    logic           BadNaNBox;
    
    if (`FPSIZES == 1) begin        // if there is only one floating point format supported
        assign BadNaNBox = 0;
        assign Sgn = In[`FLEN-1];  // sign bit
        assign Frac = In[`NF-1:0];  // fraction (no assumed 1)
        assign ExpNonZero = |In[`FLEN-2:`NF];  // is the exponent non-zero
        assign Exp = {In[`FLEN-2:`NF+1], In[`NF]|~ExpNonZero};  // exponent.  Denormalized numbers have effective biased exponent of 1
        assign ExpMax = &In[`FLEN-2:`NF];  // is the exponent all 1's
    end else if (`FPSIZES == 2) begin   // if there are 2 floating point formats supported
        //***need better names for these constants
        // largest format | smaller format
        //----------------------------------
        //      `FLEN     |     `LEN1       length of floating point number
        //      `NE       |     `NE1        length of exponent
        //      `NF       |     `NF1        length of fraction
        //      `BIAS     |     `BIAS1      exponent's bias value
        //      `FMT      |     `FMT1       precision's format value - Q=11 D=01 S=00 H=10

        // Possible combinantions specified by spec:
        //      double and single
        //      single and half

        // Not needed but can also handle:
        //      quad   and double
        //      quad   and single
        //      quad   and half
        //      double and half

        assign BadNaNBox = ~(FmtE|(&In[`FLEN-1:`LEN1])); // Check NaN boxing

        // choose sign bit depending on format - 1=larger precsion 0=smaller precision
        assign Sgn = FmtE ? In[`FLEN-1] : In[`LEN1-1];

        // extract the fraction, add trailing zeroes to the mantissa if nessisary
        assign Frac = FmtE ? In[`NF-1:0] : {In[`NF1-1:0], (`NF-`NF1)'(0)};

        // is the exponent non-zero
        assign ExpNonZero = FmtE ? |In[`FLEN-2:`NF] : |In[`LEN1-2:`NF1]; 

        // example double to single conversion:
        // 1023 = 0011 1111 1111
        // 127  = 0000 0111 1111 (subtract this)
        // 896  = 0011 1000 0000
        // sexp = 0000 bbbb bbbb (add this) b = bit d = ~b 
        // dexp = 0bdd dbbb bbbb 
        // also need to take into account possible zero/denorm/inf/NaN values

        // extract the exponent, converting the smaller exponent into the larger precision if nessisary
        //      - if the original precision had a denormal number convert the exponent value 1
        assign Exp = FmtE ? {In[`FLEN-2:`NF+1], In[`NF]|~ExpNonZero} : {In[`LEN1-2], {`NE-`NE1{~In[`LEN1-2]}}, In[`LEN1-3:`NF1+1], In[`NF1]|~ExpNonZero}; 
 
        // is the exponent all 1's
        assign ExpMax = FmtE ? &In[`FLEN-2:`NF] : &In[`LEN1-2:`NF1];
    

    end else if (`FPSIZES == 3) begin       // three floating point precsions supported

        //***need better names for these constants
        // largest format | larger format  | smallest format
        //---------------------------------------------------
        //      `FLEN     |     `LEN1      |    `LEN2       length of floating point number
        //      `NE       |     `NE1       |    `NE2        length of exponent
        //      `NF       |     `NF1       |    `NF2        length of fraction
        //      `BIAS     |     `BIAS1     |    `BIAS2      exponent's bias value
        //      `FMT      |     `FMT1      |    `FMT2       precision's format value - Q=11 D=01 S=00 H=10

        // Possible combinantions specified by spec:
        //      quad   and double and single
        //      double and single and half

        // Not needed but can also handle:
        //      quad   and double and half
        //      quad   and single and half

        // Check NaN boxing
        always_comb
            case (FmtE)
                `FMT:  BadNaNBox = 0;
                `FMT1: BadNaNBox = ~&In[`FLEN-1:`LEN1];
                `FMT2: BadNaNBox = ~&In[`FLEN-1:`LEN2];
                default: BadNaNBox = 1'bx;
            endcase

        // extract the sign bit
        always_comb
            case (FmtE)
                `FMT:  Sgn = In[`FLEN-1];
                `FMT1: Sgn = In[`LEN1-1];
                `FMT2: Sgn = In[`LEN2-1];
                default: Sgn = 1'bx;
            endcase

        // extract the fraction
        always_comb
            case (FmtE)
                `FMT: Frac = In[`NF-1:0];
                `FMT1: Frac = {In[`NF1-1:0], (`NF-`NF1)'(0)};
                `FMT2: Frac = {In[`NF2-1:0], (`NF-`NF2)'(0)};
                default: Frac = {`NF{1'bx}};
            endcase

        // is the exponent non-zero
        always_comb
            case (FmtE)
                `FMT:  ExpNonZero = |In[`FLEN-2:`NF];     // if input is largest precision (`FLEN - ie quad or double)
                `FMT1: ExpNonZero = |In[`LEN1-2:`NF1];  // if input is larger precsion (`LEN1 - double or single)
                `FMT2: ExpNonZero = |In[`LEN2-2:`NF2]; // if input is smallest precsion (`LEN2 - single or half)
                default: ExpNonZero = 1'bx; 
            endcase
            
        // example double to single conversion:
        // 1023 = 0011 1111 1111
        // 127  = 0000 0111 1111 (subtract this)
        // 896  = 0011 1000 0000
        // sexp = 0000 bbbb bbbb (add this) b = bit d = ~b 
        // dexp = 0bdd dbbb bbbb 
        // also need to take into account possible zero/denorm/inf/NaN values

        // convert the larger precision's exponent to use the largest precision's bias
        always_comb 
            case (FmtE)
                `FMT:  Exp = {In[`FLEN-2:`NF+1], In[`NF]|~ExpNonZero};
                `FMT1: Exp = {In[`LEN1-2], {`NE-`NE1{~In[`LEN1-2]}}, In[`LEN1-3:`NF1+1], In[`NF1]|~ExpNonZero}; 
                `FMT2: Exp = {In[`LEN2-2], {`NE-`NE2{~In[`LEN2-2]}}, In[`LEN2-3:`NF2+1], In[`NF2]|~ExpNonZero}; 
                default: Exp = {`NE{1'bx}};
            endcase

        // is the exponent all 1's
        always_comb
            case (FmtE)
                `FMT:  ExpMax = &In[`FLEN-2:`NF];
                `FMT1: ExpMax = &In[`LEN1-2:`NF1];
                `FMT2: ExpMax = &In[`LEN2-2:`NF2];
                default: ExpMax = 1'bx;
            endcase

    end else if (`FPSIZES == 4) begin      // if all precsisons are supported - quad, double, single, and half
    
        //    quad   |  double  |  single  |  half    
        //-------------------------------------------------------------------
        //   `Q_LEN  |  `D_LEN  |  `S_LEN  |  `H_LEN     length of floating point number
        //   `Q_NE   |  `D_NE   |  `S_NE   |  `H_NE      length of exponent
        //   `Q_NF   |  `D_NF   |  `S_NF   |  `H_NF      length of fraction
        //   `Q_BIAS |  `D_BIAS |  `S_BIAS |  `H_BIAS    exponent's bias value
        //   `Q_FMT  |  `D_FMT  |  `S_FMT  |  `H_FMT     precision's format value - Q=11 D=01 S=00 H=10

        // Check NaN boxing
        always_comb
            case (FmtE)
                2'b11:  BadNaNBox = 0;
                2'b01: BadNaNBox = ~&In[`Q_LEN-1:`D_LEN];
                2'b00: BadNaNBox = ~&In[`Q_LEN-1:`S_LEN];
                2'b10: BadNaNBox = ~&In[`Q_LEN-1:`H_LEN];
            endcase

        // extract sign bit
        always_comb
            case (FmtE)
                2'b11: Sgn = In[`Q_LEN-1];
                2'b01: Sgn = In[`D_LEN-1];
                2'b00: Sgn = In[`S_LEN-1];
                2'b10: Sgn = In[`H_LEN-1];
            endcase
            

        // extract the fraction
        always_comb
            case (FmtE)
                2'b11: Frac = In[`Q_NF-1:0];
                2'b01: Frac = {In[`D_NF-1:0], (`Q_NF-`D_NF)'(0)};
                2'b00: Frac = {In[`S_NF-1:0], (`Q_NF-`S_NF)'(0)};
                2'b10: Frac = {In[`H_NF-1:0], (`Q_NF-`H_NF)'(0)};
            endcase

        // is the exponent non-zero
        always_comb
            case (FmtE)
                2'b11: ExpNonZero = |In[`Q_LEN-2:`Q_NF];
                2'b01: ExpNonZero = |In[`D_LEN-2:`D_NF];
                2'b00: ExpNonZero = |In[`S_LEN-2:`S_NF]; 
                2'b10: ExpNonZero = |In[`H_LEN-2:`H_NF]; 
            endcase


        // example double to single conversion:
        // 1023 = 0011 1111 1111
        // 127  = 0000 0111 1111 (subtract this)
        // 896  = 0011 1000 0000
        // sexp = 0000 bbbb bbbb (add this) b = bit d = ~b 
        // dexp = 0bdd dbbb bbbb 
        // also need to take into account possible zero/denorm/inf/NaN values
        
        // convert the double precsion exponent into quad precsion
        always_comb
            case (FmtE)
                2'b11: Exp = {In[`Q_LEN-2:`Q_NF+1], In[`Q_NF]|~ExpNonZero};
                2'b01: Exp = {In[`D_LEN-2], {`Q_NE-`D_NE{~In[`D_LEN-2]}}, In[`D_LEN-3:`D_NF+1], In[`D_NF]|~ExpNonZero};
                2'b00: Exp = {In[`S_LEN-2], {`Q_NE-`S_NE{~In[`S_LEN-2]}}, In[`S_LEN-3:`S_NF+1], In[`S_NF]|~ExpNonZero};
                2'b10: Exp = {In[`H_LEN-2], {`Q_NE-`H_NE{~In[`H_LEN-2]}}, In[`H_LEN-3:`H_NF+1], In[`H_NF]|~ExpNonZero}; 
            endcase


        // is the exponent all 1's
        always_comb 
            case (FmtE)
                2'b11: ExpMax = &In[`Q_LEN-2:`Q_NF];
                2'b01: ExpMax = &In[`D_LEN-2:`D_NF];
                2'b00: ExpMax = &In[`S_LEN-2:`S_NF];
                2'b10: ExpMax = &In[`H_LEN-2:`H_NF];
            endcase

    end

    // Output logic
    assign FracZero = ~|Frac; // is the fraction zero?
    assign Man = {ExpNonZero, Frac}; // add the assumed one (or zero if denormal or zero) to create the significand
    assign NaN = (ExpMax & ~FracZero)|BadNaNBox; // is the input a NaN?
    assign SNaN = NaN&~Frac[`NF-1]&~BadNaNBox; // is the input a singnaling NaN?
    assign Inf = ExpMax & FracZero; // is the input infinity?
    assign Zero = ~ExpNonZero & FracZero; // is the input zero?
endmodule