i2c-output-expander/i2c_io_output.sv
2022-06-14 12:50:47 +03:00

90 lines
2.3 KiB
Systemverilog

`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