學生期間,做的設計比較小或者偏向 demo 類型,那麼 ip 核是會佔據設計的很大一部分。但使用 ip 核本身對學習者來說就很有意義。通過 ip 的使用,會瞭解一個工程如何組織,如何閱讀手冊,如何通過模擬結果優化修改自己的設計。本系列就會通過使用一系列的基礎 ip,討論如何組織工程,閱讀手冊,編寫基礎的粘合邏輯,testbench 以及功能模擬。

使用 ip 對於數字邏輯方面的工作來說,是非常正常的,基礎的 ip 之於數字邏輯設計,與與非門

相比大概只是設計層次上的差別。更何況對於 SoC 公司來說,購買一整個外設模塊的 ip 也是很正常的,比如 USB 之類的模塊。


本文作為本系列的第一篇,將會從我們最親近的老朋友 FIFO 開始。使用 ip 核的教程在網路上有很多,大多數文章各有特色(當然轉載的也沒那麼有特色),我也會努力寫出自己的特色。本系列將偏基礎向(囉嗦),希望能幫助逐漸入門的同學,看一遍文章,操作一遍,就能提高一點滋事水平。

什麼是 FIFO

寫到後面纔想起來,我們既然是基礎向的教程,首先應該先說說什麼是 FIFO。

FIFO 就是 Fire In Front dOOr 的縮寫,意思是城門失火,殃及池魚。。。開個玩笑。

First In First Out,先入先出隊列。FIFO 本質上只有兩種操作,讀&寫,分別從隊列的兩端讀取第一個數據,寫入最後一個數據。

如圖是一個深度為5的FIFO ,通過wtr/rd 指針的移動實現FIFO讀寫

FIFO 在數據結構課上最先和大家見面,廣泛用於計算機程序和結構中,在 FPGA 中的 FIFO 的含義和軟體中的 FIFO 完全相同,只不過更加貼近硬體的實現。在數據緩衝,跨時鐘域處理中將會大量運用到 FIFO 結構。

在 Vivado 中創建一個 ip 核

Vivado 是 Xilinx 家的集成設計環境,筆者使用的版本是 2018.1,Vivado 在 2018 年的第一個版本(以前以為.1 就是一月份出的意思)。每年據說共有 4 個版本,但目前官網上的版本還是 2018.2 。如果初學者不需要維護現有項目,使用一個次新版本一般沒有問題。Vivado 高版本可以打開低版本工程(需要 update),低版本僅能「打開」高版本工程,打開為只讀模式,需要另存為當前版本纔可以編輯。

vivado的界面我就暫時不介紹了

打開軟體後,第一感覺還是很舒服的(不知道讀者們怎麼看)。暫時先不介紹整個界面,從左側導航欄選擇 ip Catlog 就可以新建一個 ip 核,面對很多很多的 ip,可以使用上方的搜索功能,鍵入 fifo,額,發現有很多 fifo,還都是什麼 axi 什麼的。這裡我們選擇 FIFO Generator。(如果你的屏幕不夠大,你可能需要往下拉,才能看見她)關於 AXI 介面你可以從以下的鏈接瞭解,本文暫時不會涉及。

ljgibbs:深入 AXI4 匯流排(一)握手機制?

zhuanlan.zhihu.com
圖標

視你的電腦性能而定,n 秒後你打開了 ip 核的設置界面:

左側是根據你目前的設定,暫時你的 fifo 模塊長這樣。右側是 FIFO 的配置選項,共有幾個方面的配置:Basic ,Native port 等。本文基本上會把各常用的配置項都講解一下。不過筆者經歷尚淺,可能有部分配置講的不完全對,歡迎指出。

BASIC

BASIC 中玩家可以控制你 FIFO 的兩個方面,一是 FIFO 的介面,二是 FIFO 的類型。

FIFO 的介面分為兩類,一類是 Native 介面,這類介面使用比較簡單,另一類是 AXI 協議介面,這類協議口線比較多,操作相對複雜,但是 AXI 是一種標準化的匯流排介面,有很多標準化的好處並且運用廣泛。關於這兩種介面的比較,可以參看這篇文章的第一部分

ljgibbs:深入 AXI4匯流排 (四):RAM 讀取實戰?

zhuanlan.zhihu.com
圖標

我們今天主要討論在簡單設計中,使用比較廣泛的 Native 介面。

說到 FIFO 的類型,主要有兩部分的不同:1.讀寫是否使用一個時鐘 2.使用何種硬體資源。

