愛伊米

Verilog在編寫第一行程式碼之前

除非你知道自己要去的地方,否則你不可能到達那裡!

好的設計者一般都要對電路要實現的功能有清晰的認識,對資料流很清楚,知道資料如何從一個點移動到另-個點,這就是所謂的“勾劃”(walk-through)。一旦設計藍圖在腦海中變得清晰,此後釆用Verilog編寫資料路徑和控制邏輯就會變得思路清晰。

腦海中的模擬

正如大多數人玩過的象棋遊戲,我們都知道提前謀劃是何等重要,要在下一次移動供子之前考慮好此後的幾步棋應該怎麼走,以確保不會出錯,不被對手捕捉到機會。電路設計過程與下棋非常相似。當設計狀態機、資料路徑或者控制邏輯時,我們知道它們的功能。在進行設計模擬之前,我們需要思考程式碼在不同輸入和邊界條件下如何工作。如果用心去做好這一步工作,並且分析可能出現的問題,驗證工作將會變得非常高效。另外,這一步也給我們建立了自信,使我們確信整個設計非常紮實,可以很好地工作。否則,很可能出現的情況足在驗證階段反覆發現問題並進行電路修改,不斷進行補救T。作,並且最終也不能確定設計足否還隱含著沒有被發現的問題。

哪種風格—資料流或演算法

描述組合邏輯有兩種方式—使用wire(對應資料流描述方式)或者使用reg(對應演算法描述方式)。這兩種方式都能實現相同的邏輯功能,綜合後得到相同的閘電路,具體使用哪一種方式可以根據個人喜好。

資料流—短表示式舉例

演算法—短表示式舉例

當表示式非常簡單時,一般更傾向於使用資料流風格來實現,此時程式碼行數很少。然而,當表示式很長並且與很多條件有關時,資料流風格閱讀起來較為費力。此時可以使用演算法風格,可以採用if-else語句進行描述,以易於閱讀和減少錯誤發生。

資料流-長表示式舉例

演算法—長表示式舉例

暫存器型輸出

大型設計可被分解為很多塊,每個塊可進一步分解為多個模組。模組相互聯接形成完整設計。模組的輸出或者驅動另一個模組的輸入,或者作為頂層晶片的輸出引腳。在結構化和大型設計中,綜合後整個電路要保持層次化結構,而不是使整個設計扁平化。當層次化得以保留時,輸出有時必須透過相對長的路徑到達另一個模組,這會導致互聯延時增大。為了更容易滿足晶片內對建立時間的要求,內部模組應儘量採用暫存器輸出,而不是組合邏輯輸出,如圖10。1和圖10。2所示。採用暫存器輸出時,可以為後續電路留下相對寬裕的佈線延遲和輸入組合邏輯延遲。儘量使用暫存器輸出是一個非常好的設計經驗。

Verilog在編寫第一行程式碼之前

使用狀態機而不是鬆散的控制邏輯

狀態機可以實現複雜的控制功能,它可以非常容易地將所有可能的情況和邊界條件考慮進去。透過設定多個狀態,並將它們相互聯接可實現複雜的、重複的控制流程。如果狀態機需要完成的功能很少,可以用2個或3個狀態來實現。採用狀態機來實現控制功能是一個好方法,即使是隻有2個或3個狀態的簡單狀態機,都可以有效減少設計差錯。

綜合和模擬不匹配

在Verilog 2001岀現之前,設計者需要確定always塊敏感列表中包含了所有輸入訊號。下面是一個例子:

設計者必須確保所有輸入(位於表示式右邊)都出現在敏感訊號列表中。對於小型always塊,這很簡單。但是對於大型always塊來說,敏感訊號列表中很可能會漏項。設計者必須付出額外的努力和時間,用於查詢所有輸入並將它們放入敏感訊號列表中。

如果某個輸入從敏感訊號列表中漏掉了,會發生什麼情況呢?如果一個輸入發生了變化,僅當它出現在敏感訊號列表中時,模擬器才進入always塊中執行其中的語句。如果漏掉了某個敏感訊號,那麼該部分電路的模擬結果與綜合後的邏輯功能相比可能會有很大差別。由於Verilog 2001版標準中不再需要將這些輸人放在敏感訊號列表中,模擬器會自動弄清楚所有的敏感訊號,綜合後的結果也會與模擬結果相同。舉例如下。

