FIFO的使用——quartus的 fifo ip 核使用細節


  1. FIFO的使用

    1. FIFO(First In First Out),即先進先出。 FPGA 或者 ASIC 中使用到的 FIFO 一般指的是對數據的存儲具有先進先出特性的一個緩存器,常被用於數據的緩存或者高速異步數據的交互。它與普通存儲器的區別是沒有外部讀寫地址線,這樣使用起來相對簡單,但缺點就是只能順序寫入數據,順序的讀出數據,其數據地址由內部讀寫指針自動加 1 完成,不能像普通存儲器那樣可以由地址線決定讀取或寫入某個指定的地址。

    2. FIFO 從大的情況來分,有兩類結構:

      1. 單時鍾 FIFO(SCFIFO)
      2. 雙時鍾 FIFO(DCFIFO),其中雙時鍾FIFO又可以分為
        1. 普通雙時鍾(DCFIFO)
        2. 混合寬度雙時鍾FIFO(DCFIFO_MIXED_WIDTHS)
    3. FIFO 常見參數

      1. FIFO 的寬度:即 FIFO 一次讀寫操作的數據位;
      2. FIFO 的深度:指的是 FIFO 可以存儲多少個 N 位的數據(如果寬度為 N)。
      3. 滿標志: FIFO 已滿或將要滿時由 FIFO 的狀態電路送出的一個信號,以阻止 FIFO的寫操作繼續向 FIFO 中寫數據而造成溢出。
      4. 空標志: FIFO 已空或將要空時由 FIFO 的狀態電路送出的一個信號,以阻止 FIFO的讀操作繼續從 FIFO 中讀出數據而造成無效數據的讀出。讀時鍾:讀操作所遵循的時鍾,在每個時鍾沿來臨時讀數據。
      5. 讀寫時鍾:讀寫操作所遵循的時鍾,在每個時鍾沿來臨時寫數據。
    4. 實現 FIFO 的方法

      1. 第一種為用戶根據需求自己編寫 FIFO 邏輯,當對於 FIFO 的功能有特殊需求時,可以使用此種方式實現
        1. 自己編寫fifo邏輯時需要
          1. 考慮數據量,即使用片上邏輯資源實現還是使用片上的存儲資源實現
            1. 邏輯資源實現——使用寄存器,小容量的fifo
            2. 存儲資源實現——例化rom IP核,大容量的fifo
          2. 考慮數據的采樣速率、存儲速率、讀取速率與讀寫時鍾速率的匹配
          3. 考慮fifo的深度,這個還是比較重要,既要不浪費資源,又能完成數據寫入與讀出的任務,而且深度是無論自己寫還是調用IP核都需要考慮的一個問題
      2. 第二種方式為使用第三方提供的開源 IP 核,此種 IP 核以源碼的形式提供,能夠快速的應用到用戶系統中,當用戶對 FIFO 功能有特殊需求時,可以在此源碼的基礎上進行修改,以適應自己的系統需求。
      3. 第三種方式為使用EDA軟件提供的免費 FIFO IP 核,此種方式下, EDA軟件為用戶提供了友好的圖形化界面方便用戶對 FIFO 的各種參數和結構進行配置,生成的FIFO IP 核針對不同公司不同系列的器件
    5. 單時鍾fifo

      1. 單時鍾 FIFO 具有一個獨立的時鍾端口 Clock,因此所有輸入信號的讀取都是在 Clock 的上升沿進行的,所有輸出信號的變化也是在 Clock 信號的上升沿的控制下進行的,即單時鍾 FIFO 的所有輸入輸出信號都是同步於 Clock 信號的。

      2. 單時鍾 FIFO 常用於片內數據交互。例如:在 FPGA 的控制下從外部傳感器讀取到的一連串傳感器數據,首先被寫入 FIFO 中,然后再以 UART 串口波特率將數據依次發送出去。

      3. 在qurtus例化單時鍾FIFO IP核並進行仿真

        1. `timescale 1ns/1ns
          `define clk_period 20
          
          module tb_sc_fifo();
          
          reg clk;
          reg [15:0]data;
          reg rdreq;
          reg sclr;
          reg wrreq;
          
          wire almost_empty;
          wire almost_full;
          wire empty;
          wire full;
          wire [15:0]q;
          wire [7:0]usedw;
          
          sc_fifo	sc_fifo_inst (
          	.clock ( clk ),
          	.data ( data ),
          	.rdreq ( rdreq ),
          	.sclr ( sclr ),
          	.wrreq ( wrreq ),
          	.almost_empty ( almost_empty ),
          	.almost_full ( almost_full ),
          	.empty ( empty ),
          	.full ( full ),
          	.q ( q ),
          	.usedw ( usedw )
          );
          
          initial clk = 1'b1;
          always#(`clk_period/2) clk = ~clk;
          
          integer i = 0;
          integer delay = 0;
          
          initial begin
          	data = 0;
          	rdreq = 0;
          	wrreq = 0;
          	sclr = 0;
          	#2001;
              //A,B,C,D四段程序每次仿真只打開一段
          	/*----A---sclr holds 10ns
          	for(i=0;i<=50;i=i+1)begin
          		wrreq = 1'b1;
          		data = {$random}%65536;
          		#`clk_period;
          	end
          	wrreq = 1'b0;
          	sclr = 1'b1;
          	#10;//(`clk_period);
          	sclr = 1'b0;
              */
              
              /*----B---sclr holds 20ns
          	for(i=0;i<=50;i=i+1)begin
          		wrreq = 1'b1;
          data = {$random}%65536;
          		#`clk_period;
          	end
          	wrreq = 1'b0;
          	sclr = 1'b1;
          	#(`clk_period);
          	sclr = 1'b0;
              */
              
              /*----B--- no sclr
          	for(i=0;i<=50;i=i+1)begin
          		wrreq = 1'b1;
          		data = {$random}%65536;
          		#`clk_period;
          	end
          	wrreq = 1'b0;
              */
              
          	/*---D---test write and read
          	#1000;
          	for(i=0;i<10;i=i+1)begin
          		wrreq = 1'b1;
          		data = i+1;
          		#`clk_period;
          		wrreq = 1'b0;
          		#`clk_period;
          		delay = {$random}%8;
          		#(`clk_period*delay);
          	end
          	#500;
          	for(i=0;i<5;i=i+1)begin
          		rdreq = 1'b1;
          		#`clk_period;
          		rdreq = 1'b0;
          		#`clk_period;
          		delay = {$random}%8;
          		#(`clk_period*delay);
          	end
              */
          	#1000;
          	for(i=0;i<=255;i=i+1)begin
          		wrreq = 1'b1;
          		data = {$random}%65536;
          		#`clk_period;
          	end
          	wrreq = 1'b0;
          	#2000;
          	for(i=0;i<=255;i=i+1)begin
          		rdreq = 1'b1;
          		#`clk_period;
          	end
          	rdreq = 1'b0;
          	#2000;
          	$stop;
          end
          endmodule 
          
        2. A段仿真時序

        3. scfifosclr10ns

        4. B段仿真

        5. scfifosclr20ns

        6. 3圖的sclr信號維持時間沒有超過一個時鍾周期,沒有被時鍾上升沿采樣,這樣的操作會刷新fifo(flush the fifo),但是由於是同步清零的信號,沒有被時鍾采樣,所以會產生問題,剛開始我也很疑惑,sclr信號有效且不被時鍾沿采樣會造成的后果是雖然刷新了fifo,但是只是將數據用0替代,並且將usedw變成零,而且被清零的部分不能存入新的數據。5圖的sclr信號維持了1個周期,可以看出還是有很多不一樣的,會使q的輸出變成未知態,fifo的狀態正常

        7. C段仿真

        8. scfifonosclr

        9. 使用C段仿真可以看出這個scfifo在數據溢出后就不再能寫入了,會把后來的數據丟掉,

        10. D段仿真

        11. scfifotest

        12. scfifobitwr

        13. 通過D段的仿真可以看出數據與usedw的關系,以及空滿標志,almost full/almost empty 標志的變化

        14. 通過上面的仿真可以總結下圖總結了四種方式fifo對應usedw上的數據

        15. scfifo

    6. 雙時鍾fifo

      1. 雙時鍾 FIFO 結構中,寫端口和讀端口分別有獨立的時鍾,所有與寫相關的信號都是同步於寫時鍾 wrclk 的,所有與讀相關的信號都是同步於讀時鍾 rdclk 的。在雙時鍾 FIFO 的符號圖中,位於上部分的為與寫相關的所有信號,位於中間部分的為與讀相關的所有信號,位於下部的為異步清零信號

      2. 雙時鍾 FIFO 的一個典型應用就是異步數據的收發, 所謂異步數據是指數據的發送端和接收端分別使用不同的時鍾域。 使用雙時鍾 FIFO 能夠將不同時鍾域中的數據同步到所需的時鍾域系統中。例如:在一個高速數據采集系統中,實現將高速 ADC 采集的數據通過千兆以太網發送到 PC 機。

      3. 在quartus中例化雙時鍾FIFO IP核並進行實現

        1. //頂層文件
          `timescale 1ns/1ns
          `define rdclk_period 10
          `define wrclk_period 20
          
          module tb_dc_fifo();
          
          reg [15:0]data;
          reg rdclk;
          reg rdreq;
          reg wrclk;
          reg wrreq;
          
          wire [7:0]q;
          wire rdempty;
          wire [8:0]rdusedw;
          wire wrfull;
          wire [7:0]wrusedw;
          
          dc_fifo	dc_fifo_inst (
          	.data ( data ),
          	.rdclk ( rdclk ),
          	.rdreq ( rdreq ),
          	.wrclk ( wrclk ),
          	.wrreq ( wrreq ),
          	.q ( q ),
          	.rdempty ( rdempty ),
          	.rdusedw ( rdusedw ),
          	.wrfull ( wrfull ),
          	.wrusedw ( wrusedw )
          	);
          
          initial rdclk = 1'b1;
          always#(`rdclk_period/2) rdclk = ~rdclk;	
          
          initial wrclk = 1'b1;
          always#(`wrclk_period/2) wrclk = ~wrclk;
          
          integer i=0;
          reg [7:0]data_h;
          reg [7:0]data_l;
          initial begin
          	data = 0;
          	data_h = 0;
          	data_l = 0;
          	rdreq = 0;
          	wrreq = 0;
          	#201
          	for(i=0;i<=255;i=i+1)begin
          		data_h = {$random}%256;
          		data_l = {$random}%256;
          		data = {data_h,data_l};
          		wrreq = 1'b1;
          		#`wrclk_period;
          	end
          	wrreq = 1'b0;
          	#2000
          	for(i=0;i<=511;i=i+1)begin
          		rdreq = 1'b1;
          		#`rdclk_period;
          	end	
          	rdreq = 1'b0;
          	#200;
          	$stop;
          end
          endmodule 
          
        2. dcfifo仿真

        3. dcfifo細節1

        4. dcfifo細節2

        5. 對比3,4兩個圖,可以看出寫入的數據為16‘h2481;先讀出地位的8’h81,再讀出高位8‘h24;

        6. 溢出的規律與scfifo相同,如果fifo內空間只剩下1個8bit的位置,寫入時會丟棄整個16bit的數據,不會只寫入低八位;讀數據有多少就能讀出多少

        7. dcfifobitwr

        8. wrusedw會滯后一個寫時鍾周期,在被讀出時也會按照些寫時鍾變化,rdusedw在寫入數據時會按照寫時鍾變化一次遞增兩個,讀出時按照讀時鍾變化,每次減少一個

  • tips
    • LE 緊密且有效的提供了高級功能的邏輯使用。 LE 基本包含以下幾個部分:
      1. 4 輸入查找表 LUT
      2. D 觸發器(可以對每個 LE 配置可編程的寄存器為 D、 T、 JK 或 SR 觸發器操作)
      3. 組合邏輯。
    • 例化FIFOIP核的一些細節
      • do you want a common clock for reading or writing the fifo
        • yes——單時鍾fifo,單時鍾fifo輸入輸出位寬只能相同
        • no ——雙時鍾fifo,雙時鍾fifo輸入輸出位寬可以不同
      • which type of optimization do you want
        • 選擇什么種類的優化,提供了三種選擇
        • lowest latency but requires synchronized clocks,最低延遲,但要求時鍾同步,這個選項使用一個同步階段,沒有亞穩態保護,他是最小尺寸,提供良好的Fmax
        • minimal setting for unsynchronized clocks,不同步的時鍾設置為最小,這個選項使用兩個同步階段,具有良好的亞穩態保護,他是中等尺寸,提供良好的Fmax
        • best matastability protection, best Fmax and unsynchronized clocks,最好的亞穩態保護,最好的Fmax,時鍾不同步,這個選項使用三個或者更多的同步階段,具有最好的亞穩態保護,他是最大尺寸,給出了最好的fmax
        • 在使用過程中,通常我們的選擇是默認中等優化,具體工程具體分析,要求性能較好,又有資源可以選擇第三種優化方法
      • fifo設置的關鍵
      • which kind of read access do you want 'rdreq' signal
        • 兩種設置的區別,normal synchronous FIFO mode 與 show-ahead synchronous FIFO mode區別
        • 選則第一種普通方式則 rdreq 信號作為實際意義上的讀請求信號,當該信號有效時 FIFO 中的控制邏輯從存儲器中讀取一個數據輸出到 q 端。
        • 如果選中 Show-ahead 方式,則 rdreq 實際作為了讀應答信號,即 rdreq 還沒有有效時, q 端口上已經輸出了一個有效的數據, rdreq 信號有效的時候則相當於通知 FIFO 內部的控制邏輯 q 端口上的數據已經被讀取,則 FIFO 內部的邏輯會從 RAM 中再取出一個新的數據,在下一個時鍾周期輸出到 q 端口上。該模式在實際中應用也非常的普遍,因為 q 端口上的數據 與 rdreq 同時有效,沒有讀潛伏期。
        • 使用前顯模式會使fifo性能下降
      • would you like to disable any circuity protection ?
        • 如果你不需要上溢檢測和下溢檢測保護電路,可以通過下面的選項禁用他們,以此來提高fifo性能,上溢檢測保護電路主要用於在fifo滿時禁止wrreq端口,下溢檢測保護電路主要是用於在fifo空時,禁止rdreq端口,他們默認的狀態是打開的。
      • 在EDA標簽中可以看到仿真IP核所需要用到的庫文件,如果想將fifo應用到其他EDA文件中,可以通過generate netlist 生成IP_syn.v文件,但並不是所有的第三方EDA工具都支持
    • 調用一個IP核的過程(quartus)
      • 參數設置(parameter settings)
      • 電子設計自動化(EDA)
      • 總結(summary)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM