精华内容
下载资源
问答
  • 经过验证的读取不同位宽同步FIFO测试仿真文件。工程主文件参考别人自己改写的,所以主文件可以直接下载,测试文件想赚点积分,所以有需要的可以下载一下。谢谢大家支持。
  • 同步FIFO与异步FIFO的Verilog实现(附源代码和测试代码)
  • 该项目包含用于实现FT2232H(FT245同步和异步模式)和用于高速USB传输的同步FIFO的文件。 要求: ipython2 ipython notebook cocotb iverilog gource gtkwave 用法: To open ipython notebook, run: ./run.sh...
  • 同步FIFO代码和testbench

    2018-04-02 17:03:18
    此代码是同步FIFO的Verilog源代码,经上板测试是没有问题的,请大家放心使用
  • 直接可用的同步FIFO的verilog代码,自己总结的非常详细的相应文档
  • 本文件包含了同步FIFO的原理介绍以及同步FIFO的哥哥模块的相关代码 代码中还包含了测试代码 代码已经在vivado中跑通
  • 该资源是基本的同步FIFO,很适合初学者学习,如果大家觉得有用,我可以将异步发出来,这都是我自己写的,也是用于初始训练
  • 同步FIFO

    2021-07-18 13:20:13
    文章目录前言FIFO使用场景FIFO参数同步FIFO 异步FIFO请看:异步FIFO … 前言 FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,,但缺点就是只能顺序...


    异步FIFO请看: 异步FIFO
    ===========------------------
    👉注:本文所使用的的所有代码均已编译并仿真通过,仿真结果附于文中。
    👉注:更多精彩请看: 面试常问的verilog代码汇总 一文
    ===========------------


    前言

    FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,,但缺点就是只能顺序写入数据,顺序的读出数据,其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。

    FIFO使用场景

    • 异步FIFO一般用于不同时钟域之间的数据传输,在两个时钟域之间采用FIFO作为数据缓冲;
    • 对于不同宽度的数据接口也可以用FIFO,例如单片机位8位数据输出,而DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的。

    FIFO参数

    • 宽度:THE WIDTH,是FIFO一次读写操作的数据位宽
    • 深度:THE DEEPTH,是FIFO可以存储多少个N位的数据(如果宽度为N)。如一个8位的FIFO,若深度为8,它可以存储8个8位的数据,深度为12 ,就可以存储12个8位的数据
    • 满标志:FIFO已满或将要满时由FIFO的状态电路送出的一个信号,以阻止FIFO的写操作继续向FIFO中写数据而造成溢出(overflow
    • 空标志:FIFO已空或将要空时由FIFO的状态电路送出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出(underflow)
    • 读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据
    • 写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据
    • 读指针:指向下一个读出地址。读完后自动加1
    • 写指针:指向下一个要写入的地址的,写完自动加1。

    读写指针其实就是读写的地址,只不过这个地址不能任意选择,而是连续的。

    同步FIFO

    同步FIFO的读写指令由同一个时钟控制,主要由两个模块构成:控制模块和RAM。

    • 控制模块:地址控制模块可以根据读写指令,生成RAM地址;
    • RAM:根据控制模块生成的地址信号进行数据的存储和读取操作;
    • 用计数器进行空满标志的判断

    读写过程:

    • 堆栈空:读指针和写指针指向第一个存储单元;
    • 写数据:当写入一个数据时,写数据指针指向下一个存储单元;经过(深度 -1)次写数据操作后,写指针指向最后一个存储单元;当经过写操作次数为FIFO深度时,写指针将回到第一个存储单元,并且显示堆栈为满。
    • 读操作:类似于写操作,当读出全部数据后,指针指向首单元。

    下面是一个8x8同步FIFO的verilog代码:

    module syn_fifo(clk,rst_n,write_to_stack,read_from_stack,data_in,data_out);
       parameter	stack_width = 8;//FIFO宽度
       parameter	stack_heigh = 8;//FIFO深度
       parameter	stack_addr_width =3;//读写地址位宽
       
       input  		clk,rst_n;
       input 		write_to_stack,read_from_stack;//数据读写使能信号
       input		[stack_width-1:0]data_in;//写入FIFO的数据
       output		[stack_width-1:0]data_out;//读出FIFO的数据
    
       reg			[stack_width-1:0]data_out;	
       reg			[stack_addr_width-1:0]write_ptr;//每写一次,加1
       reg			[stack_addr_width-1:0]read_ptr;//每读一次,加1
       reg			[stack_addr_width:0]gap_ptr;//计数器,FIFO内部数据量
       reg			[7:0]raml[7:0];// 8*8 bit寄存器
     	
       //空满逻辑生成
       wire		    stack_full,stack_empty;
       assign		stack_full  = (gap_ptr == stack_heigh);//当数据存储量为堆栈深度时,此时堆栈满
       assign		stack_empty = (gap_ptr == 0);//当数据存储量为0时,此时堆栈空
       
       //写操作
       always@(posedge clk or negedge rst_n) begin
           if(!rst_n) begin
               write_ptr <= 0;
               gap_ptr   <= 0;   
           end
           //如果堆栈不满,并且此时写指令有效 ———>写操作
           else if(write_to_stack && (!stack_full) ) begin
               write_ptr <= write_ptr + 1;//写指针加1
               gap_ptr   <= gap_ptr + 1;//FIFO内部数据数量加1
               raml[write_ptr] <= data_in;
           end        
       end
       //读操作
      always@(posedge clk or negedge rst_n) begin
           if(!rst_n) begin
               read_ptr  <= 0;
               gap_ptr   <= 0;  
           end
          //如果堆栈不空,并且读指令有效 ———>读操作
           else if( (!stack_empty) && read_from_stack) begin
           	   read_ptr <= read_ptr + 1;//读指针加1
               gap_ptr  <= gap_ptr - 1;//FIFO内部数据数量减1
               data_out <= raml[read_ptr];
           end       
       end   
    endmodule
    
    //------------------TB-----------------------
    `timescale 1ns/1ps
    module TB;
    	reg  		clk,rst_n;
        reg 		write_to_stack,read_from_stack;//数据读写使能信号
        reg			[7:0]data_in;
        wire		[7:0]data_out;
    	
    	FIFO_buffer dut(.clk(clk),
    					.rst_n(rst_n),
    					.write_to_stack(write_to_stack),
    					.read_from_stack(read_from_stack),
    					.data_in(data_in),
    					.data_out(data_out)
    					);
    	
    	initial begin
    		clk <= 0;
    		forever begin
    			#5 clk <= ~clk;	
    		end		
    	end
    	
    	initial begin
    	   rst_n <= 0;
    	#5 rst_n <= 1;
    	end
    	
    	initial begin
    	#10 write_to_stack  <= 1;
    		read_from_stack <= 0;
    		repeat(7) begin
    	      @(posedge clk);
    	      data_in = #(0.01) $random;    
    		end
    		data_in = #(0.01) $random;
    		write_to_stack  <= 0;
    		read_from_stack <= 1;
    		repeat(8) begin
    	      @(posedge clk);
    		end
    		write_to_stack  <= 0;
    		read_from_stack <= 0;
    		
    		rst_n <= 0;
    		repeat(2) begin
    	      @(posedge clk);
    		end
    		rst_n <= 1;
    		
    	#5  write_to_stack  <= 1;
    		read_from_stack <= 0;
    		repeat(10) begin
    	      @(posedge clk);
    	      data_in = #(0.01) $random;    
    		end
    		write_to_stack  <= 0;
    		read_from_stack <= 1;
    		repeat(10) begin
    	      @(posedge clk);
    		end
    		rst_n <= 0;
    		repeat(2) begin
    	      @(posedge clk);
    		end
    		rst_n <= 1;
    		
    	#5  write_to_stack  <= 1;
    		read_from_stack <= 1;
    		repeat(10) begin
    	      @(posedge clk);
    	      data_in = #(0.01) $random;    
    		end
    		data_in = #(0.01) $random;
    		write_to_stack  <= 0;
    		read_from_stack <= 1;
    		repeat(10) begin
    	      @(posedge clk);
    		end
    		write_to_stack  <= 0;
    		read_from_stack <= 0;
    		
    	#200 $finish();
    	end
    	
    endmodule
    
    

    下图测试:为连续写入7个数据,再读出。
    结果:FIFO存储数据一致,空满标致正常;
    在这里插入图片描述
    下图测试:为写入10个数据,再读出。

    在这里插入图片描述

    展开全文
  • 系统描述了同步FIFO硬件实现过程,采用Verilog硬件描述语言实现
  • 与基于触发器的零延迟读取等待时间存储器的同步FIFO接口。 这是用SystemVerilog编写的简单同步FIFO设计。 文件design.sv是顶层设计文件,它使用反引号包括来合并其他设计文件。 同样,testbench.sv文件包括sim所需...
  • 同步fifo的asic上可综合verilog代码
  • ft2232 同步fifo

    2016-01-14 13:58:10
    Ft2232Hl 同步fifo Qt测试源码 测试程序
  • 同步fifo.zip

    2019-08-31 09:38:19
    FIFO的Verilog源代码,可以任意调整深度的,不错,已经验证过 异步fifo下次发 源代码
  • 基于VHDL和FPGA的非对称同步FIFO设计实现,FIFO是一种常用于数据缓存的电路器件,可应用于包括高速数据采集、多处理器接口和通信中的高速缓冲等各种领域。然而在某些应用,例如在某数据采集和处理系统中,需要通过...
  • 同步FIFO和异步FIFO总结

    千次阅读 多人点赞 2020-09-17 17:06:09
    同步FIFO实现`7. 异步FIFO实现8. 对FIFO进行约束9. 关于异步FIFO最小深度的计算9.1 写时钟快于读时钟,写和读的过程中没有空闲周期9.2 写时钟频率大于读时钟频率,但在读写的过程中存在空闲周期9.3 写时钟慢于读...

    1. FIFO简介

    FIFO是一种先进先出数据缓存器,它与普通存储器的区别是没有外部读写地址线,使用起来非常简单,缺点是只能顺序读写,而不能随机读写。

    2. 使用场景

    1. 数据缓冲:也就是数据写入过快,并且间隔时间长,也就是突发写入。那么通过设置一定深度的FIFO,可以起到数据暂存的功能,且使得后续处理流程平滑。
    2. 时钟域的隔离:主要用异步FIFO。对于不同时钟域的数据传输,可以通过FIFO进行隔离,避免跨时钟域的数据传输带来的设计和约束上的复杂度。比如FIFO的一端是AD,另一端是PCI;AD的采集速率是16位100KSPS,每秒的数据量是1.6Mbps。而PCI总线的速度是33MHz,总线宽度是32位
    3. 用于不同宽度的数据接口。例如单片机是8位,DSP是16。

    3. 分类

    同步FIFO:指读时钟和写时钟是同一个时钟
    异步FIFO:指读写时钟是不同的时钟。

    4. FIFO的常见参数

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

    5. FIFO设计

    5.1 空满标志生成

    1. FIFO设计的关键是产生可靠的FIFO读写指针和生成FIFO空/满状态标志。
      当读写指针相等时,表明FIFO为空,这种情况发生在复位操作时;或者当读指针读出FIFO中最后一个字后,追赶上了写指针时,这时FIFO处于满的状态。
      为了区分到底是满状态还是空状态,可以采用以下方法:
      方法1:在指针中添加一个额外的位(extra bit),当写指针增加并越过最后一个FIFO地址时,就将写指针这个未用的MSB加1,其它位回零。对读指针也进行同样的操作。此时,对于深度为2^n的FIFO,需要的读/写指针位宽为(n+1)位,如对于深度为8的FIFO,需要采用4bit的计数器,0000~1000、1001~1111,MSB作为折回标志位,而低3位作为地址指针。
    • 如果两个指针的MSB不同,说明写指针比读指针多折回了一次;如r_addr=0000,而w_addr = 1000,为满。
    • 如果两个指针的MSB相同,则说明两个指针折回的次数相等。其余位相等,说明FIFO为空;

    5.2 异步FIFO的设计还要注意跨时钟域问题

    将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测

    5.3 gray码如何判断空满

    对于“空”的判断:依然依据二者完全相等(包括MSB);
    对于“满”的判断:如下图,由于gray码除了MSB外,具有镜像对称的特点,当读指针指向7,写指针指向8时,除了MSB,其余位皆相同,不能说它为满。因此不能单纯的只检测最高位了,在gray码上判断为满必须同时满足以下3条:

    • wptr和同步过来的rptr的MSB不相等,因为wptr必须比rptr多折回一次。
    • wptr与rptr的次高位不相等,如上图位置7和位置15,转化为二进制对应的是0111和1111,MSB不同说明多折回一次,111相同代表同一位置。
    • 剩下的其余位完全相等

    6. 同步FIFO实现

    image.png
    参考链接:https://blog.csdn.net/HengZo/article/details/49683707

    代码的核心部分主要是data_count,并且full 信号是当data_count == DATA_DEPTH时拉高,除了data_count之外,还可以同过判断r_ptr和w_ptr两个指针是否相等来判断空,满信号
    ssign empty = (w_ptr == r_ptr) ? 1 : 0;
    assign full = (w_ptr[2:0] == r_ptr[2:0] && (w_ptr[3] == ~r_ptr[3])) ? 1 : 0;

    • 这里还有一个注意的点,也就是w_ptr++会越界,同步FIFO就算用data_cnt来判断空满条件,在存数据和写数据时还是应该用w_ptr来表示,如果直接用data_cnt来表示,那么设计的就是一个栈而不是FIFO了。
    module syn_fifo(
    	clk,
    	rst_n,
    	data_in,
    	w_en,
    	full,
    	data_out,
    	r_en,
    	empty);
    
    parameter DATA_WIDTH = 8;
    parameter DATA_DEPTH = 16;
    parameter ADDR_WIDTH = 4;
    
    input wire clk, rst_n;
    input wire [DATA_WIDTH-1:0] data_in;
    input wire w_en, r_en;
    
    output wire empty, full;
    output reg [DATA_WIDTH-1:0] data_out;
    
    reg [ADDR_WIDTH   : 0] data_count;
    reg [ADDR_WIDTH-1 : 0] w_ptr, r_ptr;
    reg [DATA_WIDTH-1 : 0] mem[0 : DATA_DEPTH-1];
    
    assign empty = (data_count == 'd0) ? 1 : 0;
    assign full = (data_count == DATA_DEPTH) ? 1 : 0; //data_count == DATA_DEPTH
    
    always @ (posedge clk or negedge rst_n) begin
    	if (!rst_n)
    		data_count <= 'd0;
    	else if (w_en && r_en)
    		data_count <= data_count;
    	else if (!full && w_en)
    		data_count <= data_count + 1'b1;
    	else if (!empty && r_en)
    		data_count <= data_count - 1'b1;
    	else
    		data_count <= data_count;
    end
    
    always @ (posedge clk  or negedge rst_n) begin
    	if (!rst_n)
    		mem[w_ptr] <= 'd0;
    	else if (!full && w_en)
    		mem[w_ptr] <= data_in;
    end
    
    always @ (posedge clk or negedge rst_n) begin
    	if (!rst_n)
    		w_ptr <= 'd0;
    	else if (w_en && !full)
    		w_ptr <= w_ptr + 1'b1;
    	else
    		w_ptr <= w_ptr;
    end
    
    always @ (posedge clk or negedge rst_n) begin
    	if (!rst_n)
    		r_ptr <= 'd0;
    	else if (r_en && !empty)
    		r_ptr <= r_ptr + 1'b1;
    	else
    		r_ptr <= r_ptr;
    end
    
    always @ (posedge clk  or negedge rst_n) begin
    	if (!rst_n)
    		data_out <= 'd0;
    	else if (!empty && r_en)
    		data_out <= mem[r_ptr];
    end
    
    endmodule
    

    7. 异步FIFO实现

    设计难点:

    1. 跨时钟域数据比较,需要用到同步器,减少亚稳态的传递
    2. 用到gray码,进一步减少亚稳态的产生
    3. gray码相等信号的比较 空:两个gray码相等 满:高两位相反,其余位相同。
    4. 指针计数需要比ADDR的位宽多一位,这一点和同步FIFO的设计是一样的。
      image.png

    https://www.cnblogs.com/BitArt/archive/2013/04/10/3010073.html

    module asyn_fifo(
    	clk_w,
    	clk_r,
    	rst_n,
    	r_en,
    	w_en,
    	data_in,
    	data_out,
    	full,
    	empty
    	);
    
    input wire clk_r, clk_w, rst_n;
    
    input wire r_en, w_en;
    
    input wire [7:0] data_in;
    
    output wire full, empty;
    output reg [7:0] data_out;
    
    parameter DATA_DEPTH = 8;
    parameter DATA_WIDTH = 8;
    
    parameter ADDR_WIDTH = 3;
    
    reg [3:0] w_ptr, r_ptr;
    
    reg [7:0] mem[DATA_DEPTH-1 : 0];
    
    
    always @ (posedge clk_w or negedge rst_n) begin
    	if (~rst_n)
    		w_ptr <= 'd0;
    	else if (w_en && !full)
    		w_ptr <= w_ptr + 1'b1;
    	else
    		w_ptr <= w_ptr;
    end
    wire [3:0] w_ptr_gray, r_ptr_gray;
    
    assign w_ptr_gray = w_ptr ^ (w_ptr >> 1);
    assign r_ptr_gray = r_ptr ^ (r_ptr >> 1);
    
    //
    reg [ADDR_WIDTH:0] rd1_wp, rd2_wp;
    always @ (posedge clk_r or negedge rst_n) begin
    	if (!rst_n) begin
    		rd1_wp <= 'd0;
    		rd2_wp <= 'd0;
    	end else begin
    		rd1_wp <= w_ptr_gray;
    		rd2_wp <= rd1_wp;
    	end
    end
    
    assign empty = (rd2_wp == r_ptr_gray) ? 1 : 0;
    
    
    always @ (posedge clk_r or negedge rst_n) begin
    	if (~rst_n)
    		r_ptr <= 'd0;
    	else if (r_en && !empty)
    		r_ptr <= r_ptr + 1'b1;
    	else
    		r_ptr <= r_ptr;
    end
    //wire [ADDR_WIDTH:0] r_ptr_gray;
    
    assign r_ptr_gray = r_ptr ^ (r_ptr >> 1);
    
    reg [ADDR_WIDTH:0] wd1_rp, wd2_rp;
    always @ (posedge clk_w or negedge rst_n) begin
    	if (~rst_n) begin
    		wd1_rp <= 'd0;
    		wd2_rp <= 'd0;
    	end
    	else begin
    		wd1_rp <= r_ptr_gray;
    		wd2_rp <= wd1_rp;
    	end
    end
    
    assign full = ({(~wd2_rp[ADDR_WIDTH:ADDR_WIDTH-1]),wd2_rp[ADDR_WIDTH-2:0]} == w_ptr_gray) ? 1:0;
    
    always @ (posedge clk_w or negedge rst_n) begin
    	if (~rst_n)
    		mem[w_ptr] <= 'd0;
    	else if (!empty && rd_en)
    		mem[w_ptr] <= data_in;
    end
    
    always @ (posedge clk_r or negedge rst_n) begin
    	if (~rst_n)
    		data_out <= 'd0;
    	else if (!full && w_en)
    		data_out <= mem[r_ptr];
    end
    endmodule
    

    8. 对FIFO进行约束

    set_false_path
    在设计中,不需要满足setup/hold时序的数据路径需要设置成false path
    set_disable_timing:可以使库单元的时间弧(timing arc)无效
    总的来说,set_false_path 只对data path起作用, EDA 工具还会分析计算这条时序路径, 只是不报出来是否有时序违例。
    set_disable_timing 对timing arc起作用,完全不去分析这条timing arc

    9. 关于异步FIFO最小深度的计算

    FIFO仅在数据突发时才有效,不使用与连续的数据输出和输入。如果存在连续的数据流,那么所需要的FIFO大小因该是无限的。因此需要知道突发速率,突发大小,频率等,才能确定FIFO的深度。
    最小深度的计算流程

    1. 确定读时钟fr和写时钟的频率fw, 一般情况fw>fr的
    2. 根据fr和fw计算读写一次数据的周期Tr 和Tw,根据T= 1/f
    3. 根据突发写长度的大小,计算这么多数据需要写多少时间 tw = Tw*len
    4. 根据写的时间tw计算读了多少数据 n = tw/Tr
    5. FIFO的最小深度等于 len-n

    9.1 写时钟快于读时钟,写和读的过程中没有空闲周期

    image.png
    分析过程:
    写时钟周期Tw = 1000/80 ns = 12.5ns;同理读时钟周期为20ns;
    突发写长度为120个数据,写120个数据耗时120 * 12.5 = 1500ns;
    1500ns时间内读出数据1500/20ns = 75个;
    故最小FIFO深度为120 - 75 = 45;

    9.2 写时钟频率大于读时钟频率,但在读写的过程中存在空闲周期

    image.png
    分析:
    写时钟周期T_A = 12.5ns,读时钟周期为T_B = 20ns;
    两个写时钟写一个数据,也就是写一个数据需要时间2*T_A = 25ns,那么由于突发写数据个数为120个,写这么多数据需要时间120 * 25ns = 3000ns;
    4个读时钟周期读一个数据,因此读一个数据需要时间80ns,3000ns读了3000/80 = 37.5个数据(0.5不算一个数据,没读完整),约等于37个数据。
    所以,FIFO的最小深度为120 - 37 = 83;

    9.3 写时钟慢于读时钟,且读写过程中没有空闲周期

    image.png
    分析:
    这种情况下永远也不会发生数据丢失的情况;
    fifo的深度为1

    9.4 写时钟频率小于读时钟频率,但读写过程中存在空闲周期

    image.png
    分析:
    写时钟周期1000/30 ns = 100/3 ns;读时钟周期 20ns;
    写一个数据需要2个时钟,也就是200/3 ns;读一个数据需要4个时钟,也就是80 ns;
    写120个数据需要时间8000ns,这段时间内读出数据8000/80 = 100个;
    因此,FIFO的最小深度为120 - 100 = 20;

    9.5 读写时钟速率相同,且无空闲时钟

    image.png
    分析:
    如果读写时钟之间没有相位差,则不需要FIFO就可以进行读写;
    如果二者存在相位差,只需要FIFO的深度为1即可。

    9.6 读写时钟频率一致,但在读写过程中存在空闲周期

    image.png
    分析:
    两个时钟写一个数据,需要时间40ns;
    4个时钟读一个数据,需要80ns;
    由于突发长度为120,需要120*40 = 4800ns写完;这段时间读出数据个数:4800/80 = 60;
    所以,FIFO最小深度为120 - 60 = 60;

    9.7 特定条件下,最坏情况分析FIFO最小深度

    image.png
    首先,从条件可知,写频率等于读频率;
    其次,读写可以在如下限制下的任意时刻发生:
    image.png
    image.png
    为了获得更安全的FIFO深度,我们需要考虑最坏的情况,以防数据丢失;
    对于最坏的情况,写入和读取之间的数据速率之间的差异应该是最大的。 因此,对于写操作,应考虑最大数据速率,对于读操作,应考虑最小数据速率。从上表可以看出,最快的写数据速率应该为第4种情况,写操作在最小的时间内完成;
    由于突发写长度为160,所以160个时钟写160个数据;
    由于读速度为10个时钟读8个数据,因此一个数据需要10/8个时钟;
    所以160个时钟读了160*8/10 = 128个数据;
    所以FIFO的最小深度为160-128=32.

    9.8 条件拐弯抹角的给出,需要自己提取关键信息

    image.png
    假如clkA = 25MHz,则CLKB = 100MHz;

    TA= 40ns, TB = 10ns;
    en_B = 100*40 = 4000ns;占空比为1/4;
    我们认为B为写时钟,写使能时间为4000/4 = 1000ns,则突发写长度为1000/10 = 100个数据;
    在1000ns内读出数据为1000/40 = 25个数据,所以FIFO最小深度为100 - 25 = 75

    9.9 其它情况

    输入时钟频率炜250MHz,输入数据率8Gbps,输出的时钟频率200MHz,输出的数据率为5Gbps,单位时间内输入的数据总量为4Gb,在保证数据不丢失的情况下,最少需要多大的缓冲空间,并给出分析步骤。
    解析:解答1,不考虑两个包之间的背靠背的情况
    4Gb/8Gbps * 5Gbps = 2.5Gb,因此缓冲空间=4Gb-2.5Gb = 1.5Gb;
    解答2:考虑背靠背的情况
    突发长度=8G, 写应该是0440的情况,读用最慢的情况,2.5, 2.5,0.5, 2.5,因此FIFO= 8-3 = 5G

    9. Vivado FIFO IP核使用

    1. 在IP Catalog中搜索FIFO,会出现各种各样的FIFO,一般选择FIFO generator。
      image.png
    2. 点击IP之后,会出现FIFO配置的一些选项,包括Basic Native ports, flag等
      image.png
    3. 在Basic中我们可以控制FIFO的接口形式和FIFO的类型
    • FIFO 的接口分为两类,一类是 Native 接口,这类接口使用比较简单,另一类是 AXI 协议接口,这类协议口线比较多,操作相对复杂。
    • FIFO 的类型主要区别:1.读写是否使用一个时钟 2.使用何种硬件资源
      其中区别1主要是通过common clk和 independent clk来确定,也就是同步FIFO和异步FIFO
      区别2硬件资源:分为3种。BRAM:即块RAM资源,这是FPGA内嵌的一种重要的专用RAM资源,可以在读写两端使用不同的数据宽度,可以使用 ECC (一种数据校验特性),支持 First-World Fall Through ,以及支持动态错误注入。;分布式RAM:Distributed RAM,即将FPGA中的LUT用作RAM,仅支持 First-World Fall Through 功能;专用FIFO,专用FIFO会提供很小的延迟。BRAM 是一种比较重要的资源,如果设计的 FIFO 对延时不敏感,可以使用分布式的 RAM 以节约 BRAM 资源。
    1. Stand FIFO 和 First Word Fall Through的区别
    • standard FIFO 读取数据时会延迟一个周期,也即会在使能信号拉高后延迟一个周期才有数据输出,而First word fall through会和使能信号同时输出。造成这种区别的原因在于FWFT模式下,第一个写入的数据将从RAM中提前读出到数据线。
    • FIFO 的复位使用的高电平复位,如果设计中系统复位信号是低电平有效的,那么不要忘记要将系统复位电平取反后再接入 FIFO 复位电平。一般我们在同步系统设计中使用异步复位。
      image.png
    1. status flag
    • 包括 almost Full/Empty 信号,这两个信号,顾名思义,就是在 FIFO 几乎要满或者几乎要空的情况下置起,所谓的“几乎“就是指还差一个数据满或者空
    • 这个页面上还提供握手选项,但一般我们在初级设计中不会需要 FIFO 具有这种“交互”特性,实质上 AXI 协议接口也会提供握手特性。
    • 第四个页面 Data Count,顾名思义就是提供一个信号来表示当前 FIFO 中的数据总数
      image.png
    1. 在顶层文件实例化IP
    • 在IP Source中打开Instation Template目录下的veo文件,里面就有实例化的例子
      image.png
    1. 异步FIFO IP的使用
      image.png
      注意同步化synchronization stages
      image.png
      这个值用于表示FIFOempty拉低的时间长度,同时要注意FIFO的读一定要有empty控制,并且发现empty并不是一写入数据就拉低的。

    10. FIFO IP使用注意事项

    1. 如果读写位宽不一样的情况,比如写位宽8, 读位宽32,那么当写入三次是, empty信号仍然为高电平,也就意味着是读不出数据的。
    2. FIFO的复位信号是高电平有效
    3. standard FIFO 和FWFT的区别就是读的时候需要延时一个周期和不需要延时
    4. output register:嵌入式输出寄存器可用于增加性能并向宏添加流水线寄存器,主要用于改善时序情况,但是只对Standard FIFO模式有用,添加output register, 读延时会增加1个周期
    5. Valid: This signal indicates that valid data is available on the output bus (dout).因此在FWFT模式下,只要FIFO有数据,valid信号就会拉高;而在Standard FIFO模式下,只有在读信号使能之后,valid信号才会拉高
      FWTF

    Standard FIFO

    参考链接:
    https://blog.csdn.net/Reborn_Lee/article/details/100127937
    [https://hardwaregeeksblog.files.wordpress.com/2016/12/fifodepthcalculationmadeeasy2.pdf]
    https://zhuanlan.zhihu.com/p/47847664

    展开全文
  • 同步fifo的verilog实现

    2018-12-25 18:38:48
    使用verilog实现的同步fifo(先进先出单元)。可直接综合。
  • FIFO可以认为是汽车可以驶过的单向隧道。隧道的尽头是一个带门的收费站。门一旦打开,汽车便可以离开隧道。如果那扇门从未打开,而更多的汽车继续进入隧道,那么最终隧道将充满汽车。这称为FIFO溢出,通常这不是一件...


    写在前面


    正文


    FPGA/ASIC中的FIFO

    FIFO缓冲区如何用于传输数据和跨时钟域

    缩写FIFO代表 First In First Out。FIFO在FPGA和ASIC设计中无处不在,它们是基本的构建模块之一。而且它们非常方便!FIFO可用于以下任何目的:

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

    FIFO可以认为是汽车可以驶过的单向隧道。隧道的尽头是一个带门的收费站。门一旦打开,汽车便可以离开隧道。如果那扇门从未打开,而更多的汽车继续进入隧道,那么最终隧道将充满汽车。这称为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是FPGA设计人员的基本构建模块之一,对于正确理解和正确使用至关重要!


    同步FIFO的设计

    为了简单起见,本文先设计一个同步FIFO,仅带有空满标志。
    在给出同步FIFO设计之前,有必要说说同步FIFO的原理,同步FIFO的设计很有必要,它是通往异步FIFO的基础,同步FIFO中的所有原理都理解了,异步FIFO中和同步FIFO相同的东西就不必再费心思思考了,而是直接进入重点,如何控制空满!

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

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

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

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

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

    FIFO原理图
    再看一幅图:
    在这里插入图片描述
    从这幅图中我们可以得到如下信息:

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

    这几点都很重要,到后面我们慢慢体会。

    代码设计

    // Reborn Lee 
    // blog address: https://blog.csdn.net/Reborn_Lee
    module syn_fifo#(
        parameter DATA_WIDTH = 8,
        parameter DATA_DEPTH = 8
        )(
        input i_clk,
        input i_rst,
        
        //write port
        input wr_en,
        input [DATA_WIDTH - 1 : 0] wr_data,
        output wr_full,
        //read port
        input rd_en,
        output [DATA_WIDTH - 1 : 0] rd_data,
        output rd_empty
        
    );
    
        //define ram
        
        reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1];
        reg [$clog2(DATA_DEPTH) : 0] fifo_cnt = 0;
        
        reg [$clog2(DATA_DEPTH) - 1 : 0] wr_pointer = 0;
        reg [$clog2(DATA_DEPTH) - 1 : 0] rd_pointer = 0;
        
        // keep track of the  fifo counter
        always@(posedge i_clk) begin
            if(i_rst) begin
                fifo_cnt <= 0;
            end
            else begin
                if(wr_en && !rd_en) begin //wr_en is asserted and fifo is not full
                    fifo_cnt <= fifo_cnt + 1;
                end
                else if(rd_en && !wr_en) begin // rd_en is asserted and fifo is not empty
                    fifo_cnt <= fifo_cnt - 1;
                end
            end
            
        
        end
        
        //keep track of the write  pointer
        always@(posedge i_clk) begin
            if(wr_en && !wr_full) begin
                if(wr_pointer == DATA_DEPTH - 1) begin
                    wr_pointer <= 0; 
                end
                else begin
                    wr_pointer <= wr_pointer + 1;
                end
                      
            end
        
        end
        
        
        //keep track of the read pointer 
        always@(posedge i_clk) begin
            if(rd_en && !rd_empty) begin
                if(rd_pointer == DATA_DEPTH - 1) begin
                    rd_pointer <= 0;
                end
                else begin
                    rd_pointer <= rd_pointer + 1;
                end
            end
        
        end
        
        //write data into fifo when wr_en is asserted
        always@(posedge i_clk) begin
            if(wr_en) begin
                fifo_buffer[wr_pointer] <= wr_data;
            end
        end
        
        //read data from fifo when rd_en is asserted
        //assign rd_data = (rd_en)?fifo_buffer[rd_pointer]: 'bz;
        assign rd_data = fifo_buffer[rd_pointer];
        
        assign wr_full = (fifo_cnt == DATA_DEPTH)? 1 : 0;
        assign rd_empty = (fifo_cnt == 0) ? 1 : 0;
        
    endmodule
    

    测试平台:

    `timescale 1ns / 1ps
    
    // Engineer: Reborn Lee
    // Module Name: syn_fifo_tb
    //https://blog.csdn.net/Reborn_Lee
    //
    
    
    module syn_fifo_tb(
    
        );
    
    	parameter DATA_WIDTH = 8;
        parameter DATA_DEPTH = 8;
      
        reg i_clk;
        reg i_rst;
        
        //write port
        reg wr_en;
        reg [DATA_WIDTH - 1 : 0] wr_data;
        wire wr_full;
        //read port
        reg rd_en;
        wire [DATA_WIDTH - 1 : 0] rd_data;
        wire rd_empty;
    
    
        initial begin
        	i_clk = 0;
        	forever begin
        		#5 i_clk = ~i_clk;
        	end
        end
    
        initial begin
        	
        	i_rst = 1;
        	wr_en = 0;
        	rd_en = 0;
    
        	@(negedge i_clk) i_rst = 0;
        	@(negedge i_clk) wr_en = 1;
        	wr_data = $random;
    
        	repeat(3) begin
        		@(negedge i_clk)
        		wr_data = $random;	
        	end
        	
        	
    
        	@(negedge i_clk)
        	wr_en = 0;
        	rd_en = 1;
    
        	repeat(3) begin
        		@(negedge i_clk);	
        	end
    
        	@(negedge i_clk)
        	rd_en = 0;
        	wr_en = 1;
        	wr_data = $random;
    
        	repeat(7) begin   		
        		@(negedge i_clk)
        		wr_data = $random;
        	end
    
        	#20 $finish;
    
    
        end
    
    
    	syn_fifo #(.DATA_WIDTH(DATA_WIDTH),.DATA_DEPTH(DATA_DEPTH))
    	inst_syn_fifo
    		(
    			.i_clk    (i_clk),
    			.i_rst    (i_rst),
    			.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
    
    
    

    仿真波形

    先看最直观的信息:
    在这里插入图片描述

    写入FIFO的数据依次是24,81, 09, 63,读出的数据(从读使能有效开始读)24,81,09,63,读完之后的一个时钟,不在读了,空信号拉高,表示读空了。如下图用箭头以及数字示意:

    在这里插入图片描述
    我们再看看是否写入FIFO的数据依次是24,81,09,63:

    在这里插入图片描述
    确实如此!

    再看看读数据的情况:

    在这里插入图片描述
    也确实是从0指针开始读的。

    至于,这个FIFO的某些地方值为什么是红色的,是因为没有给FIFO的存储空间赋初值,在仿真时候显示红色,未知而已,在实际的FPGA或者ASIC中,实际是随机值。

    我们再来看看设计代码中的写指针,初值为0,在写使能有效时,当时钟上升沿到达时,写指针加1:

    //keep track of the write  pointer
        always@(posedge i_clk) begin
            if(wr_en && !wr_full) begin
                if(wr_pointer == DATA_DEPTH - 1) begin
                    wr_pointer <= 0; 
                end
                else begin
                    wr_pointer <= wr_pointer + 1;
                end
                      
            end
        
        end
    

    而此时,也就是写使能有效,且时钟上升沿到来时,又对FIFO进行写操作:

    //write data into fifo when wr_en is asserted
        always@(posedge i_clk) begin
            if(wr_en) begin
                fifo_buffer[wr_pointer] <= wr_data;
            end
        end
    

    我想提醒的是,此时写入的FIFO空间地址,应该是指针加1之前的地址值(指针值),这是因为使用了非阻塞赋值,指针即使加1了,在此刻时钟上升沿写FIFO时,加1的指针还未生效,这就是非阻塞赋值的作用了。
    你不信吗?
    按照上面说的,在仿真中,指针的值应该比写入FIFO中的地址值大1.
    看看仿真图:

    在这里插入图片描述
    这本不是问题,可还是要提出来,就怕有的同学会迷!

    刚才的Verilog设计对于写采用的是同步写,但是对于读却采用的是异步读,如果我们采用同步读呢?就和FIFO写数据达成统一,我们可以猜测(其实内心很确信),读指针值超越读数据地址1,也就是说,如果读地址在时钟上升沿为2的话,其实当前读的值为1地址的值。
    那我们测试一下吧,先令读改为同步读:

    // assign rd_data = fifo_buffer[rd_pointer];
    
        always@(posedge i_clk) begin
        	if(rd_en) begin
        		rd_data <= fifo_buffer[rd_pointer];
        	end
        end
    

    然后观测仿真结果:

    在这里插入图片描述
    数据的存取倒是没有问题,先进先出。
    但可以看到的另一个情况是,1地址时,存的数据和取得数据其实都是0地址的数据。继续看仿真图:

    在这里插入图片描述
    可见,此时的地址虽然变成了1,但对于FIFO来说,并未生效,存以及取仍然按照前一个地址来存或取,这是非阻塞赋值的原因。
    但这些细节问题,并不会影响我们使用FIFO,我们使用FIFO的时候不必关注这些,我们只需要只要我们存取都是先进先出即可。
    封装成FIFO模块,用就是了!不过对于数字设计师来说,这种细节你还是要知道的,要不然用FIFO是没有灵魂的,还有就是如果面试或者笔试让你写一个FIFO你该怎么办呢?
    既然是设计,你肯定要知道细节了,因为是你设计的细节。

    VHDL版设计

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
     
    entity module_fifo_regs_no_flags is
      generic (
        g_WIDTH : natural := 8;
        g_DEPTH : integer := 32
        );
      port (
        i_rst_sync : in std_logic;
        i_clk      : in std_logic;
     
        -- FIFO Write Interface
        i_wr_en   : in  std_logic;
        i_wr_data : in  std_logic_vector(g_WIDTH-1 downto 0);
        o_full    : out std_logic;
     
        -- FIFO Read Interface
        i_rd_en   : in  std_logic;
        o_rd_data : out std_logic_vector(g_WIDTH-1 downto 0);
        o_empty   : out std_logic
        );
    end module_fifo_regs_no_flags;
     
    architecture rtl of module_fifo_regs_no_flags is
     
      type t_FIFO_DATA is array (0 to g_DEPTH-1) of std_logic_vector(g_WIDTH-1 downto 0);
      signal r_FIFO_DATA : t_FIFO_DATA := (others => (others => '0'));
     
      signal r_WR_INDEX   : integer range 0 to g_DEPTH-1 := 0;
      signal r_RD_INDEX   : integer range 0 to g_DEPTH-1 := 0;
     
      -- # Words in FIFO, has extra range to allow for assert conditions
      signal r_FIFO_COUNT : integer range -1 to g_DEPTH+1 := 0;
     
      signal w_FULL  : std_logic;
      signal w_EMPTY : std_logic;
       
    begin
     
      p_CONTROL : process (i_clk) is
      begin
        if rising_edge(i_clk) then
          if i_rst_sync = '1' then
            r_FIFO_COUNT <= 0;
            r_WR_INDEX   <= 0;
            r_RD_INDEX   <= 0;
          else
     
            -- Keeps track of the total number of words in the FIFO
            if (i_wr_en = '1' and i_rd_en = '0') then
              r_FIFO_COUNT <= r_FIFO_COUNT + 1;
            elsif (i_wr_en = '0' and i_rd_en = '1') then
              r_FIFO_COUNT <= r_FIFO_COUNT - 1;
            end if;
     
            -- Keeps track of the write index (and controls roll-over)
            if (i_wr_en = '1' and w_FULL = '0') then
              if r_WR_INDEX = g_DEPTH-1 then
                r_WR_INDEX <= 0;
              else
                r_WR_INDEX <= r_WR_INDEX + 1;
              end if;
            end if;
     
            -- Keeps track of the read index (and controls roll-over)        
            if (i_rd_en = '1' and w_EMPTY = '0') then
              if r_RD_INDEX = g_DEPTH-1 then
                r_RD_INDEX <= 0;
              else
                r_RD_INDEX <= r_RD_INDEX + 1;
              end if;
            end if;
     
            -- Registers the input data when there is a write
            if i_wr_en = '1' then
              r_FIFO_DATA(r_WR_INDEX) <= i_wr_data;
            end if;
             
          end if;                           -- sync reset
        end if;                             -- rising_edge(i_clk)
      end process p_CONTROL;
       
      o_rd_data <= r_FIFO_DATA(r_RD_INDEX);
     
      w_FULL  <= '1' when r_FIFO_COUNT = g_DEPTH else '0';
      w_EMPTY <= '1' when r_FIFO_COUNT = 0       else '0';
     
      o_full  <= w_FULL;
      o_empty <= w_EMPTY;
       
      -- ASSERTION LOGIC - Not synthesized
      -- synthesis translate_off
     
      p_ASSERT : process (i_clk) is
      begin
        if rising_edge(i_clk) then
          if i_wr_en = '1' and w_FULL = '1' then
            report "ASSERT FAILURE - MODULE_REGISTER_FIFO: FIFO IS FULL AND BEING WRITTEN " severity failure;
          end if;
     
          if i_rd_en = '1' and w_EMPTY = '1' then
            report "ASSERT FAILURE - MODULE_REGISTER_FIFO: FIFO IS EMPTY AND BEING READ " severity failure;
          end if;
        end if;
      end process p_ASSERT;
     
      -- synthesis translate_on
    end rtl;
    
    

    仿真就算了,和Verilog版一致也可。

    带有几乎空almost empty 以及几乎满 almost full的同步FIFO
    带有几乎空以及几乎满的同步FIFO设计也不是什么难事,我们只需要设置两个参数,几乎空以及几乎满的阈值,最后再将读写计数器和阈值对比,如果小于几乎空阈值,则几乎空标志有效;如果大于几乎满阈值,则几乎满标志有效。
    设计十分简单,就在上述代码基础上添加几条,这里不再赘余。


    参考资料


    交个朋友

    展开全文
  • FPGA同步FIFO代码 测试文件 仿真结果 verilog代码 FPGA信号延迟 信号输出对齐
  • 所谓FIFO就是先进先出的意思,通俗的说,就像数据从一个管道的一端进去而从管道的另一端输出。FIFO是一个没有地址端口的存储器,它依靠内部写指针(指定写数据的位置)和读指针(指定读数据的位置)来进行数据的存储...
  • 文章基于CycloneIV+STM32设计了一种新型的无线分布式采集系统,实现了数据的高可靠和同步传输。设计主要由3大部分组成:编码器、译码器、无线收发电台。在对编码器、译码器同步校准后,对待发送数据进行卷积编码,并...
  • 同步FIFO与异步FIFO

    千次阅读 2019-11-28 18:07:36
    FIFO一般用于不同时钟域之间的数据传递,FIFO根据FIFO工作的时钟域,可以分为同步FIFO与异步FIFO:同步FIFO是读时钟与写时钟为同一时钟,在时钟上升沿同时发生读写操作。异步FIFO是读写时钟为不同时钟,读写时钟彼此...

    FIFO一般用于不同时钟域之间的数据传递,FIFO根据FIFO工作的时钟域,可以分为同步FIFO与异步FIFO:同步FIFO是读时钟与写时钟为同一时钟,在时钟上升沿同时发生读写操作。异步FIFO是读写时钟为不同时钟,读写时钟彼此相互独立。
    设计关键点:区分读空和写满这两种不同的状态,即FIFO在被写满后不能再写入,从而覆盖原有数据;再被读空后不能再进行读操作,防止读取无效数据。

    将FIFO抽象为环形数组,并用两个指针,即读指针与写指针控制FIFO的读写;当fifo_rd完成一次读操作,fifo_rd加1;当fifo_wr完成一次写操作,fifo_wr加1。
    由于再写即满的状态与再读即空的状态不同,可以用指针的状态表示。
    一、同步FIFO 的RTL代码以及tb文件
    所设计的FIFO深度为16.
    其verilog代码如下:

    module sync_fifo(
    	input clk,
    	input rst_n,
    	input rd,
    	input wr,
    	input [7:0] data_in,
    	output reg [7:0] data_out,
    	output reg  full,
    	output reg  empty);
    reg [3:0] rp;
    reg [3:0] wp;
    reg [7:0] memory [15:0];
    //产生读指针和写指针的地址,以及根据写指针将数据写入fifo中,根据读指针将数据从fifo中读出
    always@(posedge clk or negedge rst_n)
    begin
    	if(!rst_n)
    	begin
    		data_out<=8'h00;
    		full<=0;
    		empty<=0;
    		rp<=4'b0000;
    		wp<=4'b0000;	
    	end
    	else if(wr&&!full)
    	begin
    		memory[wp]<=data_in;
    		wp<=wp+1'b1;
    	end
    	else if(rd&&!empty)
    	begin
    		data_out<=memory[rp];
    		rp<=rp+1'b1;
    	end
    	else
    	begin
    		data_out<=data_out;
    		wp<=wp;
    		rp<=rp;
    	end
    end
    //产生读空信号
    always@(posedge clk)
    begin
    	if(rd&&(rp==wp-1||rp==4'b1111&&4'b0000==wp))
    		empty<=1;
    	else 
    		empty<=0;
    end
    //产生写满信号
    always@(posedge clk)
    begin
    	if(wr&&(wp==rp-1||wp==4'b1111&&4'b0000==rp))
    		full<=1;
    	else
    		full<=0;
    end
    endmodule
    

    随后的tb文件如下:

    module tb();
    reg clk;
    reg rst_n;
    reg rd;
    reg wr;
    reg [7:0] data_in;
    wire [7:0] data_out;
    wire full;
    wire empty;
    
    sync_fifo u1(
    	.clk(clk),
    	.rst_n(rst_n),
    	.rd(rd),
    	.wr(wr),
    	.data_in(data_in),
    	.data_out(data_out),
    	.full(full),
    	.empty(empty));
    
    parameter cycle=20;
    initial
    begin
    	clk=0;
    forever #(cycle/2) clk=~clk;
    end
    
    initial
    begin
    	rst_n=0;
    	#40 rst_n=1;
    end
    
    initial
    begin
    integer delay1,delay2,i,j,k;
    delay1=20*(0+{$random}%(3-0+1));//0-60
    delay2=20*(0+{$random}%(2-0+1));//0-40
    #20;
    	for(i=0;i<=10;i=i+1)
    	begin
    		#delay1 rd=1;
    		#delay2 rd=0;
    	end
    	for(j=0;j<=10;j=j+1)
    	begin
    		#delay1 wr=0;
    		#delay2 wr=1;
    	end
    	for(k=0;k<=10;k=k+1)
    	begin
    	#delay2	data_in={$random}%(65);
    	#delay1 data_in={$random}%(65);
    	end
    end
    endmodule
    

    这次使用random没有产生随机数,未查明原因。。。(待续)
    在这里插入图片描述
    二、异步FIFO的RTL代码以及tb文件
    由于异步FIFO的读写时钟不一样,(采用两级寄存器同步+格雷码 ->消除亚稳态)
    1、采用两级寄存器同步:正如《硬件架构的艺术》中所提到的跨时钟域消除亚稳态的处理(通过多级同步器),降低亚稳态发生的概率。
    在这里插入图片描述
    2、格雷码:将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出问题(这是由于二进制计数器可能所有位都会发生变化,在同一个时钟沿同步多个信号的变化会产生亚稳态的问题。而使用格雷码只有一位发生变化,因此在两个时钟域间同步多个位不会产生亚稳态问题,所以需要一个二进制到格雷码的转换电路,先将地址值转换成格雷码,然后再将格雷码同步到另一个时钟域进行对比,作为读空与写满的状态检测。
    而格雷码与二进制码的相互转换:
    (1)最高位保留
    (2)其他各位进行相邻异或
    或者用另外一种方式来表述:
    二进制数与(二进制数右移1位后,MSB补0)的异或;
    即verilog代码的实现:
    assign gray_code=(bin_code>>1) ^ bin_code;
    判断读空与写满的原理:
    将格雷码转换成二进制码产生空满标志:指针的位宽为N,只能覆盖2^N地址。在两个指针相等时,有两种情况,FIFO可能处于空状态也可能处于满状态,所以需要额外的一bit来表示。当写或读转完一圈时,最高位翻转一次。因此,当读写指针最高位不同时,FIFO为写满状态,相同时为读空状态。
    直接通过格雷码产生空满标志
    判断读空时:需要读时钟域的格雷码rgray_next和被同步到读时钟域的写指针wp每一位都相同。
    判断写满时:需要写时钟域的格雷码wgray_next和被同步到写时钟域的读指针rp高两位不同,其余各位完全相同。
    异步FIFO的深度为16。
    其verilog代码如下:

    module asyn #(parameter depth=16,addr_width=4,data_width=8)(
    	input wclk,
    	input wrst_n,
    	input wr,
    	input rclk,
    	input rrst_n,
    	input rd,
    	input [data_width-1:0] data_in,
    	output reg full,
    	output reg empty,
    	output reg [data_width-1:0] data_out);
    reg [data_width-1:0] memory [depth-1:0];
    reg [addr_width-1:0] wp,rp;
    wire [addr_width-1:0] wgray1,rgray1;
    reg [addr_width-1:0] wgray2,rgray2;
    reg [addr_width-1:0] wgray_next,rgray_next;
    
    always@(posedge wclk or negedge wrst_n)
    begin
    	if(!wrst_n)
    		wp<=0;
    	else if(!full&&wr)
    	begin
    		memory[wp]<=data_in;
       		wp<=wp+1'b1;
    	end
    end
    
    always@(posedge rclk or negedge rrst_n)
    begin
    	if(!rrst_n)
    		rp<=0;
    	else if(!empty&&rd)
    	begin
    		rp<=rp+1'b1;
    		data_out<=memory[rp];
    	end
    end
    
    assign wgray1=(wp>>1)^wp;
    assign rgray1=(rd>>1)^rd;
    
    always@(posedge rclk or negedge rrst_n)
    begin
    	if(!rrst_n)
    	begin
    		rgray2<=0;
    		rgray_next<=0;
    	end
    	else
    	begin
    		rgray2<=rgray1;
    		rgray_next<=rgray2;
    	end
    end
    
    always@(posedge wclk or negedge wrst_n)
    begin
    	if(!wrst_n)
    	begin
    		wgray2<=0;
    		wgray_next<=0;
    	end
    	else 
    	begin
    		wgray2<=wgray1;
    		wgray_next<=wgray2;
    	end
    end
    
    always@(posedge wclk or negedge wrst_n)
    begin
    	if(!wrst_n)
    	begin
    		full<=0;
    	end
    	else if(wgray_next[addr_width-1:addr_width-2]==~wgray1[addr_width-1:addr_width-2]&&wgray_next[addr_width-3:0]==wgray1[addr_width-3:0])
    	begin
    		full<=1;
    	end
    	else
    		full<=0;
    end
    
    always@(posedge rclk or negedge rrst_n)
    begin
    	if(!rrst_n)
    		empty<=0;
    	else if(rgray_next[addr_width-1:0]==rgray1[addr_width-1:0])
    		empty<=1;
    	else
    		empty<=0;
    end
    endmodule
    		
    		
    

    tb如下:

    module tb #(parameter data_width=8)();
    reg wclk;
    reg wrst_n;
    reg wr;
    reg rclk;
    reg rrst_n;
    reg rd;
    reg [data_width-1:0] data_in;
    wire full;
    wire empty;
    wire [data_width-1:0] data_out;
    
    asyn u1(.wclk(wclk),
    	.wrst_n(wrst_n),
    	.wr(wr),
    	.rclk(rclk),
    	.rrst_n(rrst_n),
    	.rd(rd),
    	.data_in(data_in),
    	.full(full),
    	.empty(empty),
    	.data_out(data_out));
    
    parameter wperiod=20;
    parameter rperiod=40;
    
    initial
    begin
    	wclk=0;
    forever #(wperiod/2) wclk=~wclk;
    end
    
    initial
    begin 
    	rclk=0;
    forever #(rperiod/2) rclk=~rclk;
    end
    
    initial
    begin
    	wrst_n=0;
    	rrst_n=0;
    	#(80);
           	wrst_n=1;
    	rrst_n=1;
    	#(80);
    	wr=1;
    	rd=0;
    	data_in=2;
    	#(80);
    	wr=0;
    	rd=1;
    end
    endmodule
    
    

    波形如图所示:
    在这里插入图片描述

    展开全文
  • 基于verilog的同步FIFO设计

    千次阅读 2019-08-04 16:25:49
    基于verilog的同步FIFO设计 工具 vivado2016.2 同步fifo FIFO的英文全称为first in first out,顾名思义就是先进先出的意思。fifo又分为同步fifo与异步fifo,fifo通常作为不同时钟域之间的数据传递,以及不同数据...
  • 手写同步FIFO

    2021-03-09 08:18:41
    在逻辑设计的时候,尤其是 FPGA 设计,使用 FIFO 一般有两个方法,第一个方法是直接调用官方的FIFO IP,另外一个方法是自己设计 FIFO 。直接调用 FIFO IP使用非常简单,也不需要知道FIFO 内部的控制逻辑。但是在 IC ...
  • 同步FIFO与异步FIFO的基本原理

    千次阅读 2020-11-18 10:49:22
    FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,他与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据, 其数据地址由内部读写指针...
  • 然而在某些应用,例如在某数据采集和处理系统中,需要通过同步FIFO来连接8位A/D和16位数据总线的MCU,但是由于目前同步FIFO器件的输入与输出数据总线宽度相等,不能满足这种应用,因此通常采用输入与输出数据总线...
  • 【verilog】同步FIFO与异步FIFO

    千次阅读 2019-08-22 19:34:37
    一、同步FIFO FIFO表示先进先出的意思。是基于RAM的存储模块,一般多是用于缓冲数据,令模块独立,调用方便、随性。学习FIFO最重要的如何建立先进先出的机制。 图1:先用一个简单的示意图说明: 图1 左边有写入使能...
  • 同步fifo与异步fifo

    2019-05-29 18:29:00
     FIFO的分类根据FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO。 同步FIFO是指读时钟和写时钟为同一个时钟。在时钟沿来临时同时发生读写操作。异步FIFO是指读写时钟不一致,读写时钟是互相独立的。  FIFO...
  • FPGA 同步FIFO设计

    2015-07-19 16:48:35
    因为该设计为基本同步FIFO建模设计,在设计时仅考虑了FIFO的基本操作,该设计仍存在问题,像如在同步FIFO写操作时Full_Sig和Empty_Sig信号的同时变化问题,因区别于异步FIFO,两个信号不可同时变化.
  • 基于VHDL语言的同步FIFO设计,附有TESTBENCH文件和Modelsim仿真脚本

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 67,463
精华内容 26,985
关键字:

同步fifo