分享手撕IIC协议的几个经验

1、为什么RTL文件明明仿真没问题,Vivado例化的模块在编译电路中接地或某些信号消失

(1)检查电路的判断语句,有可能电路设置条件用于无法满足

(2)仿真尽量要将所有可能都覆盖,这可能就是随机验证的好处

(3)检查顶层模块电路端口是否冲突

(4)检查是否对同一信号多个模块赋值

2、计数器的两个用途

(1)用作模块驱动时钟分频,CLK_FREQ/I2C_FREQ = 200,计数100次翻转就可以达到250KHz

(2)用作延迟,比如说50M即0.2ns,我需要做到1s,则需要计数250000000次。

3、三态门

主要用于inout端口,当en为1时,Y作为输出,输出值取决于三态门的输入d_out;当en为0时,Y作为输入赋值给d_in。

4、内存芯片数据手册

因为AT24C64官方手册规定了数据写入芯片的完成时间最大不超过10ms。所以为了保证数据能够正确写入,单次写入数据操作完成后,最好延时10ms的时间(即保证写入时间在10ms以上,防止写入数据操作还没结束就进行写一次写操作)。

5、IIC协议模块

顶层模块、驱动模块、EEPROM读写模块、LED提示模块

(1)顶层模块

module top_e2prom(

	input			sys_clk,
	input			sys_rst_n,
	
	output			scl,
	output			led,
	
	inout			sda

);

parameter		CLK_FREQ	= 26'd50000000;	//系统时钟频率
parameter		I2C_FREQ	= 18'd250000;	//IIC_SCL的时钟频率
parameter		SLAVE_ADDR	= 7'b1010000;	//E2PROM从机地址
parameter		L_TIME 		= 17'd125000;
parameter		BIT_CTRL	= 1'b1;
parameter 		MAX_BYTE 	= 16'd256;
	
wire 	 		i2c_exec	;
wire 	 		i2c_rh_wl	;
wire [15:0]	 	i2c_addr	;
wire [7:0]	 	i2c_data_w	;
wire [7:0]	 	i2c_data_r	;
wire 	 		i2c_done	;
wire 	 		i2c_ack		;
wire 	 		dri_clk		;
wire 	 		rw_done		;
wire 	 		rw_result	;


i2c_dri#(

	.CLK_FREQ	(CLK_FREQ),//系统时钟频率
	.I2C_FREQ	(I2C_FREQ),//IIC_SCL的时钟频率
	.SLAVE_ADDR	(SLAVE_ADDR) //E2PROM从机地址

)u_i2c_dri(

	.clk(sys_clk),
	.rst_n(sys_rst_n),
	
	// i2c interface
	.i2c_exec		(i2c_exec	),
	.bit_ctrl		(BIT_CTRL	),
	.i2c_rh_wl		(i2c_rh_wl	),	//rh: read high; wl: write low
	.i2c_addr		(i2c_addr	),
	.i2c_data_w		(i2c_data_w	),
	.i2c_data_r		(i2c_data_r	),
	.i2c_done		(i2c_done	),
	.i2c_ack		(i2c_ack	),
	.scl			(scl),
	.sda			(sda),
	
	//user interface
	.dri_clk		(dri_clk)

);

e2prom_rw#(

	.MAX_BYTE(MAX_BYTE)

)u_e2prom_rw(

	.clk(dri_clk),
	.rst_n(sys_rst_n),
	
	//i2c interface
	.i2c_data_r	(i2c_data_r	),
	.i2c_done	(i2c_done	),
	.i2c_ack	(i2c_ack	),
	.i2c_rh_wl	(i2c_rh_wl	),
	.i2c_exec	(i2c_exec	),
	.i2c_addr	(i2c_addr	),
	.i2c_data_w	(i2c_data_w	),
	
	//user interface
	.rw_done	(rw_done),
	.rw_result	(rw_result)

);


rw_result_led#(

	.L_TIME(L_TIME)

)u_rw_result_led(

	.clk(dri_clk),
	.rst_n(sys_rst_n),
	
	.rw_done(rw_done),
	.rw_result(rw_result),
	
	.led(led)

);


endmodule


(2)驱动模块

module i2c_dri#(

	parameter		CLK_FREQ	= 26'd50000000,	//系统时钟频率
	parameter		I2C_FREQ	= 18'd250000,	//IIC_SCL的时钟频率
	parameter		SLAVE_ADDR	= 7'b1010000	//E2PROM从机地址

)(

	input			clk,
	input			rst_n,
	
	// i2c interface
	input				i2c_exec,
	input				bit_ctrl,
	input				i2c_rh_wl,	//rh: read high; wl: write low
	input		[15:0]	i2c_addr,
	input		[7:0]	i2c_data_w,
	output	reg	[7:0]	i2c_data_r,
	output	reg			i2c_done,
	output	reg			i2c_ack,
	output	reg			scl,
	inout				sda,
	
	//user interface
	output	reg			dri_clk

);

