// -----------------------------------------------------------------------------
// Copyright (c) 2010, ON Semiconductor.
// -----------------------------------------------------------------------------
// FILE NAME     : dig_otp_core.v
// TYPE          : RTL
// AUTHOR        : Franck Da Costa
// AUTHORS EMAIL : Franck.DaCosta@onsemi.com
// -----------------------------------------------------------------------------
// RELEASE HISTORY
// VERSION DATE        AUTHOR           DESCRIPTION
//  1.0    2010-02-24  Franck Da Costa  Initial version
// -----------------------------------------------------------------------------
// KEYWORDS : 
// -----------------------------------------------------------------------------
// PURPOSE : 
// -----------------------------------------------------------------------------

module dig_otp_core
#(
parameter               CNT_W = 6,         //
parameter               WIDTH = 1<<CNT_W
)
(
input                   clk_666ns,
input                   clk_666ns_b,
input                   vcore_rdy,

input                   hard_write,
input                   soft_write,
input                   re_latch,

input                   otp_memvalid,
input       [WIDTH-1:0] data_write,

input                   tb_sftrst_en,
input                   tb_otp_cnt,

input                   scan_mode,

output reg              otp_lat,
output                  otp_sensez,
output                  otp_sftrst,
output reg              otp_sftset,
output                  otp_reset,
output reg              otp_prog,
output                  otp_clk,

output reg              otp_cmd_clr,
output reg              otp_loaded,
output                  otp_clk_en
);

/// ****************************************************************************
/// Declarations
/// ****************************************************************************

// -----------------------------------------------------------------------------
// Parameters
// -----------------------------------------------------------------------------

// FSM states
parameter IDLE  = 4'b0001;
parameter RESET = 4'b0010;
parameter DATA  = 4'b0100;
parameter LAT   = 4'b1000;

// -----------------------------------------------------------------------------
// Internal Nets
// -----------------------------------------------------------------------------
reg           [CNT_W:0] cnt;
reg               [5:0] w_cnt;
wire              [5:0] next_w_cnt;
wire              [5:0] w_cnt_max;
reg               [3:0] state;
reg               [3:0] next_state;
reg                     otp_clk_tmp;
reg                     otp_lat_dly;
wire                    otp_lat_sb;
wire                    otp_lat_rb;
wire                    current_data;
wire                    reset_b;

/// ****************************************************************************
/// Processes
/// ****************************************************************************
assign current_data = (cnt[CNT_W]) ? 1'b0 : data_write[cnt];

