当前位置:   article > 正文

FPGA常用接口协议--IIC_iic fpga

iic fpga

前言

  IIC接口协议是一种比较简单、常用的一种接口协议,使用它的场景很广泛(最常见的如EEPROM读写、一些寄存器的配置等),是我们学习FPGA过程中常用的接口协议,本篇使用EEPROM(AT24C512B)器件作为目标器件进行IIC接口实现,其他器件读写方式略有差别,IIC协议不变。

IIC协议

  IIC (Inter-Integrated Circuit(集成电路总线)),百度百科解释如下:IIC-百度百科

  这里简单概括一下协议内容:IIC(IIC,inter-Integrated circuit),两线式串行总线,用于MCU和外设间的通信;IIC只需两根线进行数据传输(数据线SDA和时钟线SCL),以半双工方式实现MCU和外设之间数据传输,常用的传输速率为100kbps、400kbps。

注意:SDA和SCL两根总线需要上拉,使总线处于空闲状态。

IIC总线系统结构如下图:
IIC总线系统结构

IIC总线系统结构

IIC协议如下:
1.空闲状态
  协议规定,SDA和SCL同时为高电平时,总线处于空闲状态。上拉电阻保证电平处于高电平。
2.起始信号和结束信号
  起始信号:SCL为高电平时,SDA电平发生高到低的跳变。
  停止信号:SCL为高电平时,SDA电平发生低到高的跳变。
在这里插入图片描述

3.应答信号
  发送器每发送完一个字节(8个脉冲),在第9个脉冲间释放总线,接收器返回一个ACK信号,协议规定,低电平为有效应答,高电平为无效应答。
在这里插入图片描述
4.数据有效性
  协议对有效数据进行了规定:即时钟信号为高电平期间,数据必须保持稳定,时钟信号低电平期间,数据线上的电平才允许变化。也就是说,数据在时钟信号到来前必须准备好,并保持到时钟信号的下降沿之后。
在这里插入图片描述
5.数据传输
  I2C为同步传输,时钟控制数据位的传输,边沿触发。

EEPROM读写方式解析

  这里只实现“字节写”和“随机读”读写方式,其他读写方式根据实际情况略作更改即可
1.字节写
  在起始信号后第一个字节写入7bit设备地址和1bit读写信号,等待从机回复一个ACK信号,第二字节写入高字节寄存器地址,等待从机回复一个ACK信号,第三字节写入低字节寄存器地址(若地址只有八位,写入一个寄存器地址即可),等待从机回复一个ACK信号,第四个字节写入要写入的数据,等待从机回复一个ACK信号,最后是一个停止信号。
在这里插入图片描述
2.随机读
  在起始信号后第一个字节写入7bit设备地址和1bit读写信号,等待从机回复一个ACK信号,第二字节写入高字节寄存器地址,等待从机回复一个ACK信号,第三字节写入低字节寄存器地址(若地址只有八位,写入一个寄存器地址即可),等待从机回复一个ACK信号(前面的3个字节是一个虚写操作);这里之后需要增加一个起始信号,第四个字节写入7bit设备地址和1bit读信号,等待从机回复一个ACK信号,第五个字节从机回复当前地址的数据,之后从机再回复一个NOACK信号,最后是一个停止信号。
在这里插入图片描述

IIC实现(verilog)

1.宏定义文件
  宏定义文件定义了3个宏,“SIM”用于仿真和综合的切换,综合时将“SIM”注释即可;“SIM_MODEL”用于添加仿真模型,默认即可;“FIRST_SIM”用于第一次仿真和非第一次仿真的切换,如果不是第一次仿真注释即可。

`define SIM
`define SIM_MODEL
// `define FIRST_SIM
  • 1
  • 2
  • 3

2.IIC主机实现
  IIC主机用于读写主控,这里使用了两个parameter,“IIC_RATE ”用于切换IIC速率(50K、100K、400K),“REGADDR_WIDTH ”用于切换读写地址位宽(8bit、16bit)

`timescale 1ns/1ns
//------------------------------------------------------------------------------------------
`include "macro_define.v"
`define SCL_POS		(cnt_delay==scl_pos_cnt)		//cnt=0:scl posedge
`define SCL_HIG		(cnt_delay==scl_hig_cnt)		//cnt=1:scl high level :data stable
`define SCL_NEG		(cnt_delay==scl_neg_cnt)		//cnt=2:scl negedge
`define SCL_LOW		(cnt_delay==scl_low_cnt)		//cnt=3:scl low level :data change
module iic_master
#(
	parameter IIC_RATE = "100k",
	parameter REGADDR_WIDTH = 16
)
(
	input		wire 					clk				,		//50M
	input 		wire					rst_n			,		//
	output		wire					scl				,		//IIC clock
	inout		wire					sda				,		//IIC data
	
	input		wire					iic_start		,		//IIC start
	input		wire					rd_or_wr		,		//1:read 0:write
	input 		wire[6:0] 				iic_device_id	,		//IIC device address
	input 		wire[REGADDR_WIDTH-1:0]	iic_reg_addr	,		//register address
	input 		wire[7:0]				iic_wr_data		,		//write register data
	output 		reg 					iic_rd_data_en	,		//IIC done flag
	output 		reg [7:0] 				iic_rd_data		,		//read register data
	output 		reg 					iic_done		,		//IIC done flag
	output  	reg             		iic_busy=0				//IIC busy flag
);
					
					
					
					
//-------------------reg/wire--------------------------//
	reg				iic_start_t1	;
	reg				iic_start_rise	;
	reg[3:0]		cnt				;
	reg[15:0]		cnt_delay		;
	wire	  		scl_valid		;
	reg [15:0] 		delay_counter	;
	wire[15:0] 		scl_pos_cnt		;	
	wire[15:0] 		scl_hig_cnt		;	
	wire[15:0] 		scl_neg_cnt		;	
	wire[15:0] 		scl_low_cnt		;	
	wire[8*4-1:0]	iic_rate		;
	reg				scl_temp		;		
	reg[7:0] 		db_r			;		//
	reg[3:0] 		cstate			;		//
	reg 			sda_r			;		//sda output reg
	reg 			sda_link		;		//0:output 1:input		
	reg[3:0] 		num				;		//
	reg				ack_reg=0		;
