【小白入门】Verilog实现异步FIFO

这篇具有很好参考价值的文章主要介绍了【小白入门】Verilog实现异步FIFO。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

 文章来源地址https://www.toymoban.com/news/detail-483852.html

 之前也在CSDN上面写过两个FIFO相关的文章,不过代码看起来比较复杂,且注释也比较少,不利于新手入门。很多时候都没有耐心继续看下去。

http://t.csdn.cn/0dPX6

http://t.csdn.cn/lYvoY 

因为自己本身是一个初学者,就从初学者的视角来看待并学习FIFO。

为什么选择学习FIFO?

在学完双端口RAM之后看待FIFO,会觉得为什么要用FIFO呢?双端口的RAM也可以实现数据的存储与读取,读写的时钟也可以不一样,为什么不用RAM而要基于RAM来设计一个FIFO呢?

FIFO与RAM的区别是先进先出,不需要读地址和写地址。 写时钟下,将数据写入FIFO中,在读时钟下将先写入的数据先读出来,不需要向FIFO输入写地址和读地址即可完成数据的存储以及不同时钟下的读写操作,这样一听是非常方便的。在SDRAM的学习过程中,我们知道有突发长度这个东西,当突发长度为1的时候,即一个写地址对应一个写数据,这样是非常麻烦的,所以很多SDRAM如DDR3这种,都会将突发长度设置为8,即给一个首地址,后面连续读取8个数据。

【小白入门】Verilog实现异步FIFO

 

再贴一张异步FIFO的图

【小白入门】Verilog实现异步FIFO

 

在写代码之前,需要了解几个概念。

 先思考,FIFO的存储空间和RAM是一样的,就像一个n行x1列的表格,每个表格里面存放一个数据,并且对应一个地址,在读写的过程中肯定会存在表格写满的情况和读的时候里面没有数据的情况,那应该怎么判断呢?

读写同时进行时

①首先是在读的视角,如果如果读一行数据的时候,刚好也在往这一行数据里面写数据,那这个时候即可判断读空了,如果再继续向下读的话,里面就没有写进的数据,读出的数据也不是我们写进去的,就是无效的。

所以读空的判断条件是:在读时钟的视角下,写时钟同步过来的地址等于我目前正在读的地址。

【小白入门】Verilog实现异步FIFO

关于跨时钟域的问题,大家可以去搜索一下跨时钟域以及亚稳态。也可以看我的这篇文章。

http://t.csdn.cn/hvJTa

②在写的视角下, 那什么时候写满呢?因为地址是有限的嘛,当读完一个数据的时候,读对应哪个地址的数据就已经不需要了,因为我们以及读了,即读完的那个“位置”空了。所以当写完一圈,并且追上下一轮的读的时候,就代表写满了。

所以写满判断的条件是:在写的时钟下,写完一圈对应的地址,等于同步过来的读地址。

【小白入门】Verilog实现异步FIFO

 

其次在写代码的时候,还需要了解格雷码,地址是按照0000-0001-0010-xxxx这种增长的,但是在地址变化的过程中,地址中的位数会存在”跳变“,如从0001-0010这两个相邻码的时候,有两位发生了变化,这样是不好的。 具体可以参考这篇文章

http://t.csdn.cn/OiesB

以下是代码Verilog的代码

