我正在创建一个 i2c master,我总是用 3 个块构建一个有限状态机。通过接口写入/读取数据后,会出现WACK2状态,其中从机通过设置ACK来确认写入,或者当主机设置NACK时读取结束。就我而言,我正在记录,并且传输数据的最后一位分别在转换到 WACK2 状态时设置,从机无法对 sda 线设置任何内容,因为线方向尚未改变。最终数据位被捕捉到接收总线,而不是来自从机的响应。
我想问题是我的状态根据与前一个状态中的数据所暴露的相同的时钟而改变。我尝试使新数据的状态和锁存分别由 posege 和 negedge 发生,反之亦然,但这没有给出任何结果。
我需要将从从机接收到的信息锁存到 WACK2 状态下的 rx 总线中,而不是正在传输的数据位
I2C 主控代码:
`timescale 1ns / 1ps
module I2C_master(
input clk_i, rst_i,
input start_cmd, stop_cmd,
input rnw_i, // read/ not write
input wack2_master_i,
input [6:0] addr_i,
input [7:0] din_i, // data_in
output [7:0] dout_o,
output i2c_scl_o,
inout i2c_sda_io,
output ack_o,
output ready_o //data ready
);
localparam ACK = 1'b0;
localparam NACK = 1'b1;
logic sda_r, scl_r; //sda, scl registers
logic sda_dir; //direction for sda line
logic ready_r;
assign i2c_sda_io = sda_dir? sda_r : 1'bz;
assign i2c_scl_o = scl_r;
enum logic[3:0]
{
IDLE = 4'd0,
START = 4'd1,
ADDR = 4'd2,
RW = 4'd3,
WACK = 4'd4,
READ = 4'd5,
WRITE = 4'd6,
WACK2 = 4'd7,
HOLD = 4'd8,
STOP = 4'd9
}state, new_state;
logic [2:0] cnt;
logic [8:0] rx;
assign scl_r = ((state == START) || (state == STOP) || (state == IDLE))? 1 : clk_i;
//3-FSM start
always_ff@(posedge clk_i)
if(rst_i) begin
state <= IDLE;
rx <= '0;
end
else state <= new_state;
always_comb begin
new_state = state;
case(state)
IDLE : if(start_cmd) new_state = START;
START: new_state = ADDR;
ADDR : if(cnt == 0) new_state = RW;
RW : new_state = WACK;
WACK : if(i2c_sda_io == ACK)begin
if(rnw_i) new_state = READ;
else new_state = WRITE;
end
READ : if(cnt == 0) new_state = WACK2;
WRITE: if(cnt == 0) new_state = WACK2;
WACK2: new_state = HOLD;
HOLD : if(rnw_i) begin
if (rx[0] == NACK)begin
if(stop_cmd) new_state = STOP;
end
else new_state = READ;
end
else begin
if (rx[0] == ACK) begin
if (stop_cmd) new_state = STOP;
else new_state = WRITE;
end
else new_state = STOP;
end
STOP : new_state = IDLE;
endcase
end
always_ff@(posedge clk_i) begin
case(state)
IDLE :
begin
ready_r <= 1'b0;
sda_dir <= 1'b1;
sda_r <= 1'b1;
end
START:
begin
sda_dir <= 1'b1;
sda_r <= 1'b0;
cnt <= 3'd6;
end
ADDR :
begin
sda_dir <= 1'b1;
sda_r <= addr_i[cnt];
cnt <= cnt - 1;
end
RW :
begin
sda_dir <= 1'b1;
sda_r <= rnw_i;
end
WACK :
begin
sda_dir <= 1'b0;
cnt <= 3'd7;
end
READ :
begin
ready_r <= 1'b0;
sda_dir <= 1'b0;
rx <= {rx[8:1], i2c_sda_io};
cnt <= cnt - 1;
end
WRITE:
begin
ready_r <= 1'b0;
sda_dir <= 1'b1;
sda_r <= din_i[cnt];
cnt <= cnt - 1;
end
WACK2:
begin
if(rnw_i) begin
sda_dir <= 1'b1;
sda_r <= wack2_master_i;
rx <= wack2_master_i;
ready_r <= 1'b1;
end
else begin
sda_dir = 1'b0;
rx = {rx[8:1], i2c_sda_io};
ready_r = 1'b1;
end
end
HOLD : cnt <= 3'd7;
STOP :
begin
sda_dir <= 1'b1;
sda_r <= 1'b1;
end
endcase
end
//3-FSM end
assign dout_o = rx[8:1];
assign ack_o = rx[0];
assign ready_o = ready_r;
endmodule
测试班:
`timescale 1ns / 1ps
module tb;
// Inputs
reg clk_i;
reg rst_i;
reg start_cmd;
reg stop_cmd;
reg rnw;
reg [6:0] addr_i;
reg [7:0] din_i;
reg wack2_master;
// Outputs
wire [7:0] dout_o;
wire i2c_scl_o;
wire ack_o;
wire ready_o;
// Bidirectional
wire i2c_sda_io;
reg i2c_sda_io_reg;
assign i2c_sda_io = i2c_sda_io_reg;
// Instantiate the I2C_master
I2C_master uut (
.clk_i(clk_i),
.rst_i(rst_i),
.start_cmd(start_cmd),
.stop_cmd(stop_cmd),
.rnw_i(rnw),
.addr_i(addr_i),
.din_i(din_i),
.dout_o(dout_o),
.i2c_scl_o(i2c_scl_o),
.i2c_sda_io(i2c_sda_io),
.ack_o(ack_o),
.ready_o(ready_o),
.wack2_master_i(wack2_master)
);
// Clock generation
initial begin
clk_i = 0;
forever #5 clk_i = ~clk_i; // 100MHz clock
end
// Test sequence
initial begin
// Initialize inputs
rst_i = 1;
start_cmd = 0;
stop_cmd = 0;
rnw = 0;
addr_i = 7'b1010000; // Example I2C address
din_i = 8'h00;
i2c_sda_io_reg = 1'bz; // High impedance
// Reset the device
#10 rst_i = 0;
// Test 1: Write operation
#10 start_cmd = 1;
#10 start_cmd = 0;
rnw = 0; // Write operation
din_i = 8'h55; // Example data to write
#95 i2c_sda_io_reg = 1'b0;
#5 i2c_sda_io_reg = 1'bz;
#85 i2c_sda_io_reg = 1'b0;
#100 stop_cmd = 1;
#10 stop_cmd = 0;
i2c_sda_io_reg = 1'bz;
// Finish simulation
#100 $finish;
end
endmodule
您的 Verilog 代码存在多个问题。
该代码在 EDA Playground 网站上的 4 个不同模拟器上存在编译错误。 将您的代码复制到此处以查看错误消息。 您应该修复这些错误。 例如,从第一个
rx
块中删除 always_ff
将其重置到第二个 always_ff
块中:
always_ff@(posedge clk_i) begin
if(rst_i)
rx <= '0;
else
case(state)
您没有将 wack2_master 测试平台信号驱动到已知值,而是将 x 传播到设计中。