介紹
無論何時(shí),在復(fù)雜的 FPGA 設(shè)計(jì)過程中,都不可避免地需要在模塊之間發(fā)送數(shù)據(jù),實(shí)現(xiàn)這一點(diǎn)的常用的是 FIFO。
FIFO
寫入:當(dāng)寫入 FIFO 時(shí),需要確保不要寫入太多數(shù)據(jù)以致 FIFO 溢出。為了幫助解決這個(gè)問題,F(xiàn)IFO 通常有一個(gè)完整的計(jì)數(shù)標(biāo)志,有時(shí)還可以使用一個(gè)watermark端口。
watermark:本質(zhì)上告訴 FIFO 中的項(xiàng)目何時(shí)超過一定數(shù)量,這時(shí)候應(yīng)該放慢速度或不放入數(shù)據(jù)。但如果想發(fā)送特定數(shù)量的數(shù)據(jù),將需要添加額外的步驟在狀態(tài)機(jī)中管理“above watermark”的情況。在狀態(tài)機(jī)上工作時(shí),可能需要添加狀態(tài)和寄存器來管理邊緣情況(空滿情況)。
full flag:比較棘手的信號(hào),因?yàn)?full flag 可能會(huì)在輸入數(shù)據(jù)的同一時(shí)鐘變高。如果有流水線設(shè)計(jì),則需要在檢測到“full”狀態(tài)時(shí)緩沖這些數(shù)據(jù)。
count:計(jì)數(shù)可以大致了解可以進(jìn)入 FIFO 的數(shù)據(jù)量。計(jì)數(shù)的更新比watermark和full flag慢,并且會(huì)給你一個(gè)保守的 FIFO 內(nèi)的空間計(jì)數(shù)。我很想經(jīng)常使用它,但我發(fā)現(xiàn)我需要在狀態(tài)機(jī)中添加一些狀態(tài)來管理它。
Reading:從 FIFO 讀取通常不會(huì)那么糟糕,只要在空標(biāo)志不置位時(shí)不讀取即可。
Double Buffer
我?guī)煾底屛铱紤]使用雙端口block-ram 作為雙緩沖器。就像 FIFO 一樣,類似具有如下行為的讀取器和寫入器:
寫入器:將數(shù)據(jù)寫入block-ram,然后使用跨時(shí)鐘域技術(shù)將數(shù)據(jù)的大小和狀態(tài)發(fā)送給讀取器。
讀取器:讀取寫入器放入 RAM 的已知數(shù)據(jù)量。
這種方法的好處在于,寫入器知道它可以寫入多少空間,而讀取器知道它可以讀取多少數(shù)據(jù)。這非常適合流水線設(shè)計(jì)。另一個(gè)方面是寫入器可以在讀取器讀取數(shù)據(jù)時(shí)開始處理block-ram 的后半部分。不過,這種方法并不是自由操作。以下是現(xiàn)在需要由寫入器和讀取器管理的一些事情:
寫入器
ram中有多少空間(或ram的一半)
開始/結(jié)束地址指針
寫入了多少數(shù)據(jù)
讀取器
有多少數(shù)據(jù)可供讀取
開始/結(jié)束地址指針
我喜歡這種雙緩沖區(qū)給我的數(shù)據(jù)量的預(yù)知。這允許我編寫內(nèi)核,將已知數(shù)量的數(shù)據(jù)從雙緩沖區(qū)的輸出轉(zhuǎn)儲(chǔ)到另一個(gè)位置,如音頻或視頻緩沖區(qū)。不幸的是,每個(gè)使用雙緩沖器的模塊都必須設(shè)計(jì)為能夠處理上述所有問題以及更多的跨時(shí)鐘域標(biāo)志。
如果不需要擔(dān)心 FIFO(滿/空)的邊緣情況,這將是最容易使用的機(jī)制。下面將這兩種機(jī)制結(jié)合起來可能是最佳方案。
Ping Pong FIFO
Ping Pong FIFO 本質(zhì)上是一個(gè)上面描述的雙緩沖區(qū),包裹起來看起來像一個(gè) FIFO。所有地址指針和跨時(shí)鐘域通信都包含在一個(gè)簡單的模塊中。模塊如下所示:
modulePPFIFO #(parameterDATA_WIDTH=8, ADDRESS_WIDTH=4 )( //universalinput inputreset, //writeside inputwrite_clock, outputreg[1:0]write_ready, input[1:0]write_activate, output[23:0]write_fifo_size, inputwrite_strobe, input[DATA_WIDTH-1:0]write_data, outputstarved, //readside inputread_clock, inputread_strobe, outputregread_ready, inputread_activate, outputreg[23:0]read_count, output[DATA_WIDTH-1:0]read_data, outputinactive );
有單獨(dú)的寫入端和讀取端時(shí)鐘,選通用于寫入和讀取數(shù)據(jù)。不過也有一些新的信號(hào):
write_ready:這與雙緩沖區(qū)有關(guān),需要管理緩沖區(qū)的兩側(cè)。這2 bit信號(hào)告訴雙緩沖區(qū)的哪一側(cè)已準(zhǔn)備好。
0:緩沖區(qū)的下半部分準(zhǔn)備好
1:上半部分準(zhǔn)備好
write_activate:用戶告訴 Ping Pong FIFO 它想要擁有緩沖區(qū)的一側(cè)
write_fifo_size:表示用戶可以寫入 Ping Pong FIFO(PPFIFO) 的字?jǐn)?shù)。
注意:不需要在完成之前填充寫入端,PPFIFO 將跟蹤寫入的元素?cái)?shù)量并將此信息發(fā)送到讀取端,作為將遞增數(shù)字模式寫入 PPFIFO 的簡單模塊示例
/*Module:ppfifo_source * *Description:PopulateaPingPongFIFOwithanincrementingnumberpattern */ moduleppfifo_source#( parameterDATA_WIDTH=8 )( inputclk, inputrst, inputi_enable, //PingPongFIFOInterface input[1:0]i_wr_rdy, outputreg[1:0]o_wr_act, input[23:0]i_wr_size, outputrego_wr_stb, outputreg[DATA_WIDTH-1:0]o_wr_data ); //LocalParameters //Registers/Wires reg[23:0]r_count; //Submodules //AsynchronousLogic //SynchronousLogic always@(posedgeclk)begin //De-assertStrobes o_wr_stb<=?0; ??if?(rst)?begin ????o_wr_act????????<=??0; ????o_wr_stb????????<=??0; ????o_wr_data???????<=??0; ????r_count?????????<=??0; ??end ??else?begin ????if?(i_enable)?begin ??????if?((i_wr_rdy?>0)&&(o_wr_act==0))begin r_count<=??0; ????????if?(i_wr_rdy[0])?begin ??????????//Channel?0?is?open ??????????o_wr_act[0]??<=??1; ????????end ????????else?begin ??????????//Channel?1?is?open ??????????o_wr_act[1]??<=??1; ????????end ??????end ??????else?if?(o_wr_act?>0)begin if(r_count
正如所看到的,通過添加一個(gè)額外的寄存器來跟蹤添加到 PPFIFO 的數(shù)據(jù)量,不必?fù)?dān)心full flags、water marks 或者counts。
閱讀方面更容易。PPFIFO 知道首先寫入哪個(gè)緩沖區(qū),因此用戶只需要觀察一個(gè)read_ready標(biāo)志,然后使用read_activate告訴它我們有控制權(quán)。以下是從 PPFIFO 讀取數(shù)據(jù)的示例:
這里有更具體的細(xì)節(jié):
用戶監(jiān)視“read_ready”位:當(dāng)“read_ready”信號(hào)為 1 時(shí),ppfifo 為用戶準(zhǔn)備好一個(gè)數(shù)據(jù)塊。
用戶使用“read_activate”信號(hào)激活該塊并使用“read_strobe”讀取 NEXT 數(shù)據(jù)
“read_count”是緩沖區(qū)中數(shù)據(jù)元素的總數(shù)。
用戶必須在將“read_activate”設(shè)置為低之前讀取所有數(shù)據(jù)
/*Module:ppfifo_sink * *Description:WheneverdataisavailablewithintheFIFOactivateitandreaditall */ moduleppfifo_sink#( parameterDATA_WIDTH=8 )( inputclk, inputrst, //PingPongFIFOInterface inputi_rd_rdy, outputrego_rd_act, input[23:0]i_rd_size, outputrego_rd_stb, input[DATA_WIDTH-1:0]i_rd_data ); //LocalParameters //Registers/Wires reg[23:0]r_count; //Submodules //AsynchronousLogic //SynchronousLogic always@(posedgeclk)begin //De-AssertStrobes o_rd_stb<=??0; ??if?(rst)?begin ????o_rd_act??????????<=??0; ????r_count???????????<=??0; ????o_rd_stb??????????<=??0; ??end ??else?begin ????if?(i_rd_rdy?&&?!o_rd_act)?begin ??????r_count?????????<=??0; ??????o_rd_act????????<=??1; ????end ????else?if?(o_rd_act)?begin ??????if?(r_count?
下面設(shè)計(jì)一個(gè)簡單的測試模塊來演示 Ping Pong FIFO。
源代碼地址:
https://github.com/CospanDesign/verilog_ppfifo_demo
下面是幾個(gè)簡單模擬的截圖:
在讀事務(wù)開始和下一個(gè)寫事務(wù)開始時(shí)放大仿真區(qū)域:
在讀取事務(wù)之間放大
半放大
截圖可能不清晰,建議自己仿真。
總結(jié)
PPFIFO除了上面用于解決FIFO的“痛處”外,常見的還是處理高速數(shù)據(jù)流處理,下面是一個(gè)10M數(shù)據(jù)流分成兩個(gè)5M數(shù)據(jù)流的例子。
審核編輯:彭靜
-
FPGA
+關(guān)注
關(guān)注
1629文章
21729瀏覽量
602977 -
FPGA設(shè)計(jì)
+關(guān)注
關(guān)注
9文章
428瀏覽量
26506 -
數(shù)據(jù)
+關(guān)注
關(guān)注
8文章
7002瀏覽量
88938 -
fifo
+關(guān)注
關(guān)注
3文章
387瀏覽量
43647
原文標(biāo)題:乒乓操作實(shí)例講解-FIFO
文章出處:【微信號(hào):Open_FPGA,微信公眾號(hào):OpenFPGA】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論