// -----------------------------------------------------------------------------
// Copyright (c) 2011, ON Semiconductor.
// -----------------------------------------------------------------------------
// FILE NAME     : dig_serializer.v
// TYPE          : RTL
// AUTHOR        : Franck Da Costa
// AUTHORS EMAIL : Franck.DaCosta@onsemi.com
// -----------------------------------------------------------------------------
// RELEASE HISTORY
// VERSION DATE        AUTHOR        DESCRIPTION
// 0.1     05/07/07    wlav06        Initial version
// 0.2     05/31/07    wlav06        Changed the frame from being asserted
//                                   for 1 clk cycle to the entire shift time.
// 0.3     06/07/07    wlav06        removed clk gating and ipt_clk-se as i/p.
// 0.4     07/16/07    wlav06        Removed bist_clk and added a port for
//                                   clk gating which goes to ccm.
//                                   updated rtl to keep the clks toggling
//                                   until the existing transaction is completed.
//                                   Added scan mode portto generate async reset to rst
//                                   the eoc flops.
// -----------------------------------------------------------------------------
// KEYWORDS : 
// -----------------------------------------------------------------------------
// PURPOSE     : Serializes a parallel data. The data width is parameterized.
// -----------------------------------------------------------------------------

module dig_adc_serializer 
  #(parameter SERBITS    = 11, // # of bits to sealize
    CNTWIDTH             = 4   // # of bits needed to store SERBITS  
    )
   (
    // outputs
    output wire             serdata,       // output serial data
    output wire             frame,         // Indicates start of data
    output wire             serial_clk_en, // clk gating en goes to ccm
    // inputs
   
    input                   serial_clk,   // i/p clk
    input                   rst_b,         // async reset 
    input                   adc_eoc,       // eoc from adc
    input                   serializer_en,   // which starts the serializer=bist, from I2C
///    input                   scan_mode,     // indicates if we r in scan mode
    input [(SERBITS - 1):0] adc_data       // input parallel code
    );
   

   //state machine parameters
   localparam          IDLEST  = 2'b00; // Initial state
   localparam          LOADST  = 2'b01; // Load state
   localparam          SHIFTST = 2'b10; // Shift state

   
   // **************************************************************
   // state holding regs
   reg [1:0]                currentstate;
   reg [1:0]                nextstate;
   
   // when true, load the parallel word 
   // to the shift register
   reg                      load;  
   
   // Start the shift process
   reg                      shift;   
   
   //output shift register          
   reg [(SERBITS - 1):0]    outputshiftreg;
   
   // counter reg for number of bits to shift
   reg [(CNTWIDTH-1):0]     bitcount;  
   // sync regs for eoc
   // reg                     eoc_sync1;
   // reg                     eoc_sync2;       
   // sync reg's for spi_bisten
   reg                      bisten_reg;
   reg                      bisten_sync;
   // async reset for eoc flops which factors in scan
   // mode for synthesis
  // wire                     sync_rst_b;
   // **************************************************************
 

   // **************************************************************
   // output assignement
   //the output is the MSB of the output shift reg
   assign                   serdata       = outputshiftreg[(SERBITS - 1)];

   // frame 
   assign                   frame         = shift;

   // assign the clk gate en signal.
   // if the serializer_en goes low in the middle of a transaction
   // dont kill the clks immd but complete the current
   // transaction and then de assert the clk gate signal.
   assign                   serial_clk_en = (serializer_en || (currentstate != IDLEST) ) ? 1'b1 : 1'b0;

   // generate async reset for eoc flops
  // assign                   sync_rst_b    = scan_mode ? rst_b : (serializer_en & rst_b);
   
   
   // **************************************************************


   // **************************************************************
   // sync the serializer_en to the serial_clk
   always @(posedge serial_clk or negedge rst_b)
     begin: bisten
        if(!rst_b)
          begin
             bisten_reg <= 1'b0;
             bisten_sync <= 1'b0;
          end
        else
          begin
             bisten_reg <= serializer_en;
             bisten_sync <= bisten_reg;
          end
     end // block: bisten
   // **************************************************************

   
   // **************************************************************
   // The eoc does not change inbetween eoc's
   // eoc flops are reset by the generated reset
   // this is done so that if the bist_en goes down
   // when eoc is high. The eoc is held at
   // one till the clks come back and the old data
   // is shifted out. To make this clean resetting the
   // flops this way

