1 初識FPGA


文章目錄

  • 1 初識FPGA
  • 1.1 基本認知
  • 1.1.1 什麼是FPGA?
  • 1.1.2 什麼是HDL?什麼是Verilog?
  • 1.1.3 硬件開發與軟件開發
  • 1.1.4 FPGA與其他硬件的對比
  • 1.1.5 FPGA優勢與侷限性
  • 1.1.6 FPGA的應用
  • 1.1.7 FPGA的學習之路
  • 1.2 FPGA開發流程
  • 1.2.1 一般性的FPGA開發流程
  • 1.2.2 利用Vivado開發FPGA
  • 1.2.3 硬件調試與仿真(ILA核/VIO核)
  • 1.3 FPGA芯片介紹
  • 1.3.1 FPGA的發展史
  • 1.3.2 FPGA廠商及型號
  • 1.3.3 FPGA硬件結構
  • 1.3.3.1 數字電路基本結構
  • 1.3.3.2 LUT查找表
  • 1.3.3.3 可配置邏輯塊CLB
  • 1.3.3.4 其他豐富的FPGA資源
  • 1.4 Verilog基礎語法
  • 1.4.1 基礎知識
  • 1.4.2 數據類型
  • 1.4.3 運算符
  • 1.5 Verilog程序框架
  • 1.5.1 Verilog註釋
  • 1.5.2 Verilog關鍵字
  • 1.5.3 Verilog程序框架


注:筆記主要參考:

  1. B站 正點原子 教學視頻“正點原子手把手教你學FPGA-基於達芬奇Pro開發板 Artix-7 XC7A35T/XC7A100T”。
  2. 小梅哥愛漂流 教學視頻“【零基礎輕鬆學習FPGA】小梅哥Xilinx FPGA基礎入門到項目應用培訓教程”。
  3. B站搬運 “特權同學2020版《深入淺出玩轉FPGA視頻教程》 Xilinx Artix-7 FPGA快速入門、技巧與實例”。

注:工程及代碼文件放在了本人的Github倉庫。


1.1 基本認知

1.1.1 什麼是FPGA?




FPGA的虛擬化處理器模擬集羣_學習


圖1-1 Xilinx公司Atrix-7系列FPGA


FPGA的全稱是 現場可編程門陣列(Field Programmable Gate Array),簡單來説,就是能用代碼編程,直接修改FPGA芯片中數字電路的邏輯功能。那這樣就怎麼了呢?因為早期芯片生產出來後,電路就固定好不會改變了,於是功能也就固定了,這種芯片就是ASIC(專用集成電路,Application Specific Integrated Circuit)。而要想改變電路結構就需要重新設計芯片、重新“流片”、測試等,整個過程非常的 耗錢 耗時間。那突然間FPGA橫空出世,支持通過修改軟件代碼來改變硬件電路結構,是不是就非常具有開創性!😎

還記得那一系列數電實驗嗎?那些手動搭建起來非常繁瑣的數字電路,都可以通過簡單的verilog代碼直接在FPGA芯片中生成。

  • B站視頻“花式PCB設計展”,給出了許多奇奇怪怪的PCB設計,其中不乏“雜草式”接線技術,展示了手動搭建大規模數字電路的繁雜性。

1.1.2 什麼是HDL?什麼是Verilog?

HDL(硬件描述語言,Hardware Description Language)是用於描述數字電路結構和功能的語言的統稱。HDL所描述的電路可以通過綜合工具將其轉換為門級電路網表,然後將其與某種工藝的基本元件逐一對應起來,再通過佈局佈線工具轉換為電路佈線結構。

上面這一段翻譯成人話就是,要編寫FPGA的代碼(術語就叫做描述電路結構)肯定得需要一種語言吧,那C/C++、Java、Python等一眾軟件語言可以嗎?那肯定不行啊,軟件語言無法描述出清楚的電路結構,也沒有辦法約定時鐘、走線、端口等,簡單一句話,它們沒這個實力知道吧😎,所以這時候就需要專用的 硬件描述語言HDL 了。不過不像軟件語言那樣枝繁葉茂,經過近三十年的發展,只有 VerilogVHDL 二者最終脱穎而出,成為了公認的行業標準,兩者邏輯相通,但 新手建議首先學習verilog

  • VHDL由美國軍方研發,1987年就成為IEEE標準,1993年做過版本修訂。語法嚴謹。
  • Verilog起源於民間,1995年正式成為IEEE標準,2001和2005年分別做過版本修訂。語法相對自由,類似C語言,易於上手。




FPGA的虛擬化處理器模擬集羣_ci_02

VHDL

Verilog

VHDL


圖1-2 VHDL的兩個分類


那怎麼開始學習verilog HDL呢? Verilog作為一種高級的硬件描述語言,很多語法現象與 C語言 非常相似,因此在 C語言 的編程基礎上去學習 Verilog 比較容易。只不過需要特別注意培養硬件設計的思想,着重理解Verilog的 “並行”特性。多敲敲代碼總是沒錯的😉。

1.1.3 硬件開發與軟件開發

那麼為什麼有了軟件編程還需要FPGA這種硬件編程呢?換句話説,為什麼人們想要改變數字電路的邏輯功能呢,軟件又不是不能跑?主要有兩點:靈活、處理速度快。

  1. 靈活性:使用5G基站舉例子,目前一般採用FPGA芯片,這樣當有技術需要更新迭代時,只需要更新verilog代碼,就可以相應的改變硬件電路的結構,而不需要再更換整個基板(因為芯片是焊接在板子上的),這樣就大大節省了成本,體現出“靈活性”。
  2. 速度快:由於verilog編程與實際的電路結構緊密相連,天然具有並行處理數據的能力;而CPU(單片機也是一種CPU)則是採用“指令集”的形式實現編程,單個CPU核心只能串行的處理數據,通用性好,但當數據量較大、重複性較高時,處理速度明顯不如FPGA。

舉個例子,現在要求隨機生成1000個不同頻率不同相位的正弦波,然後輸出所有正弦波相加的和。軟件編程就只能先將所有的正弦波都生成出來,然後再相加。FPGA則可以同時生成1000個正弦波發生器,然後直接通過級聯的加法器輸出結果。所以顯然FPGA的速度更快。

當然,這並不是説軟件開發就不重要了,軟件開發和硬件開發各自有各自的深奧之處。但對於之前一直在使用C語言/MATLAB進行編程的同學來説,進行FPGA開發時首先要關注的點就是並行。即,不同的always塊的信號是並行處理的,在一個begin end語句內的所有信號也是並行處理的(上面提到的alwaysbegin end在1.6.3小小節介紹)。也就是説,FPGA編程要時刻認識到代碼/框圖都是與實際的硬件電路相對應的,如果沒有添加額外的控制邏輯,所有模塊一上電就會同時運行。

FPGA開發小貼士:

  • 頂層module中的輸入輸出信號需要對應實際的硬件端口。
  • 所有信號都要設定位寬,處理信號時要時時刻刻記住每一個信號的位寬是多少。

1.1.4 FPGA與其他硬件的對比

- FPGA和單片機
説到硬件編程,很多同學想到單片機也需要將代碼下載到芯片中實現功能,那FPGA和單片機又有什麼區別呢?其實也就是上面提到的,單片機本質上也是一種CPU,並且市面上常見的單片機初學開發板所搭載的CPU都是ARM架構。
對單片機編程並不改變其電路的內部連接結構,只是根據要求實現的功能來編寫運行的程序(指令),所以單片機編程和軟件編程本質上沒有區別;而FPGA編程每次都會改變內部的硬件電路。


表1-1 FPGA和單片機的區別


芯片

類型

速度

結構

應用場景

開發語言

單片機

ASIC


哈佛總線

馮諾依曼架構

工業控制

C語言

FPGA

半定製電路


