FIFO对比
预读FIFO:rdata在ren当拍有效。(xilinx First-Word Fall-Through模式)
普通FIFO:rdata在ren下拍有效。(xilinx Standard模式)
实现思路
注:实现的思路各有千秋,下面是我的实现思路。
首先用简单双口RAM包装出一个Lfifo,这个Lfifo有以下关键特征:
raddr可以由外部输入的switch_addr信号来切换
Lfifo_raddr_ori_puls=Lfifo_raddr_ori+1
Lfifo_raddr = switch_addr ? Lfifo_raddr_ori_puls : Lfifo_raddr_ori;//二选一,
正是由于raddr地址的提前导致的rdata的提前出来。
其次,在Lfifo外面包一层逻辑,这层逻辑的原理:控制Lfifo_ren、switch_addr等,也产生一些状态信号。
注:下文的buf等价于图中的fifo_rdata寄存器
整个模块的输入信号是:fifo_ren
在产生读Lfifo_ren控制逻辑的角度来看:有每一拍4种可能状态
case1.Lfifo空1 & buf空1 ----此时输入fifo_ren不会生效(后级保证不会空读) 忽略此状态
case2.Lfifo空1 & buf满0 ----if(fifo_ren==1)将buf读空,buf变成空,不读Lfifo,else 保持
case3.Lfifo有数0 & buf空1 ----fifo_ren不会生效(后级保证不会空读) ,本拍内部产生预读读Lfifo,
准备取1个数放buf,数据下一拍有效,下一拍产生load。01状态会持续2拍,只有第1拍需要读。
case4.Lfifo有数0 & buf满0 ----if(fifo_ren==1) ram_raddr切换为+1(本拍就切换地址,那么Lfifo_rdata数据下拍就预取出来了)。
补充:FIFO的状态变化是有顺序的,不可能直接从case1跳变为case4,其中必然要经过case2 | case3中间状态。
仿真效果
写完后连续读
写完后间隔读
非空就读(边写边读)
文章来源:https://www.toymoban.com/news/detail-700005.html
只写1个数据,非空读
文章来源地址https://www.toymoban.com/news/detail-700005.html
代码和tb
Lfifo
//-------------------------------------------------------
//------------------little_fifo--------------------------------
//-------------------------------------------------------
module Lfifo #(//L:Little 小FIFO
parameter RAM_TYPE = "block_ram" ,
parameter WIDTH = 9 ,
parameter DEPTH = 10
)(
input clk ,
input rst ,
input Lfifo_wr ,
input [WIDTH-1:0] Lfifo_wdata ,
input Lfifo_ren ,
input switch_addr ,//1:地址+1,相当于预取
input [WIDTH-1:0] Lfifo_rdata ,
output Lfifo_empty ,
output Lfifo_aempty //FIFO只剩最后1个,用于预取情况,来判断rdata是否有效
);
reg [DEPTH-1:0] Lfifo_waddr ={DEPTH{1'b0}} ;//RAM待写入的地址
wire [DEPTH-1:0] Lfifo_raddr ;//RAM待读出的地址
reg [DEPTH-1:0] Lfifo_raddr_ori={DEPTH{1'b0}} ;//RAM待读出的地址
reg [DEPTH-1:0] Lfifo_raddr_ori_puls={DEPTH{1'b0}} ;// Lfifo_raddr_ori + 1
assign Lfifo_empty = (Lfifo_raddr_ori==Lfifo_waddr) ;
assign Lfifo_aempty = (Lfifo_raddr_ori_puls==Lfifo_waddr) ;
always @(posedge clk or posedge rst)
begin
if(rst)
Lfifo_raddr_ori_puls <=#1 {{(DEPTH-1){1'b0}},1'b1};
else if (Lfifo_ren)
Lfifo_raddr_ori_puls <=#1 Lfifo_raddr_ori_puls + 1'b1;
end
always @(posedge clk or posedge rst)
begin
if(rst)
Lfifo_raddr_ori <=#1 {DEPTH{1'b0}};
else if (Lfifo_ren)
Lfifo_raddr_ori <=#1 Lfifo_raddr_ori_puls;
end
assign Lfifo_raddr = switch_addr ? Lfifo_raddr_ori_puls : Lfifo_raddr_ori;
always @(posedge clk) if(rst) Lfifo_waddr <=#1 {(2**DEPTH){1'b0}}; else if(Lfifo_wr) Lfifo_waddr <=#1 Lfifo_waddr +1'b1 ;
SPRAM #(RAM_TYPE,WIDTH,DEPTH) u_RAM (
.clk (clk ),//input clk ,
.wen (Lfifo_wr ),//input wen ,
.waddr(Lfifo_waddr ),//input [DEPTH-1:0] waddr ,
.wdata(Lfifo_wdata ),//input [WIDTH-1:0] wdata ,
.raddr(Lfifo_raddr ),//input [DEPTH-1:0] raddr ,
.rdata(Lfifo_rdata ) //output reg [WIDTH-1:0] rdata
);
endmodule
//-------------------------------------------------------
//------------------SPRAM--------------------------------
//-------------------------------------------------------
module SPRAM #(//sample double port RAM
parameter RAM_TYPE = "block_ram" ,
parameter WIDTH = 9 ,
parameter DEPTH = 10
)(
input clk ,
input wen ,
input [DEPTH-1:0] waddr ,
input [WIDTH-1:0] wdata ,
input [DEPTH-1:0] raddr ,
output reg [WIDTH-1:0] rdata
);
(* syn_ramstyle = RAM_TYPE , cascade_height=1 *) reg [WIDTH-1:0] RAM [(2**DEPTH)-1:0] ;
always @(posedge clk) if(wen) RAM[waddr] <=#1 wdata;
always @(posedge clk) rdata <=#1 RAM[raddr];
endmodule
SYNC_FIFO
//-------------------------------------------------------
//------------------SYNC_FIFO----------------------------
//-------------------------------------------------------
module SYNC_FIFO #(//show_ahead
parameter RAM_TYPE = "block_ram" ,
parameter WIDTH = 9 ,
parameter DEPTH = 10
)(
input clk ,
input rst ,
input fifo_wr ,
input [WIDTH-1:0] fifo_wdata ,
output fifo_full ,
input fifo_ren ,
output reg [WIDTH-1:0] fifo_rdata ,
output reg fifo_empty=1 //标志fifo_rdata寄存器内的数据是否为有效
);
//数据只能存储2个位置:1.在LFIFO内 2.fifo_rdata寄存器内
reg Lfifo_ren ;
wire [WIDTH-1:0] Lfifo_rdata ;
reg Lfifo_rdata_vld ;//fifo_rdata是有效的数据,有可能是延后一拍数据,有可能是预取数据
wire Lfifo_empty ;
wire Lfifo_aempty ;
wire load_en ;
reg switch_addr ;
reg [DEPTH :0] cnt ;
always @(posedge clk or posedge rst) begin if(rst) Lfifo_rdata_vld <=#1 1'b0; else if(switch_addr&Lfifo_aempty)Lfifo_rdata_vld <=#1 1'b0 ; else Lfifo_rdata_vld <=#1 ~Lfifo_empty ;end//只剩最后1个,还预读,那就是读出无效
always @(posedge clk or posedge rst) //空 读有关
begin
if(rst)
fifo_empty <=#1 'b1 ;
else if(Lfifo_rdata_vld==0 && fifo_ren==1'b1 )
fifo_empty <=#1 1'b1 ;
else if(Lfifo_rdata_vld==1 && load_en==1'b1 )
fifo_empty <=#1 1'b0 ;
else if(Lfifo_rdata_vld==0 && load_en==1'b1 )
fifo_empty <=#1 1'b1 ;
end
assign load_en = fifo_empty | fifo_ren;//拿走1个数据,就load一次
always @(*)
begin
case({Lfifo_empty,fifo_empty})//此处是关键
2'b00 :if(fifo_ren){switch_addr,Lfifo_ren} = 2'b11;else {switch_addr,Lfifo_ren} = 2'b00;
2'b01 :if(Lfifo_rdata_vld==0){switch_addr,Lfifo_ren} = 2'b01;else {switch_addr,Lfifo_ren} = 2'b00;
2'b10 :{switch_addr,Lfifo_ren} = 2'b00;
2'b11 :{switch_addr,Lfifo_ren} = 2'b00;
default :{switch_addr,Lfifo_ren} = 2'b00;
endcase
end
always @(posedge clk ) if(load_en) fifo_rdata <=#1 Lfifo_rdata ;
always @(posedge clk or posedge rst)
begin
if(rst)
cnt <=#1 'b0;
else if((~fifo_wr) & fifo_ren)
cnt <=#1 cnt -'b1 ;
else if(fifo_wr & (~fifo_ren))
cnt <=#1 cnt + 'b1 ;
end
assign fifo_full = cnt [DEPTH] ;
Lfifo #(RAM_TYPE,WIDTH,DEPTH) u_Lfifo(//地址控制只依赖于ren
.clk (clk ),//input clk ,
.rst (rst ),//input rst ,
.Lfifo_wr (fifo_wr ),//input Lfifo_wr ,
.Lfifo_wdata(fifo_wdata ),//input [WIDTH-1:0] Lfifo_wdata ,
.Lfifo_ren (Lfifo_ren ),//input Lfifo_ren ,
.switch_addr(switch_addr ),//input switch_addr ,
.Lfifo_rdata(Lfifo_rdata ),//input [WIDTH-1:0] Lfifo_rdata ,
.Lfifo_empty(Lfifo_empty ), //output Lfifo_empty
.Lfifo_aempty(Lfifo_aempty ) //output Lfifo_empty
);
endmodule
tb
`timescale 1ns/100ps//正确
module tb ( );
parameter RAM_TYPE = "block_ram" ;
parameter WIDTH = 9 ;
parameter DEPTH = 9 ;
reg rst=1 ;
reg clk=0 ;
always clk =#10 ~clk;
reg fifo_wr = 0 ;
reg [WIDTH-1:0] fifo_wdata ;
wire fifo_full ;
reg fifo_ren =0 ;
reg [WIDTH-1:0] fifo_rdata ;
wire fifo_empty ;
initial //产生时钟
begin
rst=1 ;
#100;@(posedge clk);#1;
rst=0 ;
#100;@(posedge clk);#1;
// fifo_wr_task(en,wdata);
// fifo_rd_task(en);
//case 1 ://写连续入1个数据,非空就立马读
fifo_wr_task(1,32'h1111);
fifo_wr_task(0,32'hxxxx);
wait(fifo_empty==0);
fifo_rd_task(1);
fifo_rd_task(0);
#100;@(posedge clk);
//case 2 ://写连续入1个数据,延时几拍后再读
fifo_wr_task(1,32'h1111);
fifo_wr_task(0,32'hxxxx);
wait(fifo_empty==0);
#50;@(posedge clk);
fifo_rd_task(1);
fifo_rd_task(0);
#1000;@(posedge clk);
//case 3 ://写连续入2个数据,第1拍非空就立马读,第二拍延时一定时间读
fifo_wr_task(1,32'h1111);
fifo_wr_task(1,32'h2222);
fifo_wr_task(0,32'hxxxx);
wait(fifo_empty==0);
fifo_rd_task(1);
fifo_rd_task(0);
#50;@(posedge clk);
fifo_rd_task(1);
fifo_rd_task(0);
#100;@(posedge clk);
//case 4 ://写连续入2个数据,第1拍非空就立马读,第二拍非空就立马读
fifo_wr_task(1,32'h1111);
fifo_wr_task(1,32'h2222);
fifo_wr_task(0,32'hxxxx);
wait(fifo_empty==0);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(0);
#100;@(posedge clk);
//case 5 ://写连续入2个数据,第1拍延时一定时间,再连续读2次
fifo_wr_task(1,32'h1111);
fifo_wr_task(1,32'h2222);
fifo_wr_task(0,32'hxxxx);
wait(fifo_empty==0);
#50;@(posedge clk);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(0);
#100;@(posedge clk);
//case 6 ://写连续入2个数据,第1拍延时一定时间,再连延时一定时间读第二拍
fifo_wr_task(1,32'h1111);
fifo_wr_task(1,32'h2222);
fifo_wr_task(0,32'hxxxx);
wait(fifo_empty==0);
#50;@(posedge clk);
fifo_rd_task(1);
fifo_rd_task(0);
#50;@(posedge clk);
fifo_rd_task(1);
fifo_rd_task(0);
#1000;@(posedge clk);
//case 7 ://写连续入3个数据,非空立马读3次
fifo_wr_task(1,32'h1111);
fifo_wr_task(1,32'h2222);
fifo_wr_task(1,32'h3333);
fifo_wr_task(0,32'hxxxx);
wait(fifo_empty==0);
#50;@(posedge clk);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(0);
#100;@(posedge clk);
//case 8 ://写连续入3个数据,非空读1次,延时,连续读2次
fifo_wr_task(1,32'h1111);
fifo_wr_task(1,32'h2222);
fifo_wr_task(1,32'h3333);
fifo_wr_task(0,32'hxxxx);
wait(fifo_empty==0);
fifo_rd_task(1);
fifo_rd_task(0);
#50;@(posedge clk);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(0);
#100;@(posedge clk);
//case 9://写连续入3个数据,非空读2次,延时,读1次
fifo_wr_task(1,32'h1111);
fifo_wr_task(1,32'h2222);
fifo_wr_task(1,32'h3333);
fifo_wr_task(0,32'hxxxx);
wait(fifo_empty==0);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(0);
#50;@(posedge clk);
fifo_rd_task(1);
fifo_rd_task(0);
#100;@(posedge clk);
//case 10://写连续入3个数据,延时 ,连续读3次
fifo_wr_task(1,32'h1111);
fifo_wr_task(1,32'h2222);
fifo_wr_task(1,32'h3333);
fifo_wr_task(0,32'hxxxx);
wait(fifo_empty==0);
#50;@(posedge clk);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(0);
#100;@(posedge clk);
//case 11://写连续入3个数据,延时 ,读1次,延时,读1次,延时,读1次
fifo_wr_task(1,32'h1111);
fifo_wr_task(1,32'h2222);
fifo_wr_task(1,32'h3333);
fifo_wr_task(0,32'hxxxx);
wait(fifo_empty==0);
#50;@(posedge clk);
fifo_rd_task(1);
fifo_rd_task(0);
#50;@(posedge clk);
fifo_rd_task(1);
fifo_rd_task(0);
#50;@(posedge clk);
fifo_rd_task(1);
fifo_rd_task(0);
#1000;@(posedge clk);
//case 11://写连续入8个数据,看full信号是否正确
fifo_wr_task(1,32'h1111);
fifo_wr_task(1,32'h2222);
fifo_wr_task(1,32'h3333);
fifo_wr_task(1,32'h4444);
fifo_wr_task(1,32'h5555);
fifo_wr_task(1,32'h6666);
fifo_wr_task(1,32'h7777);
fifo_wr_task(1,32'h8888);
fifo_wr_task(0,32'hxxxx);
wait(fifo_empty==0);
#50;@(posedge clk);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(0);
#1000;@(posedge clk);
//case 12://边写边读
fifo_wr_task(1,32'h0001);
fifo_wr_task(1,32'h0002);
fifo_wr_task(1,32'h0003);
fifo_wr<=1;fifo_wdata<=32'h0004;fifo_ren<=1;@(posedge clk); #1;
fifo_wr<=1;fifo_wdata<=32'h0005;fifo_ren<=1;@(posedge clk); #1;
fifo_wr<=1;fifo_wdata<=32'h0006;fifo_ren<=1;@(posedge clk); #1;
fifo_wr<=1;fifo_wdata<=32'h0007;fifo_ren<=1;@(posedge clk); #1;
fifo_wr<=0;fifo_wdata<=32'hxxxx;fifo_ren<=1;@(posedge clk); #1;
fifo_rd_task(1);
fifo_rd_task(1);
fifo_rd_task(0);
#100;@(posedge clk);
end
SYNC_FIFO #(RAM_TYPE,WIDTH,DEPTH) u_SYNC_FIFO (
.clk (clk ),//input clk ,
.rst (rst ),//input rst ,
.fifo_wr (fifo_wr ),//input fifo_wr ,
.fifo_wdata(fifo_wdata ),//input [WIDTH-1:0] fifo_wdata ,
.fifo_full (fifo_full ),//output fifo_full ,
.fifo_ren (fifo_ren ),//input fifo_ren ,
.fifo_rdata(fifo_rdata ),//output [WIDTH-1:0] fifo_rdata ,
.fifo_empty(fifo_empty ) //output fifo_empty
);
task fifo_wr_task ;
input en ;
input [WIDTH-1:0] wdata ;
begin
fifo_wr<=en;fifo_wdata<=wdata;@(posedge clk);#1;
end endtask
task fifo_rd_task ;
input en ;
begin
fifo_ren<= en;@(posedge clk); #1;
end endtask
endmodule
到了这里,关于【GAOPS051】(xilinx First-Word Fall-Through模式)预读FIFO的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!