您的位置:首页 > 编程语言

ALTERA FIFO 读写verilog代码

2014-04-25 14:26 302 查看

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在这个时钟来临前失能。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: