[i2c, rtl] Initial FSM implementation for target mode

1. Implemented target-mode finite state machine
2. Added tx_fifo_depth_i and tx_fifo_wvalid_i ports
3. Implemented clock stretching when TX FIFO is empty during Read
4. Implemented tx_empty, tx_nonempty, and ack_stop (host sending
   Stop after Ack) interrupts

Signed-off-by: Igor Kouznetsov <igor.kouznetsov@wdc.com>
diff --git a/hw/ip/i2c/rtl/i2c_core.sv b/hw/ip/i2c/rtl/i2c_core.sv
index f177357..32a6e97 100644
--- a/hw/ip/i2c/rtl/i2c_core.sv
+++ b/hw/ip/i2c/rtl/i2c_core.sv
@@ -379,6 +379,8 @@
     .rx_fifo_wdata_o         (rx_fifo_wdata),
 
     .tx_fifo_rvalid_i        (tx_fifo_rvalid),
+    .tx_fifo_wvalid_i        (tx_fifo_wvalid),
+    .tx_fifo_depth_i         (tx_fifo_depth),
     .tx_fifo_rready_o        (tx_fifo_rready),
     .tx_fifo_rdata_i         (tx_fifo_rdata),
 
diff --git a/hw/ip/i2c/rtl/i2c_fsm.sv b/hw/ip/i2c/rtl/i2c_fsm.sv
index d653117..b242dcc 100644
--- a/hw/ip/i2c/rtl/i2c_fsm.sv
+++ b/hw/ip/i2c/rtl/i2c_fsm.sv
@@ -31,6 +31,8 @@
   output logic [7:0] rx_fifo_wdata_o,  // byte in rx_fifo read from target
 
   input        tx_fifo_rvalid_i, // indicates there is valid data in tx_fifo
+  input        tx_fifo_wvalid_i, // indicates data is being put into tx_fifo
+  input [5:0]  tx_fifo_depth_i,  // tx_fifo_depth
   output logic tx_fifo_rready_o, // populates tx_fifo
   input [7:0]  tx_fifo_rdata_i,  // byte in tx_fifo to be sent to host
 
@@ -97,13 +99,20 @@
   logic        log_stop;      // indicates stop is been issued
   logic        restart;       // indicates repeated start state is entered into
 
