同步FIFO的verilog实现(2)——高位扩展法

一、前言

在之前的文章中,我们介绍了同步FIFO的verilog的一种实现方法:计数法。其核心在于:在同步FIFO中,我们可以很容易的使用计数来判断FIFO中还剩下多少可读的数据,从而可以判断空、满。

关于计数法实现同步FIFO的详细内容,请参考:同步FIFO的verilog实现(1)------计数法

二、高位扩展法原理

我们知道对于FIFO的设计来说,其核心在于设计读写指针,并且生成可靠的空、满信号。

当读/写地址指针在复位操作期间被置为零时,或者当读指针在从FIFO中读取了最后一个字之后追上了写指针,此时读指针和写指针相等代表着FIFO为空状态。而当写指针再次追上读指针时,此时读指针和写指针相等代表着FIFO为写满。也就是说当读写指针相等时,FIFO要么为空,要么为满。

因此我们可以将地址位扩展一位,用最高位来判断空满,其余低位还是正常用于读写地址索引。当写指针递增超过FIFO的最大地址时,写指针的MSB位将置为1,同时将其余低位设置回零。读指针也是如此。如果读指针和写指针的MSB不同,则意味着写指针比读指针多绕了一次,表示FIFO写满。如果两个指针的MSB相同,则表示两个指针的回绕次数相同,表示FIFO读空。如下图所示:

当最高位不同,且其他位相同,则表示读指针或者写指针多跑了一圈,这显然不可能发生,情况只能是写指针多跑了一圈,与就意味着FIFO被写满了。

当最高位相同,且其他位相同,则表示读指针追到了写指针或者写指针追到了读指针,而显然不会让写指针追读指针(这种情况只能是写指针超过读指针一圈),所以可能出现的情况只能是读指针追到了写指针,也就意味着FIFO被读空了。

三、同步FIFO的verilog实现

理解了原理,我们就能很快设计出相应的verilog代码:

//------------------------<高位扩展法设计同步FIFO>----------------------------
module sync_fifo1#(  
//-----------------------------<参数定义>---------------------------------
    parameter FIFO_WIDTH = 16,                          //FIFO宽度
    parameter FIFO_DEPTH = 16                           //FIFO深度
)(
//-----------------------------<接口定义>---------------------------------
    input clk,                                          //时钟信号
    input rst,                                          //复位信号
 
    input [FIFO_WIDTH-1:0] din,                         //FIFO输入数据(写数据)
    input rd_en,                                        //读使能信号 
    input wr_en,                                        //写使能信号

    output reg [FIFO_WIDTH-1:0] dout,                   //FIFO输出数据(读数据) 
    output empty,                                       //FIFO空标志 
    output full                                         //FIFO满标志 
); 

//-----------------------------<reg定义>---------------------------------
    reg [FIFO_WIDTH-1:0] fifo_buffer[FIFO_DEPTH-1:0];	//用二维数组实现RAM	
    reg [$clog2(FIFO_DEPTH):0] wr_addr;			     	//写地址(写指针),位宽要多出一位
    reg [$clog2(FIFO_DEPTH):0] rd_addr;			    	//读地址(读指针),位宽要多出一位

//-----------------------------<wire定义>---------------------------------     
    wire [$clog2(FIFO_DEPTH) - 1 : 0]	wr_addr_true;	//真实写地址指针
    wire [$clog2(FIFO_DEPTH) - 1 : 0]	rd_addr_true;   //真实读地址指针
    wire								wr_addr_msb;	//写地址指针地址最高位
    wire								rd_addr_msb;	//读地址指针地址最高位
 
    assign {wr_addr_msb,wr_addr_true} = wr_addr;		//将最高位与其他位拼接
    assign {rd_addr_msb,rd_addr_true} = rd_addr;		//将最高位与其他位拼接       

//-----------------------------<读操作>-----------------------------------
always@(posedge clk or posedge rst)begin 
	if(rst)
		rd_addr <= 0;
	else if(rd_en && !empty)begin                       //读使能有效且FIFO非空
		rd_addr	<=	rd_addr + 1'd1;                     //读指针递增
		dout	<=	fifo_buffer[rd_addr_true];               //fifo读出数据
	end
	else begin
		rd_addr	<=	rd_addr;                            
		dout	<=	dout;
	end
