英文:
How can I stop the simulation at stop bit for I2C master code?
问题
I understand that you want a translation of the provided Verilog code and testbench. Here is the translated Verilog code:
我正在尝试编写I2C主控制器的Verilog代码,但我遇到了一些问题。我能够在Quartus和ModelSim上编译并运行其测试台。但是,我想要在状态达到`STATE_STOP`时(基本上保持在`STATE_IDLE`状态),将其切换回复位模式(reset = 1)。
我可以在代码或测试台中添加什么来实现这一点?模拟在`STATE_STOP`之后继续,这点我无法理解,所以我必须在任意时间添加`$finish`或reset = 1到测试台中。
```verilog
`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
测试台:
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
正如我之前提到的,我只是随机选择了一个时间来结束模拟,但我似乎想不出一种方法,在达到最后一个状态后结束模拟并保持在空闲状态。这是模拟图像:
I hope this helps. If you have any further questions or need assistance with the code, please let me know.
<details>
<summary>英文:</summary>
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:
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/eTc05.png
</details>
# 答案1
**得分**: 1
The problem is in the design, not the testbench.
The state machine can never enter the `STATE_WACK2`, and therefore it also can never enter `STATE_STOP`. I think the bug is in `STATE_DATA`, where you probably want to transition to `WACK2`, not `WACK`:
STATE_DATA: begin
i2c_sda <= data[count];
if (count == 0) state <= STATE_WACK2;
else count <= count - 1;
end
After that is fixed, another bug is that you always stay in `STATE_STOP`. You probably want to transition into `IDLE`:
STATE_STOP: begin
i2c_sda <= 0;
state <= STATE_IDLE;
end
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`.
STATE_IDLE: begin
i2c_sda <= 1;
state <= (start) ? STATE_START : STATE_IDLE;
end
<details>
<summary>英文:</summary>
The problem is in the design, not the testbench.
The state machine can never enter the `STATE_WACK2`, and therefore it also can never enter `STATE_STOP`. I think the bug is in `STATE_DATA`, where you probably want to transition to `WACK2`, not `WACK`:
STATE_DATA: begin
i2c_sda <= data[count];
if (count == 0) state <= STATE_WACK2;
else count <= count - 1;
end
After that is fixed, another bug is that you always stay in `STATE_STOP`. You probably want to transition into `IDLE`:
STATE_STOP: begin
i2c_sda <= 0;
state <= STATE_IDLE;
end
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`.
STATE_IDLE: begin
i2c_sda <= 1;
state <= (start) ? STATE_START : STATE_IDLE;
end
</details>
# 答案2
**得分**: -1
关于仅在达到特定值后停止测试台的时间控制,您应该能够使用分层引用访问模块的本地值,然后使用等式检查与 wait() 结合使用。
要获取该值,您可以在测试台文件中使用类似 `uut.state` 的方式,在设置时间的位置(即替换 `#100`)使用 `wait(uut.state == STATE_STOP)`。
测试台应该在满足条件之前暂停,然后继续进行。
<details>
<summary>英文:</summary>
In terms of just timing the testbench to stop after a certain value is reached, you should be able to access the module local value using hierarchical reference and then use wait() with an equality check.
To get the value it would be something like `uut.state` in your testbench file and in place of a set time (i.e. replacing `#100`) use `wait(uut.state == STATE_STOP)`.
The testbench should pause there until the condition is met and proceed afterwards.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论