fifo的使用方式有很多种且方法灵活,这里不再做赘述。可以参考xilinx pg057文档及中研院总结的”fifo的使用”文档。本文档只推荐一种常用的native接口的fifo使用的建议方式,以满足大多数的应用场景。
1 Fifo的定制
- 通常情况选择independent clock模式,这样读写时钟是否同源都可以支持,便于后期维护修改。
- 根据所需深度选择distributed ram 与 blockram类型,原则上小的fifo建议使用distributed ram类型以节省block ram资源。
- 选择Standard 或FWFT模式,选择StandardFIFO ,默认为1clk延迟,FWFT无延时。当block ram类型时,可以选择添加output registers,在复杂布线情况下应该有助于布线结果,当然此时为2clk延迟。
- 使能resetpin,使能 reset synchronization,Full flags reset value值为1。
- 如果采用Prog_full信号反压写信号,需要根据链路延迟合理设置fullthreshold。
- 根据需要使能wr_data_cnt 和 rd_data_cnt。
2 fifo的复位
- 采用读写时钟中的慢速时钟产生至少8clk的复位信号进行fifo复位 , 若采用快时钟产生,保证产生宽度满足以上条件亦可。
- 复位完成后至少等待60个慢时钟周期再进行读写操作,严格来讲需要等待full信号拉低才可以(异步复位模式,Full flags reset value值为1)。此处以附带safetycircuit为例,若不带safety circuit,时间要比这个短,但在视频帧结构应用中,只要复位的时机合理,都可以保证此要求。
- 复位过程中严禁读写操作。
- 通常在视频帧结构处理中,采用视频vs的后沿构造,并满足以上需求。
- 复位过程时钟必须是稳定的。
3 fifo的读写控制逻辑
通常fifo的写使能和写数据由fifo的前后读写速度或progfull决定,保证fifo不应该被写溢出,且要保证在复位时刻写使能为0。
通常可以采用非空信号作为读使能(RST拉起来后empty也会拉高,可以保证复位时无读使能)。
也可以采用rd_data_cnt构造读使能。
也可以由独立的时序构造读使能,只要能从fifo的读写带宽上保证读使能来的时候fifo中一定由足够的数据即可。
建议对满写overflow和和空读underflow进行监测。
4 example
4.1 Example1
该用例可以应用于数据需要跨时钟域传输时,读端时钟速率大于等于写端时钟,或者是读端时钟速率略小于写端时钟速率,但可以利用数据流的行间隔时间以及合理的fifo深度,保证所有写入的数据均能够及时读出而不被新写入的数据覆盖。
此种应用时,fifo的复位可以是上电一次合理时间的复位,若是视频流,建议在视频垂直逆程通过vs的沿构造合理的周期性复位。
reg [9:0] vs_dly;
always @ (posedge wr_clk)
begin
vs_d0 <= vs;
vs_d1 <= vs_d0;
vs_d2 <= vs_d1;
end
assign vs_pos = vs_d1 && (!vs_d2);
always @ (posedge wr_clk)
begin
vs_dly <= {vs_dly[8:0],vs_pos};
end
always @ (posedge wr_clk)
begin
fifo_rst <= |vs_dly[9:0];
end
AsyFifo_Std_64x36 inst_RxFifo(
.rst ( fifo_rst ),
.wr_clk ( wr_clk ), // 27M~165M
.rd_clk ( rx_clk ), // 162.5M
.din ( wr_dat[35:0] ),
.wr_en ( wr_en ), // video in en
.rd_en ( rd_en ),
.dout ( rd_dat[35:0] ),
.full ( full ),
.empty ( empty ),
.valid ( )
);
assign rd_en =~empty;
always @ (posedge RxClk)
begin
rd_en_d1 <= rd_en ;
fifo_rx_vld <= rd_en_d1;
fifo_rd_dat[35:0] <= rd_dat[35:0];
end
4.2 Example2
本用例采用prog_full信号反压写端数据流,从而保证fifo的读写的速率匹配。通常可用在fifo前端的数据已经在一个缓存中,并且可以随时访问。这样可以通过这个机制跨到fifo的读时钟域下。这里需要根据链路的延迟合理时钟progfull的阈值。
assign rd_ram_en = ~prog_full;
always@(posedge wr_clk)
begin
rd_ram_en_d1 <= rd_ram_en;
wr_en <= rd_ram_en_d1;
wr_dat <= rd_ram_dat;
end
AsyFifo_Std_64x36 RxFifo(
.rst ( fifo_rst ),
.wr_clk ( wr_clk ),
.rd_clk ( rd_clk ),
.din ( wr_dat[35:0] ),
.wr_en ( wr_en ),
.rd_en ( ~empty),
.dout ( rd_dat[35:0] ),
.full ( ),
.empty ( empty ),
.valid ( rx_vld ),
.prog_full( prog_full)
);
always @(posedge rd_clk)
begin
vin_de_out <= rx_vld;
vin_data_out <= rd_dat;
end
4.2 Example3
该用例是典型的通过判断fifo中数据量构造读取fifo的逻辑,写端数据流可以不间断的写入fifo。所以需要保证fifo的读取速率能够大于写入速率。
通常如果有多路异步数据流需要同步到同一处理时钟域的时候,可以采用此种方式,只需保证读时钟大于写时钟即可。
读取fifo的使能信号还可以作为所有这路数据流后续所有处理逻辑的clock enable信号,以达到减少动态功耗的目的。
当然如果不采用ce的方式,也可以用读使能与上数据流中的嵌入的de,构造出新的数据使能信号。(写使能的机制能够保证所有数据能从fifo中读出)
assign wr_en = (fifo_rst || full) ? 0 : 1;
always@(posedge proc_clk)
begin
if(rd_dat_cnt >= 8) // 根据fifo深度合理设置即可
rd_set <= 1'b1;
else
rd_set <= 1'b0;
end
always@(posedge proc_clk)
begin
if(rd_dat_cnt<=4)// 根据fifo深度合理设置即可
rd_clear <= 1'b1;
else
rd_clear <= 1'b0;
end
always@(posedge proc_clk)
begin
if(rd_set)
rd_en <= 1'b1;
else if(rd_clear)
rd_en <= 1'b0;
else
rd_en <= rd_en;
end
AsyFifo_Std_64x24 inst_RxFifo(
.rst (fifo_rst ),
.wr_clk ( video_in_clk), //27M~165M
.rd_clk ( proc_clk ), //200M
.din ( {video_in_de,video_in_data[23:0]} ),
.wr_en ( wr_en ),
.rd_en ( rd_en ),
.dout ( rd_dat[24:0] ),
.full ( full ),
.empty ( ),
.rd_data_count(rd_dat_cnt)
);
always @(posedge proc_clk)
begin
if(rd_en)
begin
vin_de_out <= rd_dat[24];
vin_data_out <= rd_dat[23:0];
end
end
assign clk_ce = rd_en;
or
always @(posedge proc_clk)
begin
vin_de_out <= rd_dat[24] && rd_en;
vin_data_out <= rd_dat[23:0];
end