如何在I2C Master代码中于STOP位停止仿真并维持IDLE状态?
解决I2C Master仿真自动终止并维持IDLE状态的问题
问题描述
编写的I2C Master Verilog代码可在Quartus编译、Modelsim仿真,但当前仿真进入STATE_STOP状态后会持续运行,只能手动设置时间或调用$finish/置reset=1来终止。需求是进入STATE_STOP后自动置reset=1,让设备维持在STATE_IDLE状态,无需手动干预。
解决方案
方案一:修改测试台,自动监测状态触发复位
测试台可通过层次化访问DUT内部的state信号,当检测到进入STATE_STOP时,自动置reset=1并结束仿真。
修改后的测试台代码
module I2C_MasterTB; reg clk; reg reset; reg [7:0]data; reg [6:0]add; reg readorwrite; wire i2c_sda; wire i2c_scl; // 层次化访问DUT内部state信号,Modelsim支持该语法 wire [7:0] dut_state; assign dut_state = uut.state; 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; end // 监测DUT状态,进入STOP后触发复位并结束仿真 always @(posedge clk) begin if (dut_state == uut.STATE_STOP) begin reset <= 1; // 延迟1个时钟周期后结束仿真,确保复位逻辑生效 #2 $finish; end end endmodule
方案二:修改DUT状态机,完成STOP后自动回到IDLE
如果不需要依赖外部复位,可直接修改DUT的状态机逻辑,在STATE_STOP状态完成后自动跳转到STATE_IDLE并维持,同时保证I2C总线信号符合规范。
修改后的I2C_Master代码
`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; reg stop_done; // 标记STOP状态是否完成 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 <= STATE_IDLE; i2c_sda <= 1; count <= 8'd0; stop_done <= 1'b0; 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_WACK2; // 修正原代码死循环问题 else count <= count - 1; end STATE_WACK2: begin i2c_sda <= readorwrite; state <= STATE_STOP; end STATE_STOP: begin if (!stop_done) begin // 生成标准I2C STOP信号:SCL高电平时,SDA从0拉到1 i2c_sda <= 1; stop_done <= 1'b1; end else begin // STOP完成后跳回IDLE并维持 state <= STATE_IDLE; stop_done <= 1'b0; end end endcase end end endmodule
注:原DUT的
STATE_DATA状态在count==0时跳回STATE_WACK会导致死循环,方案二中已修正为跳转到STATE_WACK2,保证状态机能正常走到STATE_STOP。
内容的提问来源于stack exchange,提问作者Qazi Zabeer
相关产品推荐
相关产品推荐

