I2C总线协议

目录

一、协议概述

1.1 多主设备,多从设备

i. 单主设备、单从设备

ii.单主设备、多从设备

iii.多主机、多从机 

二、I2C的信号线

三、I2C的状态传输格式

3.1 空闲状态

3.2 起始状态

 3.3 读/写状态

 3.4 停止状态

 四、I2C数据传输格式

 4.1 器件地址数据

 4.2 字节地址数据

 4.3 有效数据

  4.3.1 写数据  ​编辑

4.3.2 读数据

五、协议传输规范小结

5.1 单字节

5.1.1 写数据传输

 5.1.2 读数据传输

 实例:为正点原子达芬奇开发板所提供

I2C驱动模块

EEPROM_AT24C64模块

仿真TB模块

一、协议概述

        I2C是一种两线式串行总线,用于连接微控制器以及其外围设备,具有接口线少、控制简单、器件封装形式小和通信速率较高等优点。I2C只要求两条双向线路:串行数据线(Serial Data,SDA)和串行时钟线(Serial Clock,SCL),两条线都是双向传输的。I2C协议属于半双工协议(同一时刻,数据单向流动)。

        I2C是可以多主设备多从设备的总线协议,主机通过地址索引,驱动所需的从机设备。

1.1 多主设备,多从设备

i. 单主设备、单从设备

I2C总线协议_第1张图片

ii.单主设备、多从设备

I2C总线协议_第2张图片

iii.多主机、多从机 

I2C总线协议_第3张图片

二、I2C的信号线

I2C总线协议_第4张图片

SCL:串行时钟总线,主设备向从设备传输;

SDA:串行数据总线,可双向传输数据信号。

三、I2C的状态传输格式

        I2C传输时有空闲状态起始状态读/写状态以及停止状态

3.1 空闲状态

        SCL和SDA都为高电平,此时I2C的设备都处于空闲状态

3.2 起始状态

         当SCL为高电平时,SDA从1跳转到0,则为起始位,等待数据开始传输

 3.3 读/写状态

        当SCL为低电平时,SDA可以进行数据传输;当SCL为高电平时,SDA则保持不变

 3.4 停止状态

        当SCL为高电平时,SDA信号从0到1,则为停止位,I2C则进入空闲状态

 四、I2C数据传输格式

 4.1 器件地址数据

        接收到起始信号后,主设备从高到低的顺序依次发送器件地址给从设备,器件地址一般为7bit,器件地址后面紧接着1bit读写控制位读操作则为高电平,写操作为低电平;最后紧跟1bit的应答位ACK,由从机发送给主机;若接收正确,则应答位为低电平0;接收错误,则为高电平。

 4.2 字节地址数据

        接收到字节地址的低电平应答位,则开始字节地址传输,同样是按照从高到底的顺序依次发送字节地址给从设备,字节地址可以是16bit,也可以是8bit。若是16位则分成高八位和低八位分别传输。字节地址传输完成后则从机向主机发送1bit的应答信号。

4.3 有效数据

4.3.1 写数据  I2C总线协议_第5张图片

        主机向从机每次发送一个字节的数据,按照从高位到低位的顺序发送。发送完8bit的数据后,从机会给主机发送一个bit的应答信号,若应答信号为低位,则表示从机接收完成;若应答信号为高,则表示接收失败。这里只发送了一个字节的数据,如果发送多个字节数据,只需要在接收到应答信号后继续发送后续的字节。

4.3.2 读数据

I2C总线协议_第6张图片

         从机向主机从高位到低位发送前面写入的数据,每完成一个字节的传输,主机向从机发送一个bit的应答位,此时应答位为高位表示数据有效。

五、协议传输规范小结

5.1 写数据传输

  I2C总线协议_第7张图片

 5.1 读数据传输

I2C总线协议_第8张图片

 实例:为正点原子达芬奇开发板所提供

I2C驱动模块

