同步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

五、结果

相关推荐
I'm a winner12 小时前
【IP核】 Xilinx FPGA LVDS 高速接口,含验证工程与板级测试用例
tcp/ip·fpga开发·测试用例
I'm a winner13 小时前
基于Xilinx FPGA的LVDS高速串行通信系统 - 完整源码解决方案(一)(文末附源码)
fpga开发
国科安芯15 小时前
航天器多路并联大功率电源系统设计与ASP4644均流特性分析
单片机·嵌入式硬件·fpga开发·安全性测试
techdashen2 天前
从网络栈继续往下:micro:bit、2.4GHz、调制方式,以及一个不太靠谱但很有趣的想法
网络·fpga开发
FPGA小徐2 天前
FIR 数字滤波器 --verilog设计实现
fpga开发
zlinear数据采集卡2 天前
从协议解析到波形实时显示:硬核拆解ZLinear采集卡上位机软件的开发架构
arm开发·单片机·嵌入式硬件·fpga开发·架构·开源
pcjiushizhu2 天前
ModelSim 仿真时 Simulate 无反应或只显示 Loading 的解决方法:网卡问题排查
fpga开发
FPGA小迷弟2 天前
vivado中的AXI Interconnect到底应该怎么用,他的底层原理是什么,一篇文档全部理清楚!!!
网络协议·tcp/ip·fpga开发·verilog·fpga
国科安芯2 天前
ASC4T245S分组双向控制架构深度解析:独立DIR/OE控制、QFN16封装与混合方向总线桥接
单片机·嵌入式硬件·物联网·fpga开发·架构·risc-v
尤老师FPGA3 天前
GT系列2:GT基础架构(二)
fpga开发