同步FIFO

FPGA/ASIC中的FIFO

FIFO可用于以下任何目的:

  • 跨时钟域
  • 在将数据发送到芯片外之前将其缓冲(例如,发送到DRAM或SRAM)
  • 缓冲数据以供软件在以后查看
  • 存储数据以备后用

FIFO可以认为是汽车可以驶过的单向隧道。隧道的尽头是一个带门的收费站。门一旦打开,汽车便可以离开隧道。如果那扇门从未打开,而更多的汽车继续进入隧道,那么最终隧道将充满汽车。这称为FIFO溢出,通常这不是一件好事。FIFO的深度可以认为是隧道的长度。FIFO越深,在溢出之前可以容纳更多的数据。FIFO也具有宽度,该宽度表示进入FIFO的数据的宽度(以位数为单位)。下面是任何FIFO基本接口的图像。当您查看任何FIFO时,总是会找到这些信号。通常,会有更多的信号添加其他功能,例如FIFO中的字数计数。参见下图:

FIFO可以分为写一侧和读一侧。写入一侧具有信号“写入使能wr_en”,“写入数据wr_data”和“ FIFO已满fifo_full”。设计人员切勿写入已满的FIFO! 始终检查FIFO已满标志,以确保有空间可以写入另一条数据,否则您将丢失该数据。

读取的一侧具有信号“读取使能rd_en”,“读取数据rd_data”和“ FIFO空fifo_empty”。设计人员切勿读取空的FIFO! 只要您遵循这两个基本规则,您和FIFO就会相处融洽。我再说一遍,因为它们是如此重要

FIFO的两个规则:

  • 永远不要写入完整的FIFO(溢出)
  • 永远不要从空的FIFO中读取(下溢)

FIFO本身可以由FPGA或ASIC内的专用逻辑组成,也可以由触发器(分布式寄存器)创建。综合工具将使用这两种工具中的哪一种完全取决于您使用的FPGA供应商以及代码的结构。只需知道,当您使用专用逻辑块时,与使用基于寄存器的FIFO相比,它们具有更好的性能。

同步FIFO的设计

FIFO缓冲区是一种读/写存储阵列,可自动跟踪数据进入模块的顺序并以相同顺序读出数据。在硬件中,FIFO缓冲区用于同步目的。 它通常实现为循环队列,并具有两个指针:

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

读写地址最初都位于第一个存储器位置,并且FIFO队列为空。当FIFO缓冲区的读地址和写地址之间的差等于内存阵列的大小时,则FIFO队列为Full(对于异步FIFO而言,可以设计多一位地址表示读指针以及写指针)。

FIFO可以分为同步时钟还是异步时钟,具体取决于是相同时钟(同步)还是不同时钟(异步)控制读写操作。

同步FIFO是指FIFO设计,其中使用时钟信号将数据值顺序写入存储阵列,并使用相同的时钟信号从存储阵列顺序读出数据值。 图1显示了典型FIFO的操作流程