查找表

算法實現IC驗證

Verilog、VHDL


- FPGA、ARM與DSP

  • ARM:側重於控制和傳輸,通常也包含豐富的人機交互功能的處理器。
  • DSP:為高速數據的實時採集和傳輸、複雜的運算處理應用而進行定製化的處理器。
  • FPGA:覆蓋各種ARM和DSP無法滿足的應用,更趨向於小批量、定製化、實時性要求又很高的應用。

三者某種意義上是互補的,甚至一些芯片上存在二者共生的現象,比如Xilinx的Zynq中有ARM也有FPGA,TI的達芬奇芯片中有DSP也有ARM。

- FPGA與GPU
由於人工智能非常火熱,所以人們常常將FPGA和GPU放在一起進行討論,看看誰更適合做一些AI的算法實現。

  • 峯值性能:GPU遠高於FPGA。
    GPU能夠集成上千個內核,架構高度優化,關鍵路徑可手動調整,絕對性能可接近工藝極限。
    FPGA設計資源受限,時鐘頻率受制於目標設計的佈局佈線,絕對性能有一定瓶頸,不及GPU。
  • 靈活性:FPGA遠好於GPU。
    GPU產品化之後,硬件架構固定,無法滿足一些小眾算法或新生算法對硬件架構的最優化“調整”。
    FPGA的可編程特性,使各種不同的硬件架構最優化需求都能得到滿足,一定程度上彌補絕對性能的不足。
  • 功耗:GPU的絕對功耗(200W)遠大於FPGA(10W)。
    在同等效率情況下,經過架構優化的FPGA功耗也遠低於GPU。

- 補充:FPGA與CPLD區別。

1.1.5 FPGA優勢與侷限性

經過上面的探討,簡單總結一下FPGA優勢與侷限性。


表1-2 FPGA的優勢


靈活性

並行性

集成性

可重編程,可定製

更快的速度、更高的帶寬

更多的接口和協議支持.

易於維護,方便移植、升級或擴展

滿足實時處理的要求

可將各種端接匹配元件整合到器件內部,

降低NRE成本,加速產品上市時間

有效降低BOM成本

支持豐富的外設接口,可根據需求配置

單片解決方案,可以替代很多數字芯片

減少板級走線,有效降低佈局佈線難度


FPGA技術的侷限性

  1. 絕對性能受限:在某些性能上,FPGA可能比不上專用芯片;或者至少在穩定性方面,FPGA可能要遜色一些。
  2. 靈活性是否適用:如果設計不需要太多的靈活性,FPGA的靈活性反而是一種浪費,會潛在的增加產品的成本。
  3. 功耗相對較高:相比特定功能、應用集中的ASIC,使用FPGA實現相同功能可能產生更高的功耗(相比GPU,則單位性能功耗要低很多)。
  4. 設計複雜性高:在FPGA中除了實現ASIC所具有的複雜功能,還得添加一些額外的功能,實屬一大挑戰。FPGA的設計複雜性和難度可能會給產品的開發帶來一場噩夢。

所以,基於上述FPGA的優缺點,在開發目標產品時,是否需要使用FPGA就需要考慮以下幾點:

  • 可升級性:若不需要,那麼FPGA的意義就失去大半。
  • 開發週期:相比於ASIC,FPGA開發的複雜度和開發週期會更高,要看項目是否允許。
  • 產品性能:FPGA的絕對性能比不上專用芯片,要衡量項目是否可以接受絕對性能的降低。
  • 實現成本:FPGA相對於ASIC的成本可能會高一些,要看用户是否可接受。
  • 可用性
  • 其它限制因素

1.1.6 FPGA的應用

FPGA的應用領域非常廣泛,下面舉幾個例子。

- 邏輯粘合
如一些嵌入式處理常常需要地址或外設擴展,CPLD器件尤其適合。在FPGA被髮明的早期,使用FPGA做的很大一部分工作就是邏輯粘合。今天已經少有項目會選擇一顆FPGA器件專門用於邏輯粘合的應用,但是在使用FPGA做一些大規模處理的同時,順便做些邏輯粘合的工作倒是非常普遍。



FPGA的虛擬化處理器模擬集羣_Verilog_03


圖1-3 邏輯粘合示意圖


如上圖中,左側是一顆DSP,由於EMIF接口數量不夠,可能需要拓展,就使用FPGA接了三個雙口RAM。

- 實時控制
如液晶屏或電機等設備的驅動控制,此類應用都具有很強的實時性但相對簡單,所以主要使用CPLD或低端FPGA。

- 高速信號採集和處理
如高速AD前端或圖像前端的採集和預處理,近年來持續升温的機器視覺應用也幾乎是無一例外的都使用了FPGA器件。



FPGA的虛擬化處理器模擬集羣_fpga開發_04


圖1-4 高速採集處理應用示意圖


如今很多機器視覺的應用也採用了類似的方式,只不過前端的AD會被替換成Math Sensor。

- 協議實現
如更新較快的各種有線和無線通信標準、廣播視頻及其編解碼算法、各種加密算法等,諸如此類小批量、定製化、更新換代頻繁的應用使用,FPGA比ASIC更有競爭力。如下圖所示的“SD/HD/3G SDI的協議”或者無線通信基站之間的協議實現。



FPGA的虛擬化處理器模擬集羣_學習_05


圖1-5 SD/HD/3G SDI的協議實現


- 各種原型系統驗證
FPGA支持豐富的接口協議標準,可定製性強,芯片原廠使用FPGA搭建芯片的驗證系統。

- 並行計算(算法實現)
傳統的CPU計算受限於其串行順序處理的架構,已經很難適應今天的雲計算和數據中心對大數據運算的需求。而FPGA與生俱來的並行性與靈活可編程特性是其進入高速運算領域的一大優勢。GPU雖然一直是並行處理的主流方案,但也受限於極高的成本和功耗代價。相比之下,單位功耗性能是GPU的3~4倍的FPGA則大有取而代之的趨勢。

- 片上系統SoC
目前在一些單芯片SoC產品,既有成熟的ARM硬核處理器,又有豐富的FPGA資源(IO資源、RAM資源、乘法器資源等)。這種SoC集成度高,布板面積可以做到最小化,並且內部靈活和高吞吐量的高速互聯。如Intel-Altera公司的Soc FPGA和AMD-Xilinx公司的ZYNQ。

- 從行業看FPGA的應用
最後總結一下,FPGA在通信領域、數字信號處理領域、視頻圖像處理領域、高速接口設計領域、人工智能領域、IC驗證領域以及各種定製設計中都有涉獵。

FPGA所誕生並發展的時代是一個好時代,與生俱來的一些特性也註定了它將會在這個時代的大舞台上大放光彩。

1.1.7 FPGA的學習之路

FPGA設計是一種整合的技術,要求從不同的設計領域融合多種設計技能。要想成為一名資深的FPGA開發工程師,開發一些複雜的FPGA應用,極可能涉及到多種交叉的設計技能,需要掌握交叉學科知識、擁有豐富的技能,可能還需要來自系統、軟件和硬件工程的設計技能。



FPGA的虛擬化處理器模擬集羣_fpga開發_06


圖1-6 FPGA開發可能會使用的知識領域


表1-3 FPGA開發可能需要掌握的技能


領域

硬件/DSP設計

軟件(HDL)設計

軟件/DSP設計

系統設計





板級硬件與接口設計

HDL語言的設計輸入

處理器代碼模塊的定義

處理器需求分析

DSP算法的硬件實現

腳本實現自動化處理

代碼的編寫和測試

設計數據流的定義

邏輯電路設計

設計測試平台的開發

DSP算法的軟件實現

處理器架構的選擇

功耗與去耦設計

HDL流程設計的配置管理

常規的代碼調試和驗證

硬件/軟件實現的權衡

硬件仿真