硬體資源分為三種,使用 BRAM,即塊 RAM 資源,這是 FPGA 內嵌的一種重要的專用 RAM 資源;使用分散式的 RAM (Distributed RAM),即將 FPGA 中的基礎,多用途的 LUT 查找表資源用作 RAM;使用內嵌的專用 FIFO 資源,關於這個我不是很熟悉,之後會查一下,專業 FIFO 應該會提供很小的延遲。

使用不同的硬體資源構成的 FIFO 會有不同的特性,可以在圖中的表格中看到,使用 BRAM 好處最多,可以在讀寫兩端使用不同的數據寬度,可以使用 ECC (一種數據校驗特性),支持 First-World Fall Through ,以及支持動態錯誤注入。錯誤注入一般用於模擬真實環境中,數據出現錯誤的情況。而使用分散式的 RAM 則僅支持 First-World Fall Through 功能,但 BRAM 是一種比較重要的資源,如果設計的 FIFO 對延時不敏感,可以使用分散式的 RAM 以節約 BRAM 資源。分散式 RAM 由 LUT 查找表組成,LUT 是 FPGA 的基本資源。

Native Ports

在 Native Ports 中比較重要的是設定 FIFO 的數據寬度以及 FIFO 的深度。寬度指的是數據線的位數,即同時能夠寫入讀取多少比特的數據;如果使用 BRAM ,那麼讀取數據的寬度可以和寫入數據的寬度不同。但也不可以完全不匹配,舉個例子,寫入數據寬度為8,那麼讀取數據寬度就可以為 1,2,4,8;寫入數據寬度為3,那麼讀取數據寬度只能為8。也就是說,讀取寬度可以為寫入寬度的冪級數分之一。

深度指的是 FIFO 的容量,這在 FIFO 設計中是一種主要的考量,更大但沒有必要的深度會造成資源浪費。當然,如果深度不夠則是一種更尷尬的情況。如果功能驗證中,和 FIFO 有關的部分出現了所謂的「奇怪」現象,那麼應該觀察是否是 FIFO 溢出造成的。

FIFO 的複位使用的高電平複位,如果設計中系統複位信號是低電平有效的,那麼不要忘記要將系統複位電平取反後再接入 FIFO 複位電平。一般我們在同步系統設計中使用非同步複位。

上圖中系統提示,FIFO 在讀取時有一個時鐘的 latency(延遲),潛臺詞是寫入將不會有延遲。這個延遲我們將在後文的模擬結果中分析。

Status Flags

在第三個頁面中,可以為我們的 FIFO 增加一些狀態信號,包括 almost Full/Empty 信號,這兩個信號,顧名思義,就是在 FIFO 幾乎要滿或者幾乎要空的情況下置起,所謂的「幾乎「就是指還差一個數據滿或者空。

除了我們的標準「幾乎」滿/空信號之外,玩家還可以設定自己的「幾乎」標準,稱為 programmable Full/Empty 信號,即可以自己定義「可編程幾乎」的標準為 n 個數據。

這個頁面上還提供握手選項,但一般我們在初級設計中不會需要 FIFO 具有這種「交互」特性,實質上 AXI 協議介面也會提供握手特性。

第四個頁面 Data Count,顧名思義就是提供一個信號來表示當前 FIFO 中的數據總數,對於我們演示還是相當有用,我們這裡加上計數信號,但就不再截圖了。(圖已經夠多了)

最後一個頁面是我們 FIFO 的 summary,提供完整的配置信息以供檢查我們的設計。並告知我們該 IP 所使用的硬體資源

Summary

根據報告我們瞭解到這個 FIFO 使用了一個 18K 的 BRAM,雖然這個 FIFO 很小,但還是佔據了一個最小的 18K 塊。在 8 位寬的情況下,18K 塊至少能容納 2048 的深度。

至此,選擇 OK,我們就完成了一個 FIFO IP 的創建,挺輕鬆的,不是麼。

FIFO 的介面

在完成配置之後,我們首先介紹一下 FIFO 的所有 Native 介面。所有介面分為三組 FIFO_WRITE,FIFO_READ 以及時鐘複位介面。

FIFO 的時鐘,複位信號,如果使用的是雙時鐘 FIFO,那麼讀寫信號將會有自己獨立的時鐘。複位信號可以配置為同步或者非同步複位。

FIFO 的讀寫數據 dout/din 信號,數據進出 FIFO 的通道;