//localparam define
localparam	st_idle 	= 8'b0000_0001;
localparam	st_sladdr 	= 8'b0000_0010;
localparam	st_addr16 	= 8'b0000_0100;
localparam	st_addr8 	= 8'b0000_1000;
localparam	st_data_wr 	= 8'b0001_0000;
localparam	st_addr_rd 	= 8'b0010_0000;
localparam	st_data_rd 	= 8'b0100_0000;
localparam	st_stop 	= 8'b1000_0000;

//reg define
reg				sda_dir		; //dir:direct
reg				sda_out		;
reg				st_done		;
reg				wr_flag		;
reg    [ 6:0]  	cnt       	; //scl时钟上升沿次数计数,为了准确抓取scl的高低电平
reg    [ 7:0]  	cur_state 	; //状态机当前状态
reg    [ 7:0]  	next_state	; //状态机下一状态

//地址和数据都需要做暂存处理?
reg    [15:0]  	addr_t    	; //地址暂存
reg    [ 7:0]  	data_r    	; //读取的数据
reg    [ 7:0]  	data_wr_t 	; //I2C需写的数据的临时寄存

reg    [ 9:0]  	clk_cnt   	; //分频时钟计数,50MHz分频到1MHz


//wire define
wire			sda_in;
wire   [8:0]  	clk_divide ; //模块驱动时钟的分频系数

//三态门
assign	sda = (sda_dir) ? sda_out : 1'bz;
assign	sda_in = sda;

assign	clk_divide = (CLK_FREQ/I2C_FREQ)>>2'd2;	//模块驱动时钟的分频系数,CLK_FREQ/I2C_FREQ = 200,计数200次就可以达到250KHz,(CLK_FREQ/I2C_FREQ)>>2'd2 = 50 = 110010
												//为什么不直接右移3位?因为右移2位相当于1/4倍频,是以计数clk_divide次为一个周期的标准

