Verilog 中的 I2C 从属模块不确认
I2C slave module in Verilog does not acknowledge
我用 Verilog 编写了这个 I2C 从属模块:
module I2CSlave(
input iSCL,
input iI2C_CLK,
inout bSDA,
output reg [7:0] odata,
output reg oread,
output wire oactive
);
reg incycle = 1'b0;
reg pSDA;
reg pSCL;
always @(posedge iI2C_CLK) begin
if ((pSCL) && (iSCL) && (pSDA) && (~bSDA)) begin
incycle <= 1;
end
if ((pSCL) && (iSCL) && (~pSDA) && (bSDA)) begin
incycle <= 0;
end
pSDA <= bSDA;
pSCL <= iSCL;
end
assign oactive = incycle;
localparam STATE_IDLE = 0;
localparam STATE_ADDR = 1;
localparam STATE_RW = 2;
localparam STATE_ACK = 3;
localparam STATE_DATA = 4;
localparam STATE_ACK2 = 5;
reg [7:0] i = 0;
reg [7:0] state = STATE_IDLE;
reg [6:0] addr = 7'h03;
reg addr_match = 1;
reg rw;
reg lSDA;
always @(posedge iSCL) lSDA <= bSDA;
assign bSDA = ((state == STATE_ACK) || (state == STATE_ACK2)) ? 0 : 1'bz;
assign oread = (state == STATE_ACK2);
assign ostate = i;
always @(negedge iSCL or negedge incycle) begin
if (~incycle) begin
state <= STATE_IDLE;
addr_match <= 1;
end
else if (addr_match) begin
case (state)
STATE_IDLE: begin
state <= STATE_ADDR;
i <= 7;
end
STATE_ADDR: begin
if (addr[i-1] != lSDA) addr_match <= 0;
if (i == 1) begin
state <= STATE_RW;
i <= i - 1;
end
else i <= i - 1;
end
STATE_RW: begin
rw <= lSDA;
state <= STATE_ACK;
end
STATE_ACK: begin
state <= STATE_DATA;
i = 7;
end
STATE_DATA : begin
odata[i] <= lSDA;
if (i == 0) state <= STATE_ACK2;
else i <= i - 1;
end
STATE_ACK2: begin
state <= STATE_DATA;
i = 7;
end
endcase
end
end
endmodule
目前它应该只是读取master发送的数据。它在模拟中似乎运行良好,但是当我将它上传到 FPGA 中时,有时一切正常,但有时它不确认主站发送的数据并且似乎忽略它们。我是 Verilog 的新手,所以我希望这不是一个愚蠢的问题。
您的代码存在多个问题,这些问题可能会导致仿真和综合行为不匹配。例如,以下是不可综合的,会被综合工具忽略。所以,你的初始状态会有所不同。检查日志中是否有警告。不要对 regs
使用声明赋值。 (适合电线)。
reg [7:0] i = 0;
reg [7:0] state = STATE_IDLE;
reg [6:0] addr = 7'h03;
reg addr_match = 1;
以上说明你初始化不生效
你搞砸了状态机中的阻塞和非阻塞分配。确保在 'i = 7' 的所有地方都使用 nbas。应该是
i <= 7;
并确保您在模拟中测试了足够的初始化和不同条件。
在真实硬件上 运行 随机失败的一个可能原因是您没有同步输入。
您正在对缓慢变化的信号(i2c 总线将有一个长斜率)进行采样,这些信号与您的设计时钟真正异步。根据您的运气,您将随机违反 setup/hold 次 fpga 的 d-flops,这会导致亚稳态问题。寄存器中的相同值可能在芯片的多个部分中被不同地处理。这将对您的 i2c 从属逻辑造成严重破坏。
您必须同步异步输入,在最简单的情况下,在将其提供给模块的 fsm 之前将其传递给几个寄存器。
我用 Verilog 编写了这个 I2C 从属模块:
module I2CSlave(
input iSCL,
input iI2C_CLK,
inout bSDA,
output reg [7:0] odata,
output reg oread,
output wire oactive
);
reg incycle = 1'b0;
reg pSDA;
reg pSCL;
always @(posedge iI2C_CLK) begin
if ((pSCL) && (iSCL) && (pSDA) && (~bSDA)) begin
incycle <= 1;
end
if ((pSCL) && (iSCL) && (~pSDA) && (bSDA)) begin
incycle <= 0;
end
pSDA <= bSDA;
pSCL <= iSCL;
end
assign oactive = incycle;
localparam STATE_IDLE = 0;
localparam STATE_ADDR = 1;
localparam STATE_RW = 2;
localparam STATE_ACK = 3;
localparam STATE_DATA = 4;
localparam STATE_ACK2 = 5;
reg [7:0] i = 0;
reg [7:0] state = STATE_IDLE;
reg [6:0] addr = 7'h03;
reg addr_match = 1;
reg rw;
reg lSDA;
always @(posedge iSCL) lSDA <= bSDA;
assign bSDA = ((state == STATE_ACK) || (state == STATE_ACK2)) ? 0 : 1'bz;
assign oread = (state == STATE_ACK2);
assign ostate = i;
always @(negedge iSCL or negedge incycle) begin
if (~incycle) begin
state <= STATE_IDLE;
addr_match <= 1;
end
else if (addr_match) begin
case (state)
STATE_IDLE: begin
state <= STATE_ADDR;
i <= 7;
end
STATE_ADDR: begin
if (addr[i-1] != lSDA) addr_match <= 0;
if (i == 1) begin
state <= STATE_RW;
i <= i - 1;
end
else i <= i - 1;
end
STATE_RW: begin
rw <= lSDA;
state <= STATE_ACK;
end
STATE_ACK: begin
state <= STATE_DATA;
i = 7;
end
STATE_DATA : begin
odata[i] <= lSDA;
if (i == 0) state <= STATE_ACK2;
else i <= i - 1;
end
STATE_ACK2: begin
state <= STATE_DATA;
i = 7;
end
endcase
end
end
endmodule
目前它应该只是读取master发送的数据。它在模拟中似乎运行良好,但是当我将它上传到 FPGA 中时,有时一切正常,但有时它不确认主站发送的数据并且似乎忽略它们。我是 Verilog 的新手,所以我希望这不是一个愚蠢的问题。
您的代码存在多个问题,这些问题可能会导致仿真和综合行为不匹配。例如,以下是不可综合的,会被综合工具忽略。所以,你的初始状态会有所不同。检查日志中是否有警告。不要对 regs
使用声明赋值。 (适合电线)。
reg [7:0] i = 0;
reg [7:0] state = STATE_IDLE;
reg [6:0] addr = 7'h03;
reg addr_match = 1;
以上说明你初始化不生效
你搞砸了状态机中的阻塞和非阻塞分配。确保在 'i = 7' 的所有地方都使用 nbas。应该是
i <= 7;
并确保您在模拟中测试了足够的初始化和不同条件。
在真实硬件上 运行 随机失败的一个可能原因是您没有同步输入。
您正在对缓慢变化的信号(i2c 总线将有一个长斜率)进行采样,这些信号与您的设计时钟真正异步。根据您的运气,您将随机违反 setup/hold 次 fpga 的 d-flops,这会导致亚稳态问题。寄存器中的相同值可能在芯片的多个部分中被不同地处理。这将对您的 i2c 从属逻辑造成严重破坏。
您必须同步异步输入,在最简单的情况下,在将其提供给模块的 fsm 之前将其传递给几个寄存器。