-  // Temporary assignments
-  assign tx_fifo_rready_o = tx_fifo_rvalid_i;
-  assign acq_fifo_wdata_o = {tx_fifo_rdata_i, 1'b0, 1'b0};
-  assign target_idle_o = 1'b1;
-  assign event_tx_empty_o = 1'b0;
-  assign event_tx_nonempty_o = 1'b0;
-  assign event_ack_stop_o = 1'b0;
+  // Target specific variables
+  logic        start_det;     // indicates start or repeated start is detected on the bus
+  logic        stop_det;      // indicates stop is detected on the bus
+  logic        address0_match;// indicates target's address0 matches the one sent by host
+  logic        address1_match;// indicates target's address1 matches the one sent by host
+  logic        address_match; // indicates one of target's addresses matches the one sent by host
+  logic [7:0]  input_byte;    // register for reads from host
+  logic        input_byte_clr;// clear input_byte contents
+
+  // Target bit counter variables
+  logic [3:0]  bit_idx;       // bit index including ack/nack
+  logic        bit_ack;       // indicates ACK bit been sent or received
+  logic        rw_bit;        // indicates host wants to read (1) or write (0)
+  logic        host_ack;      // indicates host acqnowledged transmitted byte
 
   // Clock counter implementation
   typedef enum logic [3:0] {
@@ -224,14 +233,84 @@
     end
   end
 
+  // (Repeated) Start condition detection by target
+  always_ff @ (posedge clk_i or negedge rst_ni) begin : s_detect
+    if (!rst_ni) begin
+      start_det <= 1'b0;
+    end else if (scl_i_q == 1'b1 && scl_i == 1'b1) begin
+      if (sda_i_q == 1'b1 && sda_i == '0) start_det <= 1'b1;
+      else start_det <= 1'b0;
+    end else begin
+      start_det <= 1'b0;
+    end
+  end
+
+  // Stop condition detection by target
+  always_ff @ (posedge clk_i or negedge rst_ni) begin : p_detect
+    if (!rst_ni) begin
+      stop_det <= 1'b0;
+    end else if (scl_i_q == 1'b1 && scl_i == 1'b1) begin
+      if (sda_i_q == '0 && sda_i == 1'b1) stop_det <= 1'b1;
+      else stop_det <= 1'b0;
+    end else begin
+      stop_det <= 1'b0;
+    end
+  end
+
+  // Bit counter on the target side
+  assign bit_ack = (bit_idx == 4'd8) && !start_det; // ack
+
+  // Increment counter on negative SCL edge
+  always_ff @ (posedge clk_i or negedge rst_ni) begin : tgt_bit_counter
+    if (!rst_ni) begin
+      bit_idx <= 4'd0;
+    end else if (start_det || bit_ack) begin
+      bit_idx <= 4'd0;
+    end else if (scl_i_q == 1'b1 && scl_i == '0) begin
+      bit_idx <= bit_idx + 1'b1;
+    end else begin
+      bit_idx <= bit_idx;
+    end
+  end
+
+  // Deserializer for a byte read from the bus on the target side
+  assign address0_match = ((input_byte[7:1] & target_mask0_i) == target_address0_i);
+  assign address1_match = ((input_byte[7:1] & target_mask1_i) == target_address1_i);
+  assign address_match = (address0_match || address1_match);
+  assign rw_bit = input_byte[0];
+
+  // Shift data in on positive SCL edge
+  always_ff @ (posedge clk_i or negedge rst_ni) begin : tgt_input_register
+    if (!rst_ni) begin
+      input_byte <= 8'h00;
+    end else if (input_byte_clr) begin
+      input_byte <= 8'h00;
+    end else if (scl_i_q == '0 && scl_i == 1'b1) begin
+      if (!bit_ack) input_byte[7:0] <= {input_byte[6:0], sda_i};  // MSB goes in first
+    end
+  end
+
+  // Detection by the target of ACK bit send by the host
+  always_ff @ (posedge clk_i or negedge rst_ni) begin : host_ack_register
+    if (!rst_ni) begin
+      host_ack <= 1'b0;
+    end else if (scl_i_q == '0 && scl_i == 1'b1) begin
+      if (bit_ack) host_ack <= ~sda_i;
+    end
+  end
+
   // State definitions
-  typedef enum logic [4:0] {
+  typedef enum logic [5:0] {
     Idle, PopFmtFifo, SetupStart, HoldStart, SetupStop, HoldStop,
         ClockLow, SetupBit, ClockPulse, HoldBit,
         ClockLowAck, SetupDevAck, ClockPulseAck, HoldDevAck,
         ReadClockLow, ReadSetupBit, ReadClockPulse, ReadHoldBit,
         HostClockLowAck, HostSetupBitAck, HostClockPulseAck, HostHoldBitAck,
-        Active, ClockStart, ClockStop
+        Active, ClockStart, ClockStop,
+        AddrRead, AddrAckWait, AddrAckSetup, AddrAckPulse, AddrAckHold,
+        TransmitWait, TransmitSetup, TransmitPulse, TransmitHold, TransmitAck,
+        AcquireByte, AcquireAckWait, AcquireAckSetup, AcquireAckPulse, AcquireAckHold,
+        PopTxFifo, StretchClock, AcquireSrP
   } state_e;
 
   state_e state_q, state_d;
@@ -239,18 +318,24 @@
   // Outputs for each state
   always_comb begin : state_outputs
     host_idle_o = 1'b1;
+    target_idle_o = 1'b1;
     sda_temp = 1'b1;
     scl_temp = 1'b1;
     fmt_fifo_rready_o = 1'b0;
     rx_fifo_wvalid_o = 1'b0;
     rx_fifo_wdata_o = 8'h00;
+    tx_fifo_rready_o = 1'b0;
     acq_fifo_wvalid_o = 1'b0;
+    acq_fifo_wdata_o = 10'b0;
     event_nak_o = 1'b0;
     event_scl_interference_o = 1'b0;
     event_sda_interference_o = 1'b0;
     event_sda_unstable_o = 1'b0;
     event_stretch_timeout_o = 1'b0;
     event_trans_complete_o = 1'b0;
+    event_tx_empty_o = 1'b0;
+    event_tx_nonempty_o = 1'b0;
+    event_ack_stop_o = 1'b0;
     unique case (state_q)
       // Idle: initial state, SDA and SCL are released (high)
       Idle : begin
@@ -444,21 +529,126 @@
         else scl_temp = 1'b0;
         fmt_fifo_rready_o = 1'b1;
       end
+      // AddrRead: read and compare target address
+      AddrRead : begin
+        target_idle_o = 1'b0;
+        if (bit_ack && address_match) begin
+          acq_fifo_wdata_o = {1'b0, 1'b1, input_byte}; // transfer data to acq_fifo
+          acq_fifo_wvalid_o = 1'b1;          // assert that acq_fifo has valid data
+          if (tx_fifo_depth_i == '0 && rw_bit) event_tx_empty_o = 1'b1;
+        end
+      end
+      // AddrAckWait: pause before acknowledging
+      AddrAckWait : begin
+        target_idle_o = 1'b0;
+      end
+      // AddrAckSetup: target pulls SDA low while SCL is low
+      AddrAckSetup : begin
+        target_idle_o = 1'b0;
+        sda_temp = 1'b0;
+      end
+      // AddrAckPulse: target pulls SDA low while SCL is released
+      AddrAckPulse : begin
+        target_idle_o = 1'b0;
+        sda_temp = 1'b0;
+      end
+      // AddrAckHold: target pulls SDA low while SCL is pulled low
+      AddrAckHold : begin
+        target_idle_o = 1'b0;
+        sda_temp = 1'b0;
+      end
+      // TransmitWait: pause before sending a bit
+      TransmitWait : begin
+        target_idle_o = 1'b0;
+      end
+      // TransmitSetup: target shifts indexed bit onto SDA while SCL is low
+      TransmitSetup : begin
+        target_idle_o = 1'b0;
+        sda_temp = tx_fifo_rdata_i[3'(bit_idx)];
+      end
+      // TransmitPulse: target shifts indexed bit onto SDA while SCL is released
+      TransmitPulse : begin
+        target_idle_o = 1'b0;
+        sda_temp = tx_fifo_rdata_i[3'(bit_idx)];
+      end
+      // TransmitHold: target shifts indexed bit onto SDA while SCL is pulled low
+      TransmitHold : begin
+        target_idle_o = 1'b0;
+        sda_temp = tx_fifo_rdata_i[3'(bit_idx)];
+      end
+      // TransmitAck: target waits for host to ACK transmission
+      TransmitAck : begin
+        target_idle_o = 1'b0;
+        if (tx_fifo_depth_i == 6'd1 && !tx_fifo_wvalid_i && host_ack) event_tx_empty_o = 1'b1;
+        if (host_ack && (start_det || stop_det)) event_ack_stop_o = 1'b1;
+      end
+      // PopTxFifo: populate tx_fifo
+      PopTxFifo : begin
+        target_idle_o = 1'b0;
+        tx_fifo_rready_o = 1'b1;
+      end
+      // StretchClock: target stretches the clock
+      StretchClock : begin
+        target_idle_o = 1'b0;
+        tx_fifo_rready_o = 1'b1;
+        scl_temp = 1'b0;
+        if (tx_fifo_depth_i == '0) event_tx_empty_o = 1'b1;
+      end
+      // AcquireByte: target acquires a byte
+      AcquireByte : begin
+        target_idle_o = 1'b0;
+        if (bit_ack) begin
+          acq_fifo_wdata_o = {1'b0, 1'b0, input_byte}; // transfer data to acq_fifo
+          acq_fifo_wvalid_o = 1'b1;          // assert that acq_fifo has valid data
+        end
+      end
+      // AcquireAckWait: pause before acknowledging
+      AcquireAckWait : begin
+        target_idle_o = 1'b0;
+      end
+      // AcquireAckSetup: target pulls SDA low while SCL is low
+      AcquireAckSetup : begin
+        target_idle_o = 1'b0;
+        sda_temp = 1'b0;
+      end
+      // AcquireAckPulse: target pulls SDA low while SCL is released
+      AcquireAckPulse : begin
+        target_idle_o = 1'b0;
+        sda_temp = 1'b0;
+      end
+      // AcquireAckHold: target pulls SDA low while SCL is pulled low
+      AcquireAckHold : begin
+        target_idle_o = 1'b0;
+        sda_temp = 1'b0;
+      end
+      // AcquireSrP: target acquires repeated Start or Stop
+      AcquireSrP : begin
+        if (start_det) acq_fifo_wdata_o = {1'b1, 1'b1, input_byte};
+        else acq_fifo_wdata_o = {1'b1, 1'b0, input_byte};
+        acq_fifo_wvalid_o = 1'b1;
+        if (tx_fifo_depth_i != '0) event_tx_nonempty_o = 1'b1;
+      end
       // default
       default : begin
         host_idle_o = 1'b1;
+        target_idle_o = 1'b1;
         sda_temp = 1'b1;
         scl_temp = 1'b1;
         fmt_fifo_rready_o = 1'b0;
         rx_fifo_wvalid_o = 1'b0;
         rx_fifo_wdata_o = 8'h00;
+        tx_fifo_rready_o = 1'b0;
         acq_fifo_wvalid_o = 1'b0;
+        acq_fifo_wdata_o = 10'b0;
         event_nak_o = 1'b0;
         event_scl_interference_o = 1'b0;
         event_sda_interference_o = 1'b0;
         event_sda_unstable_o = 1'b0;
         event_stretch_timeout_o = 1'b0;
         event_trans_complete_o = 1'b0;
+        event_tx_empty_o = 1'b0;
+        event_tx_nonempty_o = 1'b0;
+        event_ack_stop_o = 1'b0;
       end
     endcase
   end
@@ -477,13 +667,22 @@
     log_start = 1'b0;
     log_stop = 1'b0;
     restart = 1'b0;
+    input_byte_clr = 1'b0;
 
     unique case (state_q)
       // Idle: initial state, SDA and SCL are released (high)
       Idle : begin
         if (!host_enable_i && !target_enable_i) state_d = Idle; // Idle unless host is enabled
-        else if (!fmt_fifo_rvalid_i) state_d = Idle;
-        else state_d = Active;
+        else if (host_enable_i) begin
+          if (!fmt_fifo_rvalid_i) state_d = Idle;
+          else state_d = Active;
+        end else if (target_enable_i) begin
+          if (!start_det) state_d = Idle;
+          else begin
+            state_d = AddrRead;
+            input_byte_clr = 1'b1;
+          end
+        end
       end
 
       // SetupStart: SDA and SCL are released
@@ -745,6 +944,154 @@
         end
       end
 
+      // AddrRead: read and compare target address
+      AddrRead : begin
+        if (bit_ack) begin
+          if (address_match) begin
+            state_d = AddrAckWait;
+            load_tcount = 1'b1;
+            tcount_sel = tClockStart;
+          end else state_d = Idle;
+        end
+      end
+
+      // AddrAckWait: pause before acknowledging
+      AddrAckWait : begin
+        if (tcount_q == 20'd1) begin
+          state_d = AddrAckSetup;
+        end
+      end
+      // AddrAckSetup: target pulls SDA low while SCL is low
+      AddrAckSetup : begin
+        if (scl_i) state_d = AddrAckPulse;
+      end
+      // AddrAckPulse: target pulls SDA low while SCL is released
+      AddrAckPulse : begin
+        if (!scl_i) begin
+          state_d = AddrAckHold;
+          load_tcount = 1'b1;
+          tcount_sel = tClockStart;
+        end
+      end
+      // AddrAckHold: target pulls SDA low while SCL is pulled low
+      AddrAckHold : begin
+        if (tcount_q == 20'd1) begin
+          if (rw_bit) begin
+            if (tx_fifo_rvalid_i) begin
+              state_d = TransmitWait;
+              load_tcount = 1'b1;
+              tcount_sel = tClockLow;
+            end else state_d = StretchClock;
+          end else state_d = AcquireByte;
+        end
+      end
+
+      // TransmitWait: pause before sending a bit
+      TransmitWait : begin
+        if (tcount_q == 20'd1) begin
+          state_d = TransmitSetup;
+        end
+      end
+      // TransmitSetup: target shifts indexed bit onto SDA while SCL is low
+      TransmitSetup : begin
+        if (scl_i) state_d = TransmitPulse;
+      end
+      // TransmitPulse: target shifts indexed bit onto SDA while SCL is released
+      TransmitPulse : begin
+        if (!scl_i) begin
+          state_d = TransmitHold;
+          load_tcount = 1'b1;
+          tcount_sel = tClockStart;
+        end
+      end
+      // TransmitHold: target shifts indexed bit onto SDA while SCL is pulled low
+      TransmitHold : begin
+        if (tcount_q == 20'd1) begin
+          load_tcount = 1'b1;
+          tcount_sel = tClockLow;
+          if (bit_ack) begin
+            state_d = TransmitAck;
+          end else begin
+            state_d = TransmitWait;
+          end
+        end
+      end
+      // TransmitAck: target waits for host to ACK transmission
+      TransmitAck : begin
+        if (scl_i) begin
+          if (host_ack) state_d = PopTxFifo;
+          else begin
+            if (start_det || stop_det) state_d = AcquireSrP;
+          end
+        end
+      end
+
+      // PopTxFifo: populate tx_fifo
+      PopTxFifo : begin
+        if (!target_enable_i) begin
+          state_d = Idle;
+        end else if (tx_fifo_depth_i == 6'd1 && !tx_fifo_wvalid_i) begin
+          state_d = StretchClock;
+        end else begin
+          state_d = TransmitWait;
+          load_tcount = 1'b1;
+          tcount_sel = tClockLow;
+        end
+      end
+
+      // StretchClock: target stretches the clock
+      StretchClock : begin
+        if (tx_fifo_depth_i == '0) begin
+          state_d = StretchClock;
+        end else begin
+          state_d = TransmitWait;
+          load_tcount = 1'b1;
+          tcount_sel = tClockLow;
+        end
+      end
+
+      // AcquireByte: target acquires a byte
+      AcquireByte : begin
+        if (bit_ack) begin
+          state_d = AcquireAckWait;
+          load_tcount = 1'b1;
+          tcount_sel = tClockStart;
+        end
+      end
+
+      // AcquireAckWait: pause before acknowledging
+      AcquireAckWait : begin
+        if (tcount_q == 20'd1) begin
+          state_d = AcquireAckSetup;
+        end
+      end
+      // AcquireAckSetup: target pulls SDA low while SCL is low
+      AcquireAckSetup : begin
+        if (scl_i) state_d = AcquireAckPulse;
+      end
+      // AcquireAckPulse: target pulls SDA low while SCL is released
+      AcquireAckPulse : begin
+        if (!scl_i) begin
+          state_d = AcquireAckHold;
+          load_tcount = 1'b1;
+          tcount_sel = tClockStart;
+        end
+      end
+      // AcquireAckHold: target pulls SDA low while SCL is pulled low
+      AcquireAckHold : begin
+        if (tcount_q == 20'd1) begin
+          if (bit_ack) begin
+            if (start_det || stop_det) state_d = AcquireSrP;
+            else state_d = AcquireByte;
+          end
+        end
+      end
+
+      // AcquireSrP: target acquires repeated Start or Stop
+      AcquireSrP : begin
+        state_d = Idle;
+      end
+
       // default
       default : begin
         state_d = Idle;
@@ -759,6 +1106,7 @@
         log_start = 1'b0;
         log_stop = 1'b0;
         restart = 1'b0;
+        input_byte_clr = 1'b0;
       end
     endcase
   end