設計約束

在處理器上運行操作系統

系統級設計的層次結構定義

板級引腳分配

支持設計複用

代碼的配置管理

功能劃分和模塊化設計

硬件模塊調試

系統模塊的集成與接口測試

I/O特性的定義

系統級測試,調試和驗證

設計佈局

設計優化權衡

信號完整性和終端匹配

FPGA器件和封裝選擇


當然,上述技能雖多,但不用害怕,沒有人天生就懂軟件/硬件開發,所有大佬都是從“Hello World”/點燈開始的。下面就是特權同學總結的FPGA三階段:

1. 入門階段
從無到有的階段,初識FPGA,是不折不扣的“菜鳥”。
要初步瞭解FPGA是什麼、能做什麼等基本的理論。
要學會HDL語言,能夠使用EDA工具完成FPGA的代碼設計、仿真驗證、時序設計、綜合和映射。
能夠在開發板上下載並跑例程,進行初步的板級調試。
這一階段的目標是"熟練"。

2. 精通階段
提高自己的設計和調試能力。
掌握如何用合適的HDL語法風格設計出最優化的電路;讓EDA工具的不同設置功能服務於具體的設計優化。
掌握不同的板級調試手段並能熟練應用。
這一階段的目標是“精通"。

3. 從業階段
以FPGA產品開發作為自己的職業。
讓FPGA技術以最優的方式服務於產品。
有挑戰也有成就,最長、最難的階段。
這一階段的目標是“專業”。

1.2 FPGA開發流程

1.2.1 一般性的FPGA開發流程



FPGA的虛擬化處理器模擬集羣_Verilog_07


圖1-7 一般性FPGA開發流程


  1. 設計輸入:創建FPGA工程,添加設計源文件,比如HDL文件、EDIF或NGC網表文件、原理圖、IP核模塊、嵌入式處> 理器以及數字信號處理器模塊等。
  2. 設計綜合: FPGA開發工具的綜合引擎將編譯整個設計,並將HDL源文件轉譯為特定結構的設計網表
  3. 約束輸入:指定時序、佈局佈線或者其它的設計要求。如時序約束、I/O弓|腳約束和佈局佈線約束等
  4. 設計仿真:使用仿真I具對FPGA工程進行功能或時序驗證。
  5. 設計實現:將邏輯設計進一步轉譯為可以被下載燒錄到目標FPGA器件中的特定物理文件格式。
  6. 分析實現結果:對設計約束、器件資源佔用率、實現結果以及功耗等設計性能進行分析
  7. 設計優化:分析當前設計結果,對設計源文件、編譯屬性或設計約束進行修改,然後重新綜合、實現以達到設計最優化。FPGA的設計有很多迭代的過程。
  8. 板級調試:生成比特流並下載到開發板上,對FPGA器件進行板級的調試。FPGA有非常豐富的板級調試手段,比如在線邏輯分析儀可以直接查看FPGA內部引腳、接口、走線的信號變化,可以有效提升板級調試效率。



FPGA的虛擬化處理器模擬集羣_Verilog_08


圖1-8 簡化的FPGA開發流程


  1. 概念階段:或稱架構階段,完成項目前期的立項準備,如需求的定義和分析、各個設計模塊的劃分。
  2. 設計實現階段:或稱詳細設計階段,包括編寫RTL代碼、並對其進行初步的功能驗證、邏輯綜合和佈局佈線、時序驗證。
  3. FPGA器件實現階段:除了器件燒錄和板級調試外,也應該包括設計實現階段的佈局佈線和時序驗證,因為這兩個步驟也都是和FPGA器件緊密相關的。

1.2.2 利用Vivado開發FPGA

Vivado就是用來進行FPGA開發的一個工具,可以編譯硬件代碼,完成上面所説的整個FPGA開發流程。市面上主流的FPGA開發工具只有兩款,一個是Xilinx芯片(被AMD收購)專用的 Vivado,另一款則是Altera芯片(被Intel收購)專用的 Quatrus,兩者原理相同,但建議先熟練掌握一個工具而不是混着學。本教程選用正點原子達芬奇PRO開發板,搭載了Xilinx的xc7a100tfgg484芯片,所以本教程都是用Vivado進行開發。

另外,Vivado集成了 HLS( High Level Synthesis) 工具,可以實現直接使用C、C++以及System C語言對Xilinx的FPGA器件進行編程。用户無需手動創建RTL,通過高層次綜合生成HDL級的IP核,從而加速IP創建。在某些使用verilog語言描述異常複雜的場景中(如卷積神經網絡),使用HLS將高級語言轉化為對應的HDL級的IP核,就會非常方便。這種功能會在ZYNQ系列芯片的學習過程中進行介紹,本教程不涉及。

下面是Vivado左側的導航窗格,上一節所述的FPGA開發流程都在這裏完成:



FPGA的虛擬化處理器模擬集羣_學習_09


圖1-9 vivado左側導航窗格



FPGA的虛擬化處理器模擬集羣_學習_10


圖1-10 基於Vivado的FPGA開發流程


  1. 創建源文件:用户有兩種方式可以創建源文件,以描述電路結構與功能,分別是編寫“HDL代碼”、創建BD(框圖,Block Diagram)。
  2. RTL分析:vivado將上述源文件轉換為邏輯門電路
  • 此時可進行“行為仿真Behavioral Simulation”。
  1. 綜合:vivado將經過“RTL分析”後的門電路映射為FPGA器件內部的物理結構。所以“綜合”電路中看不到任何的“門”。“綜合”的結果是所使用的特定FPGA器件中實際存在着的物理結構,如“輸入緩衝”、“查找表”、“觸發器”和“輸出緩衝”等。
  • 此時可進行“綜合後功能仿真”。
  1. 佈局佈線:需要用户指定時序、佈局佈線或者其它的設計要求,如時序約束、I/O引腳約束和佈局佈線約束等。Vivado會自動根據這些約束,將“綜合”給出HDL代碼與實際FPGA器件進行映射,物理視圖與具體的芯片一致。
  • 此時可以進行所有類型的仿真。
  1. 生成比特流、下載到板材上。

注:使用vivado進行後續操作時,會自動檢查前面的步驟是否已經完成,比如直接“綜合”會自動先進行“RTL分析”、直接“生成比特流”則會自動進行整個過程,若有那個環節有錯,vivado會自動停止並給出提示。

在整個開發過程中,為了驗證電路的正確性,還會進行仿真。從上面的示意圖可以看到一共有5種仿真類型。主要分為行為仿真/功能仿真(Behavioral Simulation / Functional Simulation)、時序仿真(Timing Simulation)兩大類:

  1. 功能仿真也稱為行為仿真,主旨在於驗證電路的功能是否符合設計要求,其特點是不考慮電路門延遲與線延遲,主要是驗證電路與理想情況是否一致。
  2. 時序仿真也稱為佈局佈線後仿真,是指電路已經映射到特定的工藝環境以後,綜合考慮電路的路徑延遲與門延遲的影響,驗證電路能否在一定時序條件下滿足設計構想的過程,能較好地反映芯片的實際工作情況。但仿真耗時很長。

仿真是FPGA在板級調試前非常重要的驗證手段。 仿真可以在RTL分析前進行,但是為了提高仿真的有效性,需要先進行RTL分析、綜合。另外,佈局佈線後也可以進行更精確的仿真,但是一般不做。在對時序要求不高的場景中,可以認為時鐘的輕微延遲對電路基本沒有影響,所以為了提高開發效率,一般只進行行為仿真(Behavioral Simulation)。後續還有bug會使用ILA或VIO看波形異常,實在是找不出來問題,才會進行佈局佈線後時序仿真。

下面給出一個簡單的開發實例:假如現在我們要開發一個電路,有三個輸入信號A/B/C,輸出為 AB+C,下面給出其真值表:



