FPGA IIC 总线实现及仿真(二)
前言
在 FPGA IIC 总线实现及仿真(一)中完成了FPGA的IIC总线实现,本篇文章主要完成IIC总线的仿真
一、仿真测试程序
仿真测试首先让FPGA IIC总线向IIC设备写入10个数据0x010x0a,然后再由FPGA从IIC设备相同的数据地址中读取10个数据,能否正确读取0x010x0a。
仿真测试程序如下
module Test_IIC(
);
reg clk;
reg rst_p;
wire SCL;
wire SDA;
reg WR;
reg WR_start;
reg [7:0] data_in;
wire Write_ready;
wire Write_done;
wire [7:0] data_out;
wire Read_ready;
wire Read_done;
IIC_Top IIC_Top(
.clk(clk),
.rst_p(rst_p),
.SCL(SCL),
.SDA(SDA),
.WR(WR),
.WR_start(WR_start),
.data_in(data_in),
.Write_ready(Write_ready),
.Write_done(Write_done),
//input [7:0] Write_num,
.data_out(data_out),
.Read_ready(Read_ready),
.Read_done(Read_done)
//input [7:0] Read_num
);
///
initial begin
clk = 0;
rst_p = 1;
#100
rst_p = 0;
#1000
WR = 0;
WR_start=1;
#1000
WR_start=0;
#2000000
WR = 1;
WR_start=1;
#1000
WR_start=0;
end
always #10 clk = ~clk ;
/写数据/
reg Write_ready_r = 0;
always @(posedge clk) begin
Write_ready_r<=Write_ready;
if(WR_start==1)
data_in<=8'h01;
else
if(Write_ready_r==0 && Write_ready==1)
data_in <=data_in+1;
end
//
IIC_Slave IIC_Slave
(
.clk(clk),
.SCL(SCL),
.SDA(SDA)
);
endmodule
在测试时,我们建立了一个IIC_Slave 模块来模拟IIC从设备,代码如下
module IIC_Slave(
input clk, //工作时钟
input SCL, //IIC 时钟信号
inout SDA //IIC 数据信号
);
parameter write_addr = 8'b1010_000_0; //IIC 从设备的读地址
parameter read_addr = 8'b1010_000_1; //IIC 从设备的写地址
/
reg IIC_SCL = 0;
reg IIC_SDAin = 0;
reg IIC_SDAin_r= 0;
reg IIC_SDAout = 0;
reg IIC_SDAout_r = 0;
reg is_out = 0;
reg WR_bit = 0; //0:写数据 1:读数据
reg [7:0] Bit_cnt = 0;//接收bit数 计数器
reg [7:0] data_addr = 0;
reg [7:0] send_data = 0;//写入的数据
reg [7:0] recv_data = 0;//读出的数据
reg [7:0] data_mem [0:255]; //数据存储
integer i; //数据初始化
initial begin
for (i=0; i<255 ; i=i+1) begin
data_mem[i]<=0;
end
end
reg [3:0] IIC_state = 0;
reg [3:0] IIC_next_state = 0;
localparam [3:0] Idle_state = 0,
Start_state = 1,//start信号
WrAddr_state = 2,//设备写地址
DataAddr_state = 3,//设备读地址
ReStart_state = 4,//读数据时,重新发送start信号
RdAddr_state = 5,//数据地址
WrData_state = 6,//写数据
RdData_state = 7,//读数据
sACK_state = 8,//从设备响应
mACK_state = 9,//主设备响应
//NOP_state = 10,//主设备非应答信号
Stop_state = 11;//stop信号
assign SDA = (is_out==1) ? IIC_SDAout : 1'bz; //is_out=1 时发送,is_out=0时 时输入
/IIC 状态机///
always @ (posedge clk) begin
IIC_SCL<=SCL;
case(IIC_state)
Idle_state:begin
Bit_cnt <= 0;
if(IIC_SDAin==0 && IIC_SDAin_r==1 && SCL==1)
IIC_state<= WrAddr_state;
else
IIC_state<= Idle_state;
end
WrAddr_state:begin //写8bit设备写地址
if(IIC_SCL==0 && SCL==1)
Bit_cnt<= Bit_cnt+1;
if(Bit_cnt==8 && SCL==0)
begin
WR_bit<=recv_data[0];
IIC_state<= sACK_state;
if(recv_data==write_addr)
begin
IIC_SDAout_r<=0;
IIC_next_state<=DataAddr_state;
end
else
begin
IIC_SDAout_r<=1;
IIC_next_state<=Idle_state;
end
end
end
DataAddr_state:begin //写8bit设备读地址
if(IIC_SCL==0 && SCL==1)
Bit_cnt<= Bit_cnt+1;
if(Bit_cnt==8 && SCL==0)
begin
IIC_state<= sACK_state;
IIC_next_state<=WrData_state;
IIC_SDAout_r<=0;
//if(WR_bit==0)
//else
// IIC_next_state<=ReStart_state;
end
end
//ReStart_state:begin
// if(IIC_SDAin==0 && IIC_SDAin_r==1 && IIC_SCL==1)
// IIC_state<= RdAddr_state;
// else
// IIC_state<= ReStart_state;
//end
RdAddr_state:begin //写8bit设备读地址
if(IIC_SCL==0 && SCL==1)
Bit_cnt<= Bit_cnt+1;
if(Bit_cnt==8 && SCL==0)
begin
if(recv_data==read_addr)
begin
IIC_SDAout_r<=0;
IIC_state<= sACK_state;
IIC_next_state<=RdData_state;
end
else
begin
IIC_SDAout_r<=1;
IIC_state<= sACK_state;
IIC_next_state<=Idle_state;
end
end
end
WrData_state:begin //读数据
if(IIC_SDAin==0 && IIC_SDAin_r==1 && IIC_SCL==1)//如果收到Restart信号,则进入RdAddr_state
IIC_state<= RdAddr_state;
if(IIC_SDAin==1 && IIC_SDAin_r==0 && IIC_SCL==1)///如果收到Stop信号,则进入Idle_state
IIC_state<= Idle_state;
if(IIC_SCL==0 && SCL==1)
Bit_cnt<= Bit_cnt+1;
if(Bit_cnt==8 && SCL==0)
begin
IIC_state<= sACK_state;
IIC_next_state<=WrData_state;
IIC_SDAout_r<=0;
end
end
RdData_state:begin //读数据
if(IIC_SCL==0 && SCL==1)
Bit_cnt<= Bit_cnt+1;
if(Bit_cnt==8 && SCL==0)
begin
IIC_state<= mACK_state;
end
end
sACK_state:begin //响应
Bit_cnt <= 0;
if(IIC_SCL==0 && SCL==1)
IIC_state<= IIC_next_state;
end
mACK_state:begin //等待响应
Bit_cnt <= 0;
if(IIC_SCL==0 && SCL==1)
begin
if(IIC_SDAin==0 )
IIC_state<= IIC_next_state;
else
IIC_state<= Stop_state; //如果未响应,则结束此次读写操作
end
end
Stop_state:begin
if(IIC_SDAin==1 && IIC_SDAin_r==0 && IIC_SCL==1)
IIC_state<= Idle_state;
end
endcase
end
SDA 时序/
always @ (posedge clk) begin
if(IIC_state==sACK_state || IIC_state==RdData_state)
is_out<=1;
else
is_out<=0;
end
always @ (posedge clk) begin
if(is_out==0)
begin
IIC_SDAin_r<=IIC_SDAin;
IIC_SDAin<=SDA;
end
end
always @ (posedge clk) begin
case(IIC_state)
sACK_state:begin
IIC_SDAout<=IIC_SDAout_r;
end
RdData_state:begin
if(IIC_SCL==1 && SCL==0)
case(Bit_cnt)
0:IIC_SDAout<=send_data[7]; //先发高位
1:IIC_SDAout<=send_data[6];
2:IIC_SDAout<=send_data[5];
3:IIC_SDAout<=send_data[4];
4:IIC_SDAout<=send_data[3];
5:IIC_SDAout<=send_data[2];
6:IIC_SDAout<=send_data[1];
7:IIC_SDAout<=send_data[0];
endcase
end
default:
IIC_SDAout<=1;
endcase
end
读数据 时序/
always @ (posedge clk) begin
//if(Bit_cnt==8 && SCL==0)
//begin
if( IIC_state==RdData_state )
send_data<=data_mem[data_addr];
//end
end
写数据 时序/
always @ (posedge clk) begin
if(IIC_state==WrAddr_state || IIC_state==DataAddr_state || IIC_state==RdAddr_state
|| IIC_state==WrData_state)
begin
if(IIC_SCL==0 && SCL==1)
case(Bit_cnt)
0:recv_data[7]<=IIC_SDAin; //先收高位
1:recv_data[6]<=IIC_SDAin;
2:recv_data[5]<=IIC_SDAin;
3:recv_data[4]<=IIC_SDAin;
4:recv_data[3]<=IIC_SDAin;
5:recv_data[2]<=IIC_SDAin;
6:recv_data[1]<=IIC_SDAin;
7:recv_data[0]<=IIC_SDAin;
endcase
end
end
always @ (posedge clk) begin
if(Bit_cnt==8 && SCL==0)
begin
if( IIC_state==DataAddr_state )
data_addr<=recv_data;
else if(IIC_state==WrData_state || IIC_state==RdData_state)
data_addr<=data_addr+1;
end
end
always @ (posedge clk) begin
if(Bit_cnt==8 && SCL==0)
begin
if( IIC_state==WrData_state )
data_mem[data_addr]<=recv_data;
end
end
endmodule
二、仿真结果
首先,FPGA通过IIC总线向IIC设备写入数据,仿真结果如下图所示。图中上面红色圈为FPGA主设备时序,下边粉色圈为IIC从设备时序图。
然后,FPGA在通过IIC总线从IIC设备的相同地址读入数据,仿真结果如下图所示。图中上面紫色圈为FPGA主设备时序,下边绿色圈为IIC从设备时序。
总体读写时序如下图所示