|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | // | 
|  | // Description: I2C finite state machine | 
|  |  | 
|  | module i2c_fsm import i2c_pkg::*; | 
|  | #( | 
|  | parameter int FifoDepth = 64, | 
|  | localparam int FifoDepthWidth = $clog2(FifoDepth+1) | 
|  | ) ( | 
|  | input        clk_i,  // clock | 
|  | input        rst_ni, // active low reset | 
|  |  | 
|  | input        scl_i,  // serial clock input from i2c bus | 
|  | output logic scl_o,  // serial clock output to i2c bus | 
|  | input        sda_i,  // serial data input from i2c bus | 
|  | output logic sda_o,  // serial data output to i2c bus | 
|  |  | 
|  | input        host_enable_i, // enable host functionality | 
|  | input        target_enable_i, // enable target functionality | 
|  |  | 
|  | input        fmt_fifo_rvalid_i, // indicates there is valid data in fmt_fifo | 
|  | input        fmt_fifo_wvalid_i, // indicates data is being put into fmt_fifo | 
|  | input [6:0]  fmt_fifo_depth_i,  // fmt_fifo_depth | 
|  | output logic fmt_fifo_rready_o, // populates fmt_fifo | 
|  | input [7:0]  fmt_byte_i,        // byte in fmt_fifo to be sent to target | 
|  | input        fmt_flag_start_before_i, // issue start before sending byte | 
|  | input        fmt_flag_stop_after_i,   // issue stop after sending byte | 
|  | input        fmt_flag_read_bytes_i,   // indicates byte is an number of reads | 
|  | input        fmt_flag_read_continue_i,// host to send Ack to final byte read | 
|  | input        fmt_flag_nak_ok_i,       // no Ack is expected | 
|  |  | 
|  | output logic       rx_fifo_wvalid_o, // high if there is valid data in rx_fifo | 
|  | 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 [FifoDepthWidth-1:0]  tx_fifo_depth_i,  // tx_fifo_depth | 
|  | output logic tx_fifo_rready_o, // pop entry from tx_fifo | 
|  | input [7:0]  tx_fifo_rdata_i,  // byte in tx_fifo to be sent to host | 
|  |  | 
|  | output logic       acq_fifo_wvalid_o, // high if there is valid data in acq_fifo | 
|  | output logic [9:0] acq_fifo_wdata_o,  // byte and signal in acq_fifo read from target | 
|  | input [FifoDepthWidth-1:0] acq_fifo_depth_i, | 
|  | output logic       acq_fifo_wready_o, // local version of ready | 
|  | input [9:0]        acq_fifo_rdata_i,  // only used for assertion | 
|  |  | 
|  | output logic       host_idle_o,      // indicates the host is idle | 
|  | output logic       target_idle_o,    // indicates the target is idle | 
|  |  | 
|  | input [15:0] thigh_i,    // high period of the SCL in clock units | 
|  | input [15:0] tlow_i,     // low period of the SCL in clock units | 
|  | input [15:0] t_r_i,      // rise time of both SDA and SCL in clock units | 
|  | input [15:0] t_f_i,      // fall time of both SDA and SCL in clock units | 
|  | input [15:0] thd_sta_i,  // hold time for (repeated) START in clock units | 
|  | input [15:0] tsu_sta_i,  // setup time for repeated START in clock units | 
|  | input [15:0] tsu_sto_i,  // setup time for STOP in clock units | 
|  | input [15:0] tsu_dat_i,  // data setup time in clock units | 
|  | input [15:0] thd_dat_i,  // data hold time in clock units | 
|  | input [15:0] t_buf_i,    // bus free time between STOP and START in clock units | 
|  | input [30:0] stretch_timeout_i,  // max time target may stretch the clock | 
|  | input        timeout_enable_i,   // assert if target stretches clock past max | 
|  | input [31:0] host_timeout_i,     // max time target waits for host to pull clock down | 
|  |  | 
|  | input logic [6:0] target_address0_i, | 
|  | input logic [6:0] target_mask0_i, | 
|  | input logic [6:0] target_address1_i, | 
|  | input logic [6:0] target_mask1_i, | 
|  |  | 
|  | output logic event_nak_o,              // target didn't Ack when expected | 
|  | output logic event_scl_interference_o, // other device forcing SCL low | 
|  | output logic event_sda_interference_o, // other device forcing SDA low | 
|  | output logic event_stretch_timeout_o,  // target stretches clock past max time | 
|  | output logic event_sda_unstable_o,     // SDA is not constant during SCL pulse | 
|  | output logic event_cmd_complete_o,     // Command is complete | 
|  | output logic event_tx_stretch_o,       // tx transaction is being stretched | 
|  | output logic event_unexp_stop_o,       // target received an unexpected stop | 
|  | output logic event_host_timeout_o      // host ceased sending SCL pulses during ongoing transactn | 
|  | ); | 
|  |  | 
|  | // I2C bus clock timing variables | 
|  | logic [19:0] tcount_q;      // current counter for setting delays | 
|  | logic [19:0] tcount_d;      // next counter for setting delays | 
|  | logic        load_tcount;   // indicates counter must be loaded | 
|  | logic [31:0] stretch_idle_cnt; // counter for clock being stretched by target | 
|  | // or clock idle by host. | 
|  | logic        stretch_en; | 
|  |  | 
|  | // Bit and byte counter variables | 
|  | logic [2:0]  bit_index;     // bit being transmitted to or read from the bus | 
|  | logic        bit_decr;      // indicates bit_index must be decremented by 1 | 
|  | logic        bit_clr;       // indicates bit_index must be reset to 7 | 
|  | logic [8:0]  byte_num;      // number of bytes to read | 
|  | logic [8:0]  byte_index;    // byte being read from the bus | 
|  | logic        byte_decr;     // indicates byte_index must be decremented by 1 | 
|  | logic        byte_clr;      // indicates byte_index must be reset to byte_num | 
|  |  | 
|  | // Other internal variables | 
|  | logic        scl_q;         // scl internal flopped | 
|  | logic        sda_q;         // data internal flopped | 
|  | logic        scl_d;         // scl internal | 
|  | logic        sda_d;         // data internal | 
|  | logic        scl_i_q;       // scl_i delayed by one clock | 
|  | logic        sda_i_q;       // sda_i delayed by one clock | 
|  | logic [7:0]  read_byte;     // register for reads from target | 
|  | logic        read_byte_clr; // clear read_byte contents | 
|  | logic        shift_data_en; // indicates data must be shifted in from the bus | 
|  | logic        trans_started; // indicates a transaction has started | 
|  | logic        pend_restart;  // there is a pending restart waiting to be processed | 
|  | logic        req_restart;   // request restart | 
|  | logic        log_start;     // indicates start is been issued | 
|  | logic        log_stop;      // indicates stop is been issued | 
|  |  | 
|  | // 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 | 
|  | logic        acq_fifo_wready; | 
|  | logic        stretch_addr; | 
|  | logic        stretch_tx; | 
|  | logic        expect_stop; | 
|  |  | 
|  | // 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 acknowledged transmitted byte | 
|  |  | 
|  |  | 
|  | // Clock counter implementation | 
|  | typedef enum logic [3:0] { | 
|  | tSetupStart, | 
|  | tHoldStart, | 
|  | tSetupData, | 
|  | tClockStart, | 
|  | tClockLow, | 
|  | tClockPulse, | 
|  | tHoldBit, | 
|  | tClockStop, | 
|  | tSetupStop, | 
|  | tHoldStop, | 
|  | tNoDelay | 
|  | } tcount_sel_e; | 
|  |  | 
|  | tcount_sel_e tcount_sel; | 
|  |  | 
|  | always_comb begin : counter_functions | 
|  | tcount_d = tcount_q; | 
|  | if (load_tcount) begin | 
|  | unique case (tcount_sel) | 
|  | tSetupStart : tcount_d = 20'(t_r_i) + 20'(tsu_sta_i); | 
|  | tHoldStart  : tcount_d = 20'(t_f_i) + 20'(thd_sta_i); | 
|  | tSetupData  : tcount_d = 20'(t_r_i) + 20'(tsu_dat_i); | 
|  | tClockStart : tcount_d = 20'(thd_dat_i); | 
|  | tClockLow   : tcount_d = 20'(tlow_i) - 20'(thd_dat_i); | 
|  | tClockPulse : tcount_d = 20'(t_r_i) + 20'(thigh_i) + 20'(t_f_i); | 
|  | tHoldBit    : tcount_d = 20'(t_f_i) + 20'(thd_dat_i); | 
|  | tClockStop  : tcount_d = 20'(t_f_i) + 20'(tlow_i) - 20'(thd_dat_i); | 
|  | tSetupStop  : tcount_d = 20'(t_r_i) + 20'(tsu_sto_i); | 
|  | tHoldStop   : tcount_d = 20'(t_r_i) + 20'(t_buf_i) - 20'(tsu_sta_i); | 
|  | tNoDelay    : tcount_d = 20'h00001; | 
|  | default     : tcount_d = 20'h00001; | 
|  | endcase | 
|  | end else if (stretch_idle_cnt == '0 || target_enable_i) begin | 
|  | tcount_d = tcount_q - 1'b1; | 
|  | end else begin | 
|  | tcount_d = tcount_q;  // pause timer if clock is stretched | 
|  | end | 
|  | end | 
|  |  | 
|  | logic unused_fifo_outputs; | 
|  | assign unused_fifo_outputs = |{tx_fifo_depth_i, tx_fifo_wvalid_i, fmt_fifo_wvalid_i}; | 
|  |  | 
|  | always_ff @ (posedge clk_i or negedge rst_ni) begin : clk_counter | 
|  | if (!rst_ni) begin | 
|  | tcount_q <= '1; | 
|  | end else begin | 
|  | tcount_q <= tcount_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Clock stretching/idle detection when i2c_ctrl. | 
|  | // When in host mode, this is a stretch count for how long an external target | 
|  | // has stretched the clock. | 
|  | // When in target mode, this is an idle count for how long an external host | 
|  | // has kept the clock idle after a START indication. | 
|  | always_ff @ (posedge clk_i or negedge rst_ni) begin : clk_stretch | 
|  | if (!rst_ni) begin | 
|  | stretch_idle_cnt <= '0; | 
|  | end else if (stretch_en && scl_d && !scl_i) begin | 
|  | stretch_idle_cnt <= stretch_idle_cnt + 1'b1; | 
|  | end else if (!target_idle_o && event_host_timeout_o) begin | 
|  | // If the host has timed out, reset the counter and try again | 
|  | stretch_idle_cnt <= '0; | 
|  | end else if (!target_idle_o && scl_i) begin | 
|  | stretch_idle_cnt <= stretch_idle_cnt + 1'b1; | 
|  | end else begin | 
|  | stretch_idle_cnt <= '0; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Bit index implementation | 
|  | always_ff @ (posedge clk_i or negedge rst_ni) begin : bit_counter | 
|  | if (!rst_ni) begin | 
|  | bit_index <= 3'd7; | 
|  | end else if (bit_clr) begin | 
|  | bit_index <= 3'd7; | 
|  | end else if (bit_decr) begin | 
|  | bit_index <= bit_index - 1'b1; | 
|  | end else begin | 
|  | bit_index <= bit_index; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Deserializer for a byte read from the bus | 
|  | always_ff @ (posedge clk_i or negedge rst_ni) begin : read_register | 
|  | if (!rst_ni) begin | 
|  | read_byte <= 8'h00; | 
|  | end else if (read_byte_clr) begin | 
|  | read_byte <= 8'h00; | 
|  | end else if (shift_data_en) begin | 
|  | read_byte[7:0] <= {read_byte[6:0], sda_i};  // MSB goes in first | 
|  | end | 
|  | end | 
|  |  | 
|  | // Number of bytes to read | 
|  | always_comb begin : byte_number | 
|  | if (!fmt_flag_read_bytes_i) byte_num = 9'd0; | 
|  | else if (fmt_byte_i == '0) byte_num = 9'd256; | 
|  | else byte_num = 9'(fmt_byte_i); | 
|  | end | 
|  |  | 
|  | // Byte index implementation | 
|  | always_ff @ (posedge clk_i or negedge rst_ni) begin : byte_counter | 
|  | if (!rst_ni) begin | 
|  | byte_index <= '0; | 
|  | end else if (byte_clr) begin | 
|  | byte_index <= byte_num; | 
|  | end else if (byte_decr) begin | 
|  | byte_index <= byte_index - 1'b1; | 
|  | end else begin | 
|  | byte_index <= byte_index; | 
|  | end | 
|  | end | 
|  |  | 
|  | // SDA and SCL at the previous clock edge | 
|  | always_ff @ (posedge clk_i or negedge rst_ni) begin : bus_prev | 
|  | if (!rst_ni) begin | 
|  | scl_i_q <= 1'b1; | 
|  | sda_i_q <= 1'b1; | 
|  | end else begin | 
|  | scl_i_q <= scl_i; | 
|  | sda_i_q <= sda_i; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Registers whether a transaction start has been observed. | 
|  | // A transaction start does not include a "restart", but rather | 
|  | // the first start after enabling i2c, or a start observed after a | 
|  | // stop. | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | trans_started <= '0; | 
|  | end else if (trans_started && !host_enable_i) begin | 
|  | trans_started <= '0; | 
|  | end else if (log_start) begin | 
|  | trans_started <= 1'b1; | 
|  | end else if (log_stop) begin | 
|  | trans_started <= 1'b0; | 
|  | end | 
|  | end | 
|  |  | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | pend_restart <= '0; | 
|  | end else if (pend_restart && !host_enable_i) begin | 
|  | pend_restart <= '0; | 
|  | end else if (req_restart) begin | 
|  | pend_restart <= 1'b1; | 
|  | end else if (log_start) begin | 
|  | pend_restart <= '0; | 
|  | end | 
|  | end | 
|  |  | 
|  | // (Repeated) Start condition detection by target | 
|  | assign start_det = target_enable_i && (scl_i_q && scl_i) & (sda_i_q && !sda_i); | 
|  |  | 
|  | // Stop condition detection by target | 
|  | assign stop_det = target_enable_i && (scl_i_q && scl_i) & (!sda_i_q && sda_i); | 
|  |  | 
|  | // Bit counter on the target side | 
|  | assign bit_ack = (bit_idx == 4'd8); // 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) begin | 
|  | bit_idx <= 4'd0; | 
|  | end else if (scl_i_q && !scl_i) begin | 
|  | // input byte clear is always asserted on a "start" | 
|  | // condition. | 
|  | if (input_byte_clr || bit_ack) bit_idx <= 4'd0; | 
|  | else 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); | 
|  |  | 
|  | // 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 && scl_i) 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 && scl_i) begin | 
|  | if (bit_ack) host_ack <= ~sda_i; | 
|  | end | 
|  | end | 
|  |  | 
|  | // An artificial acq_fifo_wready is used here to ensure we always have | 
|  | // space to asborb a stop / repeat start format byte.  Without guaranteeing | 
|  | // space for this entry, the target module would need to stretch the | 
|  | // repeat start / stop indication.  If a system does not support stretching, | 
|  | // there's no good way for a stop to be NACK'd. | 
|  | logic [FifoDepthWidth-1:0] acq_fifo_remainder; | 
|  | assign acq_fifo_remainder = FifoDepth - acq_fifo_depth_i; | 
|  | assign acq_fifo_wready = acq_fifo_remainder > FifoDepthWidth'(1'b1); | 
|  |  | 
|  | // State definitions | 
|  | typedef enum logic [5:0] { | 
|  | Idle, | 
|  | /////////////////////// | 
|  | // Host function states | 
|  | /////////////////////// | 
|  | Active, PopFmtFifo, | 
|  | // Host function starts a transaction | 
|  | SetupStart, HoldStart, ClockStart, | 
|  | // Host function stops a transaction | 
|  | SetupStop, HoldStop, ClockStop, | 
|  | // Host function transmits a bit to the external target | 
|  | ClockLow, ClockPulse, HoldBit, | 
|  | // Host function recevies an ack from the external target | 
|  | ClockLowAck, ClockPulseAck, HoldDevAck, | 
|  | // Host function reads a bit from the external target | 
|  | ReadClockLow, ReadClockPulse, ReadHoldBit, | 
|  | // Host function transmits an ack to the external target | 
|  | HostClockLowAck, HostClockPulseAck, HostHoldBitAck, | 
|  |  | 
|  | ///////////////////////// | 
|  | // Target function states | 
|  | ///////////////////////// | 
|  |  | 
|  | // Target function receives start and address from external host | 
|  | AcquireStart, AddrRead, | 
|  | // Target function acknowledges the address and returns an ack to external host | 
|  | AddrAckWait, AddrAckSetup, AddrAckPulse, AddrAckHold, | 
|  | // Target function sends read data to external host-receiver | 
|  | TransmitWait, TransmitSetup, TransmitPulse, TransmitHold, | 
|  | // Target function receives ack from external host | 
|  | TransmitAck, TransmitAckPulse, WaitForStop, | 
|  | // Target function receives write data from the external host | 
|  | AcquireByte, | 
|  | // Target function sends ack to external host | 
|  | AcquireAckWait, AcquireAckSetup, AcquireAckPulse, AcquireAckHold, | 
|  | // Target function clock stretch handling. | 
|  | StretchAddr, | 
|  | StretchTx, StretchTxSetup, | 
|  | StretchAcqFull | 
|  | } state_e; | 
|  |  | 
|  | state_e state_q, state_d; | 
|  |  | 
|  |  | 
|  | // enable sda interference detection | 
|  | // Detects when the controller releases sda to be pulled high, but the line | 
|  | // is unexpectedly held low by another driver. | 
|  | logic en_sda_interf_det; | 
|  | logic [16:0] sda_rise_cnt; | 
|  |  | 
|  | // sda_rise_latency refers to the time between | 
|  | // changing sda_o to 1 and sampling sda_i as 1. | 
|  | // This value is a combination of the bus rise time and the | 
|  | // input sychronization delay | 
|  | logic [16:0] sda_rise_latency; | 
|  | assign sda_rise_latency = t_r_i + 16'h2; | 
|  |  | 
|  | // When detection is enabled, count through the rise time. | 
|  | // Once rise time count is reached, hold in place until disabled. | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | sda_rise_cnt <= '0; | 
|  | end else if (!en_sda_interf_det && |sda_rise_cnt) begin | 
|  | // When detection is disabled, 0 the count. | 
|  | // Only 0 the count if the count is currently non-zero to avoid | 
|  | // unnecessary toggling. | 
|  | sda_rise_cnt <= '0; | 
|  | end else if (en_sda_interf_det && sda_rise_cnt < sda_rise_latency) begin | 
|  | sda_rise_cnt <= sda_rise_cnt + 1'b1; | 
|  | end | 
|  | end | 
|  |  | 
|  | // There are two conditions of sda interference: | 
|  | // 1. When the host function is first enabled, but for some reason sda_i is already 0. | 
|  | // 2. Any time the host function is trying to drive a 1 but it observes a 0 instead. | 
|  | // | 
|  | // When the count is reached, we are pass the rise time period. | 
|  | // Now check for any inconsistency in the sda value. | 
|  | assign event_sda_interference_o = (host_idle_o & host_enable_i & !sda_i) | | 
|  | ((sda_rise_cnt == sda_rise_latency) & (sda_o & !sda_i)); | 
|  |  | 
|  | logic rw_bit_q; | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | rw_bit_q <= '0; | 
|  | end else if (bit_ack && address_match) begin | 
|  | rw_bit_q <= rw_bit; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Reverse the bit order since data should be sent out MSB first | 
|  | logic [7:0] tx_fifo_rdata; | 
|  | assign tx_fifo_rdata = {<<1{tx_fifo_rdata_i}}; | 
|  |  | 
|  | // The usage of target_idle_o directly confuses xcelium and leads the | 
|  | // the simulator to a combinational loop. While it may be a tool recognized | 
|  | // loop, it is not an actual physical loop, since target_idle affects only | 
|  | // state_d, which is not used directly by any logic in this module. | 
|  | // This is a work around for a known tool limitation. | 
|  | logic target_idle; | 
|  | assign target_idle = target_idle_o; | 
|  |  | 
|  | // During a host issued read, a stop was received without first seeing a nack. | 
|  | // This may be harmless but is technically illegal behavior, notify software. | 
|  | assign event_unexp_stop_o = !target_idle & rw_bit_q & stop_det & !expect_stop; | 
|  |  | 
|  | // Outputs for each state | 
|  | always_comb begin : state_outputs | 
|  | host_idle_o = 1'b1; | 
|  | target_idle_o = 1'b1; | 
|  | sda_d = 1'b1; | 
|  | scl_d = 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_unstable_o = 1'b0; | 
|  | event_cmd_complete_o = 1'b0; | 
|  | rw_bit = rw_bit_q; | 
|  | stretch_en = 1'b0; | 
|  | expect_stop = 1'b0; | 
|  | unique case (state_q) | 
|  | // Idle: initial state, SDA and SCL are released (high) | 
|  | Idle : begin | 
|  | host_idle_o = 1'b1; | 
|  | sda_d = 1'b1; | 
|  | scl_d = 1'b1; | 
|  | end | 
|  | // SetupStart: SDA and SCL are released | 
|  | SetupStart : begin | 
|  | host_idle_o = 1'b0; | 
|  | sda_d = 1'b1; | 
|  | scl_d = 1'b1; | 
|  | if (log_start) event_cmd_complete_o = pend_restart; | 
|  | end | 
|  | // HoldStart: SDA is pulled low, SCL is released | 
|  | HoldStart : begin | 
|  | host_idle_o = 1'b0; | 
|  | sda_d = 1'b0; | 
|  | scl_d = 1'b1; | 
|  | end | 
|  | // ClockStart: SCL is pulled low, SDA stays low | 
|  | ClockStart : begin | 
|  | host_idle_o = 1'b0; | 
|  | sda_d = 1'b0; | 
|  | scl_d = 1'b0; | 
|  | end | 
|  | ClockLow : begin | 
|  | host_idle_o = 1'b0; | 
|  | sda_d = fmt_byte_i[bit_index]; | 
|  | scl_d = 1'b0; | 
|  | end | 
|  | // ClockPulse: SCL is released, SDA keeps the indexed bit value | 
|  | ClockPulse : begin | 
|  | host_idle_o = 1'b0; | 
|  | sda_d = fmt_byte_i[bit_index]; | 
|  | scl_d = 1'b1; | 
|  | stretch_en = 1'b1; | 
|  | if (scl_i_q && !scl_i)  event_scl_interference_o = 1'b1; | 
|  | if (sda_i_q != sda_i)   event_sda_unstable_o = 1'b1; | 
|  | end | 
|  | // HoldBit: SCL is pulled low | 
|  | HoldBit : begin | 
|  | host_idle_o = 1'b0; | 
|  | sda_d = fmt_byte_i[bit_index]; | 
|  | scl_d = 1'b0; | 
|  | end | 
|  | // ClockLowAck: SCL pulled low, SDA is released | 
|  | ClockLowAck : begin | 
|  | host_idle_o = 1'b0; | 
|  | sda_d = 1'b1; | 
|  | scl_d = 1'b0; | 
|  | end | 
|  | // ClockPulseAck: SCL is released | 
|  | ClockPulseAck : begin | 
|  | host_idle_o = 1'b0; | 
|  | sda_d = 1'b1; | 
|  | scl_d = 1'b1; | 
|  | if (sda_i && !fmt_flag_nak_ok_i) event_nak_o = 1'b1; | 
|  | stretch_en = 1'b1; | 
|  | if (scl_i_q && !scl_i)  event_scl_interference_o = 1'b1; | 
|  | if (sda_i_q != sda_i)   event_sda_unstable_o = 1'b1; | 
|  | end | 
|  | // HoldDevAck: SCL is pulled low | 
|  | HoldDevAck : begin | 
|  | host_idle_o = 1'b0; | 
|  | sda_d = 1'b1; | 
|  | scl_d = 1'b0; | 
|  | end | 
|  | // ReadClockLow: SCL is pulled low, SDA is released | 
|  | ReadClockLow : begin | 
|  | host_idle_o = 1'b0; | 
|  | sda_d = 1'b1; | 
|  | scl_d = 1'b0; | 
|  | end | 
|  | // ReadClockPulse: SCL is released, the indexed bit value is read off SDA | 
|  | ReadClockPulse : begin | 
|  | host_idle_o = 1'b0; | 
|  | scl_d = 1'b1; | 
|  | stretch_en = 1'b1; | 
|  | if (scl_i_q && !scl_i)  event_scl_interference_o = 1'b1; | 
|  | if (sda_i_q != sda_i)   event_sda_unstable_o = 1'b1; | 
|  | end | 
|  | // ReadHoldBit: SCL is pulled low | 
|  | ReadHoldBit : begin | 
|  | host_idle_o = 1'b0; | 
|  | scl_d = 1'b0; | 
|  | if (bit_index == '0 && tcount_q == 20'd1) begin | 
|  | rx_fifo_wdata_o = read_byte;  // transfer read data to rx_fifo | 
|  | rx_fifo_wvalid_o = 1'b1;      // assert that rx_fifo has valid data | 
|  | end | 
|  | end | 
|  | // HostClockLowAck: SCL pulled low, SDA is conditional | 
|  | HostClockLowAck : begin | 
|  | host_idle_o = 1'b0; | 
|  | scl_d = 1'b0; | 
|  |  | 
|  | // If it is the last byte of a read, send a NAK before the stop. | 
|  | // Otherwise send the ack. | 
|  | if (fmt_flag_read_continue_i) sda_d = 1'b0; | 
|  | else if (byte_index == 9'd1) sda_d = 1'b1; | 
|  | else sda_d = 1'b0; | 
|  | end | 
|  | // HostClockPulseAck: SCL is released | 
|  | HostClockPulseAck : begin | 
|  | host_idle_o = 1'b0; | 
|  | if (fmt_flag_read_continue_i) sda_d = 1'b0; | 
|  | else if (byte_index == 9'd1) sda_d = 1'b1; | 
|  | else sda_d = 1'b0; | 
|  | scl_d = 1'b1; | 
|  | stretch_en = 1'b1; | 
|  | if (scl_i_q && !scl_i)  event_scl_interference_o = 1'b1; | 
|  | if (sda_i_q != sda_i)   event_sda_unstable_o = 1'b1; | 
|  | end | 
|  | // HostHoldBitAck: SCL is pulled low | 
|  | HostHoldBitAck : begin | 
|  | host_idle_o = 1'b0; | 
|  | if (fmt_flag_read_continue_i) sda_d = 1'b0; | 
|  | else if (byte_index == 9'd1) sda_d = 1'b1; | 
|  | else sda_d = 1'b0; | 
|  | scl_d = 1'b0; | 
|  | end | 
|  | // ClockStop: SCL is pulled low, SDA stays low | 
|  | ClockStop : begin | 
|  | host_idle_o = 1'b0; | 
|  | sda_d = 1'b0; | 
|  | scl_d = 1'b0; | 
|  | end | 
|  | // SetupStop: SDA is pulled low, SCL is released | 
|  | SetupStop : begin | 
|  | host_idle_o = 1'b0; | 
|  | sda_d = 1'b0; | 
|  | scl_d = 1'b1; | 
|  | end | 
|  | // HoldStop: SDA and SCL are released | 
|  | HoldStop : begin | 
|  | host_idle_o = 1'b0; | 
|  | sda_d = 1'b1; | 
|  | scl_d = 1'b1; | 
|  | event_cmd_complete_o = 1'b1; | 
|  | end | 
|  | // Active: continue while keeping SCL low | 
|  | Active : begin | 
|  | host_idle_o = 1'b0; | 
|  |  | 
|  | // If this is a transaction start, do not drive scl low | 
|  | // since in the next state we will drive it high to initiate | 
|  | // the start bit. | 
|  | // If this is a restart, continue driving the clock low. | 
|  | scl_d = fmt_flag_start_before_i && !trans_started; | 
|  | end | 
|  | // PopFmtFifo: populate fmt_fifo | 
|  | PopFmtFifo : begin | 
|  | host_idle_o = 1'b0; | 
|  | if (fmt_flag_stop_after_i) scl_d = 1'b1; | 
|  | else scl_d = 1'b0; | 
|  | fmt_fifo_rready_o = 1'b1; | 
|  | end | 
|  | // AcquireStart: hold start condition | 
|  | AcquireStart : begin | 
|  | target_idle_o = 1'b0; | 
|  | end | 
|  | // AddrRead: read and compare target address | 
|  | AddrRead : begin | 
|  | target_idle_o = 1'b0; | 
|  | rw_bit = input_byte[0]; | 
|  | 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_d = 1'b0; | 
|  | end | 
|  | // AddrAckPulse: target pulls SDA low while SCL is released | 
|  | AddrAckPulse : begin | 
|  | target_idle_o = 1'b0; | 
|  | sda_d = 1'b0; | 
|  | end | 
|  | // AddrAckHold: target pulls SDA low while SCL is pulled low | 
|  | AddrAckHold : begin | 
|  | target_idle_o = 1'b0; | 
|  | sda_d = 1'b0; | 
|  |  | 
|  | // Upon transition to next state, populate the acquisition fifo | 
|  | if (tcount_q == 20'd1) begin | 
|  | // only write to fifo if stretching conditions are not met | 
|  | acq_fifo_wvalid_o = ~stretch_addr; | 
|  | acq_fifo_wdata_o = {AcqStart, input_byte}; | 
|  | end | 
|  | end | 
|  | // TransmitWait: Check if data is available prior to transmit | 
|  | 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_d = tx_fifo_rdata[3'(bit_idx)]; | 
|  | end | 
|  | // TransmitPulse: target holds indexed bit onto SDA while SCL is released | 
|  | TransmitPulse : begin | 
|  | target_idle_o = 1'b0; | 
|  |  | 
|  | // Hold value | 
|  | sda_d = sda_q; | 
|  | end | 
|  | // TransmitHold: target holds indexed bit onto SDA while SCL is pulled low, for the hold time | 
|  | TransmitHold : begin | 
|  | target_idle_o = 1'b0; | 
|  |  | 
|  | // Hold value | 
|  | sda_d = sda_q; | 
|  | end | 
|  | // TransmitAck: target waits for host to ACK transmission | 
|  | TransmitAck : begin | 
|  | target_idle_o = 1'b0; | 
|  | end | 
|  | TransmitAckPulse : begin | 
|  | target_idle_o = 1'b0; | 
|  | if (!scl_i) begin | 
|  | // Pop Fifo regardless of ack/nack | 
|  | tx_fifo_rready_o = 1'b1; | 
|  | end | 
|  | end | 
|  | // WaitForStop just waiting for host to trigger a stop after nack | 
|  | WaitForStop : begin | 
|  | target_idle_o = 1'b0; | 
|  | expect_stop = 1'b1; | 
|  | sda_d = 1'b1; | 
|  | end | 
|  | // AcquireByte: target acquires a byte | 
|  | AcquireByte : begin | 
|  | target_idle_o = 1'b0; | 
|  | 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_d = 1'b0; | 
|  | end | 
|  | // AcquireAckPulse: target pulls SDA low while SCL is released | 
|  | AcquireAckPulse : begin | 
|  | target_idle_o = 1'b0; | 
|  | sda_d = 1'b0; | 
|  | end | 
|  | // AcquireAckHold: target pulls SDA low while SCL is pulled low | 
|  | AcquireAckHold : begin | 
|  | target_idle_o = 1'b0; | 
|  | sda_d = 1'b0; | 
|  |  | 
|  | if (tcount_q == 20'd1) begin | 
|  | acq_fifo_wdata_o = {AcqData, input_byte}; // transfer data to acq_fifo | 
|  | acq_fifo_wvalid_o = acq_fifo_wready;         // assert that acq_fifo has valid data | 
|  | end | 
|  | end | 
|  | // StretchAddr: target stretches the clock if matching address cannot be | 
|  | // despoited yet. | 
|  | StretchAddr : begin | 
|  | target_idle_o = 1'b0; | 
|  | scl_d = 1'b0; | 
|  |  | 
|  | acq_fifo_wvalid_o = ~stretch_addr; | 
|  | acq_fifo_wdata_o = {AcqStart, input_byte}; | 
|  | end | 
|  | // StretchTx: target stretches the clock when tx_fifo is empty | 
|  | StretchTx : begin | 
|  | target_idle_o = 1'b0; | 
|  | scl_d = 1'b0; | 
|  | end | 
|  | // StretchTxSetup: drive the return data | 
|  | StretchTxSetup: begin | 
|  | target_idle_o = 1'b0; | 
|  | scl_d = 1'b0; | 
|  | sda_d = tx_fifo_rdata[3'(bit_idx)]; | 
|  | end | 
|  | // StretchAcqFull: target stretches the clock when acq_fifo is full | 
|  | StretchAcqFull : begin | 
|  | target_idle_o = 1'b0; | 
|  | scl_d = 1'b0; | 
|  |  | 
|  | // When space becomes available, deposit entry | 
|  | acq_fifo_wdata_o = {AcqData, input_byte}; // transfer data to acq_fifo | 
|  | acq_fifo_wvalid_o = acq_fifo_wready;      // assert that acq_fifo has valid data | 
|  | end | 
|  | // default | 
|  | default : begin | 
|  | host_idle_o = 1'b1; | 
|  | target_idle_o = 1'b1; | 
|  | sda_d = 1'b1; | 
|  | scl_d = 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_unstable_o = 1'b0; | 
|  | event_cmd_complete_o = 1'b0; | 
|  | end | 
|  | endcase // unique case (state_q) | 
|  |  | 
|  | // start / stop override | 
|  | if (start_det || stop_det) begin | 
|  | // Only add an entry if this is a repeated start or stop. | 
|  | acq_fifo_wvalid_o = !target_idle_o; | 
|  | acq_fifo_wdata_o = start_det ? {AcqRestart, input_byte} : | 
|  | {AcqStop, input_byte}; | 
|  | event_cmd_complete_o = !target_idle_o; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign stretch_addr = !acq_fifo_wready; | 
|  |  | 
|  | // Stretch Tx phase when: | 
|  | // 1. When there is no data to return to host | 
|  | // 2. When the acq_fifo contains any entry other than a singular start condition | 
|  | //    read command. | 
|  | // | 
|  | // Only the fifo depth is checked here, because stretch_tx is only evaluated by the | 
|  | // fsm on the read path. This means a read start byte has already been deposited. | 
|  | assign stretch_tx = ~tx_fifo_rvalid_i | | 
|  | (acq_fifo_depth_i > FifoDepthWidth'(1'b1)); | 
|  |  | 
|  | // Only used for assertion | 
|  | logic unused_acq_rdata; | 
|  | assign unused_acq_rdata = |acq_fifo_rdata_i; | 
|  |  | 
|  | // Conditional state transition | 
|  | always_comb begin : state_functions | 
|  | state_d = state_q; | 
|  | load_tcount = 1'b0; | 
|  | tcount_sel = tNoDelay; | 
|  | bit_decr = 1'b0; | 
|  | bit_clr = 1'b0; | 
|  | byte_decr = 1'b0; | 
|  | byte_clr = 1'b0; | 
|  | read_byte_clr = 1'b0; | 
|  | shift_data_en = 1'b0; | 
|  | log_start = 1'b0; | 
|  | log_stop = 1'b0; | 
|  | req_restart = 1'b0; | 
|  | input_byte_clr = 1'b0; | 
|  | en_sda_interf_det = 1'b0; | 
|  | event_tx_stretch_o = 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 (host_enable_i) begin | 
|  | if (fmt_fifo_rvalid_i) state_d = Active; | 
|  | end | 
|  | end | 
|  |  | 
|  | // SetupStart: SDA and SCL are released | 
|  | SetupStart : begin | 
|  | if (tcount_q == 20'd1) begin | 
|  | state_d = HoldStart; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tHoldStart; | 
|  | log_start = 1'b1; | 
|  | end | 
|  | end | 
|  | // HoldStart: SDA is pulled low, SCL is released | 
|  | HoldStart : begin | 
|  | if (tcount_q == 20'd1) begin | 
|  | state_d = ClockStart; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockStart; | 
|  | end | 
|  | end | 
|  | // ClockStart: SCL is pulled low, SDA stays low | 
|  | ClockStart : begin | 
|  | if (tcount_q == 20'd1) begin | 
|  | state_d = ClockLow; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockLow; | 
|  | end | 
|  | end | 
|  | // ClockLow: SCL stays low, shift indexed bit onto SDA | 
|  | ClockLow : begin | 
|  | en_sda_interf_det = 1'b1; | 
|  | if (tcount_q == 20'd1) begin | 
|  | load_tcount = 1'b1; | 
|  | if (pend_restart) begin | 
|  | state_d = SetupStart; | 
|  | tcount_sel = tSetupStart; | 
|  | end else begin | 
|  | state_d = ClockPulse; | 
|  | tcount_sel = tClockPulse; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | // ClockPulse: SCL is released, SDA keeps the indexed bit value | 
|  | ClockPulse : begin | 
|  | en_sda_interf_det = 1'b1; | 
|  | if (tcount_q == 20'd1) begin | 
|  | state_d = HoldBit; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tHoldBit; | 
|  | end | 
|  | end | 
|  | // HoldBit: SCL is pulled low | 
|  | HoldBit : begin | 
|  | en_sda_interf_det = 1'b1; | 
|  | if (tcount_q == 20'd1) begin | 
|  | en_sda_interf_det = 1'b0; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockLow; | 
|  | if (bit_index == '0) begin | 
|  | state_d = ClockLowAck; | 
|  | bit_clr = 1'b1; | 
|  | end else begin | 
|  | state_d = ClockLow; | 
|  | bit_decr = 1'b1; | 
|  | end | 
|  | end | 
|  | end | 
|  | // ClockLowAck: Target is allowed to drive ack back | 
|  | // to host (dut) | 
|  | ClockLowAck : begin | 
|  | if (tcount_q == 20'd1) begin | 
|  | state_d = ClockPulseAck; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockPulse; | 
|  | end | 
|  | end | 
|  | // ClockPulseAck: SCL is released | 
|  | ClockPulseAck : begin | 
|  | if (tcount_q == 20'd1) begin | 
|  | state_d = HoldDevAck; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tHoldBit; | 
|  | end | 
|  | end | 
|  | // HoldDevAck: SCL is pulled low | 
|  | HoldDevAck : begin | 
|  | if (tcount_q == 20'd1) begin | 
|  | if (fmt_flag_stop_after_i) begin | 
|  | state_d = ClockStop; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockStop; | 
|  | end else begin | 
|  | state_d = PopFmtFifo; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tNoDelay; | 
|  | end | 
|  | end | 
|  | end | 
|  | // ReadClockLow: SCL is pulled low, SDA is released | 
|  | ReadClockLow : begin | 
|  | if (tcount_q == 20'd1) begin | 
|  | state_d = ReadClockPulse; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockPulse; | 
|  | end | 
|  | end | 
|  | // ReadClockPulse: SCL is released, the indexed bit value is read off SDA | 
|  | ReadClockPulse : begin | 
|  | if (tcount_q == 20'd1) begin | 
|  | state_d = ReadHoldBit; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tHoldBit; | 
|  | shift_data_en = 1'b1; | 
|  | end | 
|  | end | 
|  | // ReadHoldBit: SCL is pulled low | 
|  | ReadHoldBit : begin | 
|  | if (tcount_q == 20'd1) begin | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockLow; | 
|  | if (bit_index == '0) begin | 
|  | state_d = HostClockLowAck; | 
|  | bit_clr = 1'b1; | 
|  | read_byte_clr = 1'b1; | 
|  | end else begin | 
|  | state_d = ReadClockLow; | 
|  | bit_decr = 1'b1; | 
|  | end | 
|  | end | 
|  | end | 
|  | // HostClockLowAck: SCL is pulled low, SDA is conditional based on | 
|  | // byte position | 
|  | HostClockLowAck : begin | 
|  | en_sda_interf_det = 1'b1; | 
|  | if (tcount_q == 20'd1) begin | 
|  | state_d = HostClockPulseAck; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockPulse; | 
|  | end | 
|  | end | 
|  | // HostClockPulseAck: SCL is released | 
|  | HostClockPulseAck : begin | 
|  | en_sda_interf_det = 1'b1; | 
|  | if (tcount_q == 20'd1) begin | 
|  | state_d = HostHoldBitAck; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tHoldBit; | 
|  | end | 
|  | end | 
|  | // HostHoldBitAck: SCL is pulled low | 
|  | HostHoldBitAck : begin | 
|  | en_sda_interf_det = 1'b1; | 
|  | if (tcount_q == 20'd1) begin | 
|  | en_sda_interf_det = 1'b0; | 
|  | if (byte_index == 9'd1) begin | 
|  | if (fmt_flag_stop_after_i) begin | 
|  | state_d = ClockStop; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockStop; | 
|  | end else begin | 
|  | state_d = PopFmtFifo; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tNoDelay; | 
|  | end | 
|  | end else begin | 
|  | state_d = ReadClockLow; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockLow; | 
|  | byte_decr = 1'b1; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | // ClockStop: SCL is pulled low, SDA stays low | 
|  | ClockStop : begin | 
|  | if (tcount_q == 20'd1) begin | 
|  | state_d = SetupStop; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tSetupStop; | 
|  | end | 
|  | end | 
|  | // SetupStop: SDA is pulled low, SCL is released | 
|  | SetupStop : begin | 
|  | if (tcount_q == 20'd1) begin | 
|  | state_d = HoldStop; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tHoldStop; | 
|  | log_stop = 1'b1; | 
|  | end | 
|  | end | 
|  | // HoldStop: SDA and SCL are released | 
|  | HoldStop : begin | 
|  | en_sda_interf_det = 1'b1; | 
|  | if (tcount_q == 20'd1) begin | 
|  | en_sda_interf_det = 1'b0; | 
|  | if (!host_enable_i) begin | 
|  | state_d = Idle; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tNoDelay; | 
|  | end else begin | 
|  | state_d = PopFmtFifo; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tNoDelay; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | // Active: continue while keeping SCL low | 
|  | Active : begin | 
|  | if (fmt_flag_read_bytes_i) begin | 
|  | byte_clr = 1'b1; | 
|  | state_d = ReadClockLow; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockLow; | 
|  | end else if (fmt_flag_start_before_i && !trans_started) begin | 
|  | state_d = SetupStart; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tSetupStart; | 
|  | end else begin | 
|  | state_d = ClockLow; | 
|  | load_tcount = 1'b1; | 
|  | req_restart = fmt_flag_start_before_i; | 
|  | tcount_sel = tClockLow; | 
|  | end | 
|  | end | 
|  |  | 
|  | // PopFmtFifo: populate fmt_fifo | 
|  | PopFmtFifo : begin | 
|  | if (!host_enable_i) begin | 
|  | state_d = ClockStop; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockStop; | 
|  | end else if (fmt_fifo_depth_i == 7'h1) begin | 
|  | state_d = Idle; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tNoDelay; | 
|  | end else begin | 
|  | state_d = Active; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tNoDelay; | 
|  | end | 
|  | end | 
|  |  | 
|  | // AcquireStart: hold start condition | 
|  | AcquireStart : begin | 
|  | if (scl_i_q && !scl_i) begin | 
|  | // If we are not able to accept the start bit, stretch or nak | 
|  | state_d = AddrRead; | 
|  | input_byte_clr = 1'b1; | 
|  | end | 
|  | end | 
|  |  | 
|  | // AddrRead: read and compare target address | 
|  | AddrRead : begin | 
|  | if (bit_ack && address_match) begin | 
|  | state_d = AddrAckWait; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockStart; | 
|  | end else if (bit_ack && !address_match) begin | 
|  | state_d = Idle; | 
|  | end | 
|  | end | 
|  |  | 
|  | // AddrAckWait: pause before acknowledging | 
|  | AddrAckWait : begin | 
|  | if (tcount_q == 20'd1 && !scl_i) 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 | 
|  | // Stretch when requested by software or when there is insufficient | 
|  | // space to hold the start / address format byte. | 
|  | // If there is sufficient space, the format byte is written into the acquisition fifo. | 
|  | if (stretch_addr) begin | 
|  | state_d = StretchAddr; | 
|  | end else if (rw_bit_q) begin | 
|  | state_d = TransmitWait; | 
|  | end else if (!rw_bit_q) begin | 
|  | state_d = AcquireByte; | 
|  | end | 
|  | end | 
|  | end | 
|  | // TransmitWait: Evaluate whether there are entries to send first | 
|  | TransmitWait: begin | 
|  | if (stretch_tx) begin | 
|  | state_d = StretchTx; | 
|  | end else begin | 
|  | state_d = TransmitSetup; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockStart; | 
|  | 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 | 
|  | if (bit_ack) begin | 
|  | state_d = TransmitAck; | 
|  | end else begin | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tClockStart; | 
|  | state_d = TransmitSetup; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | // Wait for clock to become positive. | 
|  | TransmitAck : begin | 
|  | if (scl_i) begin | 
|  | state_d = TransmitAckPulse; | 
|  | end | 
|  | end | 
|  |  | 
|  | // TransmitAckPulse: target waits for host to ACK transmission | 
|  | // If a nak is received, that means a stop is incoming. | 
|  | TransmitAckPulse : begin | 
|  | if (!scl_i) begin | 
|  | // If host acknowledged, that means we must continue | 
|  | if (host_ack) begin | 
|  | state_d = TransmitWait; | 
|  | end else begin | 
|  | // If host nak'd then the transaction is about to terminate, go to a wait state | 
|  | state_d = WaitForStop; | 
|  | end | 
|  | end | 
|  | end | 
|  |  | 
|  | // An inert state just waiting for host to issue a stop | 
|  | // Cannot cycle back to idle directly as other events depend on the system being | 
|  | // non-idle. | 
|  | WaitForStop: begin | 
|  | state_d = WaitForStop; | 
|  | 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 && !scl_i) 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 whilREe SCL is pulled low | 
|  | AcquireAckHold : begin | 
|  | if (tcount_q == 20'd1) begin | 
|  | // If there is no space for the current entry, stretch clocks and | 
|  | // wait for software to make space. | 
|  | state_d = acq_fifo_wready ? AcquireByte : StretchAcqFull; | 
|  | end | 
|  | end | 
|  | // StretchAddr: The address phase can not yet be completed, stretch | 
|  | // clock and wait. | 
|  | StretchAddr : begin | 
|  | if (!stretch_addr) begin | 
|  | // When transmitting after an address stretch, we need to assume | 
|  | // that is looks like a Tx stretch.  This is because if we try | 
|  | // to follow the normal path, the logic will release the clock | 
|  | // too early relative to driving the data.  This will cause a | 
|  | // setup violation.  This is the same case to needing StretchTxSetup. | 
|  | state_d = rw_bit_q ? StretchTx : AcquireByte; | 
|  | end | 
|  | end | 
|  | // StretchTx: target stretches the clock when tx conditions are not satisfied. | 
|  | StretchTx : begin | 
|  | // When in stretch state, always notify software that help is required. | 
|  | event_tx_stretch_o = 1'b1; | 
|  | if (!stretch_tx) begin | 
|  | // When data becomes available, we must first drive it onto the line | 
|  | // for at least the "setup" period.  If we do not, once the clock is released, the | 
|  | // pull-up in the system will likely immediately trigger a rising clock | 
|  | // edge (since the stretch likely pushed us way beyond the original intended | 
|  | // rise).  If we do not artificially create the setup period here, it will | 
|  | // likely create a timing violation. | 
|  | state_d = StretchTxSetup; | 
|  | load_tcount = 1'b1; | 
|  | tcount_sel = tSetupData; | 
|  |  | 
|  | // When leaving stretch state, de-assert software notification | 
|  | event_tx_stretch_o = 1'b0; | 
|  | end | 
|  | end | 
|  | StretchTxSetup : begin | 
|  | if (tcount_q == 20'd1) begin | 
|  | state_d = TransmitSetup; | 
|  | end | 
|  | end | 
|  | // StretchAcqFull: target stretches the clock when acq_fifo is full | 
|  | // When space becomes available, deposit the entry into the acqusition fifo | 
|  | // and move on to receive the next byte. | 
|  | StretchAcqFull : begin | 
|  | if (acq_fifo_wready) state_d = AcquireByte; | 
|  | end | 
|  |  | 
|  | // default | 
|  | default : begin | 
|  | state_d = Idle; | 
|  | load_tcount = 1'b0; | 
|  | tcount_sel = tNoDelay; | 
|  | bit_decr = 1'b0; | 
|  | bit_clr = 1'b0; | 
|  | byte_decr = 1'b0; | 
|  | byte_clr = 1'b0; | 
|  | read_byte_clr = 1'b0; | 
|  | shift_data_en = 1'b0; | 
|  | log_start = 1'b0; | 
|  | log_stop = 1'b0; | 
|  | input_byte_clr = 1'b0; | 
|  | event_tx_stretch_o = 1'b0; | 
|  | end | 
|  | endcase // unique case (state_q) | 
|  |  | 
|  | // When a start is detected, always go to the acquire start state. | 
|  | // Differences in repeated start / start handling are done in the | 
|  | // other fsm. | 
|  | if (!target_idle && !target_enable_i) begin | 
|  | // If the target function is currently not idle but target_enable is suddenly dropped, | 
|  | // (maybe because the host locked up and we want to cycle back to an initial state), | 
|  | // transition immediately. | 
|  | // The same treatment is not given to the host mode because it already attempts to | 
|  | // gracefully terminate.  If the host cannot gracefully terminate for whatever reason, | 
|  | // (the other side is holding SCL low), we may need to forcefully reset the module. | 
|  | // TODO: It may be worth having a force stop condition to force the host back to | 
|  | // Idle in case graceful termination is not possible. | 
|  | state_d = Idle; | 
|  | end else if (start_det) begin | 
|  | state_d = AcquireStart; | 
|  | end else if (stop_det) begin | 
|  | state_d = Idle; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Synchronous state transition | 
|  | always_ff @ (posedge clk_i or negedge rst_ni) begin : state_transition | 
|  | if (!rst_ni) begin | 
|  | state_q <= Idle; | 
|  | end else begin | 
|  | state_q <= state_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | // I2C bus outputs | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | scl_q <= 1'b1; | 
|  | sda_q <= 1'b1; | 
|  | end else begin | 
|  | scl_q <= scl_d; | 
|  | sda_q <= sda_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | assign scl_o = scl_q; | 
|  | assign sda_o = sda_q; | 
|  |  | 
|  | // Host ceased sending SCL pulses during ongoing transaction | 
|  | assign event_host_timeout_o = !target_idle_o & (stretch_idle_cnt > host_timeout_i); | 
|  |  | 
|  | // Target stretched clock beyond timeout | 
|  | assign event_stretch_timeout_o = stretch_en && | 
|  | (stretch_idle_cnt[30:0] > stretch_timeout_i) && timeout_enable_i; | 
|  |  | 
|  | // Fed out for interrupt purposes | 
|  | assign acq_fifo_wready_o = acq_fifo_wready; | 
|  |  | 
|  | // Check to make sure scl_i is never a single cycle glitch | 
|  | `ASSERT(SclInputGlitch_A, $rose(scl_i) |-> ##1 scl_i) | 
|  |  | 
|  | // Make sure we never attempt to send a single cycle glitch | 
|  | `ASSERT(SclOutputGlitch_A, $rose(scl_o) |-> ##1 scl_o) | 
|  |  | 
|  | // If we are actively transmitting, that must mean that there are no | 
|  | // unhandled write commands and if there is a command present it must be | 
|  | // a read. | 
|  | `ASSERT(AcqDepthRdCheck_A, ((state_q == TransmitSetup) && (acq_fifo_depth_i > '0)) |-> | 
|  | (acq_fifo_depth_i == 1) && acq_fifo_rdata_i[0]) | 
|  |  | 
|  | endmodule |