宋桓公

【红色飓风Nano二代测评】FIFO易错点时序分析

0
阅读(7396)

一直想写一篇关于FIFO的文章,这次为了给LCD(TFT)打造接口,必须添加FIFO。

从而对Xilinx的FIFO进行了时序测试,发现和Altera的FIFO用法和时序基本是一样的。

在读FIFO时候,有一个错误是很容易忽略的。就是没有将FIFO“读空”。可能你

会觉得这怎么可能,我只要检查到FIFO空标志有效,不就证明FIFO被读空了吗?

没错,FIFO已然是空了,但是最后一个读出的数据你保存住了吗?

我们先用Altera的做个实验:首先例化一个深度为16的FIFO。先一直写数据,

直到FIFO写满为止。然后就一直读FIFO直到读空为止。程序如下:


module FIFO_V1 ( input CLK, input RSTn, output [7:0]FIFO_IN, output [7:0]FIFO_OUT ); reg Read_Sig = 1'b0; reg Write_Sig = 1'b0; reg [7:0]FIFO_Write_Data; wire Full_Sig; wire Empty_Sig; wire [7:0]FIFO_Read_Data; FIFO_Module FIFO_Mem ( .clock(CLK), .data(FIFO_Write_Data), .rdreq(Read_Sig), .wrreq(Write_Sig), .empty(Empty_Sig), .full(Full_Sig), .q(FIFO_Read_Data) ); reg [3:0]i; always @(posedge CLK or negedge RSTn) if(!RSTn) begin i <= 4'd0; Write_Sig <= 1'b0; FIFO_Write_Data <= 8'd0; end else case(i) 0: begin if(!Full_Sig) begin Write_Sig <= 1'b1; FIFO_Write_Data <= FIFO_Write_Data + 1'b1; i <= i; end else begin Write_Sig<= 1'b0; i <= i + 1'b1; end end 1: begin i <= i; end endcase reg [7:0]Read_Data; reg [3:0]j = 4'd0; always @(posedge CLK or negedge RSTn) if(!RSTn) begin Read_Sig <= 1'b0; Read_Data <= 8'd0; end else case(j) 0: begin if(Full_Sig) j <= j + 1'b1; else j <= j; end 1: begin if(!Empty_Sig) begin Read_Sig <= 1'b1; Read_Data <= FIFO_Read_Data; j <= j; end else begin Read_Data <= FIFO_Read_Data;// 加上这句才能,破除没有读空的假象 Read_Sig <= 1'b0; end end endcase assign FIFO_IN = FIFO_Write_Data; assign FIFO_OUT = Read_Data; endmodule


28~57行是将FIFO写满,60~89行是将FIFO读空,并且将读出的数据保存到

变量Read_Data。注意到85行:如果不加上这一句,你会发现最后一个数据没有

读出来,也就是FIFO没有“读空”。你可能会问,明明Empty已经至高了,为什么

在Read_Sig关闭之后,还要加这么一句,存一次数据呢?先看仿真时序图:

错误:没有加85行,没有读空的情况~~(残留假象)

image

观察数序后半段,读FIFO时序,发现Read_Data只读到了15,

16这个数据并没有保存到。这是为什么呢?



正确:有加85行,读空了的情况~~

image

此时,16这个数据被保存到了。

您可能觉得,这个85行加的太诡异了,不好理解,不要紧,

我继续用Xilinx的FIFO举例子(前面说过Altera的FIFO用

法和时序基本是一的,所以用谁举例无关紧要。)

上面这种写法比较简洁,能方便的读写FIFO。但是不利于

理解以及波形的观察,现在继续做个实验,我先写3次FIFO

,接着再读3次FIFO,那么这次肯定能将FIFO读空了是吧。

程序入下:


