同步FIFO理解及设计

一、FIFO概念

        FIFO是先进先出的首字母缩写,它描述了如何相对于时间或优先级管理数据。在这种情况下,到达的第一个数据也将是从一组数据中离开的第一个数据。 FIFO缓冲区是一种读/写存储阵列,可自动跟踪数据进入模块的顺序并以相同顺序读出数据。在硬件中,FIFO缓冲区用于同步目的。它通常实现为循环队列,并具有两个指针:

  • 读指针/读地址寄存器
  • 写指针/写地址寄存器

        FIFO 从读写时钟上来分有两类结构:同步 FIFO 和 异步FIFO。同步 FIFO 具有一个时钟(读写共用一个时钟)输入,因此所有输入信号的读取都是在这个时钟的上升沿进行的,所有输出信号的变化也是在这个时钟信号的上升沿的控制下进行的,即单时钟 FIFO 的所有输入输出信号都是同步这个时钟信号的。而在异步FIFO结构中,写端口和读端口分别有独立的时钟,所有与写相关的信号都是同步于写时钟 wr_clk,所有与读相关的信号都是同步于读时钟 rd_clk 。

二、同步FIFO

  • 写指针WP总是指向下一个时钟要写的地址;
  • 读指针RP总是指向下一个时钟要读的地址;
  • 读指针等于写指针的时候有可能为空,有可能为满。

FIFO 的常见参数:

  • FIFO 的宽度:即 FIFO 一次读写操作的数据位;
  • FIFO 的深度:指的是 FIFO 可以存储多少个 N 位的数据(如果宽度为 N)。
  • 满标志:FIFO 已满或将要满时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的写操作继续向FIFO 中写数据而造成溢出(overflow)。
  • 空标志:FIFO 已空或将要空时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的读操作继续从FIFO 中读出数据而造成无效数据的读出(underflow)。
  • 读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。
  • 写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。

同步FIFO设计:

FIFO 本质上是一个存储器,存储介质可以是寄存器,SRAM 。 DDR等。其左边接上位机,及写FIFO设备,右边接下位机,及读FIFO设备。

        上位机会对FIFO发起写操作,wr是写使能脉冲wr_dat 是写入的数据,这两个信号是对齐的。上位机在发起写操作前,会读FIFO的满状态指示信号full或almost_full,因为FIFO的存储空间可能被写满,此时再写入数据必然会发生数据丢失现象。full状态信号表示FIFO的存储空间已经被全部占满,而almost_full状态信号表示存储空间并未被全部占满,但已经达到了用户设置的临界点,用户可以用cfg_almost_full配置线设置该临界点。

        下位机对FIFO发起读操作,rd是读使能脉冲rd_dat 是读出的数据,根据存储介质类型、存储空间大小的不同,读出数据的延迟也不同,有的是rd发出后一拍输出数据,有的是两拍输出数据,要根据实际情况来定,不能一概而论。下位机在发起读之前,先要采样FIFO的状态信号empty或almost_empty,empty 表示FIFO内存全空,因此不支持读取数据,而almost_empty 表示虽然FIFO内存中尚存在一些数据,但数量较少,处于用户配置的cfg_almost_empty以下,以此发出警告。fifo_num用于实时反映FIFO内存中的数据数量原本,FIFO只需 full和 empty 两种状态信号,但是,在以Burst方式通信时,上位机灌水不是每输入一个数据先看一下full状态,而是只看一下full状态,就往FIFO中连续灌很多数据,此时,almost_full就很有必要了,它会告诉上位机停止以Burst方式灌水。同理,下位机在使用Burst方式读取时,也需要almost_empty 提醒它停止Burst 读取。两条水线一般设置为稍大于Burst的长度。

2.1 高位扩展法

        在深度为8的FIFO中,需要3bit的读写指针来分别指示读写地址3'b000-3'b111这8个地址。若将地址指针扩展1bit,则变成4bit的地址,而地址表示区间则变成了4'b0000-4'b1111。假设不看最高位的话,后面3位的表示区间仍然是3'b000-3'b111,也就意味着最高位可以拿来作为指示位。 

当最高位不同,且其他位相同,则写指针多跑了一圈,意味着FIFO被写满了
当最高位相同,且其他位相同,则表示读指针追到了写指针,意味着FIFO被读空了

