FPGA研發之道(14)寫在coding之前的鐵律

寫在coding之前的那些鐵律

 (1)註釋: 好的代碼首先必須要有註釋,註釋至少包括文件註釋,端口註釋,功能語句註釋。

     文件註釋:文件註釋就是一個說明文:這通常在文件的頭部註釋,用於描述代碼爲那個工程中,由誰寫的,日期是多少,功能描述,有哪些子功能,及版本修改的標示。這樣不論是誰,一目瞭然。即使不寫文檔,也能知道大概。

     接口描述:module的接口信號中,接口註釋描述模塊外部接口,例如AHB接口,和SRAM接口等等。這樣讀代碼的人即可能夠判斷即模塊將AHB接口信號線轉換成SRAM接口信號。

     功能語句註釋: 內部關鍵邏輯,狀態機某狀態,讀過程、寫過程。

    註釋的重要性,毋庸置疑,好的註釋,能夠提高代碼的可讀性,可維護性等等。總之,養成註釋的好習慣,代價不大,但是收益很大。

2)語句:

開始寫代碼是,在FPGA設計中,特別是在可綜合的模塊實現中,verilog的語句是很固定的。FPGA的設計中,不外乎時序邏輯和組合邏輯,除此之外,別無他法。對於開始功能編碼來說,只需知道組合邏輯信號即可生效,時序邏輯在時鐘的下一拍起效就夠了。

下面是編碼的實例。

組合邏輯:兩種組合邏輯的描述,其功能是一致的。

   assign   A = B ? 1 : D ? 3;

   always@(*)

        if(B)

          A = 1

        else  ifD

          A = 2;

        else

          A = 3;

  組合邏輯 如果是異步復位的話,描述如下

 always@(posedge sys_clk or negedge rst_n)

        if(!rst_n)

          a <= 0;

        else

          a <= b;

   也就是說,在verilog的可綜合電路的編碼中,只需要三種語句,分別是assign, always(*) 及時序的always(`CLOCK_EDGE clk )  `CLOCK_EDGE 可以是上升沿或者下降沿。

    爲什麼用always@(*) 而不是always(敏感信號列表)。“*”包含所有敏感信號列表,如果在coding過程中,漏掉了某個敏感信號,則會導致仿真不正確,例如本例中,敏感變量列表中,需要B or C 但是如果漏掉一個,仿真就會在BC有變化時,輸出沒有變化。導致仿真和功能不一致,但是對於綜合工具來說,功能還是能夠正常工作的,不會因爲敏感變量列表中的值未列全而不綜合某條語句。某些情況下,敏感列表的值可能有十幾個甚至更多,遺漏是可能發生的事情,但是爲了避免這種問題,最好採用always(*)而不用敏感變量列表的方式,來避免仿真結果不一致的情況發生。

3)賦值:老話重提,阻塞與非阻塞

很多同志喜歡鑽研阻塞賦值和非阻塞賦值,這兩種賦值,分別在always塊裏面用於的阻塞“=”給組合邏輯賦值,非阻塞”<=”給時序邏輯賦值。這應該是鐵律,應該在編碼過程中被嚴格的遵守下來。“爲什麼?,不這麼用程序也能跑”。這句話部分是正確的,疑問永遠是工程師最好的老師。

誠然,某些情況下,不嚴格的執行也跑,但是在某些情況下,實現二者就不一樣。

    對於下面兩個例子來說明,爲什麼?

對於value1的描述方式:其綜合後的如下所示

   如果從實際的編譯結果上看 bb1 cc1其使用阻塞賦值和非阻塞賦值最終的結果是一致的,因此,也就是說,某些情況下,二者的編譯結果一致。

   而對於value2的描述方式:其綜合後的電路圖如下所示。

而對於第二中描述方式,阻塞賦值和非阻塞賦值的區別就顯現出來了,從綜合後的圖中可以看到,c1信號是b1信號的寄存,而c信號和b信號爲同一信號,都爲a信號的寄存。

作爲FPGA工程師,一項基本的能力,就是要知道代碼綜合後的電路和時序,不要讓其表現和你預想的不一致,“不一致”就意味着失敗。即是代碼的失敗,也是工程的失敗

對於阻塞和非阻塞賦值區別和詳細說明來說,其能夠編寫一本書(如有時間也可專題詳述),但是對FPGA工程師,對於verilog的編碼而言,則只需要按照時序邏輯用“<=”非阻塞,組合邏輯用阻塞“=”賦值即可。不要挑戰那些規律,試圖通過語言的特性來生成特殊電路的嘗試是不可取的,開個玩笑的話,是沒有前途的,要把設計的精力放在通過可用的電路來實現需求上,不要捨本逐末。在數字電路設計中,我們需要的是一個確定的世界,“所見及所得”,不要讓你所想的和綜合編譯工具得認識不一致。這也就是不要亂用和混用這兩個賦值的原因。

4)一個變量一個“家”

   不要在兩個always語句中同一個變量賦值。(這是必須的)

   也儘量不要在同一個always語句中,對兩個變量賦值。(這是可選的)

如果是一組信號,其有共同的控制條件,則在同一always語句中賦值能夠減少代碼行數,提高可讀性,除此之外,最好分開來寫。如果幾個不太相關的信號在同一裏面賦值,其可讀性極差,在組合邏輯中,還容易產生latch

而前者賦值方式,綜合工具肯定會報錯,這到不用很擔心,因爲能夠報的錯誤時是最容易被發現的。俗語說:“咬人的狗不叫”,而對於FPGA設計來說“致命的BUG,從來不報錯”。

5)鎖存

     FPGA中不要有鎖存器的產生。最容易產生的是在always*)語句中,最後一定是所有分支條件都要描述並賦值,(一定要有最後的else)。狀態機中,同樣如此,不但需要有default的狀態,每個狀態的都要有所有的分支都要賦值。

鎖存器,是FPGA設計的大敵,因爲會導致非你想要的錯誤功能的產生,並且導致時序分析錯誤,就會產生前述的問題“所見不是所得”,並且綜合工具不會報錯。

如果你設計的電路功能,仿真正確,而實際工作不正常,有一部分的原因是生成了鎖存器,如果設計很大,不容易查的話,可以打開綜合報告,搜索“LATCH”關鍵詞,查看是否有鎖存器的產生,一句話“鎖存器,必殺之”。

時序邏輯會產生鎖存器嗎?當然不會,時序邏輯綜合結果必然是觸發器,因此不用檢查時序邏輯的分支條件。

綜上:這是寫在coding之前的話,編碼的主要功能應該是用可靠的電路來描述FPGA功能和需求,不要試圖通過語言的特性來描述功能,設計的主要精力應放在用已知的電路(組合邏輯,時序邏輯)描述未知功能。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章