基于Verilog的同步FIFO原理及例题

目录

一、基本原理

二、基于Verilog的同步FIFO例题

思路一:拓展地址位宽

思路二:增加判断标志

思路三:计数器


一、基本原理

1、FIFO基本概念:First In First Out的缩写,是一种先进先出的数据缓存器,没有外部读写地址线,只能顺序写入读出数据。

FIFO存储器主要分为基于移位寄存器类型和基于RAM类型,而RAM又有单口和双口的类型。应用较多的是基于双端口的RAM的FIFO.(双端口的RAM就是带读写地址、使能端的存储器)

2、同步FIFO:读和写都是在一个CLK下工作,对双端口RAM进行操作,产生空满信号,满时不能写入,空时不能读出。

3、空满标注是FIFO设计的关键。通过比较读写信号指针来判断空满,当读指针等于满指针时,FIFO可能是空可能是满。这里我们引入一个扩展位来判断: 首先把读写标志位+地址位全部复位,如果地址循环了奇数次,标志位置1,偶数置0当读写指针完全相等时,处于“空”;当标志位不同,地址位相等时,处于“满”

基于Verilog的同步FIFO原理及例题_第1张图片 如图为“满”判断

4、时延

在真实情境下,写进去的数不能立即读出来,需要延迟两个clk。 



二、基于Verilog的同步FIFO例题

根据题目提供的双口RAM代码和接口描述,实现同步FIFO,要求FIFO位宽和深度参数化可配置。

电路的接口如下图所示。

基于Verilog的同步FIFO原理及例题_第2张图片

 

详解:

FIFO题的难点就在空满标志的判断:

思路一:拓展地址位宽

将raddr和waddr两个地址扩展一位,用首位数字做标志来进行判断,当写操作领先整一个周期(即一个depth)时,为full,此时raddr和waddr除了首位其余相等,用                        wfull <= waddr == raddr + DEPTH;来描述。而当读写地址完全相同时,则为empty,意味着读写标志相同。 rempty <= waddr == raddr;

`timescale 1ns/1ns
/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
	,input [WIDTH-1:0] wdata      	//数据写入
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
	,output reg [WIDTH-1:0] rdata 		//数据输出
);
reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1]; //定义一定位宽的存储器
always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata; //写入数据
end 
always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr]; //读出数据
end 
endmodule  

//上述为RAM模块
/**********************************SFIFO************************************/
module sfifo#(
    parameter   WIDTH = 8,
    parameter   DEPTH = 16
)(
    input                   clk     ,
    input                   rst_n   ,
    input                   winc    ,
    input                   rinc    ,
    input       [WIDTH-1:0] wdata   ,
 
    output reg              wfull   ,
    output reg              rempty  ,
    output wire [WIDTH-1:0] rdata
);
    wire wenc,renc;
    reg [$clog2(DEPTH) : 0] waddr;
    reg [$clog2(DEPTH) : 0] raddr;
 
    //空满标志的判断
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wfull <=   0;
            rempty <=  0;
        end
        else begin
            wfull <= waddr == raddr + DEPTH;
            rempty <= waddr == raddr;
        end
    end
     
    //FIFO使能信号受空满标志的影响
    assign wenc = winc && !wfull;
    assign renc = rinc && !rempty; 


 
    //读写地址的变化
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            waddr <=  0 ;raddr <=  0 ;
        end
        else begin
            waddr <= (wenc)? waddr +  1 : waddr;
            raddr <= (renc)? raddr +  1 : raddr;
        end
    end
     
      
   //调用RAM模块 
    dual_port_RAM #(.DEPTH (DEPTH),
                    .WIDTH (WIDTH))
           RAM (
        .wclk  (clk  ),
        .wenc  (wenc ),
        .waddr (waddr),
        .wdata (wdata),
        .rclk  (clk  ),
        .renc  (renc ),
        .raddr (raddr),
        .rdata (rdata)
    );
 

    
endmodule
    

思路二:增加判断标志

空满信号判断可以更直观的加入两个标志来对周期进行计数,向上文中所提到的,首先把读写标志位+地址位全部复位,如果地址循环了奇数次,标志位置1,偶数置0当读写指针完全相等时,处于“空”;当标志位不同,地址位相等时,处于“满”。详解如下:

//节选了判断空满的一部分,其余和思路一代码相同

    reg r,w;
        always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            w <= 'd0;
            r <= 'd0;
        end
        else begin
            w <= (waddr == 4'd16)? ~w:w;
            r <= (raddr == 4'd16)? ~r:r;
        end
    end
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wfull <= 'd0;
            rempty <= 'd0;
        end
        else begin
            wfull <= (w^r && waddr == raddr)? 1:0;
            rempty <=(w==r && waddr == raddr)? 1:0;
        end
    end

思路三:计数器

添加一个计数器来判断也是常用的方法,当写使能有效的时候计数器加一;当读使能有效的时候,计数器减一,将计数器与depth进行比较来判断fifo的空满状态。具体代码就不详细写啦。这种方式写起来比较简单,但需要占用很多额外的资源,而且如果FIFO的深度越大会使得效率很低。



DONE~

基于Verilog的同步FIFO原理及例题_第3张图片

你可能感兴趣的:(数字ic知识总结,Verilog例题,fpga开发)