設計的模組化和引數化

結構化設計比非結構化設計更有用。管理者和決策者可以根據結構化設計作出明智的判斷。類似地,結構化或者模組化設計不僅易於理解和維護,而且有利於設計共享。某些電路單元(加法器、乘法器、CRC校驗電路等)能在不同的設計中被複用,可以對一組邏輯電路使用門控時鐘,冗餘或者相近的電路功能可以被合併或者消除。採用模組化設計後,所有這些都可能成為現實。此外,標準電路模組還能在多個設計或一個公司內部不同部門之間共享。

加法器、減法器的有效使用

在數字系統設計中,會經常用到加法器、減法器、乘法器等,需要大量數學運算時更是如此。需要特別提醒的是,數學運算電路會佔用大量的門/面積資源,此時,有效使用這些電路以降低硬體資源消耗就會非常有意義。設計者可以透過改進演算法,使運算電路在不同的時間為不同的其他內部電路提供運算服務,從而減少運算電路的數量。另一種方法是最佳化RTL程式碼,減少運算電路在具體實現時消耗的資源。下面的例子,展示瞭如何減少加法器/減法器的數量。

上面的例子需要使用7個加法器和1個複用器。透過編寫不同的RTL程式碼可以實現同樣的功能,此時只需要1個加法器和1個複用器。

圖10。3是兩段程式碼的綜合結果。

Verilog在編寫第一行程式碼之前

需要避免的情況

不要形成組合邏輯環路

當路徑中有反饋,卻沒有觸發器這類時序元件時,有可能出現組合邏輯環路,如下圖所示。對於有經驗的設計者來說,模組內簡單的組合邏輯環路易於發現和避免。然而,當訊號由組合邏輯電路產生,穿過多個模組,最後返回到原模組所形成的組合邏輯環路卻難以識別。可能直到對整個電路進行模擬時才會注意到該環路。此時,模擬器無法模擬出有效的結果或者會因為進入死迴圈而長時間停留在某一模擬時刻。查詢組合邏輯環路的起點較為困難,採用單步模擬方式查詢環路起點的工作非常煩瑣且令人厭煩。採用暫存器輸出的方式可以有效地減少形成組合邏輯環路的機會。

Verilog在編寫第一行程式碼之前

避免意外生成鎖存器

在時序電路設計中,一般會選擇D觸發器作為儲存元件。鎖存器也能儲存數值,但有一些不好的特性,除非真的需要,否則不應在設計中使用它。在編寫RTL程式碼時,如果沒有遵循一定的規則,可能會出現意想不到的鎖存器。

下面是一些綜合後會生成鎖存器的例子。生成鎖存器並不是設計的初衷,因此要熟悉容易生成鎖存器的程式碼結構並加以避免。使用always塊生成組合邏輯電路時,如果變數所有可能的取值沒有被考慮完全,那麼綜合後可能會生成鎖存器,如下面程式碼所示。

在上面的例子中,給出了a

一種簡單的避免產生鎖存器的方法是給變數賦初值,如下面程式碼所示。這樣即使後面的程式碼沒有覆蓋所有的取值,也不會生成鎖存器。

不要採用基於延遲的設計

邏輯設計中要避免利用門延遲產生所需要的脈衝。

如下圖所示的電路,該電路利用反相器的門延遲在與門輸岀端產生了一個正脈衝。延遲大小取決於PVT(積體電路生產工藝、工作電壓和環境溫度),這意味著延遲不是固定的,將隨著工作電壓、溫度和製作工藝而改變。當設計被移植到更先進的工藝上生產時(例如,65~28nm積體電路工藝),脈衝寬度將減小,最終成為毛刺。因此,這樣的邏輯設計不推薦使用,應該被避免。

Verilog在編寫第一行程式碼之前

不要對一個變數多次賦值

在Verilog中,不要在多個always塊內對同一個變數賦值。綜合工具將產生兩個獨立的邏輯塊,並將它們用“線或”方式連線,從而造成設計錯誤。對一個變數的賦值必須在一個always塊內進行,下面是一個進行對比分析的例子。

Verilog在編寫第一行程式碼之前

