ALTERA FIFO 读写verilog代码

ALTERA FIFO 读写verilog代码

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

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

FPGA写入数据

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


一份参考的代码如下

reg		[7:0]	fifo_wcnt;
always @(posedge led or negedge rst) begin
	if (~rst) begin
		fifo_wreq<=1'b0;
		fifo_wdat<=0;
		fifo_wcnt<=0;//测试数据
	end else begin
		if(~fifo_wfull)begin//FIFO没有写满的时候就写入数据
				fifo_wcnt<=fifo_wcnt+1'b1;				
				fifo_wreq<=1'b1;
				fifo_wdat<=fifo_wcnt+8'd2;
		end else begin
			fifo_wreq<=1'b0;
		end
	end
end


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

assign                        wfifo_full =(recFIFO_WCnt>1000)|recFIFO_wfull;


FIFO读出数据

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


一份可用的代码如下

assign                                 rfifo_ren= ~rfifo_empty;
reg                                    canread;
//fifo read
always @(posedge clk or negedge rst) begin
         if(~rst)begin
                   canread<=1'b0;
         endelse begin
                   canread<=rfifo_ren;
         end
end
 
reg                      [7:0] readcnt;//读出计数
reg                      [7:0] dispdata;//读出数据显示
always @(posedge clk or negedge rst) begin
         if(~rst)begin
                   readcnt<=0;
dispdata<=0;
         endelse begin
                   if(canread)begin
                            readcnt<=readcnt+1'b1;
                            dispdata<=rfifo_rdata;
                   endelse begin
                           
                   end
         end
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延迟一个时钟的问题,只需稍微改一下上面的代码。不过若是延迟多个时钟???还的看看具体的场合。

wire                                   rfifo_ren;
reg                                    rfifo_ren1;
assign                                 rfifo_ren= ~rfifo_empty;
always @(posedge clk or negedge rst) begin
         if(~rst) begin
                   rfifo_ren1<=1'b0;
         endelse begin
                   rfifo_ren1<=rfifo_ren;
         end
end
assign canread =rfifo_ren1&rfifo_ren;//因为ren会多延迟一个时钟失效
 
reg                      [7:0] readcnt;//读出计数
reg                      [7:0] dispdata;//读出数据显示
always @(posedge clk or negedge rst) begin
         if(~rst)begin
                   readcnt<=0;
dispdata<=0;
         endelse begin
                   if(canread)begin
                            readcnt<=readcnt+1'b1;
                            dispdata<=rfifo_rdata;
                   endelse begin
                           
                   end
         end
end



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


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Luchang-Li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值