//----------------------------------------------------//	
	
	
	assign iic_rate = IIC_RATE		;
	assign scl_valid = iic_busy		;
	
always @ (*)
	begin
		if(iic_rate=="50k")
			delay_counter <= 2000;
		else if(iic_rate=="100k")
			delay_counter <= 1000;
		else if(iic_rate=="400k")
			delay_counter <= 500;
		else	
			delay_counter <= 250;
	end 
	
assign scl_pos_cnt = (delay_counter>>2)-1;
assign scl_hig_cnt = (delay_counter>>1)-1;
assign scl_neg_cnt = (delay_counter>>1)+(delay_counter>>2)-1;
assign scl_low_cnt = delay_counter-1;

//iic_start_rise
always @ (posedge clk or negedge rst_n)
	if( !rst_n )begin 
		iic_start_t1 <= 1'b0;
		iic_start_rise <= 1'b0;
		end
	else begin	
		iic_start_t1 <= iic_start;
		iic_start_rise <= iic_start & ~iic_start_t1;
		end

//cnt_delay
always @ (posedge clk or negedge rst_n)
	if( !rst_n ) 
		cnt_delay <= 10'd0;
	else if( scl_valid && cnt_delay == delay_counter-1 ) 
		cnt_delay <= 10'd0;				
	else if(scl_valid==1) 
		cnt_delay <= cnt_delay+1'b1;	
	else	
		cnt_delay <= 10'd0;


always @ (posedge clk or negedge rst_n) begin
	if( !rst_n ) cnt <= 3'd0;
	else begin
		case ( cnt_delay )
			scl_neg_cnt:	cnt <= 3'd0;	
			scl_low_cnt:	cnt <= 3'd1;	
			scl_pos_cnt:	cnt <= 3'd2;	
			scl_hig_cnt:	cnt <= 3'd3;	
			default: ;
			endcase
		end
end

//产生iic所需要的时钟
always @ (posedge clk or negedge rst_n)
	if(!rst_n) 
		scl_temp <= 1'b1;
	else if(cnt_delay==scl_pos_cnt) 
		scl_temp <= 1'b1;			//scl rise edge
    else if(cnt_delay==scl_neg_cnt) 
		scl_temp <= 1'b0;			//scl falling edge
	else 
		scl_temp <= scl_temp;

assign scl = (scl_valid==1)?scl_temp:1;


reg	iic_done_t;
//
always @ (posedge clk or negedge rst_n)
	if(!rst_n)begin 
		iic_done_t <= 1'b0;
	end
	else begin
		iic_done_t <= iic_done;
	end

always @ (posedge clk or negedge rst_n)
	if(!rst_n)
		iic_rd_data_en<= 1'b0;
	else if(rd_or_wr)
		iic_rd_data_en <= iic_done & ~iic_done_t;
	else	
		iic_rd_data_en<= 1'b0;		
		
//---------------------------------------------//
localparam 	IDLE 	= 4'd0	,
			START1 	= 4'd1	,
			ADD1 	= 4'd2	,
			ACK1 	= 4'd3	,
			ADD2 	= 4'd4	,
			ACK2 	= 4'd5	,
			ADD2_1 	= 4'd6	,
			ACK2_1 	= 4'd7	,
			START2 	= 4'd8	,
			ADD3 	= 4'd9	,
			ACK3	= 4'd10	,
			DATA 	= 4'd11	,
			ACK4	= 4'd12	,
			STOP1 	= 4'd13	,
			STOP2 	= 4'd14	;


