【Verilog】同步FIFO原理及verilog实现(参数化)

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

        旨在学习理解,项目中还是用成熟IP靠谱~


目录

一、FIFO原理

二、同步FIFO设计

2.1 位宽和深度

2.2 空、满标志

2.3 FIFO计数

2.4 ram模型

2.5 读/写操作

三、​​​​​​​verilog代码

四、仿真验证

后记


一、FIFO原理

FIFO( First Input First Output)是指先进先出。模型如下:

【Verilog】同步FIFO原理及verilog实现(参数化)

 

        FIFO存储器是系统的缓冲环节,如果没有FIFO存储器,整个系统就不可能正常工作,它主要有几方面的功能:

        1)对连续的数据流进行缓存,防止在进机和存储操作时丢失数据;

        2)数据集中起来进行进机和存储,可避免频繁的总线操作,减轻CPU的负担;

        3)允许系统进行DMA操作,提高数据的传输速度。这是至关重要的一点,如果不采用DMA操作,数据传输将达不到传输要求,而且大大增加CPU的负担,无法同时完成数据的存储工作。

        根据FIFO工作的时钟域分为同步/异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟在时钟沿来临时同时发生读写。异步FIFO读写时钟不一致,读写相互独立。所以存在跨时钟域的处理。

        本文先考虑同步FIFO设计

        为了定位读取和写入的位置需要进行读写指针的设置。

        读指针:总是指向下一个将要读取的单元,复位时指向第一个单元(编号为0)。

        写指针:总是指向当前要被读出的数据,复位时指向第一个单元(编号为0)。

        而FIFO设计中最重要的则是空(Empty)、满(Full)信号的判断。

        当第一次读写指针相等时,表明FIFO为空,这种情况发生在复位操作时或者当读指针读出FIFO中最后一个字 后,追赶上写指针时,此时读空信号有效。

        当读写指针再次相等时,表明FIFO为满,这种情况发生在,当写指针转了一圈折回来(wrapped around)又追上了读指针。

        两种方式都是以读写指针相等作为判断标志,所以我们需要寻找其它的方法进行判断。

        在同步FIFO中,我们可以很容易的使用计数来判断FIFO中还剩下多少可读的数据,从而可以判断空、满。

        下面我们开始按步骤进行同步FIFO设计。

二、同步FIFO设计

2.1 位宽和深度

        位宽 WIDTH:表示FIFO里面每个数据的大小

        深度 DEPTH:表示FIFO能存多少个数据

        本文全部采用参数化设计,另外将空、满也设计为参数化:

parameter WIDTH = 16;

parameter DEPTH = 1024;

parameter PROG_EMPTY = 100;

parameter PROG_FULL = 800;

2.2空、满标志

  • 空信号empty

        下一次读地址 = 写地址,且只执行读操作;

  • 满信号full

        下一次写地址 = 读地址,且只执行写操作;

2.3FIFO计数

        fifo_cnt :  只写不读,fifo_cnt ++; 只读不写,fifo_cnt --;其余情况保持不变。

2.4ram模型

        verilog 2001标准支持多维数组,所有直接使用二维数组来定义memory数据类型:

reg [WIDTH-1 : 0]  ram  [DEPTH-1 : 0];

2.5读/写操作

  1. 读操作 : 读使能 && 非空,才能成功完成一次读操作:读地址+1,且从ram 中读出数据,给到输出。
  2. 写操作 : 写使能 && 非满,才能成功完成一次写操作:写地址+1,且将输入数据写入ram。

​​​​​​​​​​​​​​三、​​​​​​​verilog代码

        

module syn_fifo#(
	parameter 		WIDTH		=	16,
	parameter 		DEPTH		=	1024,
	parameter 		ADDR_WIDTH	=	clogb2(DEPTH),
	parameter 		PROG_EMPTY	=	100,
	parameter 		PROG_FULL	=	800
)(
	input 			sys_clk,
	input 			sys_rst,
	input			[WIDTH-1:0]din,
	input 			wr_en,
	input 			rd_en,
	output reg		[WIDTH-1:0]dout,
	output reg 		full,
	output reg 		empty,
	output reg 		prog_full,
	output reg 		prog_empty,
	output reg		[ADDR_WIDTH-1:0]fifo_cnt
);
//==========================================================\
//========== Define Parameter and Internal signals =========
//==========================================================/
reg		[WIDTH-1:0]			ram[DEPTH-1:0];
reg		[ADDR_WIDTH-1:0]	wr_addr;
reg		[ADDR_WIDTH-1:0]	rd_addr;
//==========================================================\
//===================== Main  Code =========================
//==========================================================/
function integer clogb2;
	input[31:0]value;
	begin
		value=value-1;
		for(clogb2=0;value>0;clogb2=clogb2+1)
			value=value>>1;
	end
endfunction
//read
always@(posedge sys_clk or posedge sys_rst)begin 
	if(sys_rst)
		rd_addr		<=		{ADDR_WIDTH{1'b0}};
	else if(rd_en && !empty)begin
		rd_addr		<=		rd_addr+1'd1;
		dout		<=		ram[rd_addr];
		end
	else begin
		rd_addr		<=		rd_addr; 
		dout		<=		dout;
		end
end
//write
always@(posedge sys_clk or posedge sys_rst)begin 
	if(sys_rst)
		wr_addr		<=		{ADDR_WIDTH{1'b0}};
	else if(wr_en && !full) begin
		wr_addr		<=		wr_addr+1'd1;
		ram[wr_addr]<=		din;
		end
	else
		wr_addr		<=		wr_addr;
end					
//fifo_cnt
always@(posedge sys_clk or posedge sys_rst)begin 
	if(sys_rst)
		fifo_cnt	<=		{ADDR_WIDTH{1'b0}};
	else if(wr_en && !full && !rd_en)
		fifo_cnt	<=		fifo_cnt + 1'd1;
	else if(rd_en && !empty && !wr_en)
		fifo_cnt	<=		fifo_cnt - 1'd1;	
	else 
		fifo_cnt	<=		fifo_cnt;
end
//empty 
always@(posedge sys_clk or posedge sys_rst)begin
	if(sys_rst)
		empty	<=	1'b1;//reset:1
	else
		empty	<=	(!wr_en && (fifo_cnt[ADDR_WIDTH-1:1] == 'b0))&&((fifo_cnt[0] == 1'b0) || rd_en);
end
//full
always@(posedge sys_clk or posedge sys_rst)begin
	if(sys_rst)
		full	<=	1'b1;//reset:1
	else
		full	<=	(!rd_en	&&	(fifo_cnt[ADDR_WIDTH-1:1]=={(ADDR_WIDTH-1){1'b1}})) && ((fifo_cnt[0] == 1'b1) || wr_en);
end
//prog_full
always@(posedge sys_clk or posedge sys_rst)begin
	if(sys_rst)	
		prog_full<=	1'b1;//reset:1
	else if(fifo_cnt > PROG_FULL)
		prog_full<=	1'b1;
	else
		prog_full<=	1'b0;
end
//prog_empty
always@(posedge sys_clk or posedge sys_rst)begin 
	if(sys_rst)
		prog_empty<=1'b1;//reset:1
	else if(fifo_cnt < PROG_EMPTY)
		prog_empty<=1'b1;
	else
		prog_empty<=1'b0;
end
endmodule

四、仿真验证

        对上面设计的FIFO进行写入/读出操作。

        为了验证程序健壮性,测试会进行写溢出和读空。

test  bench

`timescale 1ns/1ns
module tb;
parameter 			WIDTH		=	16		;
parameter 			DEPTH		=	512    	;
parameter 			ADDR_WIDTH	=	9       ;
parameter 			PROG_EMPTY	=	100     ;
parameter 			PROG_FULL	=	400     ;
reg 				sys_clk		;
reg 				sys_rst		;
reg  [WIDTH-1:0]	din    		;
reg 				wr_en  		;
reg 				rd_en  		;
reg	 [11:0]			cnt			;	
wire [WIDTH-1:0]	dout		;
wire 				full        ;
wire 				empty       ;
wire 				prog_full   ;
wire 				prog_empty  ;
wire [ADDR_WIDTH-1:0]fifo_cnt   ;

initial	begin
	sys_clk		=	0;
	sys_rst		=	1;
	#100
	sys_rst		=	0;
end
always #5	sys_clk	=	~sys_clk;

always @ (posedge sys_clk or posedge sys_rst)begin
	if(sys_rst)
		cnt			<=		'b0;
	else
		cnt			<=		cnt + 1'd1;
end
always @ (posedge sys_clk or posedge sys_rst)begin
	if(sys_rst)begin
		wr_en		<=		1'b0;
		rd_en		<=		1'b0;
		din			<=		16'b0;
		end
	else if(cnt >= 'd10 && cnt <= DEPTH + 'd40)begin
		wr_en		<=		1'b1;
		rd_en		<=		1'b0;
		din			<=		din + 1'd1;
		end
	else if((cnt >= 'd100 + DEPTH) && (cnt <= 2*DEPTH + 'd140))begin
		wr_en		<=		1'b0;
		rd_en		<=		1'b1;
		din			<=		'b0;
		end
	else begin
		wr_en		<=		1'b0;
		rd_en		<=		1'b0;
		din			<=		'b0;
		end
end


syn_fifo#(
	.WIDTH			(	WIDTH		),
	.DEPTH			(	DEPTH		),
	.ADDR_WIDTH		(	ADDR_WIDTH	),
	.PROG_EMPTY		(	PROG_EMPTY	),
	.PROG_FULL		(	PROG_FULL	)
)u_syn_fifo(
	.sys_clk		(	sys_clk		),
	.sys_rst        (	sys_rst     ),
	.din            (	din         ),
	.wr_en          (	wr_en       ),
	.rd_en          (	rd_en       ),
	.dout           (	dout        ),
	.full           (	full        ),
	.empty          (	empty       ),
	.prog_full      (	prog_full   ),
	.prog_empty     (	prog_empty  ),
	.fifo_cnt       (	fifo_cnt    )
);
endmodule

仿真结果:

写入:

【Verilog】同步FIFO原理及verilog实现(参数化)

 写满:

【Verilog】同步FIFO原理及verilog实现(参数化)

 读出:

【Verilog】同步FIFO原理及verilog实现(参数化)

 读空:

【Verilog】同步FIFO原理及verilog实现(参数化)

验证成功!

后记

问:

  1. 如何实现FWFT模式? 即读数据时不寄存输出,读使能有效的同一时钟周期,FIFO数据即出现在输出数据总线上。
  2. 异步FIFO如何实现?

咱们下期见。文章来源地址https://www.toymoban.com/news/detail-454370.html

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

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

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

相关文章

  • 手把手Verilog HDL同步Vaild-Ready握手FIFO机制

    三级目录 V-R握手FIFO机制,即是两级时钟速率不同的模块之间传递信号的机制,上游往下游传递的数据暂时缓存在FIFO中,上游和FIFO、FIFO和下游之间通过握手传递信号。即在一个FIFO模块外层套了一层握手机制。如下图: 如何用Verilog代码实现呢?我们可以这么来做, 1、先实现

    2024年02月16日
    浏览(26)
  • 【小白入门】Verilog实现异步FIFO

       之前也在CSDN上面写过两个FIFO相关的文章,不过代码看起来比较复杂,且注释也比较少,不利于新手入门。很多时候都没有耐心继续看下去。 http://t.csdn.cn/0dPX6 http://t.csdn.cn/lYvoY  因为自己本身是一个初学者,就从初学者的视角来看待并学习FIFO。 为什么选择学习FIFO? 在学

    2024年02月09日
    浏览(31)
  • 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日
    浏览(23)
  • 异步FIFO(Verilog)

    简介:        FIFO(First In First Out)是异步数据传输时经常使用的存储器。该存储器的特点是数据先进先出(后进后出)。其实,多位宽数据的异步传输问题,无论是从快时钟到慢时钟域,还是从慢时钟到快时钟域,都可以使用 FIFO 处理。异步FIFO 是指读写时钟不一致,读写

    2024年02月08日
    浏览(39)
  • FIFO的Verilog设计(三)——最小深度计算

    FIFO的设计可参考 FIFO的Verilog设计(一)——同步FIFO FPGA的Verilog设计(二)——异步FIFO 参考文献 [1]FIFO最小深度计算   在实际使用FIFO时,需要考虑FIFO的深度如何设置,如果深度设置不当,可能会出现资源浪费或者数据丢失等情况。下面将简要介绍FIFO的最小深度如何计算。

    2024年02月04日
    浏览(80)
  • FPGA的Verilog设计(二)——异步FIFO

    阅读本文前,建议先阅读下面几篇文章: 同步FIFO 二进制转格雷码的实现   在上篇文章同步FIFO中简要介绍了FIFO的基本概念以及同步FIFO的实现。本篇文章将重点介绍异步FIFO的工作原理以及硬件实现。   异步FIFO的读写时钟不同,FIFO的读写需要进行异步处理, 异步FIFO常用

    2024年02月04日
    浏览(36)
  • Verilog功能模块——读写位宽不同的异步FIFO

    FIFO系列文章目录: Verilog功能模块——异步FIFO-CSDN博客 Verilog功能模块——同步FIFO-CSDN博客 Verilog功能模块——读写位宽不同的异步FIFO-CSDN博客 Verilog功能模块——读写位宽不同的同步FIFO-CSDN博客 Verilog功能模块——标准FIFO转FWFT FIFO-CSDN博客 前面的博文已经讲了异步FIFO和同步

    2024年02月01日
    浏览(30)
  • 【FPGA】verilog语法的学习与应用 —— 位操作 | 参数化设计

    学习新语法,争做新青年 计数器实验升级,让8个LED灯每个0.5s的速率循环闪烁,流水灯ahh好久不见~ 去年光这个就把我折磨够呛。。我肉眼可见的脱发就是从那时候开始的。。在那两个月我直接掉了10斤啊喂~ (没节食、没运动、没失恋哈哈哈 产生0.5s周期的计数器 为了避免仿

    2024年02月11日
    浏览(31)
  • 【Verilog】CRC校验码生成器原理及verilog实现

    目录 一、CRC的基本原理  二、CRC生成步骤 2.1举个栗子 三、Verilog实现 四、参考资料 4.1 CRC在线计算器 CRC :Cyclic Redundancy Check循环冗余校验码         将被处理的报文比特序列当做一个二进制多项式A(x)的系数,任意一个由二进制位串组成的代码都可以和一个系数仅为‘0’和

    2024年02月11日
    浏览(25)
  • 推排序 Verilog实现原理

    推排序常常应用在操作系统的任务调度中,尝试使用硬件对堆排序进行实现,在实现的过程中不使用 function 和 tasks 语法,即真·硬件实现 也就这一个博客有介绍 堆排序的 Verilog 实现 堆排序还需要复习一遍吗? 我肯定是要的 菜鸟-堆排序 图解排序算法(三)之堆排序 可以看到,

    2023年04月19日
    浏览(19)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包