打印
[CPLD]

FIFO

[复制链接]
2535|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
梅花望青竹|  楼主 | 2012-7-16 12:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
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]));

相关帖子

沙发
梅花望青竹|  楼主 | 2012-7-16 12:53 | 只看该作者
如果写指针比读指针超前一个象限,则表明FIFO为接近空状态,如图所示。此时置标志direction为0,并且锁存其值。相应的等式为:
                                  wire dirclr_n = ~((~(wptr[n]^rptr[n-1]) & (wptr[n-1] ^ rptr[n])) | ~wrst_n;
2、FIFO模块结构
(1)顶层模块,对所有FIFO模块进行封装
(2)双口RAM模块,用于实现读写操作
(3)异步比较器,用于实现FIFO读写指针比较,并输出状态信号以生成正确的FIFO空满标志
(4)FIFO写指针与满逻辑控制模块,用于生成FIFO写地址指针并且生成FIFO满标志。
(5)FIFO读指针与空逻辑控制模块,用于生成FIFO读地址指针并且生成FIFO空标志。
代码如下:



//双端口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];


使用特权

评论回复
板凳
梅花望青竹|  楼主 | 2012-7-16 12:53 | 只看该作者
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

使用特权

评论回复
地板
梅花望青竹|  楼主 | 2012-7-16 12:53 | 只看该作者
//读指针与“满”逻辑  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;

使用特权

评论回复
5
GoldSunMonkey| | 2012-7-16 13:22 | 只看该作者
:)五大问题一个对策,XILINX的FIFO的IP即可

使用特权

评论回复
6
gaochy1126| | 2012-7-16 20:56 | 只看该作者
猴哥说对,可惜ip核实需要购买的,穷人啊!!!

使用特权

评论回复
7
mr.king| | 2012-7-16 21:16 | 只看该作者
是呀,现成免费IP和不用,自己折腾

使用特权

评论回复
8
GoldSunMonkey| | 2012-7-16 23:22 | 只看该作者
猴哥说对,可惜ip核实需要购买的,穷人啊!!!
gaochy1126 发表于 2012-7-16 20:56
这个免费

使用特权

评论回复
9
Living丶| | 2012-7-17 15:41 | 只看该作者
FIFO“空”/“满”状态

呵呵,学到一些。

使用特权

评论回复
10
梅花望青竹|  楼主 | 2012-7-17 18:53 | 只看该作者
这样子啊,感谢猴哥了1 8# GoldSunMonkey

使用特权

评论回复
11
梅花望青竹|  楼主 | 2012-7-17 18:53 | 只看该作者
相互学习,共同进步! 9# Living丶

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

98

主题

2589

帖子

7

粉丝