// -----------------------------------------------------------------------------
// Copyright (c) 2011, ON Semiconductor.
// -----------------------------------------------------------------------------
// FILE NAME     : dig_conv_fsm.v
// TYPE          : RTL
// AUTHOR        : Stephanie Cambounet
// AUTHORS EMAIL : Stephanie.Cambounet@onsemi.com
// -----------------------------------------------------------------------------
// RELEASE HISTORY
// VERSION DATE        AUTHOR                DESCRIPTION
//  1.0    2011-10-10  Stephanie Cambounet   Initial version
// -----------------------------------------------------------------------------
// KEYWORDS : 
// -----------------------------------------------------------------------------
// PURPOSE : 
// -----------------------------------------------------------------------------

module dig_adc_conv_fsm (
//Inputs
input             adc_clk,
input             rstb,
input             en,
input             eoc,
input [3:0]       channel_sel,
input             test_config_dft,
input             test_conv_start,           //external conv_en in test_config_dft
//Outputs
output            conv_en,
output reg [1:0]  channel_mux_ctrl,
output            adc_done,
output            ch1_conv_done,
output            ch2_conv_done,
output            ch3_conv_done,
output            ch4_conv_done
);

/// ****************************************************************************
/// Declarations
/// ****************************************************************************
parameter OFF       = 4'b0001;
parameter STBY      = 4'b0010;
parameter CONV      = 4'b0100;
parameter TEST      = 4'b1000;      //added for testmode_adc

`define s_OFF       state[0]
`define s_STBY      state[1]
`define s_CONV      state[2]
`define s_TEST      state[3]

`define ns_OFF      next_state[0]
`define ns_STBY     next_state[1]
`define ns_CONV     next_state[2]
`define ns_TEST     next_state[3]

// internal regs
reg  [3:0]     state;
reg  [3:0]     next_state;
reg            en_delay1;
reg            en_delay2;
reg   [2:0]    delay_cnt;
reg   [3:0]    channel_reg;                  ///changed to 4 channels
reg   [3:0]    next_channel_reg;
reg   [3:0]    first_channel;
reg            all_channels_done_reg;
reg            first_conv_cycle;
reg            conv_en_func;
// internal wires
wire        first_conv;
wire [3:0]  channel_sel_int;

/// ****************************************************************************
/// Processes
/// ****************************************************************************
// -----------------------------------------------------------------------------
// start up
// -----------------------------------------------------------------------------
always @ (posedge adc_clk or negedge rstb) begin
   if (!rstb) begin   
      en_delay1 <= 1'b0;                        // could save one flop as en= adc_en synchronous
      en_delay2 <= 1'b0;
   end 
   else begin
      en_delay1 <= en;
      en_delay2 <= en_delay1;           
   end               
end         

assign first_conv = ~en_delay2 & en_delay1;

// -----------------------------------------------------------------------------
// FSM
// -----------------------------------------------------------------------------
always @ (posedge adc_clk or negedge rstb) begin
   if (!rstb)
      state <= OFF;
   else
      state <= next_state;
end

always @ (*) begin  
   next_state = state;
   
   case (state)   
// OFF
// --------------------------------------------------------------------------
   OFF : begin
      if ( en ) begin           
         next_state = STBY;
      end
      else 
         next_state = OFF;
   end    

