利用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
开心快乐每一天!!!