异步FIFO Verilog HDL设计

异步FIFO Verilog HDL设计

一.功能需求分析及模块划分

1.1 功能需求分析

基本原理概述:FIFO (First In First Out)即是先进先出之意,其本质是特殊的RAM存储器,特殊于不能随意指定地址写入或者读出,只能够按自底向上写入或读出。

优点:
1、可以较好的解决多通道数据信号跨时钟域同步问题(单通道数据一般打两拍就可以同步成功,成功率可达90%,基本不会产生亚稳态,关于亚稳态问题可以留意我的另一篇文章,单独刨析亚稳态的产生原理及其解决方案)。
2、对于片内或者片间数据位数转换适用性较强(比如32位转64位)。
3、控制简单,稳定性尚可。

缺点:
读取地址指针指向自由度低,不能指定存储和读取,必须按其规则进行存储和读取操作。

FIFO设计几个关键点
1、写指针:写指针在复位(初试状态)时指向地址0,它总是指向下一个将要写入数据的地址,自下至上。

2、读指针:同样,读指针在复位(初试状态)时指向地址0,它总是指向下一个要读出的数据的地址,自下至上读取,如下图所示;在同一回合中,读指针不可越过写指针读取数据,否则读出的是无效数据,而且会引起FIFO状态紊乱。
在这里插入图片描述

在这里插入图片描述

3、FIFO满:假设FIFO深度为16,当(写指针-读指针)>= 15,即是相差一个深度,表示FIFO满,注意读指针指向的当前位置是还未读取的,最简单的情况就是读指针0000,写指针1111。
为了更好的表达满状态,通常给FIFO最高位加一个MSB状态位,那么最简单的情况为读指针00000,写指针10000,除最高位其余位相同;除了上述简单满情况,另一种情况就是写指针转完第一轮,在第二轮又赶上读指针,同样MSB不同,其余位相同;可以总结为:当任意深度 FIFO MSB位不同,其他位相同时为满。

4、FIFO空:基于上述添加MSB位的FIFO,FIFO空时的MSB位以及其余位都相同,即全等。

5、格雷码空满判断:由于异步FIFO是多位数据的跨时钟域处理,就本16位FIFO而言,存在多位或所有位同时跳变的情况,容易产生亚稳态,将二进制指针转换成格雷码的好处是每次临近跳变只有一位变化,不容易产生亚稳态。
16位带MSB位FIFO二进制转换成格雷码如下图所示,格雷码空状态的判断方式和二进制一样,全等情况就是空状态;但是满状态格雷码与二进制有异同之处,如下图所示;应满足读写指针的MSB位(最高位)和次高位不同,其余位置相同。
在这里插入图片描述

6、打两拍:为了防止亚稳态,将异步输入数据打两拍稳定下来的概率可达90%,目的是让信号满足后续寄存器的建立时间。
在这里插入图片描述

7、二进制转格雷码:二进制转格雷码只需与本二进制向右移一位然后按位异或操作即可。如:gray_w = (addr_w ^ (addr_w>>1));
在这里插入图片描述

1.2 模块划分
地址控制模块、存储模块、顶层模块;

二.代码实例

2.1 顶层模块
module fifo1(
	input						clk_w,
	input						clk_r,
	input						rst_n,
	input [7:0]				    data_in,
	input						wr_en,
	input						rd_en,
	input 				     	cs_n,
	
	output wire				    fifo_empty,
	output wire					fifo_full,
	output [7:0]				data_out
);

//中间变量定义
	wire [7:0]				addr_w;
	wire [7:0]				addr_r;

	reg [7:0]				data_in0;
	reg [7:0]				data_in1;
	reg [7:0]				data_in2;
	reg						wr_en0;
	reg						wr_en1;
	reg						wr_en2;
	reg						rd_en0;
	reg						rd_en1;
	reg						rd_en2;
//打两拍再加一拍
	
	initial begin
		data_in0 <= 0;
		data_in1 <= 0;
		wr_en0 <= 0;
		wr_en1 <= 0;
		rd_en0 <= 0;
		rd_en1 <= 0;
	end
	
	always@( posedge clk_w ) begin
		data_in0 <= data_in;
		wr_en0 <= wr_en;
		
		data_in1 <= data_in0;
		wr_en1 <= wr_en0;
		
		data_in2 <= data_in1;
		wr_en2 <= wr_en1;
	end
	
	always@( posedge clk_r ) begin
		rd_en0 <= rd_en;
		rd_en1 <= rd_en0;
		rd_en2 <= rd_en1;
	end
		
		
//模块interger
	addr_strl1 addr_strl1(                            
		.clk_w(clk_w),
		.clk_r(clk_r),
		.rst_n(rst_n),
		.rd_en(rd_en2),
		.wr_en(wr_en2),
		.cs_n(cs_n),
		
		.addr_w(addr_w),
		.addr_r(addr_r),
		.fifo_empty(fifo_empty),
		.fifo_full(fifo_full)
	);
	
	ram_fifo1 ram_fifo1( 
		.clk_w(clk_w),
		.clk_r(clk_r),
		.rst_n(rst_n),
		.data_in(data_in2),
		.wr_en(wr_en2),
		.rd_en(rd_en2),
		.addr_w(addr_w[7:0]),
		.addr_r(addr_r[7:0]),
		.fifo_empty(fifo_empty),
		.fifo_full(fifo_full),
		
		.data_out(data_out)
	);
	