cina

cinb

cinc

cout

0

0

0

0

0

1

0

0

1

0

0

0

1

1

0

1

0

0

1

1

0

1

1

1

1

0

1

1

1

1

1

1

  1. 創建源文件,編寫以下verilog代碼描述上述真值表所給出的邏輯功能。
module design_1(
    clk,rst_p,
    cina,cinb,cinc,
    cout
    );
    //定義輸入輸出接口
    input clk, rst_p;       //時鐘及復位信號
    input cina, cinb, cinc; //輸入的三個數據信號
    output reg cout;        //輸出數據信號

    //實現功能
    always@(posedge clk or posedge rst_p)
    if(rst_p)
        cout <= 1'b0;
    else
        cout <= (cina & cinb) | cinc;
endmodule
  1. 進行RTL分析(Run Analysis),點擊“Open Elaborated Design” → “Schematic”,即可看到RTL門級電路:


FPGA的虛擬化處理器模擬集羣_fpga開發_11


圖1-11 【例】RTL門級電路


  1. 進行綜合(Run Synthesis),將上面的門電路映射為FPGA芯片上實際的硬件電路,點擊“Open Synthesized Design” → “Schematic”查看:


FPGA的虛擬化處理器模擬集羣_fpga開發_12


圖1-12 【例】實際的硬件電路


  1. 添加硬件約束(也就是引腳分配),通過查看PYNQ-Z2原理圖可以發現系統時鐘SYSCLK引腳為H16;四個按下為高電平的按鍵開關BTN3~BTN0的硬件端口依次為L19、L20、D20、D19;LED0端口為R14。設置BTN3為復位按鍵,BTN0~BTN2分別為cina/cinb/cinc,LED0為輸出,於是分配硬件端口:


FPGA的虛擬化處理器模擬集羣_學習_13


圖1-13 【例】引腳約束


  1. 生成比特流,下載到開發板,就可以看到相應的實驗現象了。

1.2.3 硬件調試與仿真(ILA核/VIO核)

那硬件開發的過程中,肯定也會出現各種各樣的bug(實驗現象與預期不符),在FPGA開發過程中,最主要的調試方式就是看各個主要信號的波形。本小小節就來介紹使用Vivado進行FPGA程序調試的思路。

1) 硬件異常檢測
在進行程序調試前,首先要確定硬件本身沒有問題。可以按照以下步驟進行:

  1. 下載一個驗證好的例程,測試硬件是否能正常工作。
  2. 假如例程無法運行,排查接口連接、管腳分配。比如按照從電源到各個模塊的順序,測量各個模塊的電壓是否正常。
  3. 如果例程還是無法正常運行,交給硬件工程師/開發板賣家。

2) 行為仿真
確認硬件沒有問題後,那麼程序現象出問題就顯然是軟件程序的問題了。為了簡單快捷,首先可以快速排查幾個常見的錯誤:

  1. 檢查 時鐘、復位 是否接好。
  2. 檢查每一個模塊的接口是否正確。比如涉及到通信的問題,檢查主控制Master和從控制器Slave是否建立的聯繫(兩者之間的valid、ready信號)。
  3. 查看狀態機的狀態是否正常、查看數據是否有問題、查看觸發條件是否有問題。(“狀態機”後續會進行介紹)

注:任意查看“RTL分析”、“綜合”、“佈局佈線”後的三種原理圖,可以快速的查看電路連接狀態。
小技巧:

若此時還是沒有發現問題,就需要編寫仿真文件,來產生設計好的激勵以驅動可能有問題的模塊,這些激勵包括時鐘、復位、控制信號,另外也可以設計一些具有規律性的假數據來進行測試。仿真文件編寫完畢後,再點擊“運行”,就可以看到目標信號的仿真波形了。

3) ILA核與VIO核
一般通過上述“行為仿真”就能找出相應的bug了。但有些實驗涉及到與外部芯片進行通信,導致不是所有的信號都是由FPGA內部控制,比如I2C通信讀寫EEPROM、DDR3讀寫實驗等。比較幸運時,可以找到該芯片對應的仿真代碼,比如EEPROM就有對應的仿真代碼,可以直接在行為仿真中完全模擬出芯片信號的變化。但有些芯片可能沒有對應的仿真代碼;或者説有對應的仿真代碼但實在是過於複雜,進行一次有效的行為仿真需要幾十分鐘,那這個時候就需要硬件電路的幫忙。換句話説,我想直接在有問題的硬件電路上,直接抓取信號看波形。

要想實現上面這個目的,首先想到的是使用示波器,但是示波器只能看到物理存在的接口。那想查看芯片內部信號的波形該怎麼辦呢?就需要使用Vivado內置的IP核:ILA核(Integrated Logic Analyzer)VIO核(Virtual Input/Output)。下面依次介紹。



FPGA的虛擬化處理器模擬集羣_fpga開發_14


圖1-14 ILA/VIO 示意圖


Vivado的LIA/VIO通過將Probe與相應的IP核相連,來監控邏輯內部信號和端口信號,最終將數據通過下載器(JTAG接口)傳輸到PC。簡單來説,就是將數據發送到PC端(vivado軟件)進行顯示。此外,VIO還可以接收電腦數據,用來驅動內部邏輯信號或提供觸發信號。



FPGA的虛擬化處理器模擬集羣_Verilog_15


圖1-15 添加ILA/VIO的方法


上圖給出了添加ILA/VIO的方法:

  1. HDL【常用】:生成ILA的IP核,然後在代碼中進行例化。優點是方便,缺點是要修改代碼。
  2. 信號列表(Hierarchy)或原理圖(Schematic):對代碼綜合之後才能添加。優點是不用修改原代碼,缺點是綜合可能會優化掉待測信號(添加屬性可以解決)。
  3. IP Integrator(IP集成器):在IP Integrator中添加信號,但是隻有在添加硬核或軟核才會用到。

注:添加成功後,下載比特流後會在軟件窗口界面顯示波形等設置。

總結:

  1. ILA核:監控信號。可以將波形存儲在ROM之中,但會消耗FPGA內部的邏輯資源和存儲資源。
  2. VIO核:監控和驅動信號。只能顯示實時波形,但還可以通過電腦端手動更改程序的某些觸發信號。

1.3 FPGA芯片介紹

本小節來點輕鬆的,介紹一下FPGA的發展歷史以及內部結構。內部結構可能看起來有點難,但是先有個印象就行。

1.3.1 FPGA的發展史



FPGA的虛擬化處理器模擬集羣_fpga開發_16


圖1-16 FPGA發明人-Ross Freeman



FPGA的虛擬化處理器模擬集羣_ci_17


圖1-17 曾經的可編程邏輯器件


FPGA的發展史其實也就是近幾十年的事情:

  • 1970年:PLD 是第一款進入人們視野的可編程邏輯器件,採用PROM結構,輸入接口少。
  • 1978年:可編程邏輯陣列PAL 和 通用陣列邏輯GAL 採用了反熔絲技術、EPROM和EEPROM技術,結構功能仍然相對簡單。
  • 1984年:Altera公司於發明了基於CMOS和EPROM技術相結合的 CPLD,可以勝任複雜性較高、速度也較快的邏輯功能。
  • 1985年:Xilinx創始人之一Ross Freeman發明了 現場可編程門陣列FPGA,開啓了可編程邏輯的“高速”發展時代。Freeman先生髮明的FPGA是一塊全部由“開放式門”組成的計算機芯片。可靈活編程,添加各種新功能,以滿足不斷髮展的協議標準或規範,甚至可以在設計的最後階段對它進行修改和升級。