always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) begin
			cstate <= IDLE;
			sda_r <= 1'b1;
			sda_link <= 1'b1;
			num <= 4'd0;
			db_r <= 8'd0;
			iic_rd_data <= 8'b0000_0000;
			iic_done <= 1'b0;
		end
	else
		case (cstate)
			IDLE:	begin
					sda_link <= 1'b1;						//0:input;1:output
					sda_r <= 1'b1;
					iic_done <= 1'b0;
					if(iic_start_rise) begin						//iic start		
					 `ifdef SIM
						db_r <= {iic_device_id,rd_or_wr};		//write device address rd_or_wr=1:read;rd_or_wr=0:write;
						// $display("simulation");						
					 `else
						db_r <= {iic_device_id,1'b0};		//write device address rd_or_wr=1:read;rd_or_wr=0:write;
						// $display("synth");
					 `endif						
						cstate <= START1;
						iic_busy <= 1;						
						end
					else cstate <= IDLE;				
				end
			START1: begin							//iic start singal
					if(`SCL_HIG) begin				//scl = 1;
						sda_link <= 1'b1;			//0:input;1:output
						sda_r <= 1'b0;				//iic start singal
						cstate <= ADD1;
						num <= 4'd0;				//num clear
						end
					else cstate <= START1; 			
				end
			ADD1:	begin							//iic receive device id
					if(`SCL_LOW) begin
							if(num == 4'd8) begin	
									num <= 4'd0;			//num clear
									sda_r <= 1'b1;			
									sda_link <= 1'b0;		//0:input;1:output wait ack
									cstate <= ACK1;
								end
							else begin
									cstate <= ADD1;
									num <= num+1'b1;
									case (num)
										4'd0: sda_r <= db_r[7];
										4'd1: sda_r <= db_r[6];
										4'd2: sda_r <= db_r[5];
										4'd3: sda_r <= db_r[4];
										4'd4: sda_r <= db_r[3];
										4'd5: sda_r <= db_r[2];
										4'd6: sda_r <= db_r[1];
										4'd7: sda_r <= db_r[0];		//read or warte
										default: ;
										endcase
								end
						end
					else cstate <= ADD1;
				end
			ACK1:	begin									//iic after transmit device id,receive slave ack singal	
					if(`SCL_HIG)
						ack_reg <= sda;					
					else if(`SCL_NEG) begin	
						sda_r <= 1'b0;			
						sda_link <= 1'b1;		//0:input;1:output wait ack															
						if(ack_reg==0 && REGADDR_WIDTH == 16)begin
							cstate <= ADD2;					// if register address width is 16bit,go to status ADD2 
							db_r <= iic_reg_addr[15:8];		// Transmit High 8-bit Address first	
						end
						else if(ack_reg==0 && REGADDR_WIDTH == 8)begin
							cstate <= ADD2_1;				// if register address width is 8bit,go to status ADD2_1
							db_r <= iic_reg_addr[7:0];		// Transmit 8-bit Address 								
						end
						else if(ack_reg!=0)
						cstate <= IDLE;						
					end
					else cstate <= ACK1;					//wait slave respond 
				end
			ADD2:	begin									// Transmit High 8-bit Address first
					if(`SCL_LOW) begin
							if(num==4'd8) begin	
									num <= 4'd0;			//num计数清零
									sda_r <= 1'b1;
									sda_link <= 1'b0;		//sda置为高阻态(input)
									cstate <= ACK2;
								end
							else begin
									sda_link <= 1'b1;		//sda作为output
									num <= num+1'b1;
									case (num)
										4'd0: sda_r <= db_r[7];
										4'd1: sda_r <= db_r[6];
										4'd2: sda_r <= db_r[5];
										4'd3: sda_r <= db_r[4];
										4'd4: sda_r <= db_r[3];
										4'd5: sda_r <= db_r[2];
										4'd6: sda_r <= db_r[1];
										4'd7: sda_r <= db_r[0];
										default: ;
										endcase		
									cstate <= ADD2;					
								end
						end
					else cstate <= ADD2;				
				end	
			ACK2:	begin									//iic transmit high 8-bit address finish,receive slave ack singal
					if(`SCL_HIG)
						ack_reg <= sda;	
					else if(`SCL_NEG)begin
						sda_r <= 1'b0;			
						sda_link <= 1'b1;		//0:input;1:output wait ack	
						if(ack_reg==0)begin
							cstate <= ADD2_1;				//从机响应信号
							db_r <= iic_reg_addr[7:0];		// 1地址
						end
						else 
							cstate <= IDLE;
						end
					else cstate <= ACK2;			//等待从机响应
				end	
			ADD2_1:	begin									// if register address width is 16bit Transmit Low 8-bit Address			
					if(`SCL_LOW) begin						// if register address width is 8bit Transmit register Address
							if(num==4'd8) begin	
									num <= 4'd0;			//num计数清零
									sda_r <= 1'b1;
									sda_link <= 1'b0;		//sda置为高阻态(input)
									cstate <= ACK2_1;
								end
							else begin
									sda_link <= 1'b1;		//sda作为output
									num <= num+1'b1;
									case (num)
										4'd0: sda_r <= db_r[7];
										4'd1: sda_r <= db_r[6];
										4'd2: sda_r <= db_r[5];
										4'd3: sda_r <= db_r[4];
										4'd4: sda_r <= db_r[3];
										4'd5: sda_r <= db_r[2];
										4'd6: sda_r <= db_r[1];
										4'd7: sda_r <= db_r[0];
										default: ;
										endcase		
									cstate <= ADD2_1;					
								end
						end
					else cstate <= ADD2_1;				
				end
			ACK2_1:	begin								//iic transmit register address finish,receive slave ack singal							
					if(`SCL_HIG)
						ack_reg <= sda;	
					else if(`SCL_NEG)begin
						sda_r <= 1'b0;			
						sda_link <= 1'b1;		//0:input;1:output wait ack
						if(ack_reg==0)begin					
							if(rd_or_wr) begin	
								cstate <= START2;			//读操作		
								db_r <= {iic_device_id,1'b1};	//送器件地址(读操作),特定地址读需要执行该步骤以下操作										
							end	
							else begin
								cstate <= DATA; 			//写操作
								db_r <= iic_wr_data;	//写入的数据	
							end
						end
						else
								cstate <= IDLE;	
					end			
					else cstate <= ACK2_1;	//等待从机响应
				end
			START2: begin	//读操作起始位
					if(`SCL_LOW) begin
						sda_link <= 1'b1;	//sda作为output
						sda_r <= 1'b1;		//拉高数据线sda
						cstate <= START2;
						end
					else if(`SCL_HIG) begin	//scl为高电平中间
						sda_r <= 1'b0;		//拉低数据线sda,产生起始位信号
						cstate <= ADD3;
						end	 
					else cstate <= START2;
				end
			ADD3:	begin	//送读操作地址
					if(`SCL_LOW) begin
							if(num==4'd8) begin	
									num <= 4'd0;			//num计数清零
									sda_r <= 1'b1;
									sda_link <= 1'b0;		//sda置为高阻态(input)
									cstate <= ACK3;
								end
							else begin
									num <= num+1'b1;
									case (num)
										4'd0: sda_r <= db_r[7];
										4'd1: sda_r <= db_r[6];
										4'd2: sda_r <= db_r[5];
										4'd3: sda_r <= db_r[4];
										4'd4: sda_r <= db_r[3];
										4'd5: sda_r <= db_r[2];
										4'd6: sda_r <= db_r[1];
										4'd7: sda_r <= db_r[0];
										default: ;
										endcase									
									cstate <= ADD3;					
								end
						end
					else cstate <= ADD3;				
				end
			ACK3:	begin
					if(`SCL_HIG)
						ack_reg <= sda;	
					else if(`SCL_NEG)begin
						sda_r <= 1'b0;			
						sda_link <= 1'b1;		//0:input;1:output wait ack
						if(ack_reg==0)begin	
							cstate <= DATA;	//从机响应信号
							sda_link <= 1'b0;
						end
						else
							cstate <= IDLE;
					end
					else cstate <= ACK3; 		//等待从机响应
				end
			DATA:	begin
					if(rd_or_wr) begin	 //读操作
							if(num<=4'd7) begin
								cstate <= DATA;
								if(`SCL_HIG) begin	
									num <= num+1'b1;	
									case (num)
										4'd0: iic_rd_data[7] <= sda;
										4'd1: iic_rd_data[6] <= sda;  
										4'd2: iic_rd_data[5] <= sda; 
										4'd3: iic_rd_data[4] <= sda; 
										4'd4: iic_rd_data[3] <= sda; 
										4'd5: iic_rd_data[2] <= sda; 
										4'd6: iic_rd_data[1] <= sda; 
										4'd7: iic_rd_data[0] <= sda; 
										default: ;
										endcase																		
									end
								end
							else if((`SCL_LOW) && (num==4'd8)) begin
								num <= 4'd0;			//num计数清零
								cstate <= ACK4;
								end
							else cstate <= DATA;
						end
					else  begin					//write data
							sda_link <= 1'b1;	
							if(num<=4'd7) begin
								cstate <= DATA;
								if(`SCL_LOW) begin
									sda_link <= 1'b1;		//数据线sda作为output
									num <= num+1'b1;
									case (num)
										4'd0: sda_r <= db_r[7];
										4'd1: sda_r <= db_r[6];
										4'd2: sda_r <= db_r[5];
										4'd3: sda_r <= db_r[4];
										4'd4: sda_r <= db_r[3];
										4'd5: sda_r <= db_r[2];
										4'd6: sda_r <= db_r[1];
										4'd7: sda_r <= db_r[0];
										default: ;
										endcase									
									end
							 	end
							else if((`SCL_LOW) && (num==4'd8)) begin
									num <= 4'd0;
									sda_r <= 1'b1;
									sda_link <= 1'b0;		//sda置为高阻态
									cstate <= ACK4;
								end
							else cstate <= DATA;
						end
				end
			ACK4: begin
					if(`SCL_HIG)
						ack_reg <= sda;	
					else if(`SCL_NEG)begin
						sda_r <= 1'b0;			
						sda_link <= 1'b1;		//0:input;1:output wait ack
						if(ack_reg==0 && rd_or_wr==0)begin	
						cstate <= STOP1;						
						end
						else if(ack_reg==1 && rd_or_wr==1)begin	
						cstate <= STOP1;						
						end						
						else
						cstate <= IDLE;
					end
					else cstate <= ACK4;
				end
			STOP1:	begin
							sda_link <= 1'b1;	//0:input;1:output
							sda_r <= 1'b0;
					if(`SCL_HIG)begin
							iic_done <= 1'b1;
							cstate <= IDLE;
							iic_busy <= 0;
						end
					else cstate <= STOP1;
				end
			default: cstate <= IDLE;
			endcase
end

assign sda = sda_link ? sda_r:1'bz;

//---------------------------------------------
endmodule 	  

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476

3.IIC读写模型(可以理解为从机)
  IIC读写模型用于模拟EEPROM从机,用于和主机交互,一般我们写好主机程序之后,没有读写模型不能仿真,不能验证代码的正确性,这里读写模型可方便仿真。

`timescale 1ns / 1ps
//
// Company: 
// Engineer: nathanz
// 
// Create Date: 2021/05/01 18:35:23
// Design Name: 
// Module Name: iic_sim_model
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

`define SCL_POS		(cnt_delay==scl_pos_cnt)		//cnt=0:scl posedge
`define SCL_HIG		(cnt_delay==scl_hig_cnt)		//cnt=1:scl high level :data stable
`define SCL_NEG		(cnt_delay==scl_neg_cnt)		//cnt=2:scl negedge
`define SCL_LOW		(cnt_delay==scl_low_cnt)		//cnt=3:scl low level :data change

module iic_sim_model
#(
	parameter REGADDR_WIDTH = 16
)
(
    input	wire 		clk			,	
    input	wire		rst_n		,	//active-low
    input	wire		i_scl		,	//为输入
    inout	wire		io_sda		
);

/********************************************/

parameter clk_period_cnt = 1000 ;
localparam 	IDLE 		= 4'd0	,
			START1 		= 4'd1	,
			ADD1 		= 4'd2	,
			ACK1 		= 4'd3	,
			ADD2 		= 4'd4	,
			ACK2 		= 4'd5	,
			ADD2_1 		= 4'd6	,
			ACK2_1 		= 4'd7	,
			START2 		= 4'd8	,
			ADD3 		= 4'd9	,
			ACK3		= 4'd10	,
			DATA 		= 4'd11	,
			ACK4		= 4'd12	,
			STOP1 		= 4'd13	;

localparam device_id = 8'ha0;				

	reg[3:0] 		cstate					;		//
	reg 			sda_link				;		//sda direction crtl 1:output;0:input
	reg 			sda_r					;		//							
	reg[3:0] 		num						;		//
	reg 			iic_busy				;
	reg[7:0]		rec_device_id=0			;
	reg[15:0]		iic_reg_addr=0			;
	reg[7:0]		read_back_reg=0			;
	wire[7:0]		rd_data					;
	reg[7:0]		wr_data=0				;
	wire			rd_or_wr				;
	
	reg[15:0] 		cnt_delay				;	
	wire[15:0] 		scl_pos_cnt				;	
	wire[15:0] 		scl_hig_cnt				;	
	wire[15:0] 		scl_neg_cnt				;	
	wire[15:0] 		scl_low_cnt				;	
	
	wire 			iic_en=1				;	
	reg				i_scl_dly1				;	
	wire  			i_scl_rise				;	
	wire 			i_scl_fall				;	
	reg				io_sda_dly1				;	
	wire  			io_sda_rise				;	
	wire 			io_sda_fall				;


assign rd_or_wr = rec_device_id[0];

always @ (posedge clk or negedge rst_n) 
	if(!rst_n)
		begin 
		i_scl_dly1 <= 1'b0;
		io_sda_dly1 <= 1'b0;	
		end
	else
		 begin
		i_scl_dly1 <= i_scl;
		io_sda_dly1 <= io_sda;		
		end	

//sda上升沿
assign io_sda_rise = io_sda & ~io_sda_dly1;

//sda下降沿
assign io_sda_fall = ~io_sda & io_sda_dly1;

//scl上升沿
assign i_scl_rise = i_scl & ~i_scl_dly1;

//scl下降沿
assign i_scl_fall = ~i_scl & i_scl_dly1;

assign scl_valid = iic_busy;
		
assign scl_low_cnt = (clk_period_cnt>>2)-1;
assign scl_pos_cnt = (clk_period_cnt>>1)-1;
assign scl_hig_cnt = (clk_period_cnt>>1)+(clk_period_cnt>>2)-1;
assign scl_neg_cnt = clk_period_cnt-1;

//cnt_delay
always @ (posedge clk or negedge rst_n)
	if( !rst_n ) 
		cnt_delay <= 10'd0;			
	else if(scl_valid==1 && i_scl_fall==1) 
		cnt_delay <= 10'd0;	
	else	
		cnt_delay <= cnt_delay+1'b1;

always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) begin
			cstate <= IDLE;
			sda_link <= 1'b0;			
			sda_r <= 1'b1;
			num <= 4'd0;
			rec_device_id <= 8'd0;
		end
	else
		case (cstate)
			IDLE:	begin
					sda_link <= 1'b0;					//数据线sda为input
					rec_device_id <= 8'h00;
					if(iic_en) 
						begin						//iic_en==1,开始操作
						iic_busy <= 1'b1;
						cstate <= START1;		
						end
					else cstate <= IDLE;				//没有任何键被按下
				end
			START1: begin
					if(i_scl==1 && io_sda_fall==1) begin		//起始信号
						cstate <= ADD1;
						num <= 4'd0;				//num计数清零
						end
					else cstate <= START1; 				//等待起始信号
				end
			ADD1:	begin								//设备地址
					if(`SCL_NEG && num==7)begin
								num <= 4'd0;			//num计数清零
								sda_r <= 1'b1;
								sda_link <= 1'b0;		//sda置为高阻态(input)
								cstate <= ACK1;
					end	
					else if(`SCL_NEG)begin
								cstate <= ADD1;
								num <= num+1'b1;
					end
					else if(`SCL_HIG) begin					
								cstate <= ADD1;								
								case (num)
								4'd0: rec_device_id[7] <= io_sda;
								4'd1: rec_device_id[6] <= io_sda;
								4'd2: rec_device_id[5] <= io_sda;
								4'd3: rec_device_id[4] <= io_sda;
								4'd4: rec_device_id[3] <= io_sda;
								4'd5: rec_device_id[2] <= io_sda;
								4'd6: rec_device_id[1] <= io_sda;
								4'd7: rec_device_id[0] <= io_sda;
								// 4'd8: rd_or_wr <= io_sda;
							default: ;
							endcase							
						end	 
					else cstate <= ADD1;
				end
			ACK1:	begin
					sda_r <= 1'b0;				//回复ACK;
					sda_link <= 1'b1;			//sda :output					
				if(`SCL_NEG) begin
					sda_r <= 1'b1;				//
					sda_link <= 1'b0;			//sda :input			
						if(REGADDR_WIDTH == 16)begin
							cstate <= ADD2;		
						end
						else if(REGADDR_WIDTH == 8)begin
							cstate <= ADD2_1;										
						end
					else cstate <= ACK1;					
				end
			end 
			ADD2:	begin								//内存地址
					if(`SCL_NEG && num==7)begin
								num <= 4'd0;			//num计数清零
								sda_r <= 1'b1;
								sda_link <= 1'b0;		//sda置为高阻态(input)
								cstate <= ACK2;
					end	
					else if(`SCL_NEG)begin
								cstate <= ADD2;
								num <= num+1'b1;
					end				
					else if(`SCL_HIG) begin					
								cstate <= ADD2;								
								case (num)
									4'd0: ;
									4'd0: iic_reg_addr[15] <= io_sda;
									4'd1: iic_reg_addr[14] <= io_sda;
									4'd2: iic_reg_addr[13] <= io_sda;
									4'd3: iic_reg_addr[12] <= io_sda;
									4'd4: iic_reg_addr[11] <= io_sda;
									4'd5: iic_reg_addr[10] <= io_sda;
									4'd6: iic_reg_addr[9] <= io_sda;
									4'd7: iic_reg_addr[8] <= io_sda;
							default: ;
							endcase							
						end			
					else cstate <= ADD2;			
				end	
			ACK2:	begin
					sda_r <= 1'b0;				//回复ACK;
					sda_link <= 1'b1;			//sda输出				
				if(`SCL_NEG)begin
					sda_r <= 1'b1;				//回复ACK结束;
					sda_link <= 1'b0;			//sda输入
				 	cstate <= ADD2_1;						
				end
				end	
			ADD2_1:	begin						
					if(`SCL_NEG && num==7)begin
								num <= 4'd0;			//num计数清零
								sda_r <= 1'b1;
								sda_link <= 1'b0;		//sda置为高阻态(input)
								cstate <= ACK2_1;
					end	
					else if(`SCL_NEG)begin
								cstate <= ADD2_1;
								num <= num+1'b1;
					end	
					else if(`SCL_HIG) begin
								cstate <= ADD2_1;								
								case (num)
									4'd0: iic_reg_addr[7] <= io_sda;
									4'd1: iic_reg_addr[6] <= io_sda;
									4'd2: iic_reg_addr[5] <= io_sda;
									4'd3: iic_reg_addr[4] <= io_sda;
									4'd4: iic_reg_addr[3] <= io_sda;
									4'd5: iic_reg_addr[2] <= io_sda;
									4'd6: iic_reg_addr[1] <= io_sda;
									4'd7: iic_reg_addr[0] <= io_sda;
							default: ;
							endcase							
						end				
					else cstate <= ADD2_1;				
				end
			ACK2_1:	begin
					sda_r <= 1'b0;				//回复ACK;
					sda_link <= 1'b1;			//sda :output				
				if(`SCL_NEG)begin
					sda_r <= 1'b1;				//
					sda_link <= 1'b0;			//sda :input	
					if(rd_or_wr)
						cstate <= START2;
					else
						cstate <= DATA;					
				end
				else 
					cstate <= ACK2_1;	
				end
			START2: begin	//读操作起始位
					if(i_scl==1 && io_sda_fall==1) begin		//起始信号
						cstate <= ADD3;
						num <= 4'd0;				//num计数清零
						end
					else cstate <= START2; 				//等待起始信号
				end
			ADD3:	begin	//接收读操作地址
					if(`SCL_NEG && num==8)begin
								num <= 4'd0;			//num计数清零
								sda_r <= 1'b1;
								sda_link <= 1'b0;		//sda置为高阻态(input)
								cstate <= ACK3;
					end	
					else if(`SCL_NEG)begin
								cstate <= ADD3;
								num <= num+1'b1;
					end
					else if(`SCL_HIG) begin	
					case (num)
						4'd0: ;
						4'd1: rec_device_id[7] <= io_sda;
						4'd2: rec_device_id[6] <= io_sda;
						4'd3: rec_device_id[5] <= io_sda;
						4'd4: rec_device_id[4] <= io_sda;
						4'd5: rec_device_id[3] <= io_sda;
						4'd6: rec_device_id[2] <= io_sda;
						4'd7: rec_device_id[1] <= io_sda;
						4'd8: rec_device_id[0] <= io_sda;
						default: ;
					endcase
					cstate <= ADD3;
				end	
				else cstate <= ADD3;				
				end
			ACK3:	begin
					sda_r <= 1'b0;				//回复ACK;
					sda_link <= 1'b1;			//sda:output			
				if(`SCL_NEG)begin
					if(rd_or_wr==0)
					sda_link <= 1'b0;			//sda:intput
					else
					sda_link <= 1'b1;			//sda:intput	
					
					sda_r <= 1'b1;				//回复ACK结束;
					cstate <= DATA;
				end
				// if(i_scl==1 && io_sda_rise==1) begin
				 	// cstate <= DATA;					
				// end
				end
			DATA:	begin
					if(rd_or_wr) begin	 //读操作 输出
							sda_link <= 1'b1;			//sda:output	
							if(`SCL_NEG && num==7)begin
								num <= 4'd0;			//num计数清零
								sda_r <= 1'b1;
								sda_link <= 1'b0;		//sda 0:intput 1:outpu
								cstate <= ACK4;
							end	
							else if(`SCL_NEG)begin
								cstate <= DATA;
								num <= num+1'b1;
							end				
							else if(`SCL_LOW) begin		
								case (num)
								4'd0: sda_r <= read_back_reg[7];
								4'd1: sda_r <= read_back_reg[6];
								4'd2: sda_r <= read_back_reg[5];
								4'd3: sda_r <= read_back_reg[4];
								4'd4: sda_r <= read_back_reg[3];
								4'd5: sda_r <= read_back_reg[2];
								4'd6: sda_r <= read_back_reg[1];
								4'd7: sda_r <= read_back_reg[0];
								default: ;
								endcase	
								cstate <= DATA;									
								end				
						end
					else  begin	//写操作 输入
						sda_link <= 1'b0;	
							if(`SCL_NEG && num==7)begin
								num <= 4'd0;			//num计数清零
								sda_r <= 1'b1;
								sda_link <= 1'b0;		//sda置为高阻态(input)
								cstate <= ACK4;
							end	
							else if(`SCL_NEG)begin
								cstate <= DATA;
								num <= num+1'b1;
							end				
							else if(`SCL_HIG) begin					
								case (num)
								4'd0: wr_data[7] <= io_sda;
								4'd1: wr_data[6] <= io_sda;
								4'd2: wr_data[5] <= io_sda;
								4'd3: wr_data[4] <= io_sda;
								4'd4: wr_data[3] <= io_sda;
								4'd5: wr_data[2] <= io_sda;
								4'd6: wr_data[1] <= io_sda;
								4'd7: wr_data[0] <= io_sda;
								default: ;
								endcase	
								cstate <= DATA;								
							end
					end		
				end
			ACK4: begin
				if(rd_or_wr)begin
					sda_r <= 1'b1;				//回复ACK;
					sda_link <= 1'b1;			//sda output
				end
				else begin
					sda_r <= 1'b0;				//回复 NOACK;
					sda_link <= 1'b1;			//sda output
				end
				if(`SCL_NEG)begin
					sda_r <= 1'b1;				//回复ACK结束;
					sda_link <= 1'b0;			//sda input
				 	cstate <= STOP1;					
				end
				end
			STOP1:	begin
				if(io_sda_rise==1) begin						
					iic_busy <= 1'b0;
					cstate <= IDLE;				 					
				end
				end
			default: cstate <= IDLE;
			endcase
end

assign io_sda = sda_link ? sda_r:1'bz;





reg		wr_en	;
reg		rd_en	;

always @ (posedge clk or negedge rst_n) 
	if(!rst_n)
		rd_en <= 0;
	else if(cstate==DATA && rd_or_wr)
		rd_en <= 1;
	else
		rd_en <= 0;

always @ (posedge clk or negedge rst_n) 
	if(!rst_n)
		wr_en <= 0;
	else if(cstate==ACK4 && rd_or_wr==0)
		wr_en <= 1;
	else
		wr_en <= 0;	

always @ (posedge clk or negedge rst_n) 
	if(!rst_n)
		read_back_reg <= 0;
	else
		read_back_reg <= rd_data;	
		
		
		
AT24C512B
#(
	.REGADDR_WIDTH	(16				),
	.DATA_WIDTH 	(8				)
)
AT24C512B_inst
(
    .clk			(clk			),	//
    .rst_n			(rst_n			),	//active-low
	.iic_reg_addr	(iic_reg_addr	),
	.wr_data_en		(wr_en			),
	.wr_data		(wr_data		),

	.rd_en			(rd_en			),
    .rd_data_en		(rd_data_en		),		
    .rd_data		(rd_data		)
);


      
endmodule

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461

4.AT24C512B模型
  AT24C512B模型用于模拟AT24C512B EEPROM存储空间读写接口,init_memory.txt可以用于第一次仿真读写时初始化(模拟EEPROM器件初始值为0XFF),我们仿真时可以使用读写函数从memory.txt文件读写。

//*****************************************************************************
//   
// Author             	: NAZ
// Version            	: 1.0
// Filename           	: AT24C512B.v
// Date Last Modified 	: $Date: 2021/12/08 $
// Date Created       	: $Date: 2021/11/04 $ 
// Design Name      	: AT24C512B
//
// Describe         	:
//   This module is used for simulation,when IIC read data ,Read the data in memory.txt
//		when when IIC write data,write the data to memory.txt
//
//*****************************************************************************
`include "macro_define.v"
`timescale 1ns/1ns
module AT24C512B
#(
	parameter REGADDR_WIDTH = 16,
	parameter DATA_WIDTH 	= 8
)
(
    input	wire 					clk			,	//
    input	wire					rst_n		,	//active-low
	input 	wire[REGADDR_WIDTH-1:0]	iic_reg_addr,
	input	wire					wr_data_en	,
	input	wire[7:0]				wr_data		,
	
	input	wire					rd_en		,
    output	wire					rd_data_en	,		
    output	reg[7:0]				rd_data=0		
);

/******************reg/wire********************/
reg  [DATA_WIDTH-1:0]   register[2**REGADDR_WIDTH-1:0];
 
 

// init memory, AT24C512B initial values are 0xff
`ifdef FIRST_SIM
initial begin
$readmemh("../sim/init_memory.txt",register);
end
`else
initial begin
$readmemh("../src/memory.txt",register);
end
`endif

//write the register
always @(posedge clk)
     if(wr_data_en)
         register[iic_reg_addr] <= wr_data;	 
		 
//write the register values to memory.txt
always@(posedge clk)
  if(wr_data_en)
      $writememh("../src/memory.txt",register);

//when iic read data
 always @(posedge clk)begin
     if(rd_en)
         rd_data <= register[iic_reg_addr];
 end

endmodule


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

5.初始化文件和读写文件
  init_memory.txt文件为初始化文件(内容为按一定格式的0XFF文件),memory.txt文件为读写文件(为仿真后生成文件)。如有需要可联系博主(992716773@qq
.com)。

6.仿真顶层文件
  仿真顶层文件可以用于读写具体的地址,将IIC读写方式使用task方式写为函数的方式,方便不同地址读写,并且在读写时打印读写信息。

//*****************************************************************************
//   
// Author             	: NAZ
// Version            	: 1.0
// Filename           	: iic_master_tb.v
// Date Last Modified 	: $Date: 2021/12/08 $
// Date Created       	: $Date: 2021/10/05 $ 
// Design Name      	: iic_master_tb
//
// Describe         	:
//   This module is used for IIC simulation excitation
//
//*****************************************************************************"
`timescale 1ns/1ns
`include "macro_define.v"
module iic_master_tb();
reg                         clk           ;
reg                         rst_n         ;
// 与控制器通信信号
reg                        iic_en		;        
reg                        rd_or_wr	      ;
reg[6:0]      	         device_addr=7'h50	;
reg[15:0]      	         iic_reg_addr   ;
reg[7:0]      	         iic_reg_data   ;
wire[7:0]      	         read_data	;
wire		               iic_done	      ;
wire              	   iic_busy	      ;

// 外部信号
wire              	scl			;
wire              	sda			;



//***************************************************
// testcase
// 时钟
always 
begin
    #10  clk = ~clk;
end

// 初始化
initial
begin
    clk = 0;
    rst_n = 0;
	iic_en <= 1'b0;
	rd_or_wr <= 0;
    #100;
    rst_n = 1;
    #1000;
	
	
	iic_write(7'h50,16'h0000,32'hAA);
	#100
	iic_read(7'h50,16'h0002);
	#100	
	iic_write(7'h50,16'h0001,32'h01);	
	#100	
	iic_write(7'h50,16'h0002,32'h02);	
	#100	
	iic_write(7'h50,16'h0003,32'h03);		
	#100	
	iic_write(7'h50,16'h0004,32'h55);
	#100
	iic_read(7'h50,16'h0004);
	#100
	iic_read(7'h50,16'h1000);
	#100	
	iic_read(7'h50,16'h1234);
	
	$stop;

    end 




task	iic_write(
	input	[6:0]	iic_device_id,	
	input	[15:0]	iic_addr,	
	input	[31:0]	iic_data
);
begin
	rd_or_wr  = 1'b0;
	device_addr	= iic_device_id;
	iic_reg_addr =	iic_addr;
	iic_reg_data = iic_data;
	#100	
	iic_en <= 1;
	#100 
    iic_en = 0;
    #1400000;
	$display("current simulation time is %t,write addr is 16'h%h,write data is 8'h%h.",$time,iic_reg_addr,iic_reg_data);		
end
endtask	

task	iic_read(
	input	[6:0]	iic_device_id,	
	input	[15:0]	iic_addr
);
begin
	rd_or_wr  = 1'b1;
	device_addr	= iic_device_id;
	iic_reg_addr =	iic_addr;
	#100	
	iic_en <= 1;
	#100 
    iic_en = 0;	
    #1400000;
	$display("current simulation time is %t,read addr is 16'h%h,read data is 8'h%h.",$time,iic_reg_addr,read_data);	
end
endtask	


//***************************************************
// 例化顶层文件
iic_master
#(
	.IIC_RATE 		("100k"		),						//50k,100k or 400k
	.REGADDR_WIDTH 	(16			)
)
iic_master_inst
(
	.clk			(clk			),					//50MHz主时钟
	.rst_n			(rst_n			),					//复位信号

	.scl			(scl			),					//IIC时钟
	.sda			(sda			),					//IIC数据
		
	.iic_start		(iic_en			),					//IIC使能
	.rd_or_wr		(rd_or_wr		),					//读写标志:读1,写0
	.iic_device_id	(device_addr	),					//IIC器件地址
	.iic_reg_addr	(iic_reg_addr	),					//寄存器地址
	.iic_wr_data	(iic_reg_data	),					//寄存器数据
	.iic_rd_data_en	(),
	.iic_rd_data	(read_data		),					//接收数据
	.iic_done		(iic_done		),					//IIC完成标志
	.iic_busy		(iic_busy		)					//IIC忙
					);

`ifdef SIM_MODEL
iic_sim_model
#(
	.REGADDR_WIDTH(16)
)
iic_sim_model_inst
(
    .clk			(clk			),	//50Mhz
    .rst_n			(rst_n			),	//active-low
    .i_scl			(scl			),	//为输入
    .io_sda			(sda			)
);
`endif	
	
	
	
	
	
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161

仿真(modelsim)

  在初始化阶段,初始化函数初始化AT24C512B内存储空间为全0XFF。
在这里插入图片描述
  继续运行仿真,可以看到打印信息,写入和读取的值,以读取地址0x0002为例,在未向该地址写入时,返回的读取值为FF(初始值),在写入0x0004后再读取该地址,返回读取值与写入一致,其他地址可以自己定义验证。对应的memory地址空间在写入值后,内存空间随之改变。对应的memory.txt文件随着写入的值同时更新。
在这里插入图片描述
在这里插入图片描述

    若有相关问题可以互相讨论

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/832788
推荐阅读
相关标签
  

闽ICP备14008679号