Files
CGA-bench/data/myproject/clean/i2c_master_tb_clean.v

209 lines
5.5 KiB
Coq
Raw Normal View History

2026-05-22 10:02:42 +08:00
`timescale 1ns/1ps
module tb_i2c_master;
reg clk;
reg rst_n;
reg [6:0] addr;
reg rw;
reg [7:0] data_in;
reg start_tx;
tri1 scl;
tri1 sda;
reg slave_scl_low;
reg slave_sda_low;
reg nack_first_ack;
reg arbitration_request;
reg [7:0] read_value;
reg saw_arbitration_lost;
wire [7:0] data_out;
wire busy;
wire ack_err;
integer errors;
integer checks;
localparam IDLE = 4'd0;
localparam ADDR_DRIVE = 4'd3;
localparam ADDR_SAMPLE = 4'd4;
localparam ACK1 = 4'd5;
localparam READ = 4'd8;
localparam ACK2 = 4'd9;
localparam STOP_A = 4'd10;
localparam CLOCK_STRETCH = 4'd12;
localparam ARBITRATION_LOST = 4'd13;
assign scl = slave_scl_low ? 1'b0 : 1'bz;
assign sda = slave_sda_low ? 1'b0 : 1'bz;
i2c_master dut (
.clk(clk),
.rst_n(rst_n),
.addr(addr),
.rw(rw),
.data_in(data_in),
.start_tx(start_tx),
.data_out(data_out),
.busy(busy),
.ack_err(ack_err),
.scl(scl),
.sda(sda)
);
always #5 clk = ~clk;
always @(*) begin
slave_sda_low = 1'b0;
if (dut.state == ACK1) begin
if (!nack_first_ack) begin
slave_sda_low = 1'b1;
end
end else if (dut.state == ACK2 && !dut.latched_rw) begin
slave_sda_low = 1'b1;
end else if (dut.state == READ) begin
slave_sda_low = ~read_value[dut.bit_count];
end
if (arbitration_request &&
(dut.state == ADDR_DRIVE || dut.state == ADDR_SAMPLE) &&
dut.tx_shift[7]) begin
slave_sda_low = 1'b1;
end
end
always @(posedge clk) begin
if (dut.state == ARBITRATION_LOST) begin
saw_arbitration_lost <= 1'b1;
end
end
task check;
input cond;
input string message;
begin
checks = checks + 1;
if (!cond) begin
errors = errors + 1;
$display("CHECK FAILED: %0s", message);
end
end
endtask
task reset_dut;
begin
rst_n = 1'b0;
start_tx = 1'b0;
addr = 7'h00;
rw = 1'b0;
data_in = 8'h00;
nack_first_ack = 1'b0;
arbitration_request = 1'b0;
slave_scl_low = 1'b0;
read_value = 8'h00;
repeat (3) @(posedge clk);
rst_n = 1'b1;
@(posedge clk);
end
endtask
task launch_transfer;
input [6:0] tx_addr;
input tx_rw;
input [7:0] tx_data;
begin
addr = tx_addr;
rw = tx_rw;
data_in = tx_data;
start_tx = 1'b1;
@(posedge clk);
start_tx = 1'b0;
end
endtask
task wait_for_idle;
integer timeout;
begin
timeout = 0;
while ((busy || dut.state != IDLE) && timeout < 200) begin
@(posedge clk);
timeout = timeout + 1;
end
check(timeout < 200, "I2C transfer timed out");
end
endtask
initial begin
clk = 1'b0;
rst_n = 1'b0;
addr = 7'h00;
rw = 1'b0;
data_in = 8'h00;
start_tx = 1'b0;
slave_scl_low = 1'b0;
nack_first_ack = 1'b0;
arbitration_request = 1'b0;
read_value = 8'h00;
saw_arbitration_lost = 1'b0;
errors = 0;
checks = 0;
reset_dut();
launch_transfer(7'h42, 1'b0, 8'hA5);
wait_for_idle();
check(ack_err == 1'b0, "write transaction should complete without ack_err");
check(busy == 1'b0, "busy should deassert after write");
check(scl === 1'b1 && sda === 1'b1, "bus should return high after STOP");
read_value = 8'h3C;
launch_transfer(7'h42, 1'b1, 8'h00);
wait_for_idle();
check(data_out == 8'h3C, "read transaction should capture slave data");
check(ack_err == 1'b0, "read transaction should complete without ack_err");
nack_first_ack = 1'b1;
launch_transfer(7'h42, 1'b0, 8'h55);
wait_for_idle();
check(ack_err == 1'b1, "NACK on first ACK should set ack_err");
nack_first_ack = 1'b0;
fork
begin
wait (dut.state == STOP_A);
slave_scl_low = 1'b1;
repeat (3) @(posedge clk);
slave_scl_low = 1'b0;
end
join_none
launch_transfer(7'h42, 1'b0, 8'hC3);
wait_for_idle();
check(ack_err == 1'b0, "clock stretch should not force ack_err");
saw_arbitration_lost = 1'b0;
fork
begin
wait (dut.state == ADDR_SAMPLE && dut.tx_shift[7]);
arbitration_request = 1'b1;
repeat (2) @(posedge clk);
arbitration_request = 1'b0;
end
join_none
launch_transfer(7'h60, 1'b0, 8'hF0);
wait_for_idle();
check(saw_arbitration_lost, "arbitration loss path should be reachable");
check(ack_err == 1'b1, "arbitration loss should set ack_err");
if (errors == 0) begin
$display("Hint: Total mismatched samples is 0 out of %0d samples", checks);
end else begin
$display("Hint: Total mismatched samples is %0d out of %0d samples", errors, checks);
end
$display("Mismatches: %0d in %0d samples", errors, checks);
$finish;
end
endmodule