fifo的使用方式有很多種且方法靈活,這里不再做贅述。可以參考xilinx?pg057文檔及中研院總結(jié)的”fifo的使用”文檔。本文檔只推薦一種常用的native接口的fifo使用的建議方式,以滿足大多數(shù)的應(yīng)用場景。
1?Fifo的定制
- 通常情況選擇independent clock模式,這樣讀寫時(shí)鐘是否同源都可以支持,便于后期維護(hù)修改。
- 根據(jù)所需深度選擇distributed ram 與 blockram類型,原則上小的fifo建議使用distributed?ram類型以節(jié)省block?ram資源。
- 選擇Standard 或FWFT模式,選擇StandardFIFO ,默認(rèn)為1clk延遲,F(xiàn)WFT無延時(shí)。當(dāng)block?ram類型時(shí),可以選擇添加output?registers,在復(fù)雜布線情況下應(yīng)該有助于布線結(jié)果,當(dāng)然此時(shí)為2clk延遲。
- 使能resetpin,使能 reset?synchronization,F(xiàn)ull flags reset value值為1。
- 如果采用Prog_full信號反壓寫信號,需要根據(jù)鏈路延遲合理設(shè)置fullthreshold。
- 根據(jù)需要使能wr_data_cnt 和 rd_data_cnt。
2?fifo的復(fù)位
- 采用讀寫時(shí)鐘中的慢速時(shí)鐘產(chǎn)生至少8clk的復(fù)位信號進(jìn)行fifo復(fù)位 , 若采用快時(shí)鐘產(chǎn)生,保證產(chǎn)生寬度滿足以上條件亦可。
- 復(fù)位完成后至少等待60個(gè)慢時(shí)鐘周期再進(jìn)行讀寫操作,嚴(yán)格來講需要等待full信號拉低才可以(異步復(fù)位模式,F(xiàn)ull flags reset value值為1)。此處以附帶safetycircuit為例,若不帶safety?circuit,時(shí)間要比這個(gè)短,但在視頻幀結(jié)構(gòu)應(yīng)用中,只要復(fù)位的時(shí)機(jī)合理,都可以保證此要求。
- 復(fù)位過程中嚴(yán)禁讀寫操作。
- 通常在視頻幀結(jié)構(gòu)處理中,采用視頻vs的后沿構(gòu)造,并滿足以上需求。
- 復(fù)位過程時(shí)鐘必須是穩(wěn)定的。
3?fifo的讀寫控制邏輯
通常fifo的寫使能和寫數(shù)據(jù)由fifo的前后讀寫速度或progfull決定,保證fifo不應(yīng)該被寫溢出,且要保證在復(fù)位時(shí)刻寫使能為0。
通??梢圆捎梅强招盘栕鳛樽x使能(RST拉起來后empty也會拉高,可以保證復(fù)位時(shí)無讀使能)。
也可以采用rd_data_cnt構(gòu)造讀使能。
也可以由獨(dú)立的時(shí)序構(gòu)造讀使能,只要能從fifo的讀寫帶寬上保證讀使能來的時(shí)候fifo中一定由足夠的數(shù)據(jù)即可。
建議對滿寫overflow和和空讀underflow進(jìn)行監(jiān)測。
4?example
4.1?Example1
該用例可以應(yīng)用于數(shù)據(jù)需要跨時(shí)鐘域傳輸時(shí),讀端時(shí)鐘速率大于等于寫端時(shí)鐘,或者是讀端時(shí)鐘速率略小于寫端時(shí)鐘速率,但可以利用數(shù)據(jù)流的行間隔時(shí)間以及合理的fifo深度,保證所有寫入的數(shù)據(jù)均能夠及時(shí)讀出而不被新寫入的數(shù)據(jù)覆蓋。
此種應(yīng)用時(shí),fifo的復(fù)位可以是上電一次合理時(shí)間的復(fù)位,若是視頻流,建議在視頻垂直逆程通過vs的沿構(gòu)造合理的周期性復(fù)位。
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信號反壓寫端數(shù)據(jù)流,從而保證fifo的讀寫的速率匹配。通??捎迷趂ifo前端的數(shù)據(jù)已經(jīng)在一個(gè)緩存中,并且可以隨時(shí)訪問。這樣可以通過這個(gè)機(jī)制跨到fifo的讀時(shí)鐘域下。這里需要根據(jù)鏈路的延遲合理時(shí)鐘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中數(shù)據(jù)量構(gòu)造讀取fifo的邏輯,寫端數(shù)據(jù)流可以不間斷的寫入fifo。所以需要保證fifo的讀取速率能夠大于寫入速率。
通常如果有多路異步數(shù)據(jù)流需要同步到同一處理時(shí)鐘域的時(shí)候,可以采用此種方式,只需保證讀時(shí)鐘大于寫時(shí)鐘即可。
讀取fifo的使能信號還可以作為所有這路數(shù)據(jù)流后續(xù)所有處理邏輯的clock enable信號,以達(dá)到減少動態(tài)功耗的目的。
當(dāng)然如果不采用ce的方式,也可以用讀使能與上數(shù)據(jù)流中的嵌入的de,構(gòu)造出新的數(shù)據(jù)使能信號。(寫使能的機(jī)制能夠保證所有數(shù)據(jù)能從fifo中讀出)
assign wr_en = (fifo_rst || full) ? 0 : 1;
always@(posedge proc_clk)
begin
if(rd_dat_cnt >= 8) // 根據(jù)fifo深度合理設(shè)置即可
rd_set <= 1'b1;
else
rd_set <= 1'b0;
end
always@(posedge proc_clk)
begin
if(rd_dat_cnt<=4)// 根據(jù)fifo深度合理設(shè)置即可
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