FPGA学习之FIFO

FIFO是一种先进先出的电路,使用在需要产生数据接口的部分,用来存储、缓冲在两个一部时钟之间的数据传输。在一部电路中,由于时钟之间周期和相位完全独立,因为数据丢失概率不为零。使用FIFO可以在两个不同时钟域系统之间快速而方便地传输实时数据。在网络接口、图像处理灯方面,FIFO得到广泛的应用。
FIFO的设计难点:产生可靠的FIFO读写指针和生成FIFO“空”、“满”状态标识。
异步FIFO地址最好使用Gray计数器,这是因为,采用二进制数时又可能变化计数一次所有位都会有所变化,而Gray码计数器就只是计数一次数据变化一次。
 
1、FIFO“空”/“满”状态
由于FIFO“空”/“满”状态都表明读写指针相等,所以必须要准确区分是空还是满。
解决办法是将FIFO地址空间按最高量为划分成4个象限,每当读写地址相等时,通过对最高两位译码以申城正空的空”/“满”标志。
如果写指针比读指针之后一个象限,则表明FIFO接近满。此时置标志位direction为1,并且锁存其值。相应的等式为:
wire dirset_n = ~(( wptr[N]^rptr[N-1]) & ~(wptr[N-1]^rptr[N]));
 
 
FPGA学习之FIFO(12) - 白色 - gor
如果写指针比读指针超前一个象限,则表明FIFO为接近空状态,如图所示。此时置标志direction为0,并且锁存其值。相应的等式为:
                                  wire dirclr_n = ~((~(wptr[n]^rptr[n-1]) & (wptr[n-1] ^ rptr[n])) | ~wrst_n;
FPGA学习之FIFO(12) - 白色 - gor
2、FIFO模块结构
(1)顶层模块,对所有FIFO模块进行封装
(2)双口RAM模块,用于实现读写操作
(3)异步比较器,用于实现FIFO读写指针比较,并输出状态信号以生成正确的FIFO空满标志
(4)FIFO写指针与满逻辑控制模块,用于生成FIFO写地址指针并且生成FIFO满标志。
(5)FIFO读指针与空逻辑控制模块,用于生成FIFO读地址指针并且生成FIFO空标志。
 
FPGA学习之FIFO(12) - 白色 - gor
代码如下:
 
 
//双端口RAM模块
module dp_ram(
  rdata,
  wdata,
  waddr,
  raddr,
  wclken,
  wclk
 );
 
parameter DATA_WIDTH = 8;               //双口RAM的数据位宽
parameter ADDR_WIDTH = 4;               //双口RAM的地址位宽
parameter DEPTH = 1<<ADDR_WIDTH;        //RAM深度 = 2*ADDR_WIDTH
 
output [DATA_WIDTH-1:0]rdata;           //读出的数据
input  [DATA_WIDTH-1:0]wdata;           //写入的数据
input  [ADDR_WIDTH-1:0]waddr, raddr;    //读写数据地址
input  wclken;                          //写时钟使能,高电平有效
input  wclk;                            //写时钟,上升沿有效
 
reg    [DATA_WIDTH-1:0]MEN[0:DEPTH-1];
 
always @(posedge wclk)
if(wclken) MEN[waddr] <= wdata;
 
assign rdata = MEN[raddr];
 
endmodule
 
 
//异步比较器模块
module async_cmp(
aempty_n,
afull_n,
wptr,
rptr,
wrst_n
);
parameter ADDR_WIDTH = 4;
parameter N          = ADDR_WIDTH - 1;
 
output aempty_n, afull_n;
input  [N:0]wptr;
input  [N:0]rptr;
input  wrst_n;
 
reg  direction;
wire high = 1'b1;
wire dirset_n = ~(( wptr[N]^rptr[N-1]) & ~(wptr[N-1]^rptr[N]));
wire dirclr_n = ~((~(wptr[N]^rptr[N-1]) & (wptr[N-1]^rptr[N])) | ~wrst_n);
 
always @(posedge high or negedge dirset_n or negedge dirclr_n)
begin
if(!dirclr_n) direction <= 1'b0;
else if(!dirset_n) direction <= 1'b1;
else direction <= high;
end
 
assign aempty_n = ~((wptr == rptr) && !direction);
assign afull_n  = ~((wptr == rptr) &&  direction);
 
endmodule
 
 
 
//读指针与“满”逻辑
module rptr_empty( rempty, rptr, aempty_n, rreq, rclk, rrst_n );
parameter ADDR_WIDTH = 4;
output rempty;
output [ADDR_WIDTH-1:0]rptr;
input aempty_n;
input rreq, rclk, rrst_n;
reg [ADDR_WIDTH-1:0]rptr,rbin; reg rempty, rempty2;
wire [ADDR_WIDTH-1:0]rgnext, rbnext;
//寄存器输出Gray码读地址指针
always @(posedge rclk or negedge rrst_n)
if(!rrst_n) begin rbin <= 0; rptr <= 0; end else begin rbin <= rbnext; rptr <= rgnext; end //Gray码计数逻辑 assign rbnext = !rempty ? rbin + rreq : rbin;//二进制递增 assign rgnext = (rbnext>>1) ^ rbnext; //二进制到Gray码的转换 //rempty被aempty异步置位,并且当读指针递加后撤除 always @(posedge rclk or negedge aempty_n) begin if(!aempty_n) {rempty, rempty2} <= 2'b11; else {rempty, rempty2} <= {rempty2, ~aempty_n}; end endmodule //写指针与满逻辑模块 module wptr_full( wfull, wptr, afull_n, wreq, wclk, wrst_n ); parameter ADDR_WIDTH = 4; output wfull; output [ADDR_WIDTH-1:0]wptr; input afull_n; input wreq, wclk, wrst_n; reg [ADDR_WIDTH-1:0]wptr, wbin; reg wfull, wfull2; wire [ADDR_WIDTH-1:0]wgnext, wbnext; //寄存器输出Gray码读地址指针 always @(posedge wclk or negedge wrst_n) begin if(!wrst_n) begin wbin <= 0; wptr <= 0; end else begin wbin <= wbnext; wptr <= wgnext; end end //Gray码计数逻辑 assign wbnext = !wfull ? wbin + wreq : wbin; //二进制递增 assign wgnext = (wbnext>>1) ^ wbnext; //二进制到Gray码的转换 //当FIFO复位时,状态wfull也被复位,此外其也被afull_n异步置位,并且当读指针递加后撤除 always @(posedge wclk or negedge wrst_n or negedge afull_n) begin if(!wrst_n) {wfull, wfull2} <= 2'b00; else if(!afull_n) {wfull, wfull2} <= 2'b11; else {wfull, wfull2} <= {wfull2, ~afull_n}; end endmodule module async_fifo( rdata, wfull, rempty, wdata, wreq, wclk, wrst_n, rreq, rclk, rrst_n ); parameter DATA_WIDTH = 8; parameter ADDR_WIDTH = 4; output [DATA_WIDTH-1:0]rdata; //从fifo读出的数据 output wfull; //fifo数据满了,写不下了 output rempty; //fifo被读空了,别再读了 input [DATA_WIDTH-1:0]wdata; //写进fifo的数据 input wreq; //写允许 input wclk; //写时钟 input wrst_n; //写复位 input rreq; //读允许 input rclk; //读时钟 input rrst_n; //读复位 //一下wire类型只是起到两个模块之间的连线作用 wire [ADDR_WIDTH-1:0]wptr, rptr; wire [ADDR_WIDTH-1:0]waddr, raddr; wire aempty_n, afull_n; dp_ram dp_ram( .rdata(rdata), .wdata(wdata), .waddr(wptr), .raddr(rptr), .wclken(wreq), .wclk(wclk) ); defparam dp_ram.DATA_WIDTH = DATA_WIDTH; defparam dp_ram.ADDR_WIDTH = ADDR_WIDTH; async_cmp async_cmp( .aempty_n(aempty_n), .afull_n(afull_n), .wptr(wptr), .rptr(rptr), .wrst_n(wrst_n) ); defparam async_cmp.ADDR_WIDTH = ADDR_WIDTH; rptr_empty rptr_empty( .rempty(rempty), .rptr(rptr), .aempty_n(aempty_n), .rreq(rreq), .rclk(rclk), .rrst_n(rrst_n) ); defparam rptr_empty.ADDR_WIDTH = ADDR_WIDTH; wptr_full wptr_full( .wfull(wfull), .wptr(wptr), .afull_n(afull_n), .wreq(wreq), .wclk(wclk), .wrst_n(wrst_n) ); defparam wptr_full.ADDR_WIDTH = ADDR_WIDTH; endmodule 简单笔记:

1、defparam 重定义参数

       语法:defparam path_name = value ;

  低层模块的参数可以通过层次路径名重新定义

2、menory生成办法:

reg [DATA_WIDTH-1:0]MEN[0:DEPTH-1];

3、一个技巧:

//rempty被aempty异步置位,并且当读指针递加后撤除 always @(posedge rclk or negedge aempty_n) begin if(!aempty_n) {rempty, rempty2} <= 2'b11; else {rempty, rempty2} <= {rempty2, ~aempty_n}; end

4、用wire去连接两个模块

 

//以下wire类型只是起到两个模块之间的连线作用

wire [ADDR_WIDTH-1:0]wptr, rptr;

wire [ADDR_WIDTH-1:0]waddr, raddr;

转载于:https://www.cnblogs.com/serdes/archive/2013/02/18/2916276.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值