endmodule
2.2 地址控制模块
module addr_strl1(                            
	input							clk_w,
	input							clk_r,
	input							rst_n,
	input							rd_en,
	input							wr_en,
	input							cs_n,
	
	output	reg [8:0]				addr_w,
	output	reg [8:0]				addr_r,
	output							fifo_empty,
	output							fifo_full
);
	
	wire [8:0]				gray_w;
	wire [8:0]				gray_r;

//地址生成
	always@( posedge clk_w or negedge rst_n ) begin
		if( !rst_n ) 
			addr_w <= 9'b0;
		else if( wr_en && cs_n )
			if( addr_w == 9'd257 )
				addr_w <= 9'b0;
			else
				addr_w <= addr_w + 9'b1;
		else
			addr_w <= addr_w;
	end
	
	always@( posedge clk_r or negedge rst_n ) begin
		if( !rst_n ) 
			addr_r <= 9'b0;
		else if( rd_en && cs_n )
			if( addr_r == 9'd257 )
				addr_r <= 9'b0;
			else
				addr_r <= addr_r + 9'b1;                      
		else
			addr_r <= addr_r;
	end

//跨时钟域数据对比,格雷码转换,防止亚稳态
	assign gray_w = (addr_w ^ (addr_w>>1));
	assign gray_r = (addr_r ^ (addr_r>>1));
	
//空/满逻辑判断
	assign fifo_full   = ( (gray_w[8] != gray_r[8]) && (gray_w[7] != gray_r[7]) && (gray_w[6:0] == gray_r[6:0]) )?1'b1:1'b0;

	assign fifo_empty  = ( gray_w == gray_r )?1'b1:1'b0;
	
endmodule
2.3 RAM存储模块
module ram_fifo1(
	input						clk_w,
	input						clk_r,
	input						rst_n,
	input [7:0]					data_in,
	input						wr_en,
	input						rd_en,
	input [7:0]					addr_w,
	input [7:0]					addr_r,
	input						fifo_empty,
	input						fifo_full,
	
	output reg [7:0]			data_out
);

//ram存储器配置,宽度8,深度256	
	reg [7:0]				ram[255:0];

//读逻辑设计	
	always@( posedge clk_w ) begin
		if( wr_en && !fifo_full && !rd_en )
			ram[addr_w] <= data_in;
		else if( wr_en && fifo_empty && rd_en )
			ram[addr_w] <= data_in;
		else if( wr_en && !fifo_empty && !fifo_full && rd_en ) begin
			ram[addr_w] <= data_in;
		end
	end
	
	always@( posedge clk_r or negedge rst_n ) begin        //功能覆盖还不够,要加,比如空了还会继续读。
		if( !rst_n ) begin
			data_out <= 0;
		end
		else if( rd_en && !fifo_empty && !wr_en )
			data_out <= ram[addr_r];
		else if( rd_en && fifo_full && wr_en )
			data_out <= ram[addr_r];
		else if( wr_en && !fifo_empty && !fifo_full && rd_en ) 
			data_out <= ram[addr_r];
	end

//写逻辑设计
	
	
endmodule

三.功能仿真

3.1 基础仿真代码

此处考虑到篇幅问题,对上述代码的代码覆盖率和功能覆盖率皆不全面,对性能要求严苛的同学欢迎交流学习。

`timescale 1ns/1ns
`define clock_period 20

module fifo1_tb();
	reg					clk_w;
	reg					clk_r;
   reg					rst_n;
	reg	[7:0]			data_in;
	reg					wr_en;
	reg					rd_en;
	reg					cs_n;
	
	wire				fifo_empty;
	wire				fifo_full;
	wire [7:0]			data_out;
	
	fifo1 fifo1(
		.clk_w(clk_w),
		.clk_r(clk_r),
		.rst_n(rst_n),
		.data_in(data_in),
		.wr_en(wr_en),
		.rd_en(rd_en),
		.cs_n(cs_n),
		
		.fifo_empty(fifo_empty),
		.fifo_full(fifo_full),
		.data_out(data_out)
	);
		
	initial begin
		clk_w = 0;
		clk_r = 0;
		data_in = 0;
		wr_en = 0;
		rd_en = 0;
		cs_n = 0;
	end
	
	always#( `clock_period/2 ) clk_w = ~clk_w;
	always#( `clock_period/4 ) clk_r = ~clk_r;
	
	initial begin
		rst_n = 0;
		#( `clock_period*10 );
		rst_n = 1;
		#( `clock_period*10 + 1'b1 );
		cs_n = 1;
		wr_en = 1;
		repeat(255)
			begin
				#( `clock_period );data_in = data_in + 8'b1;
			end
		#( `clock_period );
		wr_en = 0;
		
		#( `clock_period*10 + 1'b1 );
		
		rd_en = 1;
		@(posedge fifo_empty);
		cs_n = 0;
		rd_en = 0;
		#2000;
		$stop;
	end

endmodule
3.2 基础功能仿真通过

在这里插入图片描述

因作者才疏学浅,本文有误之处劳烦同学们批评指正。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值