同步FIFO可以使用计数方式来判断空满,但是异步FIFO不能,因为写指针和读指针根本不在同一个时钟域,计数器无法处理这样的计数。

	RTL:
		module syn_fifo#(
		    parameter DATA_WIDTH = 8,
		    parameter DATA_DEPTH = 8
		    )(
		        input   clk,
		        input   rst_n,
		        //wr_port
		        input   wr_en,
		        input   [DATA_WIDTH-1:0] wr_data,
		        output  wr_full,
		        //rd_port
		        input rd_en,
		        output  reg [DATA_WIDTH-1:0] rd_data,
		        output  rd_empty
		    );
		    
		    reg [DATA_WIDTH-1:0] fifo_buffer [0:DATA_DEPTH-1];
		    reg [$clog2(DATA_DEPTH):0]   fifo_cnt;
		    reg [$clog2(DATA_DEPTH)-1:0]    wr_pointer;
		    reg [$clog2(DATA_DEPTH)-1:0]    rd_pointer;
		   /*
		    always@(posedge clk ,negedge rst_n) begin
		        if(!rst_n)
		            fifo_cnt <= 0;
		        else if(wr_en && !(rd_en))
		            fifo_cnt <= fifo_cnt + 1;
		        else if(rd_en && !(wr_en))
		            fifo_cnt <= fifo_cnt - 1;
		    end
		    */
		    
		    always@(posedge clk ,negedge rst_n) begin
		        if(!rst_n)
		            fifo_cnt <= 0;
		        else 
		            case({wr_en,rd_en})
		                2'b01:
		                    if(fifo_cnt > 0)
		                        fifo_cnt <= fifo_cnt - 1;
		                2'b10:
		                    if(fifo_cnt < 8)
		                        fifo_cnt <= fifo_cnt + 1;
		                default:
		                        fifo_cnt <= fifo_cnt;
		            endcase
		    end
		   
		    always@(posedge clk ,negedge rst_n) begin
		        if(!rst_n)
		            wr_pointer <= 0;
		        else if(wr_en && !(wr_full)) begin     
		             if(wr_pointer == DATA_DEPTH -1)
		                wr_pointer <= 0;
		             else
		                wr_pointer <= wr_pointer + 1;
		        end
		    end
		    
		    always@(posedge clk ,negedge rst_n) begin
		        if(!rst_n)
		            rd_pointer <= 0;
		        else if(rd_en && !(rd_empty)) begin     
		             if(rd_pointer == DATA_DEPTH -1)
		                rd_pointer <= 0;
		             else
		                rd_pointer <= rd_pointer + 1;
		        end
		    end
		    
		    always@(posedge clk) begin
		        if(wr_en && !(wr_full))
		            fifo_buffer[wr_pointer] <= wr_data;
		    end
		    
		    always@(posedge clk) begin
		        if(rd_en && !(rd_empty))
		            rd_data <= fifo_buffer[rd_pointer];
		    end
		      
		    assign wr_full = (fifo_cnt == DATA_DEPTH)?1'b1:1'b0;
		    assign rd_empty = (fifo_cnt == 0)?1'b1:1'b0;
		endmodule
		

	Testbench:
		`timescale 1ns / 1ps
		
		module tb_syn_fifo();
		parameter DATA_WIDTH = 8;
		parameter DATA_DEPTH = 8;
		    
		reg clk;
		reg rst_n;
		reg [DATA_WIDTH-1:0] wr_data;  
		wire [DATA_WIDTH-1:0] rd_data;
		reg    wr_en;
		reg    rd_en;
		wire   wr_full;
		wire   rd_empty;
		
		initial begin
		    clk = 0;
		    forever begin
		        #10 clk = ~clk;
		    end
		end
		
		initial begin
		    rst_n = 0;
		    wr_en = 0;
		    rd_en = 0;
		
		    @(negedge clk) rst_n = 1;
		    @(negedge clk) wr_en = 1;
		    wr_data = $random;
		
		    repeat(3) begin
		        @(negedge clk)
		        wr_data = $random;	
		    end
		      
		    @(negedge clk)
		    wr_en = 0;
		    rd_en = 1;
		
		    repeat(3) begin
		        @(negedge clk);	
		    end
		
		    @(negedge clk)
		    rd_en = 0;
		    wr_en = 1;
		    wr_data = $random;
		
		    repeat(7) begin   		
		        @(negedge clk)
		        wr_data = $random;
		    end
		
		    #20 $finish;
		
		end    
		
		 syn_fifo #(
		    .DATA_WIDTH(DATA_WIDTH),
		    .DATA_DEPTH(DATA_DEPTH)
		    )u_syn_fifo(
		        .clk        (clk),
		        .rst_n      (rst_n),
		        .wr_en      (wr_en),       
		        .wr_data    (wr_data),     
		        .wr_full    (wr_full),    
		        .rd_en      (rd_en),       
		        .rd_data    (rd_data),     
		        .rd_empty   (rd_empty)    
		    );
		
		   
		endmodule

FPGA基础知识极简教程(3)从FIFO设计讲起之同步FIFO篇_李锐博恩的博客-CSDN博客_vhdl 同步fifo

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jay丶ke

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值