TestBench的IIC从机设计
0赞这个testbench的编写是基于之前日志里的基于FPGA的IIC通信而做的,原来只是做了简单的主机写随机地址EEPROM, 然后主机读随机地址EEPROM,都是只有一个字节的,做的是最简单的通信。所以这次testbench设计IIC从机也是和前面的相对应 的。
Testbench代码:
`timescale 1ns/1ns
module testbench();
// DATE: Wed Aug 15 08:35:56 2007
// TITLE:
// MODULE: iic_top09
// DESIGN: iic_top09
// FILENAME: iic_top09
// PROJECT: top_dram
// VERSION: Version
// Inputs
reg clk;
reg rst_n;
reg sw1;
reg sw2;
// Outputs
wire scl;
wire led_d2;
wire led_d3;
wire led_d4;
wire led_d5;
// Bidirs
wire sda;
// Instantiate the UUT
iic_top09 d (
.clk(clk),
.rst_n(rst_n),
.sw1(sw1),
.sw2(sw2),
.scl(scl),
.sda(sda),
.led_d2(led_d2),
.led_d3(led_d3),
.led_d4(led_d4),
.led_d5(led_d5)
);
initial begin
clk = 0;
forever #10 clk = ~clk;//产生50MHz的时钟
end
initial begin
rst_n = 0; //复位
#2000 rst_n = 1; //复位持续2us后结束
end
initial begin
sw1 = 1;
sw2 = 1;
#20000000 sw1 = 0; //delay 20ms,iic write
#20000000 sw1 = 1; //delay 20ms,iic idle
#20000000 sw2 = 0; //delay 20ms,iic read
#20000000 sw2 = 1; //delay 20ms,iic idle
end
reg start; //start=1:通信中,start=0:通信结束
initial start = 0;
always @ (sda) begin
if(scl && !sda) start = 1; //接收到起始位
if(scl && sda) start = 0; //接收到结束位
end
reg sda_link; //sda控制位
reg sda_out; //
reg[7:0] device_addr; //器件地址
reg[7:0] eeprom_addr; //数据地址
reg[7:0] eeprom_data; //数据
reg[7:0] addr0_data,addr1_data;//0地址和1地址数据存储器
reg lsb_data; //
initial begin
device_addr = 8'd0;
sda_link = 1'b0;
// lsb_data = 1'b0;
end
always @ (posedge start) begin //数据通信中
repeat (8) begin //接收器件地址
@ (posedge scl);
#10 device_addr = device_addr << 1;
device_addr[0] = sda;
end
if(device_addr != 8'b10100000) begin
$display ("device address ERROR!");
$stop;
end
@ (posedge scl);
sda_out = 0; //ack
sda_link = 1;
@ (negedge scl);
sda_link = 0;
repeat (8) begin //接收数据地址
@ (posedge scl);
#10 eeprom_addr = eeprom_addr << 1;
eeprom_addr[0] = sda;
end
@ (posedge scl);
sda_out = 0; //ack
sda_link = 1;
@ (negedge scl);
sda_link = 0;
@ (posedge scl);
lsb_data = sda;
@ (negedge scl);
if(lsb_data == sda) write_eeprom;
else read_eeprom;
end
task write_eeprom;
begin
eeprom_data[0] = lsb_data;
repeat (7) begin
@ (posedge scl);
#10 eeprom_data = eeprom_data << 1;
eeprom_data[0] = sda;
end
# 10;
case (eeprom_addr) //存储数据到相应地址,本测试只仿真0地 址和1地址
0: addr0_data = eeprom_data;
1: addr1_data = eeprom_data;
default: ;
endcase
@ (posedge scl);
sda_out = 0; //ack
sda_link = 1;
@ (negedge scl);
sda_link = 0;
end
endtask
reg[7:0] addr_data;//
task read_eeprom;
begin
// device_addr[0] = lsb_data;
repeat (8) begin //接收器件地址
@ (posedge scl);
#10 device_addr = device_addr << 1;
device_addr[0] = sda;
end
if(device_addr != 8'b10100001) begin
$display ("device address ERROR!");
$stop;
end
@ (posedge scl);
sda_out = 0; //ack
sda_link = 1;
case (eeprom_addr)
0: addr_data = addr0_data;
1: addr_data = addr1_data;
default: ;
endcase
repeat (8) begin
@ (posedge scl);
sda_out = addr_data[7];
#10 addr_data = addr_data << 1;
end
@ (posedge scl);
sda_out = 0; //ack
@ (negedge scl);
sda_link = 0;
end
endtask
assign sda = sda_link ? sda_out : 1'bz;
endmodule
仿真波形:
前面是主机写,后面是主机收,重点可以看应答位以及byte_data1。(byte_data1是主机 写入EEPROM的数据,byte_data2是 主机读出的写入数据)。
这个图是放大了的写入数据过程(观察sda的变化):
这个图是放大了的读出数据的过程: