利用FPGA自带RAM资源设计同步FIFO


利用xilinx开发板自带的ram资源来构造一个同步FIFO
使用的开发板为genesys2

1、同步FIFO设计的基本原理

同步FIFO设计的基本原理为,一个读指针,一个写指针,(读写指针位宽为RAM地址位宽加1),(最高位为标志位)复位时,读写指针均指向0x00位置,FIFO为空,进行写操作时,先写入数据,写指针加1,进行读操作时,先读出数据,读指针加1。这样当写指针追上读指针(二者最高位相反,其他位均相同),表明FIFO写满。当读指针追上写指针(二者完全相同),表明FIFO为空。具体设计时需要根据时序稍作修改。

2、RAM前期准备,

一般来说FPGA例化的双口RAM,在输出时,当检测到输出使能信号时,数据并不会立即读出,而是会延时一个时钟周期读出,而一般的fifo在检测到读使能信号时,数据便会立即读出。为了解决这个问题,在双口RAM例化时下图中的Primitives Output Register不要勾选。
在这里插入图片描述

3、同步FIFO的标志信号

设计的FIFO有full,empty,al_empty,al_full这4个指示信号,可以满足一般同步FIFO的使用需求。
有的设计中full信号是当fifo中数据写满才会拉高,而我设计的FIFO,当写最后一个数据时full信号会同时拉高,这样对FIFO进行操作,比如说“FIFO未满就写”,前一种设计就可能会出错,而我的设计可以避免这种错误发生。
empty信号,当fifo读最后一个数据时,empty信号会同时拉高,这样避免了对FIFO进行操作,“非空就读”时出现错误。
almostfull 信号可以通过参数设置阈值,比如一个32深度的fifo,almostfull阈值设置为28,则当almostfull拉高后还可继续写4次。
almostempty 信号可以通过参数设置阈值,比如一个32深度的fifo,almostempty阈值设置为4,则当almostempty拉高后还可继续读4次。实际使用中不建议almostempty的值设置过小。

用questasim进行仿真结果如下所示:

在这里插入图片描述

代码如下所示:

//利用xilinx开发板自带的ram资源来构造一个同步FIFO
//使用的开发板为genesys2
//2020-7-4   Felix
`timescale   1ns/1ps
module syn_fifo_yang(
	rstn,		//复位信号,低电平有效
	clk,
	wr_en,
	din,
	rd_en,
	dout,
	empty,
	full,
	al_empty,
	al_full
);
parameter dw = 16;		//数据位宽
parameter aw = 5;		//地址宽度
parameter gr = 4;		//读的阈值,al_empty拉高后还可继续读4次
parameter gw = 28;		//写阈值,当 fifo中有28个数字,拉高
parameter N = (1<<aw);
parameter MAX = (1<<aw);
`define straix  12

input 				rstn;
input 				clk;
input 				wr_en;
input [dw-1:0]	 	din;
input 				rd_en;
output [dw-1:0] 	dout;
output 			empty;
output 			full;
output  			al_empty;
output 				al_full;

reg [aw:0] wp;					//写指针
wire [aw:0] wp_next;			//写指针的下一个状态
reg [aw:0] rp;					//读指针
wire [aw:0] rp_next;			//读指针的下一个状态
reg [aw:0] count;               //fifo中有多少个数
wire full_l;
wire empty_l;
reg full_reg;
reg empty_reg;

assign full = full_l | full_reg;
assign empty = empty_l | empty_reg;

always@(posedge clk or negedge rstn) begin
	if(!rstn)
		full_reg <= 1'b0;
	else 
		full_reg <= full_l;
end
always@(posedge clk or negedge rstn) begin
	if(!rstn)
		empty_reg <= 1'b0;
	else 
		empty_reg <= empty_l;
end
always@(posedge clk or negedge rstn) begin
	if(!rstn)
		wp <= {(aw+1){1'b0}};
	else if(wr_en & !full_reg)
		wp <= wp_next;
	else
		wp <= wp;
end
always@(posedge clk or negedge rstn) begin
	if(!rstn)
		rp <= {aw+1{1'b0}};
	else if(rd_en & !empty_reg)
		rp <= rp_next;
	else
		rp <= rp;
end
assign wp_next = wp + {{aw{1'b0}},1'b1};
assign rp_next = rp + {{aw{1'b0}},1'b1};


assign empty_l = (wp == rp_next && rd_en) ? 1:
				wp == rp ? 1:0;

assign full_l = (wp_next[aw-1:0] == rp[aw-1:0] && wp_next[aw] != rp[aw] && wr_en) ? 1:
				(wp[aw-1:0] == rp[aw-1:0] && wp[aw] != rp[aw]) ? 1: 0;
		
always@(posedge clk or negedge rstn) begin
	if(!rstn)
		count <= 'b0;
	else 
		count <= wp - rp;
end

assign	al_empty = empty | (count <= gr+1);
assign al_full = full | (count >= gw-1);

`ifdef straix

TPRAM_32_16 u_TPRAM_32_16(
   .QA					(dout),
   .AA					(rp[aw-1:0]),
   .CLKA				(clk),
   .CENA				(!rd_en),
   .AB					(wp[aw-1:0]),
   .DB					(din),
   .CLKB				(clk),
   .CENB				(!wr_en)
);
`else
	ram_32x16    u_ram_64x16(
	.clka			(clk), 
	.wea			(wr_en), 
	.addra			(wp[aw-1:0]),
	.dina			(din), 
	.clkb			(clk), 
	.addrb			(rp[aw-1:0] 
	.doutb			(dout));	
`endif

endmodule

开心快乐每一天!!!
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值