I2C总线协议_第9张图片

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/08/01 15:46:06
// Design Name: 
// Module Name: i2c_dri
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module i2c_dri(
	input 			clk,                    			//系统时钟,50MHz
	input 			rst_n,                  			//系统复位信号
	input 			i2c_exec,               			//i2c开始执行传输信号
	input 			bit_ctrl,               			//地址字节控制信号
	input [15:0]	i2c_addr,               	//读写字节地址信号
	input 			i2c_rh_wl,              			//i2c读写控制信号
	input [7:0]		i2c_data_w,              	//i2c写入数据的信号
				
	output reg 		scl,              			//i2c的scl时钟信号
	output 			sda,              			//i2c的sda数据信号
	output reg[7:0] i2c_data_r,       		//i2c读出数据的信号
	output reg 		i2c_done,         			//i2c信号传输完成信号
	output reg 		i2c_ack,          			//i2c的应答信号
			
	output reg 		clk_dri						//i2c的驱动时钟
	);
	
	parameter  	ST_IDLE 	= 8'b0000_0001,                     //
				ST_ADDR 	= 8'b0000_0010,
				ADDR16	 	= 8'b0000_0100,
				ADDR8		= 8'b0000_1000,
				DATA_WR  	= 8'b0001_0000,
				ADDR_RD		= 8'b0010_0000,
				DATA_RD		= 8'b0100_0000,
				STOP 		= 8'b1000_0000;
	reg [7:0]	current_state,next_state;
	reg 		st_done;													//状态结束信号
	reg 		sda_dir;                                                	//I2C数据方向控制
	wire 		sda_in;                   									//SDA输入信号
	reg 		sda_out;                         							//SDA输出信号
	reg [6:0]	cnt;														//i2c计数
	reg 		wr_flag;													//读写标志信号
	reg [15:0]	addr_t;														//地址寄存器
	reg [7:0]	data_wr_t;													//写入数据的临时寄存器
	reg [7:0]	data_r; 													//读取的数据
	
	parameter 	SLAVE_ADDR 	= 7'b1010000; 							//从机地址
	parameter 	CLK_FREQ	= 26'd50_000_000;
	parameter	I2C_FREQ	= 18'd250_000;
	wire	[8:0] 	clk_divide; 									//模块驱动时钟的分频系数
	reg		[7:0]	clk_cnt;
	assign clk_divide	= (CLK_FREQ/I2C_FREQ)>>2'd2;                //模块驱动时钟的分频系数
	