在FPGA剛發明的時,常以有多少“門”(與門、或門、非門等邏輯資源)來衡量一個FPGA。而現在“門”的概念已經逐漸被淡化,FPGA不僅強調其邏輯資源,還包括其他豐富的資源,如可編程IO單元、豐富的佈線資源、靈活的時鐘管理單元、嵌入式塊RAM以及各種通用的內嵌功能單元(如內嵌乘法器、內嵌硬核IP等),很多器件還順應市場需求內嵌專用的硬件模塊。

注:FPGA從高到低分為好幾個級別:系統級、算法級、寄存器級(RTL級、行為級)、門級(佈局佈線後,有延遲)、開關級。



FPGA的虛擬化處理器模擬集羣_學習_18


圖1-18 野心十足的ARM+FPGA架構


近些年來,可編程器件的龍頭老大Xilinx和Altera更是相繼推出了硬核CPU+FPGA的產品,此舉大有單芯片橫掃千軍的架勢。比如上圖所示的藍色PS部分就是CPU硬核(ARM架構),剩下的黃色部分就是FPGA,這樣的產品包括Xilinx的ZYNQ系列和Altera的SoC FPGA。

1.3.2 FPGA廠商及型號



FPGA的虛擬化處理器模擬集羣_ci_19


圖1-19 國內外FPGA市場佔有率


國際上,FPGA的主要生產廠商有:

  • Altera(已被Intel收購)與Xilinx(已被AMD收購):把持中高端市場,長年佔據超70%的市場份額。
  • Microsemi(收購Actel):高度聚焦於FPGA的安全應用領域,如國防和航空。
  • Lattice:在中低端市場有一定市場份額。
  • QuickLogic公司:主要從事客户定製化標準產品。

注:正所謂集羣效應,主流的FPGA供應商幾乎都來自美國加利福尼亞。

那我這一頓庫庫介紹FPGA,其他的啥也沒説。但是很多初學者(比如我)在剛學FPGA找資料的時候,總是會看到什麼FPGA、ZYNQ、PYNQ啥的,被這些不同的芯片型號整的暈頭轉向,那現在就來捋一捋他們的關係:

  • 首先,FPGA、ZYNQ、PYNQ本質上都是FPGA芯片,“最純淨”的可編程邏輯器件就是FPGA
  • 那後來呢,大家發現FPGA這個東西頭腦簡單、四肢發達(就是雖然數據並行計算超快,但是編寫verilog還是有點費時間的),有些事情還是用軟件語言處理效率更方便,於是就可以使用Vivado的 HLS( High Level Synthesis) 工具,將C語言代碼編譯成一個的IP核(軟核,可以調用的硬件電路模塊),就可以在電路中調用該模塊了;
  • 那對於用軟件語言這事,芯片製造商也不能閒着,於是它們就把一個ARM芯片和一個FPGA芯片組合起來,推出了ZYNQ系列芯片。其中的ARM芯片就是硬核,可以使用C/C++編程,編程效率大大提升;
  • 再後來呢,大家發現C/C++對於機器學習的支持不如Python好(雖然機器學習的底層代碼是使用C/C++編寫的),因為Python裏面有很多現成的庫,直接調用現有算法非常方便,於是進一步改良,使得ZYNQ中的ARM芯片也支持Python,於是就推出了PYNQ系列




支持Python


可編程邏輯_FPGA芯片

ZYNQ系列

PYNQ系列

處理系統_ARM芯片


圖1-20 芯片型號的發展順序


那Xilinx官網上着重介紹了自家的 FPGA芯片 和 ZYNQ系列芯片,咱們就大概看看它的產品線(Altera那邊也差不多,就不多説了),如下圖所示:



FPGA的虛擬化處理器模擬集羣_fpga開發_20


圖1-21 Xilinx的FPGA、SoC產品


從上圖可以看到主要有FPGA芯片、SoC(System on Chip, 片上系統)兩大類。這兩個大類各自包含四個子系列,每個子系列又細分為多種產品線,而每一個產品線都有很多型號(圖中未給出具體型號)。可以大致認為,該產品樹從上到下分別對應了片上資源從少到多。

目前市面上最常見的初學者開發板,就是FPGA系列中的Atrix-7系列(xc7a35tfgg484/xc7a100tfgg484)、以及Zynq-7000系列(7000/7020)。

1.3.3 FPGA硬件結構

本小小節介紹FPGA內部的硬件結構,對於初學者來説,裏面的很多圖一看就非常複雜,想搞懂很困難。但是不要放棄,本篇文章只是用做科普,一些看似很難的圖只需要有一個大概的印象即可,後續做實驗時,涉及到相應的模塊則會繼續講解。另外在基礎的開發實驗中,只需要瞭解各模塊的接口,能把接口接對即可,可以暫時不用對內部結構有特別深入的理解。

1.3.3.1 數字電路基本結構

下面給出了在設計數字電路的過程中,經常用到的一些基本結構。下面可能只給出一些基本的示意圖,關於更詳細的基本電路介紹可以在任意一門《數字電子技術基礎》課程上講到,可以在 B站/chatgpt 搜索相應關鍵詞。

1) MOS管

所有門電路的功能都可以由PMOS管和NMOS管(統稱“晶體管”) 來實現的。下面給出了其物理結構和基本的電路符號。當然,這裏僅是簡單的示意圖,實際上還有結型場效應管、絕緣柵型場效應管(又細分為增強型和耗盡型),具體細節可以查看任意一本《模擬電子技術基礎》課本(推薦清華大學出版社 童詩白主編)。



FPGA的虛擬化處理器模擬集羣_ci_21


圖1-22 NMOS和PMOS示意圖


  • NMOS管在Gate端輸入為0時,D端–>S端斷開;在GS端輸入為1時,D端–>S端導通。
  • PMOS管在Gate端輸入為0時,D端–>S端導通;在GS端輸入為1時,D端–>S端斷開。

2) 基本邏輯門電路



FPGA的虛擬化處理器模擬集羣_FPGA的虛擬化處理器模擬集羣_22


圖1-23 基本邏輯門電路


上圖給出數字電路中最常見的基本邏輯門電路。上圖所示的每一個門電路都是由最基本的 電阻二極管三極管 組成的。比如:

  • 1個NMOS管+1個PMOS管可以實現 非門 電路。
  • 2個PMOS管+2個NMOS管可以實現 與非門 電路。
  • 2個PMOS管+2個NMOS管可以實現 或非門 電路。
  • 3個PMOS管+3個NMOS管可以實現 與門 電路。
  • 3個PMOS管+3個NMOS管可以實現 或門 電路。

注:具體結構可以查看《數字電子技術基礎》。

3) 觸發器
通過將門電路進行組合,就可以得到更加複雜的“觸發器”。觸發器一般都需要時鐘輸入,作用一般是鎖存數據,或者多個觸發器和外圍電路配合形成計數器。而多個計數器級聯,就可以產生任意週期的脈衝信號。觸發器的種類多種多樣,比如RS觸發器、D觸發器、JK觸發器、T觸發器、T’觸發器等,下圖以D觸發器為例。



FPGA的虛擬化處理器模擬集羣_學習_23


圖1-24 D觸發器電路結構及邏輯符號


可以看到D觸發器由基本的非門、與非門構成。比如 FPGA的虛擬化處理器模擬集羣_FPGA的虛擬化處理器模擬集羣_24FPGA的虛擬化處理器模擬集羣_ci_25 均設置為高電平時,輸出FPGA的虛擬化處理器模擬集羣_Verilog_26 就會在 時鐘FPGA的虛擬化處理器模擬集羣_Verilog_27 高電平時同步更新為 輸入FPGA的虛擬化處理器模擬集羣_fpga開發_28

4) 組合邏輯和時序邏輯

那有了上面的門電路、觸發器等數字電路的基本元器件,就可以搭建各種各樣的數字電路了。但是注意數字電路根據是否需要時鐘又分成 組合邏輯時序邏輯。下面是其示意圖:



