I am trying to write Verilog code for I2C master, and there are a couple of problems I am facing. I was able to compile and run its testbench on Quartus and modelsim, respectively. However, I am trying to have it switch back to reset mode on (reset = 1) once the state reaches STATE_STOP (Basically have it stay in STATE_IDLE).
What can I add to either the code or the testbench to make it that way? The simulation continues after STATE_STOP, which I am not able to understand, and so I have to, at an arbitrary time, add either $finish or reset = 1 in the testbench.
`timescale 1ns/1ps
module I2C_Master(
input wire clk,
input wire reset,
input wire [7:0]data,
input wire [6:0]add,
input wire readorwrite,
output reg i2c_sda,
output reg i2c_scl
);
localparam STATE_IDLE = 0;
localparam STATE_START = 1;
localparam STATE_ADDR = 2;
localparam STATE_RW = 3;
localparam STATE_WACK = 4;
localparam STATE_DATA = 5;
localparam STATE_STOP = 6;
localparam STATE_WACK2 = 7;
reg [7:0] state;
reg [7:0] count;
always @(posedge clk) begin
if (reset == 1)
i2c_scl <= 1;
else begin
if (state == STATE_START)
i2c_scl <= 0;
else if (state == STATE_IDLE || state == STATE_STOP)
i2c_scl <= 1;
else
i2c_scl <= ~i2c_scl;
end
end
always @(posedge clk) begin
if (reset == 1) begin
state <= 0;
i2c_sda <= 1;
count <= 8'd0;
end
else begin
case(state)
STATE_IDLE: begin
i2c_sda <= 1;
state <= STATE_START;
end
STATE_START: begin
i2c_sda <= 0;
state <= STATE_ADDR;
count <= 6;
end
STATE_ADDR: begin
i2c_sda <= add[count];
if (count == 0) state <= STATE_RW;
else count <= count - 1;
end
STATE_RW: begin
i2c_sda <= readorwrite;
state <= STATE_WACK;
end
STATE_WACK: begin
i2c_sda <= 0;
state <= STATE_DATA;
count <= 7;
end
STATE_DATA: begin
i2c_sda <= data[count];
if (count == 0) state <= STATE_WACK;
else count <= count - 1;
end
STATE_WACK2: begin
i2c_sda <= readorwrite;
state <= STATE_STOP;
end
STATE_STOP: begin
i2c_sda <= 0;
end
endcase
end
end
endmodule
The testbench:
module I2C_MasterTB;
reg clk;
reg reset;
reg [7:0]data;
reg [6:0]add;
reg readorwrite;
wire i2c_sda;
wire i2c_scl;
I2C_Master uut (
.clk(clk),
.reset(reset),
.data(data),
.add(add),
.readorwrite(readorwrite),
.i2c_sda(i2c_sda),
.i2c_scl(i2c_scl)
);
initial begin
clk = 0;
forever begin
clk = #1 ~clk;
end
end
initial begin
reset = 1;
add <= 7'h50;
data <= 8'haa;
readorwrite <= 0;
#10;
reset = 0;
#100;
reset = 1;
end
endmodule
As I already mentioned, I only arbitrarily chose a time period to finish the simulation, but I can't seem to think of the way to the end the simulation as soon as it reaches the last state and have it stay in idle state. Here is the simulation:

The problem is in the design, not the testbench.
The state machine can never enter the
STATE_WACK2, and therefore it also can never enterSTATE_STOP. I think the bug is inSTATE_DATA, where you probably want to transition toWACK2, notWACK:After that is fixed, another bug is that you always stay in
STATE_STOP. You probably want to transition intoIDLE:You also need a control signal to decide when you want to start an I2C transfer, otherwise, the state machine will start up again. You should add an input signal to the design for that, like
start.