`timescale 1ns / 1ps
module synch_fifo #(
    parameter   P_DEEP      =   8 , // FIFO深度(读写数据量)
    parameter   P_DEEPWID   =   3 , // FIFO地址的位宽
    parameter   P_BITWID    =   5   // FIFO宽度(读写数据位)
)(
    input                       i_clk           ,
    input                       i_rst_n         ,

    input                       wr_en           ,
    input [P_BITWID - 1 : 0]    wr_data         ,
    input [P_DEEPWID - 1 : 0]   cfg_almost_full ,
    output                      almost_full     ,
    output                      full            ,

    input                       rd_en           ,
    input [P_DEEPWID - 1 : 0]   cfg_almost_empty,
    output[P_BITWID - 1 : 0]    rd_data         ,
    output                      almost_empty    ,
    output                      empty           ,

    output[P_DEEPWID : 0]       fifo_num        
);

wire[P_DEEPWID - 1 : 0]         r_wr_ptr    ;
wire[P_DEEPWID - 1 : 0]         r_rd_ptr    ;
reg[P_DEEPWID : 0]              r_wr_ptr_exp;
reg[P_DEEPWID : 0]              r_rd_ptr_exp;
reg[P_BITWID - 1 : 0]           r_memory[P_DEEP - 1 : 0];
reg[P_BITWID - 1 : 0]           r_rd_data   ;              
integer                         i           ;

assign          rd_data  = r_rd_data  ;
assign          r_wr_ptr = r_wr_ptr_exp[P_DEEPWID - 1 : 0];
assign          r_rd_ptr = r_rd_ptr_exp[P_DEEPWID - 1 : 0];

always@(posedge i_clk,negedge i_rst_n)begin
    if(!i_rst_n)
        r_wr_ptr_exp <= 'd0;
    else if(wr_en && !full)
        r_wr_ptr_exp <= r_wr_ptr_exp + 1;
    else 
        r_wr_ptr_exp <= r_wr_ptr_exp;
end
always@(posedge i_clk,negedge i_rst_n)begin
    if(!i_rst_n)
        r_rd_ptr_exp <= 'd0;
    else if(rd_en && !empty)
        r_rd_ptr_exp <= r_rd_ptr_exp + 1;    
    else 
        r_rd_ptr_exp <= r_rd_ptr_exp;
end

always@(posedge i_clk,negedge i_rst_n)begin
    if(!i_rst_n)
        for (i = 0 ; i < P_DEEP ; i = i + 1) begin
            r_memory [i] <= {(P_BITWID){1'b0}};
        end 
    else if(wr_en && !full)
        r_memory[r_wr_ptr] <= wr_data;
end
       
always@(posedge i_clk,negedge i_rst_n)begin
    if(!i_rst_n)
        r_rd_data <= 'd0;
    else if (rd_en && !empty) 
        r_rd_data <= r_memory[r_rd_ptr];
end

assign fifo_num         = r_wr_ptr_exp - r_rd_ptr_exp;
assign full             = (fifo_num == P_DEEP); // | ((fifo_num == P_DEEP - 1) & wr_en & ~rd_en);
assign almost_full      = (fifo_num >= cfg_almost_full) ;//| ((fifo_num == cfg_almost_full) & wr_en & ~rd_en);
assign empty            = (fifo_num == 0) ;//| ((fifo_num == 1) & ~wr_en & rd_en);
assign almost_empty     = (fifo_num <= cfg_almost_empty) ;//| ((fifo_num == cfg_almost_empty) & ~wr_en & rd_en);



endmodule

 tb文件

`timescale 1ns / 1ps
module synch_fifo_tb();
parameter   P_DEEP      =   8 ;// FIFO深度(读写数据量)
parameter   P_DEEPWID   =   3 ;// FIFO地址的位宽
parameter   P_BITWID    =   8 ;// FIFO宽度(读写数据位)
reg                        i_clk           ;
reg                        i_rst_n         ;
reg                        wr_en           ;
reg  [P_BITWID - 1 : 0]    wr_data         ;
reg  [P_DEEPWID - 1 : 0]   cfg_almost_full ;
wire                       almost_full     ;
wire                       full            ;
reg                        rd_en           ;
reg [P_DEEPWID - 1 : 0]    cfg_almost_empty;
wire[P_BITWID - 1 : 0]     rd_data         ;
wire                       almost_empty    ;
wire                       empty           ;
wire[P_DEEPWID : 0]        fifo_num        ;  

synch_fifo #(
    .P_DEEP       (P_DEEP   ) , // FIFO深度(读写数据量)
    .P_DEEPWID    (P_DEEPWID) , // FIFO地址的位宽
    .P_BITWID     (P_BITWID )   // FIFO宽度(读写数据位)
)
synch_fifo_u0(
    .i_clk           (i_clk  ),
    .i_rst_n         (i_rst_n),
    .wr_en           (wr_en           ),
    .wr_data         (wr_data         ),
    .cfg_almost_full (6),
    .almost_full     (almost_full     ),
    .full            (full            ),
    .rd_en           (rd_en           ),
    .cfg_almost_empty(2),
    .rd_data         (rd_data         ),
    .almost_empty    (almost_empty    ),
    .empty           (empty           ),
    .fifo_num        (fifo_num)
);
always #10 i_clk = ~i_clk;	

initial begin
	i_clk = 1'b0;					//初始时钟为0
	i_rst_n <= 1'b0;				//初始复位
	wr_data <= 'd0;
	wr_en <= 1'b0;
	rd_en <= 1'b0;
    #50;
    i_rst_n <= 1'b1;
    #20;	
//重复8次写操作,让FIFO写满 	
	repeat(8) begin
		@(negedge i_clk)begin	
			wr_en <= 1'b1;
            rd_en <= 1'b0;
			wr_data <= $random;	//生成8位随机数
		end
	end
    //重复8次读操作,让FIFO读空 	
	repeat(8) begin
		@(negedge i_clk)begin		
			wr_en <= 1'b0;
			rd_en <= 1'd1;
		end
	end
//重复4次写操作,写入4个随机数据	
	repeat(4) begin
		@(negedge i_clk)begin		
			wr_en <= 1'b1;
			wr_data <= $random;	//生成8位随机数
			rd_en <= 1'b0;
		end
	end
//持续同时对FIFO读写,写入数据为随机数据	
	forever begin
		@(negedge i_clk)begin		
			wr_en <= 1'b1;
			wr_data <= $random;	//生成8位随机数
			rd_en <= 1'b1;
		end
	end
    #100;
    $finish;
end
endmodule

PS:   借鉴帖子 https://blog.csdn.net/wuzhikaidetb/article/details/12113604

        白老师的《数字IC设计入门》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值