FPGA的虛擬化處理器模擬集羣_fpga開發_29


圖1-25 組合邏輯和時序邏輯示意圖


  1. 組合邏輯的輸出只與當前輸入有關,時序邏輯的輸出與當前輸出和過去的狀態都有關。
  2. 組合邏輯立即反應當前輸入狀態,時序邏輯還必須在時鐘邊沿觸發後輸出新值。
  3. 組合邏輯容易出現競爭、冒險現象,時序邏輯一般不會出現競爭、冒險現象。
  4. 組合邏輯的時序較難保證,時序邏輯更容易達到時序收斂,時序邏輯更可控。
  5. 組合邏輯只適合簡單的電路,時序邏輯能夠勝任大規模的邏輯電路。

簡單一句話:組合邏輯立即變化,時序邏輯隨着時鐘變化。

那下面就來一步步介紹FPGA內部是如何實現上述這些數字電路的基本結構的。

1.3.3.2 LUT查找表

實際上,FPGA通過 LUT(Look-up table,查找表)結構 來實現可編程電路。LUT就是一張已經設計好的結果已知的查找表,這個表存儲着不同的輸入對應的所有可能的結果,這些結果對應LUT中一個唯一的地址。而組成LUT的RAM本質上也由晶體管組成,所以 LUT本質上也是晶體管的組合

【例】2輸入LUT實現的與非門電路,可以用4個預存儲的數據實現既定功能。輸入x和y的4種不同組合可以看出是0~3的4個不同地址,每個地址都對應一個輸出結果。



FPGA的虛擬化處理器模擬集羣_FPGA的虛擬化處理器模擬集羣_30


圖1-26 2輸入LUT實現與非門


當然實際的FPGA當中,使用的並不是這麼簡單的2輸入查找表。具體到 Xilinx Artix7系列FPGA器件,它使用的是 6輸入LUT結構。這6個獨立的輸入(稱為A1~A6)可以配置為單輸出(O6)模式和雙輸出(O5和O6)模式。

  • 6輸入1輸出的LUT內部對應着1個64個地址的存儲單元。
  • 5輸入2輸出的LUT內部對應着2個獨立的32個地址的存儲單元。



FPGA的虛擬化處理器模擬集羣_fpga開發_31


圖1-27 LUT實現多輸入可編程電路


實際上,通過配置,上圖不僅可以實現“多輸入與門”電路結構,同樣可以實現“多輸入或門”,以至於任意多輸入的組合,都可以實現。

1.3.3.3 可配置邏輯塊CLB



FPGA的虛擬化處理器模擬集羣_ci_32


圖1-28 可配置邏輯塊CLB


以Xilinx主流的7系列為例,一顆FPGA內部通常都會有數千到數十萬不等的 可配置邏輯塊(Configurable Logic Block,簡稱CLB)。呈矩陣排布的CLB就構成了最基本的FPGA邏輯資源的架構,CLB塊之間有很豐富的佈線池。Xilinx7系列的可配置邏輯塊可以有效的支持以下特性:

  • 使用6輸入查找表技術;
  • 可選的2個5輸入查找表功能;
  • 可實現分佈式RAM和移位寄存器功能;
  • 用於運算功能的專用高速進位鏈;
  • 支持資源高利用率的豐富複用開關。

從微觀角度看,CLB內部主要由2個更小的單位Slice所組成。兩個Slice之間有連線,且每個Slice都有獨立的高速進位鏈以及獨立的佈線通道連接到矩陣開關,通過矩陣開關可以實現Slice與FPGA大布線池之間的靈活編程。每個slice單元則包含了以下更小的功能塊:

  • 4個邏輯功能發生器(或查找表);
  • 8個存儲單元(或觸發器);
  • 功能豐富的複用開關;
  • 進位鏈。

圍繞在CLB周圍豐富的行、列走線稱為 佈線池,它用於銜接FPGA的各個CLB以及其它相關的資源。在FPGA芯片四周的小矩形以及延伸出去的短線,則表示FPGA和外部芯片接口的 IO塊

  • 佈線池:類似於PCB板材上的走線,用於銜接FPGA上的CLB等資源。總線池越大,就意味着可以走更多的信號。
  • IO塊:FPGA和外部其他芯片進行數據交互的接口。所以FPGA也必須有很豐富的IO塊。
1.3.3.4 其他豐富的FPGA資源



FPGA的虛擬化處理器模擬集羣_學習_33


圖1-29 主流的FPGA架構


從整個FPGA的配置來看,不僅包含了很多CLB資源,還包括以下豐富的資源:

  1. 以成塊出現的FPGA內嵌存儲器(塊RAM)。
  2. 用於產生不同時鐘頻率的鎖相環( PLL時鐘發生器)以及相應的時鐘佈線資源。
  3. 高速串行收發器。
  4. 外部存儲器控制器(硬核IP)。
  5. 用於實現數字信號處理的乘累加模塊(DSP Slice)。
  6. 模擬數字轉換模塊(Xilinx FPGA器件特有的ADC,簡稱XADC):支持最多16個可複用通道,對於一些工業應用領域(如電壓監控、小信號採集)來説,非常實用。

1) 塊RAM



FPGA的虛擬化處理器模擬集羣_Verilog_34


圖1-30 Xilinx BRAM接口框圖


塊RAM就是以成塊出現的FPGA內嵌存儲器。因為在一些高速的數據處理、緩存、算法實現、信號處理等場合,需要內部的一個緊耦合的RAM來實現對數據的高速的緩存和讀寫,所以塊RAM應運而生。從上圖所示的FPGA內部的塊RAM接口框圖來看,塊RAM本質上都是36kb大小的雙輸入雙輸出:左側有DIA和DIB兩個獨立的數據輸入,右側也分別是DOA和DOB兩個獨立的輸出,上下則是用於級聯的接口。但當然也可以根據需求,配置成簡單的單輸入單輸出,這個後續在學習“RAM IP核”時再具體介紹。

實際上,FPGA內嵌的存儲器單元包括塊RAM(BRAM)分佈式RAMBRAM 就是上一段介紹的結構,分佈式RAM 則是基於CLB的查找表。這些存儲器單元都可以配置為隨機存取存儲器(RAM)、只讀存儲器(ROM)、FIFO或移位寄存器,非常靈活。

2) 時鐘資源(PLL時鐘發生器)

1) 塊RAM



FPGA的虛擬化處理器模擬集羣_學習_35


圖1-31 Xilinx FPGA時鐘資源類比


FPGA內部充斥着各種各樣的連線,如果我們把這些邏輯間的互連線比喻為大城市裏面密密麻麻的街道和馬路,那麼專為快速佈線而定製的 時鐘佈線資源 則是城市裏的快速路。FPGA內部的 時鐘佈線池 也是橫平豎直的矩陣式排布,意圖讓每一條“小路”能夠儘快地找到可以就近“上高速”的“匝道”。換回到FPGA內部電路來説,就是希望時鐘源產生的時鐘信號,可以通過儘可能小的延遲,快速地、同步地 達到各個資源,以驅動電路各模塊正常協調工作。注意時鐘信號的最大延遲,限制了FPGA的時鐘頻率。

Xilinx FPGA內部會將時鐘佈線資源劃分到不同的“時鐘區”中,每個時鐘區對應一定數量的IO口數量、邏輯資源、存儲器資源或DSP slice資源,同時也會有一個CMT(Clock management tiles)相對應。於是,由 PLL時鐘發生器 產生的不同時鐘頻率的鎖相環以及相應的時鐘佈線資源,就可以被輸送到不同的“時鐘區”。

3) 數字信號處理塊



FPGA的虛擬化處理器模擬集羣_學習_36


圖1-32 Xilinx DSP示意圖