`timescale 1ns / 1ps

module asyn_fifo1
(
		input		             rst_n		,
		
		input			         wr_clk		,
		input	                 wr_en		,
		input		 [7:0]       data_in	,
		input			         rd_clk		,
		input			         rd_en		,
		
		output		             full	    ,
		output			         empty		,
		output reg	[7:0]	     data_out	
);

		reg		[7:0]    ram_mem[255:0]    	;    //定义一个位宽为8bit深度为256的双端口RAM
		
		wire	[7:0]        rd_addr	    ;
		wire	[7:0]        wr_addr		;
		reg		[8:0]    rd_addr_ptr	    ;    //格雷码需要移位运算,且判断写满信号也需要多一位
		reg		[8:0]    wr_addr_ptr	    ;    
		
		wire  	[8:0]    rd_addr_gray	    ;
		reg		[8:0]    rd_addr_gray1	    ;
		reg		[8:0]    rd_addr_gray2	    ;
		wire	[8:0]    wr_addr_gray	    ;
		reg		[8:0]    wr_addr_gray1	    ;
		reg		[8:0]    wr_addr_gray2	    ;
		
		assign	rd_addr[7:0] = rd_addr_ptr[7:0];
		assign	wr_addr[7:0] = wr_addr_ptr[7:0];
		assign	rd_addr_gray = (rd_addr_ptr>>1) ^ rd_addr_ptr;		//bin to gray
		assign	wr_addr_gray = (wr_addr_ptr>>1) ^ wr_addr_ptr;
		
		//dual port ram
		integer	i;
		always @(posedge wr_clk or negedge rst_n)        //写时钟下初始化RAM
		begin
			if(rst_n == 1'b0)
				for(i=0;i<256;i=i+1)
					ram_mem[i] <= 1'b0;
			else if(wr_en && ~full)                      //写使能且没有写满
				ram_mem[wr_addr] = data_in;
			else
				ram_mem[wr_addr] = ram_mem[wr_addr];
		end
		
		//rd_addr_ptr++ and wr_addr_ptr++
		always @(posedge rd_clk or negedge rst_n)        //读时钟下,对读地址进行操作
		begin
			if(rst_n == 1'b0)
				rd_addr_ptr <= 1'b0;
			else if(rd_en && ~empty)
				rd_addr_ptr <= rd_addr_ptr + 1'b1;
			else
				rd_addr_ptr <= rd_addr_ptr;
		end
		always @(posedge wr_clk or negedge rst_n)        //写时钟下,对写地址进行操作
		begin
			if(rst_n == 1'b0)
				wr_addr_ptr <= 1'b0;
			else if(wr_en && ~full)
				wr_addr_ptr <= wr_addr_ptr + 1'b1;
			else
				wr_addr_ptr <= wr_addr_ptr;
		end
		
		//gray and two regsiter
		always @(posedge wr_clk or negedge rst_n)       //写时钟视角下,把读时钟同步到自己的时钟下
		begin
			if(rst_n == 1'b0)begin
				rd_addr_gray1 <= 1'b0;
				rd_addr_gray2 <= 1'b0;
			end
			else begin
				rd_addr_gray1 <= rd_addr_gray;
				rd_addr_gray2 <= rd_addr_gray1;
			end
		end
		always @(posedge rd_clk or negedge rst_n)        //读时钟视角下,把写时钟同步到自己的时钟下
		begin
			if(rst_n == 1'b0)begin
				wr_addr_gray1 <= 1'b0;
				wr_addr_gray2 <= 1'b0;
			end
			else begin
				wr_addr_gray1 <= wr_addr_gray;
				wr_addr_gray2 <= wr_addr_gray1;
			end
		end
		
		//data_out
		always @(posedge rd_clk or negedge rst_n)
		begin
			if(rst_n == 1'b0)
				data_out <= 1'b0;
			else if(rd_en && ~empty)
				data_out <= ram_mem[rd_addr];
			else
				data_out <= 1'b0;
		end
		
		assign empty = (rd_addr_gray == wr_addr_gray2)?1'b1:1'b0;    //判断读没读空
		assign full = (wr_addr_gray[8:7] != rd_addr_gray2[8:7]) && (wr_addr_gray[6:0] == rd_addr_gray2[6:0]);        //判断是否写满
endmodule

 

以下是tb仿真文件代码

`timescale 1ns / 1ps


module asyn_fifo1_tb();

        reg	                       	rst_n		;
		reg			                wr_clk		;
		reg	          	            wr_en		;
		reg		 [7:0]              data_in	    ;
		reg			                rd_clk		;
		reg			                rd_en		;

		wire		                full	    ;
		wire			            empty		;
		wire     [7:0]	            data_out	;
		
