TestBench I2C Slave SDA 不会变低
TestBench I2C Slave SDA won't go low
我正在尝试编写一个 I2C 从设备并对其进行隔离测试。
我有一个模拟应该在 write_ack
为高时将 SDA
拉低(也由红点突出显示)。但是,您可以看到 SDA
保持不变。
部分我认为这与我使用 force
方法和延迟进行测试的方式有关。
感谢任何帮助。
我找到了似乎有帮助的关键字 release
。
下面的代码和 EDA 游乐场在这里:https://edaplayground.com/x/6snM
/**
I2C Slave to Read/Write 8 bits of data only
*/
`timescale 1ns / 1ps
module Slave(
inout wire SDA,
input wire SCL);
reg [4:0] IDLE = 4'b0000;
reg [4:0] START = 4'b0001;
reg [4:0] READ_ADDRESS = 4'b0010;
reg [4:0] READ_WRITE = 4'b0011;
reg [4:0] DATA = 4'b0100;
reg [4:0] DATA_ACK = 4'b0101;
reg [4:0] STOP = 4'b0110;
reg [4:0] ADDRESS_ACK = 4'b0111;
reg [4:0] state = 4'b0010;
reg [6:0] slaveAddress = 7'b0001000;
reg [7:0] addr;
reg [6:0] addressCounter = 7'b0000000;
reg [7:0] data;
reg [6:0] dataCounter = 7'b0000000;
reg readWrite = 1'b0;
reg start = 0;
reg write_ack = 0;
assign SDA = (write_ack == 1) ? 0 : 'b1z;
always @(negedge SDA) begin
if ((start == 0) && (SCL == 1))
begin
start <= 1;
addressCounter <= 0;
dataCounter <= 0;
end
end
always @(posedge SDA) begin
if (state == DATA_ACK && SCL == 1)
begin
start <= 0;
state <= READ_ADDRESS;
end
end
always @(posedge SCL)
begin
if (start == 1)
begin
case (state)
READ_ADDRESS:
begin
addr[addressCounter] <= SDA;
addressCounter <= addressCounter + 1;
if (addressCounter == 6)
begin
state <= READ_WRITE;
end
end
READ_WRITE:
begin
readWrite <= SDA;
state <= ADDRESS_ACK;
end
ADDRESS_ACK:
begin
write_ack <= 1;
state <= DATA;
end
DATA:
begin
write_ack <= 0;
data[dataCounter] <= SDA;
dataCounter <= dataCounter + 1;
if (dataCounter == 8)
begin
state <= DATA_ACK;
write_ack <= 1;
end
end
DATA_ACK:
begin
write_ack <= 0;
state <= STOP;
end
STOP:
begin
start <= 0;
state <= READ_ADDRESS;
end
endcase
end
end
endmodule
测试代码
/**
Testing I2C Slace for reading/writing 8 bits of data only
*/
`timescale 1ns / 1ps
module Slave_TB ();
reg clk;
wire SDA;
wire SCL;
pullup(SDA);
pullup(SCL);
reg [6:0] addressToSend = 7'b0001000;
reg readWite = 1'b1;
reg [7:0] dataToSend = 8'b01100111;
integer ii=0;
initial begin
clk = 0;
force SCL = clk;
forever begin
clk = #1 ~clk;
force SCL = clk;
end
end
Slave #() UUT
(.SDA(SDA),
.SCL(SCL));
initial
begin
$display("Starting Testbench...");
clk = 0;
force SCL = clk;
#11
// Set SDA Low to start
force SDA = 0;
// Write address
for(ii=0; ii<7; ii=ii+1)
begin
$display("Address SDA %h to %h", SDA, addressToSend[ii]);
#2 force SDA = addressToSend[ii];
end
// Are we wanting to read or write to/from the device?
$display("Read/Write %h SDA: %h", readWite, SDA);
#2 force SDA = readWite;
$display("SDA: %h", SDA);
#2; // Wait for ACK bit
for(ii=0; ii<8; ii=ii+1)
begin
$display("Data SDA %h to %h", SDA, dataToSend[ii]);
#2 force SDA = dataToSend[ii];
end
#2; // Wait for ACK bit
// Force SDA high again, we are done
#2 force SDA = 1;
#100;
$finish();
end
initial
begin
// Required to dump signals to EPWave
$dumpfile("dump.vcd");
$dumpvars(0);
end
endmodule
不使用 force
,更传统的方法是向测试台添加三态缓冲区,就像您在设计中所做的那样。
对于SDA
,创建一个缓冲控制信号(drive_sda
)和一个测试平台数据信号(sda_tb
)。使用一个task
驱动一个字节并等待ACK。
因为SCL
不是inout
,所以不需要上拉,可以直接用clk
驱动。
module Slave_TB;
reg clk;
wire SDA;
wire SCL = clk;
pullup(SDA);
reg [6:0] addressToSend = 7'b000_1000; //8
reg readWite = 1'b1; //write
reg [7:0] dataToSend = 8'b0110_0111; //103 = 0x67
reg sda_tb;
reg drive_sda;
assign SDA = (drive_sda) ? sda_tb : 1'bz;
integer ii=0;
initial begin
clk = 0;
forever begin
clk = #1 ~clk;
end
end
Slave UUT
(.SDA(SDA),
.SCL(SCL));
initial begin
$display("Starting Testbench...");
drive_sda = 0;
sda_tb = 1;
#11;
// Set SDA Low to start
drive_sda = 1;
sda_tb = 0;
write({addressToSend, readWite});
write(dataToSend);
// Force SDA high again, we are done
#2;
drive_sda = 1;
sda_tb = 1;
#50;
$finish;
end
task write (reg [7:0] data);
integer ii;
for (ii=7; ii>=0; ii=ii-1) begin
$display("Data SDA %h to %h", SDA, data[ii]);
#2;
drive_sda = 1;
sda_tb = data[ii];
end
#2 drive_sda = 0;
endtask
initial begin
// Required to dump signals to EPWave
$dumpfile("dump.vcd");
$dumpvars(0);
end
endmodule
我正在尝试编写一个 I2C 从设备并对其进行隔离测试。
我有一个模拟应该在 write_ack
为高时将 SDA
拉低(也由红点突出显示)。但是,您可以看到 SDA
保持不变。
部分我认为这与我使用 force
方法和延迟进行测试的方式有关。
感谢任何帮助。
我找到了似乎有帮助的关键字 release
。
下面的代码和 EDA 游乐场在这里:https://edaplayground.com/x/6snM
/**
I2C Slave to Read/Write 8 bits of data only
*/
`timescale 1ns / 1ps
module Slave(
inout wire SDA,
input wire SCL);
reg [4:0] IDLE = 4'b0000;
reg [4:0] START = 4'b0001;
reg [4:0] READ_ADDRESS = 4'b0010;
reg [4:0] READ_WRITE = 4'b0011;
reg [4:0] DATA = 4'b0100;
reg [4:0] DATA_ACK = 4'b0101;
reg [4:0] STOP = 4'b0110;
reg [4:0] ADDRESS_ACK = 4'b0111;
reg [4:0] state = 4'b0010;
reg [6:0] slaveAddress = 7'b0001000;
reg [7:0] addr;
reg [6:0] addressCounter = 7'b0000000;
reg [7:0] data;
reg [6:0] dataCounter = 7'b0000000;
reg readWrite = 1'b0;
reg start = 0;
reg write_ack = 0;
assign SDA = (write_ack == 1) ? 0 : 'b1z;
always @(negedge SDA) begin
if ((start == 0) && (SCL == 1))
begin
start <= 1;
addressCounter <= 0;
dataCounter <= 0;
end
end
always @(posedge SDA) begin
if (state == DATA_ACK && SCL == 1)
begin
start <= 0;
state <= READ_ADDRESS;
end
end
always @(posedge SCL)
begin
if (start == 1)
begin
case (state)
READ_ADDRESS:
begin
addr[addressCounter] <= SDA;
addressCounter <= addressCounter + 1;
if (addressCounter == 6)
begin
state <= READ_WRITE;
end
end
READ_WRITE:
begin
readWrite <= SDA;
state <= ADDRESS_ACK;
end
ADDRESS_ACK:
begin
write_ack <= 1;
state <= DATA;
end
DATA:
begin
write_ack <= 0;
data[dataCounter] <= SDA;
dataCounter <= dataCounter + 1;
if (dataCounter == 8)
begin
state <= DATA_ACK;
write_ack <= 1;
end
end
DATA_ACK:
begin
write_ack <= 0;
state <= STOP;
end
STOP:
begin
start <= 0;
state <= READ_ADDRESS;
end
endcase
end
end
endmodule
测试代码
/**
Testing I2C Slace for reading/writing 8 bits of data only
*/
`timescale 1ns / 1ps
module Slave_TB ();
reg clk;
wire SDA;
wire SCL;
pullup(SDA);
pullup(SCL);
reg [6:0] addressToSend = 7'b0001000;
reg readWite = 1'b1;
reg [7:0] dataToSend = 8'b01100111;
integer ii=0;
initial begin
clk = 0;
force SCL = clk;
forever begin
clk = #1 ~clk;
force SCL = clk;
end
end
Slave #() UUT
(.SDA(SDA),
.SCL(SCL));
initial
begin
$display("Starting Testbench...");
clk = 0;
force SCL = clk;
#11
// Set SDA Low to start
force SDA = 0;
// Write address
for(ii=0; ii<7; ii=ii+1)
begin
$display("Address SDA %h to %h", SDA, addressToSend[ii]);
#2 force SDA = addressToSend[ii];
end
// Are we wanting to read or write to/from the device?
$display("Read/Write %h SDA: %h", readWite, SDA);
#2 force SDA = readWite;
$display("SDA: %h", SDA);
#2; // Wait for ACK bit
for(ii=0; ii<8; ii=ii+1)
begin
$display("Data SDA %h to %h", SDA, dataToSend[ii]);
#2 force SDA = dataToSend[ii];
end
#2; // Wait for ACK bit
// Force SDA high again, we are done
#2 force SDA = 1;
#100;
$finish();
end
initial
begin
// Required to dump signals to EPWave
$dumpfile("dump.vcd");
$dumpvars(0);
end
endmodule
不使用 force
,更传统的方法是向测试台添加三态缓冲区,就像您在设计中所做的那样。
对于SDA
,创建一个缓冲控制信号(drive_sda
)和一个测试平台数据信号(sda_tb
)。使用一个task
驱动一个字节并等待ACK。
因为SCL
不是inout
,所以不需要上拉,可以直接用clk
驱动。
module Slave_TB;
reg clk;
wire SDA;
wire SCL = clk;
pullup(SDA);
reg [6:0] addressToSend = 7'b000_1000; //8
reg readWite = 1'b1; //write
reg [7:0] dataToSend = 8'b0110_0111; //103 = 0x67
reg sda_tb;
reg drive_sda;
assign SDA = (drive_sda) ? sda_tb : 1'bz;
integer ii=0;
initial begin
clk = 0;
forever begin
clk = #1 ~clk;
end
end
Slave UUT
(.SDA(SDA),
.SCL(SCL));
initial begin
$display("Starting Testbench...");
drive_sda = 0;
sda_tb = 1;
#11;
// Set SDA Low to start
drive_sda = 1;
sda_tb = 0;
write({addressToSend, readWite});
write(dataToSend);
// Force SDA high again, we are done
#2;
drive_sda = 1;
sda_tb = 1;
#50;
$finish;
end
task write (reg [7:0] data);
integer ii;
for (ii=7; ii>=0; ii=ii-1) begin
$display("Data SDA %h to %h", SDA, data[ii]);
#2;
drive_sda = 1;
sda_tb = data[ii];
end
#2 drive_sda = 0;
endtask
initial begin
// Required to dump signals to EPWave
$dumpfile("dump.vcd");
$dumpvars(0);
end
endmodule