// STBY
// --------------------------------------------------------------------------
   STBY : begin
      if (!en ) begin            
         next_state = OFF;
      end
      else if (delay_cnt == 3'd1) begin            
//         next_state = CONV;                        
          if (!test_config_dft)  
            next_state = CONV;
          else 
            next_state = TEST;       //added for test mode_adc
      end
   end  

// CONV
// --------------------------------------------------------------------------
   CONV : begin
      if (!en ) begin            
         next_state = OFF;
      end   
      else if ( eoc ) begin
         next_state = STBY;
      end
      else begin
         next_state = CONV;
      end
   end

// TEST
// --------------------------------------------------------------------------
   TEST : begin
      if (!en ) begin            
         next_state = OFF;
      end   
      else if ( eoc ) begin
         next_state = STBY;
      end
      else if (!test_config_dft) begin
         next_state = STBY;
      end      
      else begin
         next_state = TEST;
      end
   end


   default : next_state = OFF;
  
   endcase
end


// -----------------------------------------------------------------------------
// delay_cnt
// -----------------------------------------------------------------------------
always @ (posedge adc_clk or negedge rstb) begin
   if (!rstb) begin   
      delay_cnt <= 3'd0;
   end 
   else begin 
      if (state == STBY) begin
         if (delay_cnt == 3'd0) begin
 ///           if ( first_conv ) begin            
 ///              delay_cnt <= 6'd60;
 ///            end
 ///           else
               delay_cnt <=  3'd7;         ///delay_max
         end 
         else begin
            delay_cnt <= delay_cnt - 3'd1;
         end
      end   
      else begin
         delay_cnt <= 3'd0;
      end
   end   
end


// -----------------------------------------------------------------------------
// conv_en in functional mode
// -----------------------------------------------------------------------------
always @ (posedge adc_clk or negedge rstb) begin
   if (!rstb) begin   
      conv_en_func <= 1'b0;                
   end 
   else if ((state == CONV )) begin
         if (first_conv_cycle) begin
            conv_en_func <= 1'b1;         
         end
         else    
            conv_en_func <= 1'b0;         
   end
   else
      conv_en_func <= 1'b0;               
end


// -----------------------------------------------------------------------------
// conv_en_test
// -----------------------------------------------------------------------------
 assign conv_en = conv_en_func | ( test_conv_start & test_config_dft ) ; 

// -----------------------------------------------------------------------------
// first_conv_cycle
// -----------------------------------------------------------------------------
always @ (posedge adc_clk or negedge rstb) begin
   if (!rstb) begin   
      first_conv_cycle <= 1'b0;
   end 
   else if ((next_state == CONV ) && (state == STBY )) begin
      first_conv_cycle <= 1'b1;
   end   
   else
      first_conv_cycle <= 1'b0;
end   

// -----------------------------------------------------------------------------
// channels: updated to 5 channels
// -----------------------------------------------------------------------------
// do not need anymore to latch channel_sel as requests themselves are latched.


//next_channel calculation
// -----------------------
always @ (posedge adc_clk or negedge rstb) begin
   if (!rstb) begin   
      next_channel_reg <= 4'b0001;        //to start with channel 1 in round robin 
   end     
      
//   else if (first_conv_delayed) begin  //to calculate correctly the second channel in a first conversion (predictable)
   else if (first_conv) begin  //to calculate correctly the second channel in a first conversion (predictable)
      next_channel_reg <= first_channel;      
   end
            
   else if ( |(next_channel_reg & channel_sel_int)== 1'b0 ) begin
      next_channel_reg <= {next_channel_reg[2:0],next_channel_reg[3]};
   end
   
   else if (conv_en) begin
      next_channel_reg <= {next_channel_reg[2:0],next_channel_reg[3]};
   end
end

//channel update
// --------------
always @ (posedge adc_clk or negedge rstb) begin
   if (!rstb) begin
      channel_reg <= 4'b0001;
   end      
//   else if (first_conv_delayed) begin  //to be sure to start each first conversion with the smallest selected channel (predictable)
   else if (first_conv) begin  //to be sure to start each first conversion with the smallest selected channel (predictable)
      channel_reg <= first_channel;      
   end       
   else if ((next_state == CONV) && ( state == STBY))  begin
      channel_reg <= next_channel_reg;
   end
end

//Round Robin detection: all selected channels have been converted
//---------------------------------------------------------------- 
always @ (posedge adc_clk or negedge rstb) begin
   if (!rstb) begin
            all_channels_done_reg <= 1'b0;
   end
   else if ( all_channels_done_reg ) begin
            all_channels_done_reg <= 1'b0;
   end         
   else if (eoc) begin                   //because channel=next channel during CONV state
      all_channels_done_reg <=(next_channel_reg <= channel_reg);
   end
end


//channel_mux output decoding
//---------------------------
always @ (*) begin
   case (channel_reg)
      4'b0001:  channel_mux_ctrl = 2'b00;  // channel 1   
      4'b0010:  channel_mux_ctrl = 2'b01;  // channel 2   
      4'b0100:  channel_mux_ctrl = 2'b10;  // channel 3   
      4'b1000:  channel_mux_ctrl = 2'b11;  // channel 4   
      default:  channel_mux_ctrl = 2'b00;
    endcase  
end

// first channel decoding
//-----------------------      
always @ (*) begin      
      casex (channel_sel_int) 
         4'bxxx1: first_channel = 4'b0001;
         4'bxx10: first_channel = 4'b0010;
         4'bx100: first_channel = 4'b0100;
         4'b1000: first_channel = 4'b1000;
         default: first_channel = 4'b0001;
      endcase
   end

// to avoid to gate the en_sync, channels=00000_sel is made equivalent to channel_sel=11111
//assign channel_sel_int = (latch_channel_sel == 5'b00000)?  ~latch_channel_sel : latch_channel_sel; 
assign channel_sel_int = (channel_sel == 4'b0000)?  ~channel_sel : channel_sel; 

///assign adc_done = all_channels_done_reg  & ~test_config_dft  ;     
assign adc_done = all_channels_done_reg    ;     

assign ch1_conv_done = eoc & channel_reg[0];    ///& ~test_config_dft
assign ch2_conv_done = eoc & channel_reg[1];    ///& ~test_config_dft
assign ch3_conv_done = eoc & channel_reg[2];    ///& ~test_config_dft
assign ch4_conv_done = eoc & channel_reg[3];    ///& ~test_config_dft


// -----------------------------------------------------------------------------
    
`undef s_OFF
`undef s_STBY
`undef s_CONV
`undef s_TEST
`undef ns_OFF  
`undef ns_STBY 
`undef ns_CONV       
`undef ns_TEST
                 
endmodule