//   always @(posedge serial_clk or negedge sync_rst_b)    // SC
//     begin: eocdlyd                                      //
//        if(!sync_rst_b)                                  //
//          begin                                          //
//              eoc_sync1 <= 1'b0;                         //
//              eoc_sync2 <= 1'b0;                         //
//           end                                           //
//         else                                            //
//           begin                                         //
//              eoc_sync1 <= adc_eoc;                      //
//              eoc_sync2 <= eoc_sync1;                    //
//           end                                           //
//      end // block: eocdlyd                              //

//assign   eoc_sync2 = adc_eoc ;     // SC

   // **************************************************************
   
   
   // ************************************************************** 
   // latch the outputshift reg and bitcount
   always @(posedge serial_clk or negedge rst_b)
     begin: shft
        if (!rst_b)
          begin
             outputshiftreg <= {(SERBITS){1'b0}};
             bitcount       <= {(CNTWIDTH){1'b0}};
          end
        else
          begin
             // if load is true then load
             // the shift register with adc_data
             // and the counter with # of bits to shift
             // here the counter: bitcount <= 10
             // as we shift out 10 bits of the adc adc_data
             if (load)
               begin
                  outputshiftreg <= adc_data;
                  bitcount       <= SERBITS;
                  
               end
             else
               begin   
                  // if shift shift out the contents of the 
                  // shift reg starting from msb       
                  if (shift)
                    begin
                       outputshiftreg <= outputshiftreg << 1;
                       // decrement the bit count by 1
                       // bitcount <= bitcount -1
                       bitcount       <= bitcount - {{(CNTWIDTH-1){1'b0}}, 1'b1};
                    end
                  else
                    begin
                       // if neither load/shift hold values
                       outputshiftreg <= outputshiftreg;
                       bitcount       <= bitcount;                  
                    end // else: !if(shift)
               end // else: !if(load)
          end // else: !if(!rst_b)
     end // block: SEQ
   // **************************************************************



   // ************************************************************** 
   //sequential portion of control state machine
   always @(posedge serial_clk or negedge rst_b)
     begin: sequential
        if (!rst_b)
          begin
             currentstate <= IDLEST;
          end
        else
          begin
             // assign default values 
             currentstate  <= nextstate;
          end
     end // block: SEQ
   // **************************************************************


  
   // **************************************************************
   //combinational portion of the control state machine
   //always @(currentstate or bisten_sync or adc_eoc or bitcount)
   always @(*)
     begin: combo
        
        case(currentstate)
          // -------------------------------------
          IDLEST: // Initial state
            begin  
               if (bisten_sync && adc_eoc )
                 // if our blk is enabled start the fsm
                 begin
                    load      = 1'b0;
                    shift     = 1'b0;
                    nextstate = LOADST;
                 end
               else
                 begin
                    load      = 1'b0;
                    shift     = 1'b0;
                    nextstate = IDLEST;
                 end 
            end // case: IDLEST
          
          // -------------------------------------
          LOADST: // Load state 
            begin              
               // we unconditionally goto the shiftstate
               // we said we would complete the transaction
               // once started
               shift     = 1'b0;                   
               load      = 1'b1;                    
               nextstate = SHIFTST;                
            end // case: LOADST
          
          // -------------------------------------
          SHIFTST: // Shift state
            begin         
               if ( (bitcount == {(CNTWIDTH){1'b0}} ) )
                 // if i have shifted out all the bits
                 // reset everything and goback to load state
                 // to load the next parallel data
                 begin
                    if(bisten_sync && adc_eoc) 
                      begin
                         shift     = 1'b0;  
                         load      = 1'b0;
                         nextstate = LOADST;
                      end // if ( (bitcount == {(CNTWIDTH){1'b0}} ) && (bisten_sync) )
                    else
                      begin
                         shift     = 1'b0;  
                         load      = 1'b0;  
                         nextstate = IDLEST; 
                      end // else: !if(bisten_sync)
                 end // if ( (bitcount == {(CNTWIDTH){1'b0}} ) )
               else
                 // if i have not shifted all the bits
                 // the remain in the shift state
                 begin
                    shift     = 1'b1;
                    load      = 1'b0;
                    nextstate = SHIFTST;
                 end
            end // case: SHIFTST
          
          // -------------------------------------
          default: 
            begin
               shift     = 1'b0;
               load      = 1'b0;
               nextstate = IDLEST;
            end// case: default
          // -------------------------------------
        endcase // case(currentstate)
     end // block: COMB

endmodule // dig_serializer