module test; // Inputs reg CLK; reg [7:0] a; reg [7:0] b; reg ce; reg [31:0] din; reg wr_en; reg rd_en; // Outputs wire [7:0] s; wire full; wire empty; wire [31:0] dout; // Instantiate the Unit Under Test (UUT) FIFO_Test uut ( .CLK(CLK), .a(a), .b(b), .ce(ce), .s(s), .din(din), .wr_en(wr_en), .rd_en(rd_en), .full(full), .empty(empty), .dout(dout) ); initial begin // Initialize Inputs CLK = 0; a = 0; b = 0; ce = 0; din = 0; wr_en = 0; rd_en = 0; // Wait 100 ns for global reset to finish #100; // Add stimulus here forever #5 CLK = ~CLK; end reg [2:0]i = 3'd0; always @(posedge CLK) case(i) 0://Write First begin if(!full)begin wr_en<= 1'b1; din <= 32'd1; i <= i + 1'b1; end else wr_en<= 1'b0; end 1://Write again begin if(!full)begin wr_en<= 1'b1; din <= 32'd2; i <= i + 1'b1; end else wr_en<= 1'b0; end 2://Write again begin if(!full)begin wr_en<= 1'b1; din <= 32'd3; i <= i + 1'b1; end else wr_en<= 1'b0; end 3: begin wr_en<= 1'b0; i <= i + 1'b1; end 4://Read begin if(!empty)begin rd_en <= 1'b1; i <= i + 1'b1;end else rd_en <= 1'b0; end 5://Read again begin if(!empty)begin rd_en <= 1'b1; i <= i + 1'b1;end else rd_en <= 1'b0; end 6://Read again begin if(!empty)begin rd_en <= 1'b1; i <= i + 1'b1;end else rd_en <= 1'b0; end 7: begin i <= i; end endcase endmodule


波形如下:

image

我特意将空信号,和读使能信号,用不同的颜色标识,以便观察:

在写完,1,2,3这3个数据后,开始读FIFO,此时首先将读使

能置高,我们发现在读使能信号置高的一个时钟周期后,第一个

数据才“探出头”,同样的,空信号,在最后一个数据“探出头”

时就置高了。所以在需要保存FIFO读出的数据时是需要注意的,

其实,并不是FIFO没有读空,而是最后一个数据读出来的时候

你没有去保存它,而是认为FIFO已经空了。

总结:保存FIFO读出的数据的时候要注意“头和尾”

头:在“读使能信号”一个时钟周期后的时钟上升沿,数据

发生变化,也就是FIFO的第一个数据输出。所以你想保存第一个

数据,必须是在“读使能信号”置高的两个周期之后。

尾:空信号置高后,最后一个数据输出。所以你想保存最后

一个数据,必须在空信号置高后一个周期保存。

最后,介绍下在ISE下如何添加FIFO:

之前在添加时碰到一个错误:

ERROR:NgdBuild:604 - logical block
'YourInstanceName/BU2/U0/grf.rf/mem/gbm.gbmg.gbmga.ngecc.bmg/blk_mem_generato
r/valid.cstr/ramloop[0].ram.r/v5_noinit.ram/SDP.WIDE_PRIM18.TDP' with type
'RAMB18SDP' could not be resolved. A pin name misspelling can cause this, a
missing edif or ngc file, case mismatch between the block name and the edif
or ngc file name, or the misspelling of a type name. Symbol 'RAMB18SDP' is
not supported in target 'spartan6'.

网上查了很久,什么需要添加路径,什么软件的BUG,试了都不顶用。最后按照如下的操作步骤,就

没有报错了。

首先,在工程上单击右键,选择new source:

image

image

image

image

image

image

image

余下的下一步,最后Generate即可!

image

Generate,这个过程感觉等待的时间还是比较久(quartus一秒搞定的说)。

最后出现如下这个表示成功了:

image

image

再修改例化部分加到你的代码里即可~~

这篇博客其实也是为下一篇博客做准备,下一篇将FIFO运用到LCD驱动。

写累了,休息,休息~~


技术讨论欢迎加群~~电子技术协会 362584474

Baidu
map