數字信號處理(Digital Signal Processing,簡稱DSP)塊是Xilinx FPGA內部最複雜的運算單元。DSP塊是內嵌到FPGA中的算術邏輯單元(ALU),它由3個不同的鏈路塊組成:DSP塊的算術鏈路由一個加減器連接到乘法器,再連接到一個乘累加器所組成。DSP用於實現數字信號處理的乘累加模塊(DSP Slice)。可用於一些算法實現和運算功能。

當然在一些簡單的應用中,無需對DSP內部的結構做如此深入的理解,只要會用即可。

4) 高速串行收發器



FPGA的虛擬化處理器模擬集羣_ci_37


圖1-33 Xilinx Artix7 GTP Transceivers資源


FPGA支持各種高速差分對,從幾百MHz的普通LVDS接口到上GHz或數十GHz的Gbit串行收發器,可以滿足各種高速數據傳輸的需求。通常在FPGA器件內部提供高速的串化器和解串器,以及低時延、高速率的時鐘處理單元。普通的LVDS接口,小規模的FPGA器件中也能夠提供多達幾十對的差分接口,通常既可以作為LVDS接口,也可以複用為一般的IO引腳使用。在Artix7系列FPGA器件中達到6.6Gb/s的GTP Transceivers有2到16個不等,能夠滿足一般性的應用。

注:

  • GTP Transceiver是FPGA裏一種線速度達500Mb/sà6.6Gb/s的收發器,利用FPGA內部可編程資源可對其進行靈活地配置,使其適合不同的需要如以太網、SATA1.0接口等,它的作用是各種高速串行接口的物理層。
  • LVDS(Low-Voltage Differential Signaling) 低電壓差分信號,是一種低功耗、低誤碼率、低串擾和低輻射的差分信號技術,這種傳輸技術可以達到155Mbps以上。

5) 外部存儲器控制器



FPGA的虛擬化處理器模擬集羣_Verilog_38


圖1-34 Xilinx MIG IP核 可綜合示例


外部存儲器控制器(Memory Interface Generator)通常是硬核IP。在進行高速數據採集、緩存、處理等場合,可以大大方便FPGA和高速Memory之間的交互。由於FPGA的片內存儲器(如BRAM)容量受限,所以對DDR3/DDR4等外部高速存儲器的支持也成為了中高端FPGA器件必備的資源。FPGA器件內部往往內嵌了一個或多個DDR3/DDR4控制器硬核IP,包括用户接口(User Interface)模塊、存儲器控制器(Memory Controller)模塊、初始化和校準(Initialization/Calibration)模塊、物理層模塊等。

這部分的原理非常重要,基本上能夠獨立完成“DDR讀寫實驗”,就算是從“FPGA一竅不通”進階成“FPGA熟練掌握”了,屬於是大多數人學習FPGA的分水嶺,這部分的實驗後續會詳細介紹。

6) 模擬數字轉換模塊



FPGA的虛擬化處理器模擬集羣_fpga開發_39


圖1-35 XADC內部功能框圖


Xilinx FPGA器件特有的ADC,簡稱XADC,將模擬信號處理混合到FPGA器件。XADC包括:

  • 2個ADC,具有12bit位寬和1MSPS採樣速率,可以外接精密基準電壓源作為參考電壓。
  • 專門的温度傳感器(±4°C 最大誤差)和電壓傳感器(±1% 最大誤差),用於監控FPGA器件本身的工作狀態。
  • 提供了多達16個差分通道可複用的模擬電壓採集接口,對於很多工業應用(如電壓監控、小信號量採集等)來説非常實用。
  • 專門的控制接口,可以和FPGA邏輯互連,便於編程控制。

1.4 Verilog基礎語法

1.4.1 基礎知識

1) 關於邏輯值

  • 邏輯 0:表示低電平,也就對應電路GND;
  • 邏輯 1:表示高電平,也就是對應我們電路的VCC;
  • 邏輯 X:表示不定態,可能是高電平,也可能是低電平;
  • 邏輯 Z:表示高阻態,外部沒有激勵信號,是一個懸空狀態。

建議初學者將所有的信號值都規定好,也就是隻使用前兩個邏輯值。

2) 關於數字的表示
Verilog數字進制格式包括二進制('b)、八進制('o)、十進制('d)和十六進制('h)。
一般常用的二進制、十進制和十六進制。記得一定要規定數字的位寬(這個位寬是二進制的位寬),有時候可能不規定(默認32位)也能正常運行,但不要自己埋雷💣。

舉個例子,現在要表示十進制數255:
二進制 表示為:8’b1111_1111。
十進制 表示為:8’d255。
十六進制表示為:8’hff。
注:數字分隔符(也就是下劃線)可以隨便加,自己看得舒服就行。

小技巧:使用windows自帶的計算器,打開程序員模式,就可以同時看到一個數字的四種進制的表示。對於FPGA開發來説,這個工具比較方便。

3) 關於命名
標識符(identifier)就是模塊名、端口名、信號名等,可以類比為軟件語言中各個變量、函數的名稱。verilog對命名標識符的過程做出瞭如下規定(必須遵守):

1.標識符可以是任意一組字母、數字、$(dollar符號)和_(下劃線)符號的組合。
2.標識符的第一個字符必須是字母或者下劃線。
3.標識符是區分大小寫的。

除此之外,廣大verilog開發者還有一些約定成俗的建議:

1.不建議大小寫混合使用,也就是全部大寫或全部小寫。
2.模塊名、普通內部信號建議全部小寫,常量建議全部大寫。
3.信號命名最好體現信號的含義,簡潔、清晰、易懂;

以下是一些推薦的寫法:

1.用有意義的有效的名字如 clk_count、MAX_ADDR 等。
2.用下劃線區分詞,如cpu_addr。
3.採用一些前綴或後綴,比如時鐘採用clk前綴:clk_50,clk_cpu。

1.4.2 數據類型

在 Verilog 語言中,常用的主要有三大數據類型:寄存器數據類型、線網數據類型和參數數據類型。顧名思義,寄存器數據類型(reg)描述的是一個可以存儲數據的單元,而線網數據類型(wire)則是描述一個物理連線,參數數據類型(parameter)就是常量。下面依次介紹。

  • reg類型的數據只能在 always 語句和 initial 語句(只出現再仿真中,不會被綜合)中被賦值,默認初始值為不定值x。若always塊帶有時鐘信號(時序邏輯),則該寄存器變量對應為觸發器;若always塊不帶有時鐘信號(組合邏輯),則該寄存器變量對應為硬件連線。
  • wire類型表示物理連線,所以線網類型的變量不能儲存值,它的值是由驅動它的元件所決定的,且一旦有變化就會立即變化(組合邏輯)。若沒有元件驅動到wire型信號上,則默認為高阻值z。驅動線網類型變量的元件有門、連續賦值語句、assign等。
    注(可能要刪):線網數據類型包括 wire 型和 tri 型,兩者區別不大,但一般只使用 wire 類型。
  • parameter類型用來定義常量,一次(只寫一次parameter)可以定義多個參數,參數與參數之間需要用逗號隔開。每個參數定義的右邊必須是一個常數表達式。paramter型數據常用於定義狀態機的狀態、數據位寬、延遲大小等(一些有意義的常數),可以提高程序的可讀性和可維護性。

綜上所述,每個信號的控制邏輯如下圖所示。一直沒有提到的是,時序邏輯信號會等待時鐘邊沿再進行變化;組合邏輯信號會立即變化。為了保證FPGA具有良好的時序性,一般的信號都使用時序邏輯,只有一些控制信號、狀態信號、模塊之間的連接信號才使用組合邏輯,當然還需要開發者的綜合考量。









FPGA的虛擬化處理器模擬集羣_FPGA的虛擬化處理器模擬集羣_40



圖1-36 Verilog中的數據類型


