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

module owi_clk (
//Inputs
input        vcore_rdy,          // Asynchronous reset

input        clk_12MHz,          // Oscillator clock @ 12MHz +/-10%

input        owi_ck,             // OneWire synchro clock @ 32768Hz +/-10%
input        owi_data,           // OneWire data @ 115.2kbps

input        rx_tx_clk_en,       // RX access: need for both rx and tx clocks
input        tx_clk_en,          // TX access: need for tx clock only
input  [3:0] bit_cnt,            // Bit number clocked on tx_clk
input        ib_delay,           // Inter Byte delay

input        otp_no_tx_resync,   // OTP: disable TX synchro @ bits 3.5 and 7    
input        otp_no_rx_resync,   // OTP: disable RX synchro on data edges

//Outputs
output       rx_clk,             // RX clock (sampling)
output       tx_clk              // TX clock (emitting + FSM)
);

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

// State parameters
// -----------------------------------------------------------------------------
parameter IDLE = 6'b000001;
parameter RX   = 6'b000010;
parameter TXW  = 6'b000100;
parameter TX1  = 6'b001000;
parameter TX2  = 6'b010000;
parameter TX3  = 6'b100000;

`define s_IDLE  owi_state[0]
`define s_RX    owi_state[1]
`define s_TXW   owi_state[2]
`define s_TX1   owi_state[3]
`define s_TX2   owi_state[4]
`define s_TX3   owi_state[5]

`define ns_IDLE  next_owi_state[0]
`define ns_RX    next_owi_state[1]
`define ns_TXW   next_owi_state[2]
`define ns_TX1   next_owi_state[3]
`define ns_TX2   next_owi_state[4]
`define ns_TX3   next_owi_state[5]

// Internal signals
// -----------------------------------------------------------------------------
reg         [5:0] owi_state;
reg         [5:0] next_owi_state;
reg         [8:0] owi_ck_cnt;
reg         [6:0] owi_bit_cnt;
reg         [6:0] owi_bit_lgth;
reg         [3:0] owi_ck_range;
reg               owi_ck_reg1;
reg               owi_ck_reg2;
reg               owi_ck_reg3;
reg               owi_data_reg1;
reg               owi_data_reg2;
reg               owi_data_reg3;

wire              rx_cgc_en;
wire              tx_cgc_en;

/// ****************************************************************************
/// Processes
/// ****************************************************************************

/// -----------------------------------------------------------------------------
/// Synchro for edge detection
/// -----------------------------------------------------------------------------
always @ (posedge clk_12MHz or negedge vcore_rdy) begin
   if (!vcore_rdy) begin
      owi_data_reg1 <= 1'b0;
      owi_data_reg2 <= 1'b0;
      owi_data_reg3 <= 1'b0;
   end
   else begin
      owi_data_reg1 <= owi_data;
      owi_data_reg2 <= owi_data_reg1;
      owi_data_reg3 <= owi_data_reg2;
   end
end

always @ (posedge clk_12MHz or negedge vcore_rdy) begin
   if (!vcore_rdy) begin
      owi_ck_reg1 <= 1'b0;
      owi_ck_reg2 <= 1'b0;
      owi_ck_reg3 <= 1'b0;
   end
   else begin
      owi_ck_reg1 <= owi_ck;
      owi_ck_reg2 <= owi_ck_reg1;
      owi_ck_reg3 <= owi_ck_reg2;
   end
end

/// -----------------------------------------------------------------------------
/// Ranges
/// -----------------------------------------------------------------------------

