`timescale 1ps/1ps `default_nettype none module i2c_io_output #(parameter [6:0] I2C_ADDR = 7'h5a, parameter IO_BYTES_COUNT = 2, localparam DATA_WIDTH = IO_BYTES_COUNT * 8) (input wire clock, input wire reset, input wire i_scl, input wire i_sdi, output wire o_sdo, output reg [DATA_WIDTH-1:0] o_data); /* -------- I2C Receiver -------- */ logic [7:0] i2c_data; logic i2c_strobe; logic i2c_start; logic i2c_stop; logic i2c_ack; i2c_receiver i2c_receiver_impl (.clock(clock), .reset(reset), .i_scl(i_scl), .i_sdi(i_sdi), .o_sdo(o_sdo), .o_data(i2c_data), .o_strobe(i2c_strobe), .o_start(i2c_start), .o_stop(i2c_stop), .i_ack(i2c_ack)); /* -------- FSM -------- */ enum int unsigned { ST_IDLE = 0, ST_RECEIVE_ADDR, ST_RECEIVE_BYTE, ST_COMPLETE } state; localparam BYTE_CNTR_W = $clog2(IO_BYTES_COUNT); logic [BYTE_CNTR_W-1:0] byte_cntr; logic [DATA_WIDTH-1:0] data; always_ff @(posedge clock) if (reset) begin state <= ST_IDLE; byte_cntr <= '0; end else begin if (i2c_stop) state <= ST_IDLE; case (state) ST_IDLE: begin if (i2c_start) state <= ST_RECEIVE_ADDR; end ST_RECEIVE_ADDR: begin if (i2c_strobe) if (i2c_data == {I2C_ADDR, 1'b0}) begin i2c_ack <= 1'b1; byte_cntr <= '0; state <= ST_RECEIVE_BYTE; end else begin i2c_ack <= 1'b0; state <= ST_IDLE; end end ST_RECEIVE_BYTE: begin if (i2c_strobe) begin data <= {data[DATA_WIDTH-8-1:0], i2c_data}; // MSB first byte_cntr <= byte_cntr + 1'b1; if (byte_cntr == BYTE_CNTR_W'(IO_BYTES_COUNT-1)) state <= ST_COMPLETE; end end ST_COMPLETE: begin o_data <= data; state <= ST_IDLE; end endcase end endmodule // i2c_io_output