assign w_cnt_max = (hard_write) ? ((current_data) ? ((tb_otp_cnt) ? 6'd47 : 6'd46) : 'd1) :
                                  ((current_data) ? 6'd2 : ((tb_sftrst_en)? 6'd2 : 6'd1));

assign reset_b = (scan_mode) ? vcore_rdy : hard_write | soft_write | re_latch;

assign otp_clk_en = (hard_write | soft_write | re_latch | otp_cmd_clr | ~otp_loaded) & vcore_rdy;

always @ (posedge clk_666ns or negedge reset_b) begin
   if (!reset_b) begin
      cnt <= {CNT_W+1{1'b0}};
   end
   else if ((w_cnt == w_cnt_max) && (state == DATA) && !cnt[CNT_W]) begin
      cnt <= cnt + {{CNT_W{1'b0}},1'b1};
   end
end

assign next_w_cnt = w_cnt + 6'd1;

always @ (posedge clk_666ns_b or negedge reset_b) begin
   if (!reset_b) begin
      w_cnt <= 6'd0;
   end
   else if (otp_clk_tmp && (w_cnt != w_cnt_max)) begin
      w_cnt <= next_w_cnt;
   end
   else begin
      w_cnt <= 6'd0;
   end
end

// OTP_SFTRST
// -----------------------------------------------------------------------------
assign otp_sftrst = soft_write & otp_reset & tb_sftrst_en;

// OTP_SFTSET - OTP_PROG
// -----------------------------------------------------------------------------
always @ (posedge clk_666ns_b or negedge reset_b) begin
   if (!reset_b) begin
      otp_prog   <= 1'b0;
      otp_sftset <= 1'b0;
   end
   else if (otp_clk_tmp && (w_cnt == 6'd0)) begin
      otp_prog   <= hard_write & current_data;
      otp_sftset <= soft_write & current_data;
   end
   else if (next_w_cnt == w_cnt_max) begin
      otp_prog   <= 1'b0;
      otp_sftset <= 1'b0;
   end
end

// OTP_CLK
// -----------------------------------------------------------------------------
always @ (posedge clk_666ns or negedge reset_b) begin
   if (!reset_b) begin
      otp_clk_tmp <= 1'b0;
   end
   else if ((state == DATA) && !otp_clk_tmp && !cnt[CNT_W]) begin
      otp_clk_tmp <= 1'b1;
   end
   else if (w_cnt == w_cnt_max) begin
      otp_clk_tmp <= 1'b0;
   end
end

assign otp_clk = otp_clk_tmp & |cnt;

// FSM
// -----------------------------------------------------------------------------
always @ (posedge clk_666ns_b or negedge vcore_rdy) begin
   if (!vcore_rdy) begin
      state <= IDLE;
   end
   else begin
      state <= next_state;
   end
end

always @ (*) begin

   next_state = state;
   
   case (state)
   IDLE : begin
      if (!otp_loaded)
         next_state = LAT;
      else if(hard_write || soft_write)
         next_state = RESET;
      else if(re_latch)
         next_state = LAT;
   end

   RESET : begin
      next_state = DATA;
   end

   DATA : begin
      if(cnt[CNT_W])
         next_state = (soft_write) ? IDLE : LAT;
   end
   LAT : begin
      if(otp_memvalid)
         next_state = IDLE;
   end
   endcase
end

// OTP_RESET
// -----------------------------------------------------------------------------
assign otp_reset = (state == RESET);

// OTP_LAT - OTP_SENSEZ
// -----------------------------------------------------------------------------
// assign otp_lat_sb = vcore_rdy; 
// assign otp_lat_rb = (scan_mode) ? 1'b1 : (tb_sftrst_en | otp_memvalid) | ~otp_lat_sb; 

// always @ (posedge clk_125khz_b or negedge otp_lat_sb or negedge otp_lat_rb) begin
//    if (!otp_lat_sb) begin
//       otp_lat <= 1'b1;
//    end
//    else if (!otp_lat_rb) begin
//       otp_lat <= 1'b0;
//    end
//    else if (next_state == LAT) begin
//       otp_lat <= 1'b0;
//    end
//    else begin
//       otp_lat <= 1'b1;
//    end
// end


// otp_memvalid low dont force a relatch anymore
always @ (posedge clk_666ns_b or negedge vcore_rdy ) begin
   if (!vcore_rdy) begin
      otp_lat <= 1'b1;
   end
   else if (next_state == LAT) begin
      otp_lat <= 1'b0;
   end
   else begin
      otp_lat <= 1'b1;
   end
end

always @ (posedge clk_666ns_b or negedge vcore_rdy) begin
   if (!vcore_rdy) begin
      otp_lat_dly <= 1'b1;
   end
   else begin
      otp_lat_dly <= otp_lat;
   end
end
assign otp_sensez = otp_lat & otp_lat_dly;

// OTP_LOADED
// -----------------------------------------------------------------------------
always @ (posedge clk_666ns_b or negedge vcore_rdy) begin
   if (!vcore_rdy) begin
      otp_loaded <= 1'b0;
   end
   else if (next_state == LAT) begin
      otp_loaded <= 1'b0;
   end
   else if ((state == LAT) && (next_state == IDLE)) begin
      otp_loaded <= 1'b1;
   end
end

// OTP_CMD_CLR
// -----------------------------------------------------------------------------
always @ (posedge clk_666ns_b or negedge vcore_rdy) begin
   if (!vcore_rdy) begin
      otp_cmd_clr <= 1'b0;
   end
   else if ((state != IDLE) && (next_state == IDLE)) begin
      otp_cmd_clr <= 1'b1;
   end
   else begin
      otp_cmd_clr <= 1'b0;
   end
end

endmodule