// Ratio counter
// -----------------------------------------------------------------------------
always @ (posedge clk_12MHz or negedge vcore_rdy) begin
   if (!vcore_rdy) begin
      owi_ck_cnt <= 9'h000;
   end
   else if (owi_ck_reg2 && !owi_ck_reg3) begin  // Reset @ each owi_ck posedge
      owi_ck_cnt <= 9'h000;
   end
   else if (owi_ck_cnt != 9'h1ff) begin         // Clamp
      owi_ck_cnt <= owi_ck_cnt + 9'h001;
   end
end

// Range
// -----------------------------------------------------------------------------
always @ (posedge clk_12MHz or negedge vcore_rdy) begin
   if (!vcore_rdy) begin
      owi_ck_range <= 4'd0;
   end
   else if (owi_ck_reg2 && !owi_ck_reg3) begin
      if (`ns_IDLE) begin  // Prevent LUT from being refreshed during a transfer
         if      (owi_ck_cnt < 300) owi_ck_range <= 4'd0;
         else if (owi_ck_cnt < 311) owi_ck_range <= 4'd1;
         else if (owi_ck_cnt < 321) owi_ck_range <= 4'd2;
         else if (owi_ck_cnt < 332) owi_ck_range <= 4'd3;
         else if (owi_ck_cnt < 343) owi_ck_range <= 4'd4;
         else if (owi_ck_cnt < 354) owi_ck_range <= 4'd5;
         else if (owi_ck_cnt < 364) owi_ck_range <= 4'd6;
         else if (owi_ck_cnt < 375) owi_ck_range <= 4'd7;
         else if (owi_ck_cnt < 386) owi_ck_range <= 4'd8;
         else if (owi_ck_cnt < 396) owi_ck_range <= 4'd9;
         else if (owi_ck_cnt < 407) owi_ck_range <= 4'd10;
         else if (owi_ck_cnt < 418) owi_ck_range <= 4'd11;
         else if (owi_ck_cnt < 429) owi_ck_range <= 4'd12;
         else if (owi_ck_cnt < 439) owi_ck_range <= 4'd13;
         else                       owi_ck_range <= 4'd14;
      end
   end
end

// LUT
// -----------------------------------------------------------------------------
always @ (*) begin
   case (owi_ck_range)
   4'd0    : owi_bit_lgth =  7'd83;
   4'd1    : owi_bit_lgth =  7'd86;
   4'd2    : owi_bit_lgth =  7'd89;
   4'd3    : owi_bit_lgth =  7'd92;
   4'd4    : owi_bit_lgth =  7'd95;
   4'd5    : owi_bit_lgth =  7'd98;
   4'd6    : owi_bit_lgth = 7'd101;
   4'd7    : owi_bit_lgth = 7'd104;
   4'd8    : owi_bit_lgth = 7'd107;
   4'd9    : owi_bit_lgth = 7'd110;
   4'd10   : owi_bit_lgth = 7'd113;
   4'd11   : owi_bit_lgth = 7'd116;
   4'd12   : owi_bit_lgth = 7'd119;
   4'd13   : owi_bit_lgth = 7'd122;
   default : owi_bit_lgth = 7'd125;
   endcase
end

/// -----------------------------------------------------------------------------
/// FSM
/// -----------------------------------------------------------------------------
always @ (posedge clk_12MHz or negedge vcore_rdy) begin
   if (!vcore_rdy) begin
      owi_state <= IDLE;
   end
   else begin
      owi_state <= next_owi_state;
   end
end

always @ (*) begin

   next_owi_state = owi_state;

   case (owi_state)
   
   IDLE    : begin
      if (tx_clk_en) begin
         next_owi_state = TXW;            // TX
      end
      else if (!owi_data_reg2 && owi_data_reg3) begin
         next_owi_state = RX;             // RX
      end 
   end
   
   RX    : begin
      if (!rx_tx_clk_en) begin
         next_owi_state = IDLE;
      end 
   end
   
   TXW   : begin
      if (owi_ck_reg2 && !owi_ck_reg3) begin
         if (bit_cnt == 4'd0) begin       // Start on owi_ck posedge
            next_owi_state = TX1;
         end
         else if (bit_cnt == 4'd3) begin  // Resync on owi_ck posedge for bit 3.5
            next_owi_state = TX2;
         end
         else begin
            next_owi_state = TX3;         // Resync on owi_ck posedge for bit 7
         end
      end
   end
   
   TX1   : begin
      if (bit_cnt == 4'd2) begin
         if (otp_no_tx_resync) begin
            next_owi_state = TX3;         // When no resync, go directly to TX3 to wait for ib_delay negedge
         end
         else if (owi_bit_cnt == {1'b0,owi_bit_lgth[6:1]}) begin
            next_owi_state = TXW;         // When resync, go to TXW to wait for owi_clk posedge
         end
      end 
   end
   
   TX2   : begin
      if (bit_cnt == 4'd6) begin
         if (owi_bit_cnt == 7'd0) begin
            next_owi_state = TXW;         // When resync, go to TXW to wait for owi_clk posedge
         end
      end 
   end
   
   TX3   : begin
      if (!tx_clk_en || (bit_cnt == 4'd0) && !ib_delay) begin
         next_owi_state = IDLE;           // Wait for ib_delay negedge
      end 
   end
   
   default :  begin
      next_owi_state = IDLE;
   end
   
   endcase
end

/// -----------------------------------------------------------------------------
/// 12MHz division for RX and TX clock generation
/// -----------------------------------------------------------------------------
always @ (posedge clk_12MHz or negedge vcore_rdy) begin
   if (!vcore_rdy) begin
      owi_bit_cnt <= 7'd0;
   end
   else if (`ns_IDLE || `ns_TXW) begin
      owi_bit_cnt <= 7'd0;
   end
   else if (`s_TXW && (`ns_TX1 || `ns_TX3)) begin        // Synch @ start and bit 7
      owi_bit_cnt <= {1'b0,owi_bit_lgth[6:1]} - 7'd1;
   end
   else if (`s_TXW && `ns_TX2) begin                     // Synch @ bit 3.5
      owi_bit_cnt <= owi_bit_lgth;
   end
   else if (`ns_RX && (owi_data_reg2 ^ owi_data_reg3)) begin
      if (`s_IDLE) begin                                 // Synch @ start
         owi_bit_cnt <= {1'b0,owi_bit_lgth[6:1]} - 7'd4;
      end
      else if (!otp_no_rx_resync) begin                  // Resynch @ data edge (opt)
         owi_bit_cnt <= {1'b0,owi_bit_lgth[6:1]} - 7'd4;
      end
   end
   else if (owi_bit_cnt == 7'd0) begin
      owi_bit_cnt <= owi_bit_lgth;
   end
   else begin
      owi_bit_cnt <= owi_bit_cnt - 7'd1;
   end
end

/// -----------------------------------------------------------------------------
/// Clock generation
/// -----------------------------------------------------------------------------
assign rx_cgc_en = `s_RX & (owi_bit_cnt == 7'd0);

dig_cgc cgc_rx_clk (
   .clkout  ( rx_clk ),
   .clkin   ( clk_12MHz ),
   .en      ( rx_cgc_en ),
   .se      ( 1'b0 )
);

assign tx_cgc_en = (`s_TXW & (`ns_TX1 | `ns_TX3)) |
                   ((`s_TX1 | `s_TX2 | `s_TX3) & (owi_bit_cnt == {1'b0,owi_bit_lgth[6:1]})) |
                   (`s_RX & (owi_bit_cnt == owi_bit_lgth - 7'd8)) ;

dig_cgc cgc_tx_clk (
   .clkout  ( tx_clk ),
   .clkin   ( clk_12MHz ),
   .en      ( tx_cgc_en ),
   .se      ( 1'b0 )
);

endmodule