asyn_fifo1    asyn_fifo1_inst
(
		.rst_n	       (rst_n)	     ,	
		.wr_clk	       (wr_clk)      ,
		.wr_en	       (wr_en) 	     ,
		.data_in       (data_in)	 ,
		.rd_clk        (rd_clk)	     ,
		.rd_en	       (rd_en) 	     ,
		.full	       (full)        ,
		.empty	       (empty)       ,
		.data_out	   (data_out)
);
		initial wr_clk = 0;
		always #10 wr_clk = ~wr_clk;        //写时钟为50MHz
		
		initial rd_clk = 0;
		always #30 rd_clk = ~rd_clk;        //读时钟频率为写时钟的1/3
		
		always @(posedge wr_clk or negedge rst_n)    //不停的向FIFO中写0-255的数据
		begin
			if(rst_n == 1'b0)
			     data_in <= 0;
			else if (wr_en)
			     data_in <= data_in+1'b1;
			 else
			      data_in <= data_in;
		  end
			  initial  begin        
			     rst_n = 0;
			     wr_en = 0;
			     rd_en = 0;
			     #200;                //时间为200ns时,允许写入
			     rst_n = 1;
			     wr_en = 1;
			     #20000;               //时间再过20000ns时,不允许写,开始读
			     wr_en = 0;
			     rd_en = 1;
			     #20000;                //时间再过20000ns时,读停止
			     rd_en=0;
			     $stop;
			  end
endmodule

波形分析

200ns时,数据开始写入FIFO中,如下图

【小白入门】Verilog实现异步FIFO 写满时,full信号拉高,后面继续不停写入,但满了,都是无效的写入。

【小白入门】Verilog实现异步FIFO 

20200ns时,开始读数据

【小白入门】Verilog实现异步FIFO 读完后,empty信号拉高,表示读空。

【小白入门】Verilog实现异步FIFO 

由于写时钟为读时钟的三倍,从整体的波形图中也可以看出,写满数据的时间是读完数据时间的1/3.

【小白入门】Verilog实现异步FIFO 

 

 

 

 

 

 

到了这里,关于【小白入门】Verilog实现异步FIFO的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • IC设计入门——异步FIFO

    在异步FIFO中,数据读取和写入操作使用不同的时钟频率。由于写入和读取时钟不同步,因此称为异步FIFO。通常,这些用于数据需要从一个时钟域传递到另一个时钟域的系统中,这通常称为“时钟域交叉”。因此,异步FIFO有助于在两个工作于不同时钟的系统之间同步数据流。

    2024年02月19日
    浏览(43)
  • 【Verilog】同步FIFO原理及verilog实现(参数化)

            旨在学习理解,项目中还是用成熟IP靠谱~ 目录 一、FIFO原理 二、同步FIFO设计 2.1 位宽和深度 2.2 空、满标志 2.3 FIFO计数 2.4 ram模型 2.5 读/写操作 三、​​​​​​​verilog代码 四、仿真验证 后记 FIFO( First Input First Output)是指先进先出。模型如下:           F

    2024年02月05日
    浏览(34)
  • <FPGA>异步FIFO的Verilg实现方法

            在上篇文章:同步FIFO的两种Verilog设计方法(计数器法、高位扩展法)中我们介绍了FIFO的基本概念,并对同步FIFO的两种实现方法进行了仿真验证。而异步FIFO因为读写时钟不一致,显然无法直接套用同步FIFO的实现方法,所以在本文我们将用Verilog实现异步FIFO的设计。

    2024年02月07日
    浏览(66)
  • Verilog基础之十四、FIFO实现

    目录 一、FIFO 1.1 定义 1.2 实现方式 1.3 实现原理   二、代码实现 三、仿真结果 3.1 复位阶段 3.2 写入阶段 3.3 读取阶段 3.4 同时读写或不读不写 四、参考资料     FIFO(First in First out)为先进先出队列,具有存储功能,可用于不同时钟域间传输数据以及不同的数据宽度进行数据

    2024年02月13日
    浏览(31)
  • 【Verilog】用双口RAM实现同步FIFO

    端口说明如下表。 双口RAM端口说明: 同步FIFO端口说明: 输入描述: 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 双口RAM和代码框架: 同步FIFO,就是我们学习其他经典计算机语言(如C语言)的数据结

    2024年02月07日
    浏览(39)
  • 同步FIFO的verilog实现(2)——高位扩展法

            在之前的文章中,我们介绍了同步FIFO的verilog的一种实现方法:计数法。其核心在于:在同步FIFO中,我们可以很容易的使用计数来判断FIFO中还剩下多少可读的数据,从而可以判断空、满。         关于计数法实现同步FIFO的详细内容,请参考:同步FIFO的verilog实现(

    2024年02月09日
    浏览(36)
  • 【FIFO】异步 FIFO 设计

    目录   写在前面 简介 传递多个异步信号 同步 FIFO 指针 异步FIFO指针 二进制 FIFO 指针注意事项 FIFO测试问题 格雷码计数器 ‑ 样式 #1 格雷码模式 格雷码计数器基础 额外的格雷码计数器注意事项 格雷码计数器 ‑ 样式 #2 处理满空情况 产生空标志 产生满标志 不同的时钟速度

    2024年02月02日
    浏览(62)
  • FPGA中FIFO的应用(二)——异步FIFO设计

    ⭐️作者简介: 小瑞同学 ,一个努力精进的 FPGA 和通信学习者。 🍎个人主页:小瑞同学的博客主页 🌻个人信条:越努力,越幸运! ⏰日期:2023.12.3 🔍来源:自学经历 📖文章内容概述:介绍了 异步FIFO 的基本工作原理和深度计算,通过仿真观察了其读写过程。 连载系列

    2024年02月03日
    浏览(44)
  • 基于异步FIFO的串口回环测试

      当涉及到串口通信的硬件设计和软件开发时,进行有效的测试是至关重要的。串口回环测试是一种常见的测试方法,用于验证串口通信的功能和稳定性。在许多应用中,为了确保串口通信的正常运行,往往需要使用缓冲区或FIFO来管理数据的传输和接收。   基于异步F

    2024年02月16日
    浏览(62)
  • Verilog同步FIFO设计

    同步FIFO(synchronous)的 写时钟和读时钟为同一个时钟,FIFO内部所有逻辑都是同步逻辑 ,常常用于交互数据缓冲。 异步FIFO:数据写入FIFO的时钟和数据读出FIFO的时钟是异步的(asynchronous) 典型同步FIFO有三部分组成: (1) FIFO写控制逻辑; (2)FIFO读控制逻辑; (3)FIFO 存储实体(

    2024年02月12日
    浏览(61)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包