分享

ALTERA FIFO 读写verilog代码

 薛董_艾瑞 2018-01-31

FIFO,在FPGA中是一种非常基本,使用非常广泛的模块。FPGA高手可能觉得不值一提,但对于像我这样的新手,有时却是个大问题,弄了一个多月,总算有所进展,希望把自己的一些总结写下来,一方面希望对其他入门者有所帮助,另一方面希望看到的高手们批评指正。另外得特别感谢特权同学的教程。

         在大学中编了两年单片机(断断续续),C语言的串行思维深入人心,一下子转换到FPGA并行的工作方式和有些奇异的编程风格,一开始还真有点处理不好。另一方面,对FPGA各种延迟非常困惑,比如RAM模块读取延迟,FIFO读取数据延迟等,常常不知道如何处理。下面说说我现在的理解和解决办法。

FPGA写入数据

写入相对简单,只需要在时钟上升沿来临之时使能wrreq和写入data,当然还需要判断FIFO full信号。时序图如下,在第一个时钟之前使能wrreq信号,在第一个时钟及以后的几个时钟上升沿FIFO判断wrreq有效并且读入数据。(图片为转载并 编辑)


一份参考的代码如下

  1. reg     [7:0]   fifo_wcnt;  
  2. always @(posedge led or negedge rst) begin  
  3.     if (~rst) begin  
  4.         fifo_wreq<=1'b0;  
  5.         fifo_wdat<=0;  
  6.         fifo_wcnt<=0;//测试数据  
  7.     end else begin  
  8.         if(~fifo_wfull)begin//FIFO没有写满的时候就写入数据  
  9.                 fifo_wcnt<=fifo_wcnt+1'b1;                 
  10.                 fifo_wreq<=1'b1;  
  11.                 fifo_wdat<=fifo_wcnt+8'd2;  
  12.         end else begin  
  13.             fifo_wreq<=1'b0;  
  14.         end  
  15.     end  
  16. end  


这里fifo_wfull值得注意,在没有延迟的时候这个信号可以直接为FIFO直接输出的full信号,但实际中通常都会有一个或者多个时钟的延迟。可以结合使用wrusedw信号对FIFO保留一定的余量,比如FIFO容量为1024,定义如下:

  1. assign                        wfifo_full =(recFIFO_WCnt>1000)|recFIFO_wfull;  


FIFO读出数据

         时序图如下,在第二个时钟上升沿来临后使能rdreq信号,第三个时钟FIFO判断rdreq信号,然后第四个时钟来临时方可读取数据(在empty信号表示FIFO飞空的时候有效)。因此需要注意这两个时钟的延迟,但是我们的重点应该放在获取、处理数据上,而不应该是处理这些烦心的时序操作上。


一份可用的代码如下

  1. assign                                 rfifo_ren= ~rfifo_empty;  
  2. reg                                    canread;  
  3. //fifo read  
  4. always @(posedge clk or negedge rst) begin  
  5.          if(~rst)begin  
  6.                    canread<=1'b0;  
  7.          endelse begin  
  8.                    canread<=rfifo_ren;  
  9.          end  
  10. end  
  11.    
  12. reg                      [7:0] readcnt;//读出计数  
  13. reg                      [7:0] dispdata;//读出数据显示  
  14. always @(posedge clk or negedge rst) begin  
  15.          if(~rst)begin  
  16.                    readcnt<=0;  
  17. dispdata<=0;  
  18.          endelse begin  
  19.                    if(canread)begin  
  20.                             readcnt<=readcnt+1'b1;  
  21.                             dispdata<=rfifo_rdata;  
  22.                    endelse begin  
  23.                              
  24.                    end  
  25.          end  
  26. end  


解释:empty为0后,ren随之变为1,使能FIFO读出信号(不是上述时序图内容)。下一个时钟来临时FIFO判断ren使能,同时这个时钟之后canread方才读入~empty信号为1使能。再下一个时钟来临时,FIFO输出信号,而canread已经有效,外设读取数据。

         比较奇怪的是ALTERA的文档中并没说empty有延迟,对FIFO直接的操作中也并没有发现这个延迟,也就是说本来FIFO已经读空了,但是FIFO的empty信号并没有有效,如果完全按照empty信号读取会导致多读出一个或多个数据。在随后的一个cy68013 USB2.0接口实现实际用FIFO时发现了empty具有一个时钟的延迟,发送256组数据却显示读取了257组数据。但是这里如果像full一样采取保留一定余量的方法会导致FIFO中始终会暂留有一些数据,除非你知道你自己发送了多少个数据。

         上面的代码中使用这种流水线式寄存器转移方法很好的处理了FIFO信号延迟的问题,同样使用它还可以轻易的解决empty延迟一个时钟的问题,只需稍微改一下上面的代码。不过若是延迟多个时钟???还的看看具体的场合。

  1. wire                                   rfifo_ren;  
  2. reg                                    rfifo_ren1;  
  3. assign                                 rfifo_ren= ~rfifo_empty;  
  4. always @(posedge clk or negedge rst) begin  
  5.          if(~rst) begin  
  6.                    rfifo_ren1<=1'b0;  
  7.          endelse begin  
  8.                    rfifo_ren1<=rfifo_ren;  
  9.          end  
  10. end  
  11. assign canread =rfifo_ren1&rfifo_ren;//因为ren会多延迟一个时钟失效  
  12.    
  13. reg                      [7:0] readcnt;//读出计数  
  14. reg                      [7:0] dispdata;//读出数据显示  
  15. always @(posedge clk or negedge rst) begin  
  16.          if(~rst)begin  
  17.                    readcnt<=0;  
  18. dispdata<=0;  
  19.          endelse begin  
  20.                    if(canread)begin  
  21.                             readcnt<=readcnt+1'b1;  
  22.                             dispdata<=rfifo_rdata;  
  23.                    endelse begin  
  24.                              
  25.                    end  
  26.          end  
  27. end  



解释:加了读取保护功能的FIFO在empty使能之后rfifo_ren使能也不会有问题,关键是canread信号。empty信号为1后,rfifo_ren随即为0,在下一个时钟来临时FIFO输出一个数据(上一个时钟rfifo_ren还是有效的)并判断rfifo_ren失能,停止输出数据。但是由于empty多延迟了一个时钟,因此这个数据是不需要的,如果& 上~empty则可以使canread在这个时钟来临前失能。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多