Files
CGA-bench/data/myproject/clean/i2c_master_clean.v
2026-05-22 10:02:42 +08:00

237 lines
7.5 KiB
Verilog

`timescale 1ns/1ps
module i2c_master (
input clk,
input rst_n,
input [6:0] addr,
input rw,
input [7:0] data_in,
input start_tx,
output reg [7:0] data_out,
output reg busy,
output reg ack_err,
inout scl,
inout sda
);
localparam IDLE = 4'd0;
localparam START_A = 4'd1;
localparam START_B = 4'd2;
localparam ADDR_DRIVE = 4'd3;
localparam ADDR_SAMPLE = 4'd4;
localparam ACK1 = 4'd5;
localparam WRITE_DRIVE = 4'd6;
localparam WRITE_SAMPLE = 4'd7;
localparam READ = 4'd8;
localparam ACK2 = 4'd9;
localparam STOP_A = 4'd10;
localparam STOP_B = 4'd11;
localparam CLOCK_STRETCH = 4'd12;
localparam ARBITRATION_LOST = 4'd13;
localparam ERROR = 4'd14;
reg [3:0] state;
reg [2:0] bit_count;
reg [7:0] tx_shift;
reg [7:0] rx_shift;
reg scl_drive_low;
reg sda_drive_low;
reg latched_rw;
wire scl_in;
wire sda_in;
assign scl = scl_drive_low ? 1'b0 : 1'bz;
assign sda = sda_drive_low ? 1'b0 : 1'bz;
assign scl_in = scl;
assign sda_in = sda;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
bit_count <= 3'd0;
tx_shift <= 8'd0;
rx_shift <= 8'd0;
scl_drive_low <= 1'b0;
sda_drive_low <= 1'b0;
latched_rw <= 1'b0;
data_out <= 8'd0;
busy <= 1'b0;
ack_err <= 1'b0;
end else begin
case (state)
IDLE: begin
busy <= 1'b0;
scl_drive_low <= 1'b0;
sda_drive_low <= 1'b0;
bit_count <= 3'd7;
rx_shift <= 8'd0;
if (start_tx) begin
busy <= 1'b1;
ack_err <= 1'b0;
tx_shift <= {addr, rw};
latched_rw <= rw;
state <= START_A;
end
end
START_A: begin
busy <= 1'b1;
if (scl_in && sda_in) begin
sda_drive_low <= 1'b1;
scl_drive_low <= 1'b0;
state <= START_B;
end else begin
ack_err <= 1'b1;
state <= ERROR;
end
end
START_B: begin
busy <= 1'b1;
scl_drive_low <= 1'b1;
bit_count <= 3'd7;
sda_drive_low <= ~tx_shift[7];
state <= ADDR_DRIVE;
end
ADDR_DRIVE: begin
busy <= 1'b1;
scl_drive_low <= 1'b0;
sda_drive_low <= ~tx_shift[7];
state <= ADDR_SAMPLE;
end
ADDR_SAMPLE: begin
busy <= 1'b1;
scl_drive_low <= 1'b0;
if (tx_shift[7] && !sda_in) begin
ack_err <= 1'b1;
state <= ARBITRATION_LOST;
end else if (bit_count == 3'd0) begin
sda_drive_low <= 1'b0;
state <= ACK1;
end else begin
tx_shift <= {tx_shift[6:0], 1'b0};
bit_count <= bit_count - 1'b1;
scl_drive_low <= 1'b1;
sda_drive_low <= ~tx_shift[6];
state <= ADDR_DRIVE;
end
end
ACK1: begin
busy <= 1'b1;
scl_drive_low <= 1'b0;
sda_drive_low <= 1'b0;
bit_count <= 3'd7;
if (!sda_in) begin
if (latched_rw) begin
rx_shift <= 8'd0;
state <= READ;
end else begin
tx_shift <= data_in;
sda_drive_low <= ~data_in[7];
state <= WRITE_DRIVE;
end
end else begin
ack_err <= 1'b1;
state <= ERROR;
end
end
WRITE_DRIVE: begin
busy <= 1'b1;
scl_drive_low <= 1'b0;
sda_drive_low <= ~tx_shift[7];
state <= WRITE_SAMPLE;
end
WRITE_SAMPLE: begin
busy <= 1'b1;
scl_drive_low <= 1'b0;
if (tx_shift[7] && !sda_in) begin
ack_err <= 1'b1;
state <= ARBITRATION_LOST;
end else if (bit_count == 3'd0) begin
sda_drive_low <= 1'b0;
state <= ACK2;
end else begin
tx_shift <= {tx_shift[6:0], 1'b0};
bit_count <= bit_count - 1'b1;
scl_drive_low <= 1'b1;
sda_drive_low <= ~tx_shift[6];
state <= WRITE_DRIVE;
end
end
READ: begin
busy <= 1'b1;
scl_drive_low <= 1'b0;
sda_drive_low <= 1'b0;
rx_shift <= {rx_shift[6:0], sda_in};
if (bit_count == 3'd0) begin
data_out <= {rx_shift[6:0], sda_in};
state <= ACK2;
end else begin
bit_count <= bit_count - 1'b1;
end
end
ACK2: begin
busy <= 1'b1;
scl_drive_low <= 1'b0;
sda_drive_low <= 1'b0;
if (!latched_rw && sda_in) begin
ack_err <= 1'b1;
end
state <= STOP_A;
end
STOP_A: begin
busy <= 1'b1;
scl_drive_low <= 1'b0;
sda_drive_low <= 1'b1;
if (!scl_in) begin
state <= CLOCK_STRETCH;
end else begin
state <= STOP_B;
end
end
CLOCK_STRETCH: begin
busy <= 1'b1;
scl_drive_low <= 1'b0;
sda_drive_low <= 1'b1;
if (scl_in) begin
state <= STOP_B;
end
end
STOP_B: begin
busy <= 1'b0;
scl_drive_low <= 1'b0;
sda_drive_low <= 1'b0;
state <= IDLE;
end
ARBITRATION_LOST: begin
busy <= 1'b0;
scl_drive_low <= 1'b0;
sda_drive_low <= 1'b0;
ack_err <= 1'b1;
state <= IDLE;
end
default: begin
busy <= 1'b0;
scl_drive_low <= 1'b0;
sda_drive_low <= 1'b0;
ack_err <= 1'b1;
state <= IDLE;
end
endcase
end
end
endmodule