//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
	always @(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
			clk_dri <= 1'b0;
			clk_cnt <= 8'b0;
		end
		else if(clk_cnt == (clk_divide[8:1]-1'b1))begin
			clk_dri <= ~clk_dri;
			clk_cnt <= 8'b0;
		end
		else begin
			clk_dri <= clk_dri;
			clk_cnt <= clk_cnt + 1'b1;
		end
	end
	
//同步时序描述状态转移
	always @(posedge clk_dri or negedge rst_n)begin
		if(!rst_n)
			current_state <= ST_IDLE;
		else
			current_state <= next_state ;
	end

//组合逻辑判断状态转移条件
	always @(*)begin
		next_state = ST_IDLE;
		case(current_state)
			ST_IDLE :begin
				if(i2c_exec)begin
					next_state <= ST_ADDR;
				end
				else
					next_state <= ST_IDLE;
			end
			
			ST_ADDR :begin
				if(st_done)begin
					if(bit_ctrl)
						next_state <= ADDR16;
					else
						next_state <= ADDR8;
				end
				else
					next_state <= ST_ADDR;
			end
			
			ADDR16 : begin
				if(st_done)
					next_state <= ADDR8;
				else
					next_state <= ADDR16;
			end
			
			ADDR8 : begin
				if(st_done)begin
					if(i2c_rh_wl)
						next_state <= ADDR_RD;
					else
						next_state <= DATA_WR;
				end
				else
					next_state <= ADDR8;
			end
			
			ADDR_RD : begin
				if(st_done)
					next_state <= DATA_RD;
				else
					next_state <= ADDR_RD;
			end
			
			DATA_RD : begin
				if(st_done)
					next_state <= STOP;
				else
					next_state <= DATA_RD;
			end
			
			DATA_WR : begin
				if(st_done)
					next_state <= STOP;
				else
					next_state <= DATA_WR;
			end
			
			STOP : begin
				if(st_done)
					next_state <= ST_IDLE;
				else
					next_state <= STOP;
			end
			
			default : next_state <= ST_IDLE;
		endcase
	end
	
assign sda = sda_dir ? sda_out : 1'bz;                         //sda数据输出或高阻
assign sda_in = sda;                                           //sda数据输入
//时序电路描述状态输出
always @(posedge clk_dri or negedge rst_n)begin
	if(!rst_n)begin
		scl 		<= 1'b1;
		sda_out 	<= 1'b1;
		sda_dir 	<= 1'b1;
		i2c_done 	<= 1'b0;
		i2c_ack 	<= 1'b0;
		cnt			<= 7'b0;
		st_done 	<= 1'b0;
		i2c_data_r	<= 8'b0;
		wr_flag 	<= 1'b0;
		addr_t 		<= 16'b0;
		data_wr_t	<= 1'b0;
		data_r		<= 8'b0;
	end
	st_done <= 1'b0;
	cnt 	<= cnt + 1'b1;
	case(current_state)
		ST_IDLE : begin
			scl			<= 1'b1;
			sda_out		<= 1'b1;
			i2c_done 	<= 1'b0;
			cnt 		<= 7'b0;
			if(i2c_exec)begin
				wr_flag 	<= i2c_rh_wl;
				addr_t 		<= i2c_addr;
				data_wr_t	<= i2c_data_w;
				i2c_ack		<= 1'b0;
			end
		end
		
		ST_ADDR : begin
			case(cnt)
				7'd1:	sda_out <= 1'b0;			//开始I2C传输
				7'd3:	scl		<= 1'b0;
				7'd4:	sda_out <= SLAVE_ADDR[6];
				7'd5: 	scl 	<= 1'b1;
				7'd7:	scl 	<= 1'b0;
				7'd8: 	sda_out <= SLAVE_ADDR[5];
				7'd9: 	scl 	<= 1'b1;
				7'd11:	scl 	<= 1'b0;
				7'd12: 	sda_out <= SLAVE_ADDR[4];
				7'd13: 	scl 	<= 1'b1;
				7'd15:	scl 	<= 1'b0;
				7'd16: 	sda_out <= SLAVE_ADDR[3];
				7'd17: 	scl 	<= 1'b1;
				7'd19:	scl 	<= 1'b0;
				7'd20: 	sda_out <= SLAVE_ADDR[2];
				7'd21: 	scl 	<= 1'b1;
				7'd23:	scl 	<= 1'b0;
				7'd24: 	sda_out <= SLAVE_ADDR[1];
				7'd25: 	scl 	<= 1'b1;
				7'd27:	scl 	<= 1'b0;
				7'd28: 	sda_out <= SLAVE_ADDR[0];
				7'd29:	scl 	<= 1'b1;
				7'd31:	scl		<= 1'b0;
				7'd32: 	sda_out <= 1'b0;                           //写操作
				7'd33:	scl		<= 1'b1;
				7'd35:	scl		<= 1'b0;
				7'd36:	begin
					sda_dir <= 1'b0;
					sda_out <= 1'b1;
				end
				7'd37: 	scl <= 1'b1;
				7'd38:	begin                                      //从机应答
					st_done <= 1'b1;
					if(sda_in)									   //高电平表示未应答
						i2c_ack <= 1'b1;
					end
				7'd39: 	begin
					scl <= 1'b0;
					cnt <= 7'b0;
				end
				default: ;
			endcase
		end
		
		ADDR16 	: begin
			case(cnt)
				7'd0:begin
					sda_dir <= 1'b1;
					sda_out <= addr_t[15];
				end
				7'd1	:scl <= 1'b1;
				7'd3	:scl <= 1'b0;
				7'd4	:sda_out <= addr_t[14];
				7'd5	:scl <= 1'b1;
				7'd7	:scl <= 1'b0;
				7'd8	:sda_out <= addr_t[13];
				7'd9	:scl <= 1'b1;
				7'd11	:scl <= 1'b0;
				7'd12	:sda_out <= addr_t[12];
				7'd13	:scl <= 1'b1;
				7'd15	:scl <= 1'b0;
				7'd16	:sda_out <= addr_t[11];
				7'd17	:scl <= 1'b1;
				7'd19	:scl <= 1'b0;
				7'd20	:sda_out <= addr_t[10];
				7'd21	:scl <= 1'b1;
				7'd23	:scl <= 1'b0;
				7'd24	:sda_out <= addr_t[9];
				7'd25	:scl <= 1'b1;
				7'd27	:scl <= 1'b0;
				7'd28	:sda_out <= addr_t[8];
				7'd29	:scl <= 1'b1;
				7'd31	:scl <= 1'b0;
				7'd32	:begin
					sda_dir <= 1'b0;
					sda_out <= 1'b1;
				end
				7'd33	:scl <= 1'b1;
				7'd34	:begin
					st_done <= 1'b1;
					if(sda_in)
						i2c_ack <= 1'b1;
				end
				7'd35	:begin
					scl <= 1'b0;
					cnt <= 1'b0;
				end
				default : ; 
			endcase
		end
		
		ADDR8 : begin
			case(cnt)
				7'd0:begin
					sda_dir <= 1'b1;
					sda_out <= addr_t[7];
				end
				7'd1	:scl <= 1'b1;
				7'd3	:scl <= 1'b0;
				7'd4	:sda_out <= addr_t[6];
				7'd5	:scl <= 1'b1;
				7'd7	:scl <= 1'b0;
				7'd8	:sda_out <= addr_t[5];
				7'd9	:scl <= 1'b1;
				7'd11	:scl <= 1'b0;
				7'd12	:sda_out <= addr_t[4];
				7'd13	:scl <= 1'b1;
				7'd15	:scl <= 1'b0;
				7'd16	:sda_out <= addr_t[3];
				7'd17	:scl <= 1'b1;
				7'd19	:scl <= 1'b0;
				7'd20	:sda_out <= addr_t[2];
				7'd21	:scl <= 1'b1;
				7'd23	:scl <= 1'b0;
				7'd24	:sda_out <= addr_t[1];
				7'd25	:scl <= 1'b1;
				7'd27	:scl <= 1'b0;
				7'd28	:sda_out <= addr_t[0];
				7'd29	:scl <= 1'b1;
				7'd31	:scl <= 1'b0;
				7'd32	:begin
					sda_dir <= 1'b0;
					sda_out <= 1'b1;
				end
				7'd33	:scl <= 1'b1;
				7'd34	:begin
					st_done <= 1'b1;
					if(sda_in)
						i2c_ack <= 1'b1;
				end
				7'd35	:begin
					scl <= 1'b0;
					cnt <= 1'b0;
				end
				default : ; 
			endcase
		end
		
		DATA_WR : begin
			case(cnt)
				7'd0	:begin
						sda_out 	<= data_wr_t[7];
						sda_dir 	<= 1'b1;
				end
				7'd1	:scl 		<= 1'b1;
				7'd3	:scl 		<= 1'b0;
				7'd4	:sda_out 	<= data_wr_t[6];
				7'd5	:scl 		<= 1'b1;
				7'd7	:scl 		<= 1'b0;
				7'd8	:sda_out 	<= data_wr_t[5];
				7'd9	:scl 		<= 1'b1;
				7'd11	:scl 		<= 1'b0;
				7'd12	:sda_out 	<= data_wr_t[4];
				7'd13	:scl 		<= 1'b1;
				7'd15	:scl 		<= 1'b0;
				7'd16	:sda_out 	<= data_wr_t[3];
				7'd17	:scl 		<= 1'b1;
				7'd19	:scl 		<= 1'b0;
				7'd20	:sda_out 	<= data_wr_t[2];
				7'd21	:scl 		<= 1'b1;
				7'd23	:scl 		<= 1'b0;
				7'd24	:sda_out 	<= data_wr_t[1];
				7'd25	:scl 		<= 1'b1;
				7'd27	:scl 		<= 1'b0;
				7'd28	:sda_out 	<= data_wr_t[0];
				7'd29	:scl 		<= 1'b1;
				7'd31	:scl 		<= 1'b0;
				7'd32	:begin
					sda_dir <= 1'b0;
					sda_out <= 1'b1;
				end
				7'd33	:scl 		<= 1'b1;
				7'd34	:begin
					st_done <= 1'b1;
					if(sda_in)
						i2c_ack <= 1'b1;
				end
				7'd35	:begin
					scl <= 1'b0;
					cnt <= 1'b0;
				end
				default : ;
			endcase
		end
		
		ADDR_RD :begin
			case(cnt)
				7'd0 	:begin
					sda_dir <= 1'b1;
					sda_out <= 1'b1;
				end
				7'd1	:scl 		<= 1'b1;
				7'd2	:sda_out 	<= 1'b0;
				7'd3	:scl 		<= 1'b0;
				7'd4 	:sda_out 	<= SLAVE_ADDR[6];
				7'd5	:scl 		<= 1'b1;
				7'd7	:scl		<= 1'b0;
				7'd8	:sda_out 	<= SLAVE_ADDR[5];
				7'd9	:scl 		<= 1'b1;
				7'd11	:scl		<= 1'b0;
				7'd12	:sda_out 	<= SLAVE_ADDR[4];
				7'd13	:scl 		<= 1'b1;
				7'd15	:scl		<= 1'b0;
				7'd16	:sda_out 	<= SLAVE_ADDR[3];
				7'd17	:scl 		<= 1'b1;
				7'd19	:scl		<= 1'b0;
				7'd20	:sda_out 	<= SLAVE_ADDR[2];
				7'd21	:scl 		<= 1'b1;
				7'd23	:scl		<= 1'b0;
				7'd24	:sda_out 	<= SLAVE_ADDR[1];
				7'd25	:scl 		<= 1'b1;
				7'd27	:scl		<= 1'b0;
				7'd28	:sda_out 	<= SLAVE_ADDR[0];
				7'd29	:scl		<= 1'b1;
				7'd31	:scl 		<= 1'b0;
				7'd32	:sda_out 	<= 1'd1;                               //读操作
				7'd33	:scl 		<= 1'b1;
				7'd35  	:scl 		<= 1'b0;
				7'd36 	:begin
					sda_dir <= 1'b0;
					sda_out <= 1'b1;
				end
				7'd37	:scl 		<= 1'b1;
				7'd38	:begin
					st_done <= 1'd1;
					if(sda_in)
						i2c_ack <= 1'b1;
				end
				7'd39	:begin
					scl <= 1'b0;
					cnt <= 1'b0;
				end
				default : ;
			endcase
		end
		
		DATA_RD : begin
			case(cnt)
				7'b0	:sda_dir <= 1'b0;
				7'd1 	:begin
					data_r[7] 	<= sda_in;
					scl 		<= 1'b1;
				end
				7'd3	:scl 	<= 1'b0;
				7'd5	:begin
					data_r[6]	<= sda_in;
					scl 		<= 1'b1;
				end
				7'd7	:scl 	<= 1'b0;
				7'd9	:begin
					data_r[5]	<= sda_in;
					scl 		<= 1'b1;
				end
				7'd11	:scl 	<= 1'b0;
				7'd13	:begin
					data_r[4]	<= sda_in;
					scl 		<= 1'b1;
				end
				7'd15	:scl 	<= 1'b0;
				7'd17	:begin
					data_r[3]	<= sda_in;
					scl 		<= 1'b1;
				end
				7'd19	:scl 	<= 1'b0;
				7'd21	:begin
					data_r[2]	<= sda_in;
					scl 		<= 1'b1;
				end
				7'd23	:scl 	<= 1'b0;
				7'd25	:begin
					data_r[1]	<= sda_in;
					scl 		<= 1'b1;
				end
				7'd27	:scl 	<= 1'b0;
				7'd29	:begin
					data_r[0]	<= sda_in;
					scl 		<= 1'b1;
				end
				7'd31	:scl	<= 1'b0;
				7'd32	:begin
					sda_dir <= 1'b1;
					sda_out <= 1'b1;
				end
				7'd33	:scl 		<= 1'b1;
				7'd34	:st_done 	<= 1'b1;               //非应答
				7'd35	:begin
					scl <= 1'b0;
					cnt <= 7'd0;
					i2c_data_r		<= data_r;
				end
				default : ;
			endcase
		end
		
		STOP : begin
			case(cnt)
				7'd0 : begin
					sda_dir <= 1'b1;
					sda_out <= 1'b0;
				end
				7'd1 : scl <= 1'b1;
				7'd3 : sda_out <= 1'b1;
				7'd15 : st_done <= 1'b1;
				7'd16 : begin
					cnt <= 7'd0;
					i2c_done <= 1'b1;
				end
				default : ;
			endcase
		end
	endcase	
end

endmodule

EEPROM_AT24C64模块

`timescale 1ns/1ns
`define timeslice 1250
module EEPROM_AT24C64(
scl,
sda
);
input scl; 
inout sda; 
reg out_flag; 
reg[7:0] memory[8191:0]; 
reg[12:0]address; 
reg[7:0]memory_buf; 
reg[7:0]sda_buf; 
reg[7:0]shift; 
reg[7:0]addr_byte_h; 
reg[7:0]addr_byte_l; 
reg[7:0]ctrl_byte; 
reg[1:0]State;
integer i;
//---------------------------
parameter
r7 = 8'b1010_1111, w7 = 8'b1010_1110, //main7
r6 = 8'b1010_1101, w6 = 8'b1010_1100, //main6
r5 = 8'b1010_1011, w5 = 8'b1010_1010, //main5
r4 = 8'b1010_1001, w4 = 8'b1010_1000, //main4
r3 = 8'b1010_0111, w3 = 8'b1010_0110, //main3
r2 = 8'b1010_0101, w2 = 8'b1010_0100, //main2
r1 = 8'b1010_0011, w1 = 8'b1010_0010, //main1
r0 = 8'b1010_0001, w0 = 8'b1010_0000; //main0
assign sda = (out_flag == 1) ? sda_buf[7] : 1'bz;

initial
begin
addr_byte_h = 0;
addr_byte_l = 0;
ctrl_byte = 0;
out_flag = 0;
sda_buf = 0;
State = 2'b00;
memory_buf = 0;
address = 0;
shift = 0;
for(i=0;i<=8191;i=i+1)
memory[i] = 0;
end
always@(negedge sda)
begin
if(scl == 1)
begin
State = State + 1;
if(State == 2'b11)
disable write_to_eeprom;
end
end

always@(posedge sda)
begin
if(scl == 1) 
stop_W_R;
else
begin
casex(State)
2'b01:begin
read_in;
if(ctrl_byte == w7 || ctrl_byte == w6
|| ctrl_byte == w5 || ctrl_byte == w4
|| ctrl_byte == w3 || ctrl_byte == w2
|| ctrl_byte == w1 || ctrl_byte == w0)
begin
State = 2'b10;
write_to_eeprom; 
end
else
State = 2'b00;
end
2'b11:
read_from_eeprom;
default:
State = 2'b00;
endcase
end
end 
task stop_W_R;
begin
State = 2'b00;
addr_byte_h = 0;
addr_byte_l = 0;
ctrl_byte = 0;
out_flag = 0;
sda_buf = 0;
end
endtask

task read_in;
begin
shift_in(ctrl_byte);
shift_in(addr_byte_h);
shift_in(addr_byte_l);
end
endtask

task write_to_eeprom;
begin
shift_in(memory_buf);
address = {addr_byte_h[4:0], addr_byte_l};
memory[address] = memory_buf;
State = 2'b00;
end
endtask

task read_from_eeprom;
begin
shift_in(ctrl_byte);
if(ctrl_byte == r7 || ctrl_byte == w6
|| ctrl_byte == r5 || ctrl_byte == r4
|| ctrl_byte == r3 || ctrl_byte == r2
|| ctrl_byte == r1 || ctrl_byte == r0)
begin
address = {addr_byte_h[4:0], addr_byte_l};
sda_buf = memory[address];
shift_out;
State = 2'b00;
end
end
endtask
task shift_in;
output[7:0]shift;
begin
@(posedge scl) shift[7] = sda;
@(posedge scl) shift[6] = sda;
@(posedge scl) shift[5] = sda;
@(posedge scl) shift[4] = sda;
@(posedge scl) shift[3] = sda;
@(posedge scl) shift[2] = sda;
@(posedge scl) shift[1] = sda;
@(posedge scl) shift[0] = sda;
@(negedge scl)
begin
#(`timeslice);
out_flag = 1;
sda_buf = 0;
end
@(negedge scl)
begin
#(`timeslice-250);
out_flag = 0;
end
end
endtask
task shift_out;
begin
out_flag = 1;
for(i=6; i>=0; i=i-1)
begin
@(negedge scl);
#`timeslice;
sda_buf = sda_buf << 1;
end
@(negedge scl) #`timeslice sda_buf[7] = 1;
@(negedge scl) #`timeslice out_flag = 0;
end
endtask
endmodule

仿真TB模块

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/08/01 17:35:16
// Design Name: 
// Module Name: i2c_dri_tb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


`timescale 1ns/1ps
module i2c_dri_tb();
	parameter  T = 20;                      //时钟周期为20ns
	parameter  IIC_WR_CYCYLE  = 100;
	parameter  SLAVE_ADDR = 7'b1010000 ;    //EEPROM从机地址
	parameter  CLK_FREQ   = 26'd50_000_000; //模块输入的时钟频率
	parameter  I2C_FREQ   = 18'd250_000;    //IIC_SCL的时钟频率
	
	reg clk;
	reg rst_n;
	
	reg i2c_exec;
	reg bit_ctrl;
	reg i2c_rh_wl;
	reg [15:0]i2c_addr;
	reg [7:0]i2c_data_w;
	reg [13:0]delay_cnt;
	reg [3:0]flow_cnt;
	
	wire [7:0]i2c_data_r;
	wire i2c_done;
	wire scl;
	wire sda;
	wire clk_dri;
	
	initial begin
		clk = 1'b0;
		rst_n = 1'b0;
		#(T+1)rst_n = 1'b1;
	end
	
	always #(T/2)clk = ~clk;
	always @(posedge clk_dri or negedge rst_n)begin
		if(!rst_n)begin
			i2c_exec 	<= 1'b0;
			bit_ctrl 	<= 1'b0;
			i2c_rh_wl 	<= 1'b0;
			i2c_addr 	<= 16'h0;
			i2c_data_w 	<= 8'h0;
			flow_cnt	<= 3'b0;
			delay_cnt 	<= 1'b0;
		end
		else begin
			case(flow_cnt)
			'd0 : flow_cnt <= flow_cnt + 1'b1;
			'd1 : begin
				i2c_exec <= 1'b1;
				bit_ctrl <= 1'b1;
				i2c_rh_wl <= 1'b0;
				i2c_addr <= 16'h0555;
				i2c_data_w <= 8'hAA;
				flow_cnt <= flow_cnt + 1'b1;
			end
			'd2 : begin 
					i2c_exec <= 1'b0;
					flow_cnt <= flow_cnt + 1'b1;
				end    
				'd3 : begin
					if(i2c_done)
						flow_cnt <= flow_cnt + 1'b1;
				end
				'd4 : begin
					delay_cnt <= delay_cnt + 1'b1;
					if(delay_cnt == IIC_WR_CYCYLE - 1'b1)
						flow_cnt <= flow_cnt + 1'b1;
				end
				'd5 : begin
						i2c_exec <= 1'b1;
						bit_ctrl <= 1'b1;                   
						i2c_rh_wl <= 1'b1;           //读操作     
						i2c_addr <= 16'h0555;
						i2c_data_w <= 8'hAA;        
						flow_cnt <= flow_cnt + 1'b1;                    
				end
				'd6 : begin 
					i2c_exec <= 1'b0;
					flow_cnt <= flow_cnt + 1'b1;
				end 
				'd7 : begin
					if(i2c_done)
						flow_cnt <= flow_cnt + 1'b1;
				end
				default:;
			endcase    
		end
	end

pullup(sda);

i2c_dri #(
    .SLAVE_ADDR  (SLAVE_ADDR),  //EEPROM从机地址
    .CLK_FREQ    (CLK_FREQ  ),  //模块输入的时钟频率
    .I2C_FREQ    (I2C_FREQ  )   //IIC_SCL的时钟频率
) u1(
	.clk(clk),                    			//系统时钟,50MHz
	.rst_n(rst_n),                  			//系统复位信号
	.i2c_exec(i2c_exec),               			//i2c开始执行传输信号
	.bit_ctrl(bit_ctrl),               			//地址字节控制信号
	.i2c_addr(i2c_addr),               	//读写字节地址信号
	.i2c_rh_wl(i2c_rh_wl),              			//i2c读写控制信号
	.i2c_data_w(i2c_data_w),              	//i2c写入数据的信号
	
	.scl(scl),              			//i2c的scl时钟信号
	.sda(sda),              			//i2c的sda数据信号
	.i2c_data_r(i2c_data_r),       		//i2c读出数据的信号
	.i2c_done(i2c_done),         			//i2c信号传输完成信号
	.i2c_ack(i2c_ack),          			//i2c的应答信号
	
	.clk_dri(clk_dri)						//i2c的驱动时钟
	);
EEPROM_AT24C64 u_EEPROM_AT24C64(
    .scl         (scl),
    .sda         (sda)
    );

endmodule	

你可能感兴趣的:(高速接口协议,verilog,fpga开发)