FIFO的讀寫使能 wr_en/rd_en 信號 ,在讀寫使能高電平的情況下,時鐘上升沿時刻會進行讀寫操作;

FIFO 的狀態信號,包括 full,empty 等,在相應的狀態下,這些信號會被置起;

在頂層文件中示例化 FIFO

在一個系統設計中,我們會給系統編寫一個頂層文件。所謂頂層文件,即所有設計文件以功能模塊的形式組合,一一被實例化到一個 v 文件中。那麼這個 v 文件一方面位於所有文件的頂層,另一方面,所有的綜合,佈局布線都是以這個頂層文件為單位進行。

換句話說,本文所做的系統很小,小到只有一個 FIFO。但我們還是為這個小小的 FIFO 編寫一個頂層文件。在 source 界面右鍵,添加源文件,選擇添加 design 文件。

命名為 fifo_top.v 並在頂層文件中實例化 fifo 模塊。關於實例化,這裡簡單說下,實例化在 verilog 中類似軟體編程中的實例化對象概念。即通過實例化,在當前源文件中,調用一個現有的模塊,並制定實例化模塊的名字以及埠的連接,舉個栗子:

wire 連線a;
fifo_generator_0(模塊本身的名字) my_fifo(我們實例化的模塊的名字) (
.模塊的埠 a (連線a), // input clk
);

即實例化一個 fifo_generator_0 模塊,命名為 my_fifo。並將一條連接線 連線a 連接到實例化的 my_fifo 模塊的埠 a。關於模塊本身的名字和實例化模塊的名字,不知道這麼解釋會不會比較容易理解:

int a = 1;
//int:(模塊本身的名字)-> 變數類型
//a : (我們實例化的模塊的名字) -> 變數名
// = 1 :( .模塊的埠 a (連線a) )
//; : ;

將實例化模塊比作變數,模塊本身比作變數類型,定義埠連接比作定義變數值。

好了,回到我們的頂層模塊,實例化 FIFO 模塊,並定義頂層模塊的輸入輸出埠,我們這裡直接使用 FIFO 的埠作為頂層模塊的埠。

那麼,怎麼實例化,要怎麼知道 FIFO 的埠,嗯,自己對著 ip 核生成界面寫一下吧。。開玩笑的。實際上你可以這樣:

還是 sources 界面,在下方選擇 IP Sources,點開 ip 核的箭頭,可以找到一個 veo 文件,裡面就是實例化的模板。vho 文件對應的是 VHDL 語言。veo 文件打開後長這樣,複製即可。

這裡插播一個廣告,推薦使用 VSCode 編輯器,好用又好看的開源免費編輯器,微軟良心出品,就是低配機器啟動速度可能捉雞。感興趣的可以戳下方鏈接瞭解。

VSCode 佈道指南 V1.0 (一)?

zhuanlan.zhihu.com圖標

module fifo_top(
input clk,
input rst,
input [7 : 0] din,
input wr_en,
input rd_en,
output [7 : 0] dout,
output full,
output almost_full,
output empty,
output almost_empty,
output [3 : 0] data_count,
output prog_full,
output prog_empty

);

fifo_generator_0 my_fifo (
.clk(clk), // input clk
.rst(rst), // input rst
.din(din), // input [7 : 0] din
.wr_en(wr_en), // input wr_en
.rd_en(rd_en), // input rd_en
.dout(dout), // output [7 : 0] dout
.full(full), // output full
.almost_full(almost_full), // output almost_full
.empty(empty), // output empty
.almost_empty(almost_empty), // output almost_empty
.data_count(data_count), // output [3 : 0] data_count
.prog_full(prog_full), // output prog_full
.prog_empty(prog_empty) // output prog_empty
);
endmodule

未完待續

倒不是我想要把一篇文章硬要分成兩篇文章湊字數,實在是寫著寫著發現寫得實在太長了。。

本文的下篇已經寫了不少,應該很快就會完成,在下篇中我們將瞭解到:

如何添加,編寫 testbench 並展開模擬;

通過模擬結果,結合數據手冊,分析 FIFO 的特性。

因為本文想要走基礎向的風格,所以歡迎私信我或者在評論中留下你們的問題以及建議,這能幫助我寫出更好的基礎向的文章。另外關於 FIFO 本身有什麼疑問也可以提出來,我們可以在下篇中一起研究研究。

聲明

除截圖外,本文配圖來自網路。

推薦閱讀:

查看原文 >>
相關文章