//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作,这次是生成时钟频率。之前的是做延迟,比如说50M即0.2ns,我需要做到1s,则需要计数250000000次。
always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0)begin
		clk_cnt <= 10'd0;
		dri_clk <= 1'b0;
	end
	else
		if(clk_cnt == clk_divide[8:1] - 1'b1) begin	//clk_divide=110010, clk_divide[8:1]表示右移一位,即计数到中间时刻翻转,形成计数clk_divide次为一个周期
			clk_cnt <= 10'd0;
			dri_clk <= ~dri_clk;
		end
		else begin
			clk_cnt <= clk_cnt + 1'b1;
			dri_clk <= dri_clk;
		end

//三段式状态机
always@(posedge dri_clk or negedge rst_n)
	if(rst_n == 1'b0)
		cur_state <= st_idle;
	else
		cur_state <= next_state;		

always@(*)begin
	next_state = st_idle;
	case(cur_state)
		st_idle:
			if(i2c_exec)
				next_state = st_sladdr;
			else
				next_state = st_idle;
		st_sladdr:
			if(st_done)
				if(bit_ctrl)
					next_state = st_addr16;
				else
					next_state = st_addr8;						
			else
				next_state = st_sladdr;			
		st_addr16:
			if(st_done)
				next_state = st_addr8;
			else
				next_state = st_addr16;
		st_addr8:
			if(st_done)
				if(wr_flag == 1'b0)
					next_state = st_data_wr;
				else
					next_state = st_addr_rd;						
			else
				next_state = st_addr8;
		st_data_wr:
			if(st_done)
				next_state = st_stop;
			else
				next_state = st_data_wr;
		st_addr_rd:
			if(st_done)
				next_state = st_data_rd;
			else
				next_state = st_addr_rd;
		st_data_rd:
			if(st_done)
				next_state = st_stop;
			else
				next_state = st_data_rd;
		st_stop:
			if(st_done)
				next_state = st_idle;
			else
				next_state = st_stop;
		default:next_state = st_idle;
	endcase
end

always@(posedge dri_clk or negedge rst_n)
    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;                          
        data_r    <= 8'b0;                          
        i2c_data_r<= 8'b0;                          
        wr_flag   <= 1'b0;                          
        addr_t    <= 16'b0;                          
        data_wr_t <= 8'b0;                          
    end
	else begin
	    st_done <= 1'b0 ;                            
        cnt     <= cnt +7'b1 ;                       
		case(cur_state)
            //空闲状态
			st_idle:begin
				scl     <= 1'b1;                     
				sda_out <= 1'b1;                     
				sda_dir <= 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_sladdr:
				case(cnt)
					7'd1 : sda_out <= 1'b0;			 //开始I2C,scl为高电平时sda变为低电平
					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;		//0:写标志,1:读标志
					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 == 1'b1)			//是否响应,0:有应答,1:无应答。这个信号由E2PROM自己产生,连接到sda上,高电平表示未应答
							i2c_ack <= 1'b1;		//拉高应答标志位
					end
					7'd39 : begin
						scl <= 1'b0;				//时钟特性
						cnt <= 7'd0;
					end
					default:;
				endcase
			//写高八位字地址
			st_addr16:
				case(cnt)
					7'd0 : begin
                        sda_dir <= 1'b1 ;            //三态门打开
                        sda_out <= addr_t[15];       //传送字地址
					end
					7'd1 : scl <= 1'b1;			 //开始I2C,scl为高电平时sda变为低电平
					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;			//结束I2C
					end
					7'd33 : scl <= 1'b1;			//时钟特性
					7'd34 : begin
						st_done <= 1'b1;
						if(sda_in == 1'b1)
							i2c_ack <= 1'b1;
					end
					7'd35 : begin
						scl <= 1'b0;				//时钟特性
						cnt <= 7'd0;
					end
					default:;
				endcase
			//写低八位字地址
			st_addr8:
				case(cnt)
					7'd0 : begin
                        sda_dir <= 1'b1 ;           //三态门打开
                        sda_out <= addr_t[7];       //传送字地址
					end
					7'd1 : scl <= 1'b1;			 //开始I2C,scl为高电平时sda变为低电平
					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;			//结束I2C
					end
					7'd33 : scl <= 1'b1;			//时钟特性
					7'd34 : begin
						st_done <= 1'b1;
						if(sda_in == 1'b1)
							i2c_ack <= 1'b1;
					end
					7'd35 : begin
						scl <= 1'b0;				//时钟特性
						cnt <= 7'd0;
					end
					default:;
				endcase
			//写数据
			st_data_wr:
				case(cnt)
					7'd0 : begin
                        sda_dir <= 1'b1 ;            //三态门打开
                        sda_out <= data_wr_t[7];       //传送字地址
					end
					7'd1 : scl <= 1'b1;			 //开始I2C,scl为高电平时sda变为低电平
					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;			//结束I2C
					end
					7'd33 : scl <= 1'b1;			//时钟特性
					7'd34 : begin
						st_done <= 1'b1;
						if(sda_in == 1'b1)
							i2c_ack <= 1'b1;
					end
					7'd35 : begin
						scl <= 1'b0;				//时钟特性
						cnt <= 7'd0;
					end
					default:;
				endcase
			//写地址以读数据
            st_addr_rd:
                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'b1;          //1:读
                    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 == 1'b1)           //高电平表示未应答
                            i2c_ack <= 1'b1;         //拉高应答标志位    
                    end   
                    7'd39: begin
                        scl <= 1'b0;
                        cnt <= 7'b0;
                    end
                    default : ;
                endcase
			//读数据
            st_data_rd:                        //读取数据(8 bit)
                case(cnt)
                    7'd0: 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'b0;
                        i2c_data_r <= data_r;
                    end
                    default  :  ;
                endcase
			//结束状态
            st_stop:	                           //结束I2C操作
                case(cnt)
                    7'd0: begin
                        sda_dir <= 1'b1;             //结束I2C
                        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'b0;
                        i2c_done <= 1'b1;            //向上层模块传递I2C结束信号
                    end
                    default  : ;
				endcase
			endcase
		end

endmodule

(3)EEPROM读写模块

//因为AT24C64官方手册规定了数据写入芯片的完成时间最大不超过10ms
//所以为了保证数据能够正确写入,单次写入数据操作完成后,最好延时10ms的时间。
module e2prom_rw(

	input 					clk,
	input 					rst_n,
	
	//i2c interface
	input 		[7:0]		i2c_data_r,
	input 					i2c_done,
	input 					i2c_ack,
	output reg 				i2c_rh_wl,
	output reg				i2c_exec,
	output reg	[15:0]		i2c_addr,
	output reg	[7:0]		i2c_data_w,
	
	//user interface
	output reg				rw_done,
	output reg				rw_result

);

parameter WR_WAIT_TIME = 14'd5000;
parameter MAX_BYTE = 16'd256;

//reg define
reg	[13:0]	wait_cnt;	//延时计数器
reg	[1:0]	flow_cnt;	//状态流控制

//E2PROM 读写测试,先写后读,并比较读出的值与写入的值是否一致
always@(posedge clk or negedge rst_n)
	if(rst_n == 1'b0) begin
		i2c_rh_wl	<=	1'b0;
        i2c_exec	<=	1'b0;
        i2c_addr	<=	16'b0;
        i2c_data_w	<=	8'b0;
        rw_done		<=	1'b0;
        rw_result	<=	1'b0;
		wait_cnt	<=	14'b0;
		flow_cnt    <=	2'b0;
	end
	else begin
		i2c_exec	<=	1'b0;
		rw_done		<=	1'b0;
		case(flow_cnt)
			//单次写
			2'd0: begin
				wait_cnt <= wait_cnt + 14'b1;
				//延迟1ms
				if(wait_cnt == (WR_WAIT_TIME - 14'b1)) begin
					wait_cnt <= 14'b0;
					//写256次后进入读状态
					if(i2c_addr == MAX_BYTE) begin	//i2c_data_w == MAX_BYTE会一直不能满足条件,i2c_data_w最大为255
													//i2c_data_w == 16'd3,仿真文件只计数到3,所以不会出现错误
													//i2c_data_w == MAX_BYTE,编译发现永远无法满足这个条件,rw_done等信号一直为初始值,所以led等信号直接接地,模块被吞
						i2c_rh_wl <= 1'b1;
						i2c_addr <= 16'b0;
						flow_cnt <= 2'd2;
					end
					//继续写,直到写256次
					else begin
						flow_cnt <= flow_cnt + 2'b1;
						i2c_exec <= 1'b1;
					end
				end
			end
			//地址和数据加1,继续写
			2'd1: begin
				if(i2c_done == 1'b1) begin
					flow_cnt   <= 2'd0;
					i2c_addr <= i2c_addr + 16'b1;				
					i2c_data_w <= i2c_data_w + 8'b1;		
				end
			end
			//单次读
			2'd2: begin
				flow_cnt <= flow_cnt + 2'b1;
				i2c_exec <= 1'b1;
			end
			//读256次
			2'd3: begin
				if(i2c_done == 1'b1) begin
					if((i2c_addr[7:0] != i2c_data_r) || (i2c_ack == 1'b1))begin
						rw_done		<=	1'b1;
						rw_result	<=	1'b0;
					end
					else if(i2c_addr == (MAX_BYTE - 16'b1))begin
						rw_done		<=	1'b1;
						rw_result	<=	1'b1;
					end
					else begin
						flow_cnt	<=	2'd2;
						i2c_addr <= i2c_addr + 16'b1;
					end
				end
			end
			default:;
		endcase
	end
endmodule

(4)LED提示模块

//****************************************Copyright (c)***********************************//
//原子哥在线教学平台:www.yuanzige.com
//技术支持:http://www.openedv.com/forum.php
//淘宝店铺:https://zhengdianyuanzi.tmall.com
//关注微信公众平台微信号:"正点原子",免费获取ZYNQ & FPGA & STM32 & LINUX资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2023-2033
//All rights reserved                                  
//----------------------------------------------------------------------------------------
// File name:           rw_result_led
// Created by:          正点原子
// Created date:        2023年5月17日14:17:02
// Version:             V1.0
// Descriptions:        PL_LED0灯常亮表示读写测试正确,PL_LED0闪烁表示读写测试错误
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module rw_result_led 
    #(parameter L_TIME = 17'd125_000
    )
    (
    input        clk       ,  //时钟信号
    input        rst_n     ,  //复位信号
                 
    input        rw_done   ,  //结束标志
    input        rw_result ,  //E2PROM读写测试完成
    output  reg  led          //E2PROM读写测试结果 0:失败 1:成功
);

//reg define
reg          rw_done_flag;    //读写测试完成标志
reg  [16:0]  led_cnt     ;    //led计数

//*****************************************************
//**                    main code
//*****************************************************

//读写测试完成标志
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        rw_done_flag <= 1'b0;
    else if(rw_done)
        rw_done_flag <= 1'b1;
end        

//错误标志为1时PL_LED0闪烁,否则PL_LED0常亮
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        led_cnt <= 17'd0;
        led <= 1'b0;
    end
    else begin
        if(rw_done_flag) begin
            if(rw_result)                          //读写测试正确
                led <= 1'b1;                       //led灯常亮
            else begin                             //读写测试错误
                led_cnt <= led_cnt + 17'd1;
                if(led_cnt == (L_TIME - 17'b1)) begin
                    led_cnt <= 17'd0;
                    led <= ~led;                   //led灯闪烁
                end                  
                else
                    led <= led;
            end
        end
        else
            led <= 1'b0;                           //读写测试完成之前,led灯熄灭
    end    
end

endmodule

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值