end

//-----------------------------<写操作>-----------------------------------
always@(posedge clk or posedge rst)begin 
	if(rst)
		wr_addr <= 0;
	else if(wr_en && !full)begin                       //写使能有效且FIFO非满
		wr_addr	<=	wr_addr + 1'd1;                    //读指针递增
		fifo_buffer[wr_addr_true] <= din;              //数据写入fifo
	end
	else begin
		wr_addr	<=	wr_addr;                            
	end
end

//-----------------------------<通过地址扩展位更新空/满信号>-----------------------------------
assign	empty = ( wr_addr == rd_addr ) ? 1'b1 : 1'b0;                                              //当所有位相等时,读指针追到了写指针,FIFO被读空
assign	full  = ((wr_addr_msb != rd_addr_msb ) && ( wr_addr_true == rd_addr_true )) ? 1'b1 : 1'b0; //当最高位不同但是其他位相等时,写指针超过读指针一圈,FIFO被写满

endmodule

四、测试代码

给出如下的测试代码:

`timescale 1ns/1ns
//-----------------------------<高位扩展法同步FIFO测试>---------------------------------
module tb_sync_fifo1();
    parameter WIDTH	= 8;
    parameter DEPTH	= 8;

    reg                 clk         ;
    reg                 rst         ;

    reg  [WIDTH-1:0]	din    		;
    reg 				wr_en  		;
    reg 				rd_en  		;
    wire [WIDTH-1:0]	dout		;
    wire 				full        ;
    wire 				empty       ;

//-----------------------------<测试模块例化>---------------------------------
sync_fifo1 #(
	.FIFO_WIDTH	(WIDTH),		 //FIFO宽度
    .FIFO_DEPTH (DEPTH)          //FIFO深度
)
sync_fifo_u1(
	.clk		(clk		),
	.rst		(rst		),
	.din     	(din 	    ),
	.rd_en		(rd_en		),
	.wr_en		(wr_en		),
                 
	.dout    	(dout	    ),	
	.empty		(empty		),	
	.full		(full		)	
);

//-----------------------------<模块测试>---------------------------------
initial begin
	clk = 1'b0;							//初始时钟为0
	rst <= 1'b0;						//初始复位
	din <= 'd0;		
	wr_en <= 1'b0;		
	rd_en <= 1'b0;
#10
    rst <= 1'b1;
#10 
    rst <= 1'b0;
    repeat(10)
    #10 begin
        wr_en <= 1'b1;
        rd_en <= 1'b0;
        din <= $random;                   //生成8位的随机数
    end

    repeat(10)
    #10 begin
        wr_en <= 1'b0;
        rd_en <= 1'b1;
    end
$finish;
end

//------------------------------<设置时钟>----------------------------------------
always #5 clk = ~clk;			

endmodule

五、结果

相关推荐
fei_sun10 小时前
【Verilog】第一章作业
fpga开发·verilog
深圳市雷龙发展有限公司longsto10 小时前
基于FPGA(现场可编程门阵列)的SD NAND图片显示系统是一个复杂的项目,它涉及硬件设计、FPGA编程、SD卡接口、NAND闪存控制以及图像显示等多个方面
fpga开发
9527华安15 小时前
FPGA实现PCIE3.0视频采集转10G万兆UDP网络输出,基于XDMA+GTH架构,提供工程源码和技术支持
网络·fpga开发·udp·音视频·xdma·pcie3.0·万兆网
able陈15 小时前
为什么verilog中递归函数需要定义为automatic?
fpga开发
fei_sun16 小时前
【Verilog】第二章作业
fpga开发·verilog
碎碎思16 小时前
如何使用 Vivado 从源码构建 Infinite-ISP FPGA 项目
fpga开发·接口隔离原则
江山如画,佳人北望19 小时前
fpga-状态机的设计及应用
fpga开发
晓晓暮雨潇潇20 小时前
Xilinx IP核(3)XADC IP核
fpga开发·vivado·xadc·ip核
CWNULT20 小时前
AMD(Xilinx) FPGA配置Flash大小选择
fpga开发
碎碎思1 天前
很能体现FPGA硬件思维的一道面试题
fpga开发