1.写一个数据(0x12)到EEPROM(AT24C04)的地址0, 再读出地址0的内容。
2.实例化 iic_com 模块和 chipscope 的两个模块 chipscope_icon 和 chipscope_ila。使用
50Mhz 时钟做为 chipscope 的采样时钟。采样的信号为从 EEPROM 读出的数据 RdData。
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
module eeprom_test(
input CLK_50MHz,
input RSTn,
output [3:0]LED,
output SCL,
inout SDA
);
wire [7:0] RdData; //读数据
wire Done_Sig; //iic_com块中通信结束信号
reg [3:0] i;
reg [3:0] rLED;
reg [7:0] rAddr;
reg [7:0] rData;
reg [1:0] isStart;
assign LED = rLED;
/*****************************************/
/* EEPROM write and read */
/*****************************************/
always@(posedge CLK_50MHz or negedge RSTn)
if(!RSTn)
begin
i <= 4'd0;
rAddr <= 8'd0;
rData <= 8'd0;
isStart <= 2'b00;
rLED <= 4'b0000;
end
else
case(i)
0: //eeprom write 0x12 to addr 0
if(Done_Sig)begin isStart <= 2'b00;i <= i + 1'b1;end //在iic_com模块中isDone需要开始复位
else begin isStart <= 2'b01; rData <= 8'h12; rAddr <= 8'd0;end //isStart == 01 写开始
1: //eeprom read addr 0
if(Done_Sig)begin isStart <= 2'b00; i <= i + 1'b1;end
else begin isStart <= 2'b10; rAddr <= 8'd0; end //isStart ==10 读开始
2:
begin rLED <= RdData[3:0];end
endcase
/**********************************/
// 通信程序 //
/*********************************/
iic_com inst_iic_com(
.CLK(CLK_50MHz),
.RSTn(RSTn),
.Start_Sig(isStart),
.Addr_Sig(rAddr),
.WrData(rData),
.RdData(RdData),
.Done_Sig(Done_Sig),
.SCL(SCL),
.SDA(SDA)
);
wire [35:0] CONTROL0;
wire [255:0] TRIG0;
icon_debug inst_icon_debug(
.CONTROL0(CONTROL0)
); /* synthesis syn_black_box syn_noprune=1 */;
chipscoepe_ila inst_chipscope_ila(
.CONTROL(CONTROL0),
.CLK(CLK_50MHz),
.TRIG0(TRIG0)
); /* synthesis syn_black_box syn_noprune=1 */;
assign TRIG0[7:0] = RdData;
endmodule
程序先从case0(写0x12数据到eeprom中地址0)开始。当Done_Sig为0时(在iic_com模块中isDone开始复位后), isStart <= 2’b01——Start_Sig[0]为1,选择写操作; rData <= 8’h12——写入数据为0x12; rAddr <= 8’d0——写入地址为0。
写完后,iic_com中isDone置1,即Done_Sig=1,执行i=i+1;跳转到case1。
case1同case0,只是只有读地址0操作。
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 20:54:09 07/22/2019
// Design Name:
// Module Name: iic_com
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module iic_com(
input CLK,
input RSTn,
input [1:0] Start_Sig, //选择读或写操作
input [7:0] Addr_Sig, //eeprom 字的地址
input [7:0] WrData, //写入字的内容
output[7:0] RdData, //输出读字节的内容到chipscope
output Done_Sig, //iic通信结束完成信号
output SCL,
output SDA
);
parameter F100K = 9'd500; //100KHz的时钟分频系数
reg [4:0] i; //流程
reg [4:0] Go; //总体流程
reg [8:0] C1; //计数器
reg [7:0] rData; //读数据寄存器
reg rSCL;
reg rSDA;
reg isAck;
reg isDone; //完成信号寄存器
reg isOut; //输入输出寄存器,SDA为1输出,为0输入
assign Done_Sig = isDone;
assign RdData = rData;
assign SCL = rSCL;
assign SDA = isOut ? rSDA:1'bz; //蓝线表示高阻态,说明sda口此时作为输入口
//*******************************************************************//
//* IIC读写处理程序 ***//
//*******************************************************************//
always@(posedge CLK or negedge RSTn)
if(!RSTn)begin
i <= 5'd0;
Go <= 5'd0;
C1 <= 9'd0;
rData <= 8'd0;
rSCL <= 1'b1;
rSDA <= 1'b1;
isAck <= 1'b1;
isDone <= 1'b0;
isOut <= 1'b1;
end
else if(Start_Sig[0]) //IIC数据写
case(i)
0: //start
begin
isOut <= 1; //SDA端口输出
if(C1 == 0)
rSCL <= 1'b1;
else if(C1 == 200)
rSCL <= 1'b0; //SCL由高变低
if(C1 == 0)
rSDA <= 1'b1;
else if(C1 == 100)
rSDA <= 1'b0; //SDA先由高边低
if(C1 == 250 - 1)
begin
C1 <= 9'd0; i<= i + 1'b1;
end
else C1 <= C1 + 1'b1;
end
1: //write Device addr
begin
rData <= {4'b1010, 3'b000, 1'b0};
i <= 5'd7; //跳转到输出8个位
Go <= i + 1'b1; //input
end
2: //write word addr
begin
rData <= Addr_Sig; //input
i <= 5'd7;
Go <= i + 1'b1;
end
3: //write Data
begin
rData <= WrData ; //WrData input
i <= 5'd7;
Go <= i + 1'b1;
end
4: //stop
begin
isOut <= 1'b1;
if(C1 == 0)
rSCL <= 1'b0;
else if(C1 == 50)
rSCL <= 1'b1; //scl由低变高
if(C1 == 0)
rSDA <=0;
else if(C1 == 150)
rSDA <=1; //sda由低变高
if(C1 == 250 - 1)
begin
C1 <= 9'd0;
i <= i + 1'b1;
end
else C1 <= C1 + 1'b1;
end
5: //写IIC结束
begin
isDone <= 1'b1;
i <= i + 1'b1;
end
6: //
begin
isDone <= 1'b0;
i <= 5'd0;
end
7,8,9,10,11,12,13,14: //发送Device addr/word addr/write data
begin
isOut <= 1'b1;
rSDA <= rData[14-i]; //高位先发送
if(C1 == 0)
rSCL <= 1'b0;
else if(C1 == 50)
rSCL <= 1'b1;
else if(C1 == 150)
rSCL <= 1'b0;
if(C1 == F100K - 1) //F100K 分屏系数
begin
C1 <= 9'd0;
i <= i + 1'b1;
end
else C1 <= C1 + 1'b1;
end
15: //waiting for acknowledge 来等Ack或者NAck
begin
isOut <= 1'b0; //SDA端口改为输入
if(C1 == 100) isAck <= SDA;
if(C1 == 0)rSCL <= 1'b0;
else if(C1 == 50)rSCL <=1'b1;
else if(C1 == 150)rSCL <= 1'b0;
if(C1 == F100K - 1)
begin
C1 <= 9'd0;
i <= i + 1'b1;
end
else C1 <= C1 + 1'b1;
end
16:
if(isAck != 0)i<= 5'd0;
else i <= Go;
endcase
else if(Start_Sig[1]) //IIC数据读
case(i)
0: //start
begin
isOut <= 1; //SDA端口输出
if(C1 == 0)rSCL <= 1'b1;
else if(C1 == 200)rSCL <= 1'b0; //SCL由高变低
if(C1 == 0)rSDA <= 1'b1;
else if(C1 ==100)rSDA <= 1'b0; //SDA先由高变低
if(C1 == 250 -1)
begin
C1 <= 9'd0;
i <= i + 1'b1;
end
else C1 <= C1 + 1'b1;
end
1: //write device addr
begin
rData <= {4'b1010,3'b000,1'b0}; //note
i <= 5'd9; //跳转到9 输出8个位,相比于7多两种,16-9 //14-7
Go <= i + 1'b1;
end
2: //write word addr
begin
rData <= Addr_Sig;
i <= 5'd9;
Go <= i + 1'b1;
end
3: //start again
begin
isOut <= 1'b1;
if(C1 == 0)rSCL <= 1'b0;
else if(C1 ==50)rSCL <= 1'b1;
else if(C1 == 250)rSCL <= 1'b0;
if(C1 == 0)rSDA <= 1'b0;
else if(C1 == 50)rSDA <= 1'b1;
else if(C1 == 150)rSDA <= 1'b0;
if(C1 == 300 - 1)
begin
C1 <= 9'd0;
i <= i + 1'b1;
end
else
C1 <= C1 + 1'b1;
end
4: //write device addr(read)
begin
rData <= {4'b1010,3'b000,1'b1};
i <= 5'd9;
Go <= i + 1'b1;
end
5: //read data
begin
rData <= 8'd0;
i <= 5'd9;
Go <= i + 1'b1;
end
6: //stop
begin
isOut <= 1'b1;
if(C1 == 0)rSCL <= 1'b0;
else if(C1 == 50)rSCL <= 1'b1;
if(C1 == 0)rSDA <= 1'b0;
else if(C1 == 150)rSDA <= 1'b1;
if(C1 == 250 -1)begin C1 <= 9'd0; i <= i + 1'b1;end
else C1 <= C1 + 1'b1;
end
7: //写IIC结束
begin
isDone <= 1'b1;
i <= i + 1'b1;
end
8:
begin
isDone <= 1'b0;
i <=5'd0;
end
9,10,11,12,13,14,15,16: //发送Device addr(write)/word addr/device addr(read)
begin
isOut <= 1'b1;
rSDA <= rData[16-i];
if(C1 == 0)rSCL <= 1'b0;
else if(C1 == 50)rSCL <= 1'b1;
else if(C1 == 150)rSCL <= 1'b0;
if(C1 == F100K - 1)
begin
C1 <= 9'd0;
i <= i + 1'b1;
end
else C1 <= C1 + 1'b1;
end
17: //waiting for acknowledge
begin
isOut <= 1'b0; //SDA端口改为输入
if(C1 == 100)isAck <= SDA;
if(C1 == 0) rSCL <= 1'b0;
else if(C1 == 50)rSCL <= 1'b1;
else if(C1 == 150)rSCL <= 1'b0;
if(C1 == F100K -1)
begin
C1 <= 9'd0;
i <= i + 1'b1;
end
else C1 <= C1 + 1'b1;
end
18:
if(isAck != 0)i<=5'd0;
else i <= Go;
19,20,21,22,23,24,25,26: //read data
begin
isOut <= 1'b0;
if(C1 == 100)rData[26-i] <= SDA;
if(C1 == 0 )rSCL <= 1'b0;
else if(C1 == 50)rSCL <= 1'b1;
else if(C1 == 150)rSCL <= 1'b0;
if(C1 == F100K - 1)
begin
C1 <= 9'd0;
i <= i + 1'b1;
end
else C1 <= C1 + 1'b1;
end
27: //no acknowledge
begin
isOut <= 1'b1;
if(C1 == 0)rSCL <= 1'b0;
else if(C1 == 50)rSCL <= 1'b1;
else if(C1 == 150)rSCL <= 1'b0;
if(C1 == F100K - 1)
begin
C1 <= 9'd0;
i <= Go;
end
else C1 <= C1 + 1'b1;
end
endcase
endmodule
先需要复位(RSTn==0),isDone=0;顶层模块读取到Done_Sig=01,iic_com模块中Start_Sig[0]=1,开始IIC写操作。
0:写起始信号
由IIC通信时序,SCL保持高电平,SDA由高变低(拉低)。发送开始信号。
1:写设备地址
设备地址为1010_0000,写完地址跳转到 7。
A2,A1为0(可以扩展at24c04),A8根据AT24C04D数据手册 为字地址的最高位(0x12 ——0001_0010)。bit0为0时写操作。
7(7~14):发送设备地址,字地址,写的数据
SDA选择输出,高位先发送。每个F100K(500)发送一个位。
15:等待ACK/NACK
SDA选择输入,当isACK为0,答应有效。等待500跳转16.
16:当isAck为0时,跳转到2(i=GO,此时GO为2);isAck为1 ,i=0;
2:写字地址,再调到7,7-16循环,和1相同。
3:写数据,再跳转到7,7-16循环,和1相同。
4:停止位。
5:写IIC结束,输出isDone=1;
6:isDone =0,i=0;
0:start 起始信号
1:写设备地址,设备地址为1010_0000,写完地址跳转到 9。
9(9~16):写设备地址,字地址, 写的数据。
17:等待ACK/NACK
18:当isAck为0时,跳转到2(i=GO,此时GO为2);isAck为1 ,i=0;
2:写 字 地址,再跳转到9,重复9~18;
3:start 起始信号
4:读设备地址
1010_0001,再跳转到9,重复9~18;
5:读 字的数据
初始rData=0,i=19;
19~26:读数据
SDA为输入,
27:等待ACK/NACK
(程序来自黑金 AX545/516 开发板 Verilog 教程)