在多個always塊內對同一個變數賦值可能不會報錯,always塊較少時也很容易發現這一問題,如下面程式碼所示。但當一個電路規模較大時,內部可能有多個always塊,此時容易犯的錯誤是在一個always塊可能對某個變數賦了初值,而在另一個always塊中賦了實際值。應仔細檢查,避免這類問題出現。

Verilog在編寫第一行程式碼之前

初步完成RTL程式碼之後

無論編寫程式碼時有多麼仔細,程式碼中都會出現bug,應在完成程式碼編寫後趁熱打鐵,趁著對程式碼還熟悉及時查詢問題,不要留到以後再解決。很多情況下,你並沒有意識到程式碼中存在錯誤,也不是想把問題留到以後去解決,而是設計團隊的其他人告訴你時,你才意識到這個程式碼存在設計錯誤。此時也許已經過了2天、2個月甚至2年,再查詢問題的難度就會增大很多。

程式碼編寫完成並進行第一遍修改後,應再一次對程式碼進行檢視,按照你腦海中的步驟重新對設計檢查一遍,這樣不止會避免較為明顯的錯誤,還可能發現隱藏較深的邊界問題,這將大大提高工作效率。最為重要的是,驗證工程師的工作量會減少,設計者本人也不需要花花費更多的時間來重新消化原來的設計並修改程式碼中的問題。總之,問題越早解決越好。

多數情況下,進行程式碼編寫時,我們會使用剪下和貼上來避免重複和枯燥的程式碼輸入工作,此後再對貼上的程式碼段進行修改,比如修改地址、索引等。對於這類枯燥的工作,設計者本能地會感到厭煩並且注意力不容易集中,恰恰此時最容易出現低階錯誤。程式碼編寫完成後,應對程式碼進行目測檢查,一些簡單的問題,比如程式碼縮排、空格和對齊等,很容易進行目測檢查,也很容易找出剪下和貼上時岀現的錯誤。編寫完RTL程式碼之後,應及時目測檢查一遍。

對發現bug感到驚喜

我們不會因為設計中有bug就否定設計者的成績,而是要讓設計者明白bug的確會存在,簡單的bug比如拼寫錯誤,在每個設計者身上都會發生,這在驗證過程中很容易發現。然而,對於邊界問題、由於沒有充分理解而造成的設計錯誤,或者多個事件組合在一起時才會發生的錯誤等,在模擬期間有時也難以發現。當進行程式碼分析時,你可能會發現,在那麼努力地進行設計和檢查之後,仍然存在一些問題。此時,對問題進行深入分析有助於使設計者獲得更多的設計經驗從而變得更加成熟,並在以後的設計中避免類似的問題。

設計要面向未來使用需求

典型的晶片或電路內部都有對映在處理器儲存空間中的暫存器,驅動程式可以對暫存器進行配置或者透過讀取狀態暫存器的值得到電路的工作狀態。進行暫存器定義時,建議將功能相同的位元位單獨分組並編址,不要將功能不同的暫存器合併在一起編址。例如,要定義3個狀態位和3個控制位,不要將它們放在一個位元組中,應將3個狀態位放到一個位元組屮(剩下的5位元作為保留位),將3個控制位放到另一個位元組中,這樣,狀態位和控制位就被編入不同的位元組中,具有不同的記憶體對映地址。另外應將粘滯位(sticky bit)和常規位元位(regular bit)放在不同的位元組中,因為粘滯位需要單獨的電源供電。在電路中恰當地設定暫存器並進行合理的編址,會簡化硬體電路和驅動程式的設計。

另外,進行電路設計時要考慮到今後的設計需求。在電路架構設計時,即使已經做了充分的考慮,也可能會進行設計修改。例如,目前所需的編碼數值為6,使用了一個3位暫存器,考慮到將來的需求,可以選擇4位暫存器,以便於今後增大編碼數值。另外,應確保重要的位元位或暫存器可以被軟體訪問,雖然這會增加硬體資源消耗,但是如果不這樣做該數值就不能被軟體讀取,這會給除錯帶來不便。

NOW

學習Xilinx FPGA最好的資料其實就是官方手冊,下表總結了部分手冊的主要介紹內容,關注我,持續更新中……