注:與軟件語言中直接使用等號進行賦值不同,verilog中,時序邏輯信號一般使用非阻塞賦值<=,組合邏輯信號一般使用阻塞賦值=。極少數情況不是這樣,後續有空我再單獨寫文章闡述阻塞和非阻塞的原理。

1.4.3 運算符

Verilog中的操作符按照功能可以分為下述7種類型:

算術運算符:



符號

使用方法

説明

+

a + b

加法

-

a - b

減法

*

a * b

乘法

/

a / b

去a/b的商

%

a % b

取a/b的餘數

注:無特殊規定時,都是二進制的運算。

關係運算符:



符號

使用方法

説明

>

a > b

a大於b

<

a < b

a小於b

>=

a >= b

a大於等於b

<=

a <= b

a小於等於b

==

a == b

a等於b

!=

a != b

a不等於b

注:上面的小於等於符號與 非阻塞賦值 符號相同,區別是關係運算符只用於if-else的控制邏輯中,而非阻塞賦值則是用於always塊中的賦值。

邏輯運算符:



符號

使用方法

説明

&&

a && b

a整體和b整體的與

||

a || b

a整體和b整體的或

!

a ! b

a整體和b整體的非

注:整體的意思就是看這個數是否為0,不為0這個數就表示邏輯1,為0就表示邏輯0。

條件操作符:



符號

使用方法

説明

? :

a ? b : c

a為真就選擇b,a為假就選擇c

注:當if-else的控制邏輯比較簡單時,就用條件操作符,增加可讀性。

位運算符:



符號

使用方法

説明

~

~a

a按位取反

&

a & b

a與b按位與

|

a | b

a與b按位或

^

a ^ b

a與b按位異或

注:上述的按位操作的a、b要求位數相同,且返回的結果位數與它們相同。

移位運算符:



符號

使用方法

説明

<<

a << b

邏輯左移,a左移b位,低位補0

>>

a >> b

邏輯右移,a右移b位,高位補0

<<<

a <<< b

算數左移,a左移b位,低位補0

>>>

a >>> b

算術右移,a右移b位,高位補符號位(最高位)

注:

  1. 邏輯移位是針對無符號數使用;算術移位是針對有符號整數使用,可以快速滿足有符號數的乘以/除以2的整數次冪運算。
  2. 左移位寬增加,右移位寬不變。

拼接運算符:



符號

使用方法

説明

{ }

{a , b}

將a、b拼接起來,作為一個新的信號

注:這個符號使用的非常廣泛,比如一個有意思的用法就是實現 循環移位:假如有一個4位的流水燈信號reg [3:0] led,每個時鐘沿都希望得到其循環移位的結果,於是有led <= {led[2:0] , led[3]}

下面給出上述運算符的優先級別,下表截圖自《Verilog數字系統設計教程》-夏宇聞:



FPGA的虛擬化處理器模擬集羣_ci_41


圖1-37 Verilog運算符優先級


注:遇事不決小括號,媽媽再也不用擔心我的學習,so easy!😎

1.5 Verilog程序框架

1.5.1 Verilog註釋

Verilog的註釋有兩種:

  1. 行註釋://
  2. 塊註釋:/* */

就不贅述了。

1.5.2 Verilog關鍵字

略。可自行查看書籍《Verilog數字系統設計教程》-夏宇聞。

1.5.3 Verilog程序框架

1. 模塊module

module block(a,b,c);
   input       a, b;    //模塊的輸入不需要規定類型
   output wire c;       //模塊的輸出一定要規定類型
   
   wire d;              //定義內部信號

   assign d = a | b;    //組合邏輯輸出
   assign c = d ? a : b;//組合邏輯輸出
endmodule

根據上述代碼,可以看出每個Verilog模塊所包括4個主要的部分:端口定義、IO説明、內部信號聲明、功能定義。
注:顯然都採用組合邏輯,c信號可能會發生競爭冒險等現象,出現不穩定的毛刺,強烈不推薦這樣的代碼邏輯。使用組合邏輯則會消除這種現象,下一小節介紹。

2. always塊

module block(
    clk,       //系統時鐘信號
    reset_n,   //復位信號,n表示低電平復位
    in_sig1,   //輸入信號1
    in_sig2,   //輸入信號2
    out_sig    //輸出信號
);
   input            clk, reset_n;
   input            in_sig1, in_sig2;
   output reg [1:0] out_sig;

   parameter CONST = 16'haaff;//這個常量僅作展示使用

   always@(posedge clk or negedge reset_n)
   if(!reset_n)
       out_sig <= 2'b00;//這裏也可以寫2'd0
   else
       out_sig <= {in_sig1, in_sig2};
endmodule

上述代碼的功能為:在系統不復位時,將兩個輸入信號拼接在一起輸出出去。

可以發現使用了always塊增強程序的時序性能,@表示監測後面括號裏的內容,posedge表示上升沿,negedge表示下降沿。這樣就保證了always@塊裏面的所有信號的變化都只在規定的邊沿發生,也就是時序邏輯。
always@塊裏面沒有時鐘,而是寫做always@(*),則代表always@塊裏面的信號會在任意一個信號發生變化時變化,也就是組合邏輯。

3. begin end
在一個module-endmodule中,可以定義多個always@塊,它們都是同時運行的(並行)。但是在一個always@塊中,通常使用if-else if- ... -else來控制,這個邏輯的控制是串行運行的,也就是電路會執行第一個符合條件的分支。

假如某一個分支包含多條語句,如同C語言中的大括號一樣,就需要begin-end語句將這些語句括起來,如下所示:

always@(posedge clk or negedge reset_n)
if(!reset_n) begin
   out_sig1 <= 2'b00;
   out_sig2 <= 2'b00;
end
else begin
   out_sig1 <= {in_sig1, in_sig2};
   out_sig2 <= {in_sig2, in_sig1};
end

4. 模塊的調用
那軟件語言中有子函數的概念,指直接調用一些封裝好的函數。在verilog編程中,這個子函數就變成了一個封裝好的芯片(包含輸入輸出)。模塊的調用(例化)有兩種方法,下面以第2小節的block模塊舉例:

顯式調用:

block another_name(
   .clk(clk),           //系統時鐘信號
   .reset_n(reset_n),   //復位信號,n表示低電平復位
   .in_sig1(in_sig1),   //輸入信號1
   .in_sig2(in_sig2),   //輸入信號2
   .out_sig(out_sig)    //輸出信號
);

可以看到,上面的調用中,首先寫出要調用模塊的名字(block),然後再起一個別名(another_name),當然這個別名可以和模塊名稱相同;但假如多次調用這個模塊,就需要有所區分。裏面所有的輸入輸出也可以使用別的信號進行代替,只需要修改括號內的名稱即可。裏面的信號順序也可以隨意更換,也可以不寫(如果不影響功能的話)。

當然,假如想更改模塊中的常量,可以按照如下方式,注意加#號。

block #(
   .CONST(CONST)        //重新定義模塊中的常量值
)another_name(
   .clk(clk),           //系統時鐘信號
   .reset_n(reset_n),   //復位信號,n表示低電平復位
   .in_sig1(in_sig1),   //輸入信號1
   .in_sig2(in_sig2),   //輸入信號2
   .out_sig(out_sig)    //輸出信號
);

隱式調用:
假如有時候上層模塊定義的信號與要調用的模塊內的輸入輸出信號名稱完全相同,並且順序完全相同,就可以省略一些麻煩,直接隱式調用:

block #(
   .CONST(CONST) //重新定義模塊中的常量值
)another_name(
   clk,          //系統時鐘信號
   reset_n,      //復位信號,n表示低電平復位
   in_sig1,      //輸入信號1
   in_sig2,      //輸入信號2
   out_sig       //輸出信號
);

注意,哪怕只有一個信號的名稱不同/順序不同,都只能使用顯式調用,否則會報錯。

。。。也許還會有更新