FPGA研發(fā)之道(14)寫在coding之前的鐵律
寫在coding之前的那些鐵律
本文引用地址:http://www.ex-cimer.com/article/265175.htm(1)注釋: 好的代碼首先必須要有注釋,注釋至少包括文件注釋,端口注釋,功能語句注釋。
文件注釋:文件注釋就是一個(gè)說明文:這通常在文件的頭部注釋,用于描述代碼為那個(gè)工程中,由誰寫的,日期是多少,功能描述,有哪些子功能,及版本修改的標(biāo)示。這樣不論是誰,一目了然。即使不寫文檔,也能知道大概。
接口描述:module的接口信號中,接口注釋描述模塊外部接口,例如AHB接口,和SRAM接口等等。這樣讀代碼的人即可能夠判斷即模塊將AHB接口信號線轉(zhuǎn)換成SRAM接口信號。
功能語句注釋: 內(nèi)部關(guān)鍵邏輯,狀態(tài)機(jī)某狀態(tài),讀過程、寫過程。
注釋的重要性,毋庸置疑,好的注釋,能夠提高代碼的可讀性,可維護(hù)性等等??傊?,養(yǎng)成注釋的好習(xí)慣,代價(jià)不大,但是收益很大。
(2)語句:
開始寫代碼是,在FPGA設(shè)計(jì)中,特別是在可綜合的模塊實(shí)現(xiàn)中,verilog的語句是很固定的。在FPGA的設(shè)計(jì)中,不外乎時(shí)序邏輯和組合邏輯,除此之外,別無他法。對于開始功能編碼來說,只需知道組合邏輯信號即可生效,時(shí)序邏輯在時(shí)鐘的下一拍起效就夠了。
下面是編碼的實(shí)例。
組合邏輯:兩種組合邏輯的描述,其功能是一致的。
assign A = B ? 1 : D ? 2 :3;
always@(*)
if(B)
A = 1
else if(D)
A = 2;
else
A = 3;
組合邏輯 如果是異步復(fù)位的話,描述如下
always@(posedge sys_clk or negedge rst_n)
if(!rst_n)
a <= 0;
else
a <= b;
也就是說,在verilog的可綜合電路的編碼中,只需要三種語句,分別是assign, always(*) 及時(shí)序的always(`CLOCK_EDGE clk ) 。 `CLOCK_EDGE 可以是上升沿或者下降沿。
為什么用always@(*) 而不是always(敏感信號列表)?!?”包含所有敏感信號列表,如果在coding過程中,漏掉了某個(gè)敏感信號,則會導(dǎo)致仿真不正確,例如本例中,敏感變量列表中,需要B or C 但是如果漏掉一個(gè),仿真就會在B或C有變化時(shí),輸出沒有變化。導(dǎo)致仿真和功能不一致,但是對于綜合工具來說,功能還是能夠正常工作的,不會因?yàn)槊舾凶兞苛斜碇械闹滴戳腥痪C合某條語句。某些情況下,敏感列表的值可能有十幾個(gè)甚至更多,遺漏是可能發(fā)生的事情,但是為了避免這種問題,最好采用always(*)而不用敏感變量列表的方式,來避免仿真結(jié)果不一致的情況發(fā)生。
(3)賦值:老話重提,阻塞與非阻塞
很多同志喜歡鉆研阻塞賦值和非阻塞賦值,這兩種賦值,分別在always塊里面用于的阻塞“=”給組合邏輯賦值,非阻塞”<=”給時(shí)序邏輯賦值。這應(yīng)該是鐵律,應(yīng)該在編碼過程中被嚴(yán)格的遵守下來?!盀槭裁?,不這么用程序也能跑”。這句話部分是正確的,疑問永遠(yuǎn)是工程師最好的老師。
誠然,某些情況下,不嚴(yán)格的執(zhí)行也跑,但是在某些情況下,實(shí)現(xiàn)二者就不一樣。
對于下面兩個(gè)例子來說明,為什么?
?
對于value1的描述方式:其綜合后的如下所示
?
如果從實(shí)際的編譯結(jié)果上看 b和b1 及c和c1其使用阻塞賦值和非阻塞賦值最終的結(jié)果是一致的,因此,也就是說,某些情況下,二者的編譯結(jié)果一致。
?
而對于value2的描述方式:其綜合后的電路圖如下所示。
?
而對于第二中描述方式,阻塞賦值和非阻塞賦值的區(qū)別就顯現(xiàn)出來了,從綜合后的圖中可以看到,c1信號是b1信號的寄存,而c信號和b信號為同一信號,都為a信號的寄存。
作為FPGA工程師,一項(xiàng)基本的能力,就是要知道代碼綜合后的電路和時(shí)序,不要讓其表現(xiàn)和你預(yù)想的不一致,“不一致”就意味著失敗。即是代碼的失敗,也是工程的失敗
對于阻塞和非阻塞賦值區(qū)別和詳細(xì)說明來說,其能夠編寫一本書(如有時(shí)間也可專題詳述),但是對FPGA工程師,對于verilog的編碼而言,則只需要按照時(shí)序邏輯用“<=”非阻塞,組合邏輯用阻塞“=”賦值即可。不要挑戰(zhàn)那些規(guī)律,試圖通過語言的特性來生成特殊電路的嘗試是不可取的,開個(gè)玩笑的話,是沒有前途的,要把設(shè)計(jì)的精力放在通過可用的電路來實(shí)現(xiàn)需求上,不要舍本逐末。在數(shù)字電路設(shè)計(jì)中,我們需要的是一個(gè)確定的世界,“所見及所得”,不要讓你所想的和綜合編譯工具得認(rèn)識不一致。這也就是不要亂用和混用這兩個(gè)賦值的原因。
(4)一個(gè)變量一個(gè)“家”
不要在兩個(gè)always語句中同一個(gè)變量賦值。(這是必須的)
也盡量不要在同一個(gè)always語句中,對兩個(gè)變量賦值。(這是可選的)
如果是一組信號,其有共同的控制條件,則在同一always語句中賦值能夠減少代碼行數(shù),提高可讀性,除此之外,最好分開來寫。如果幾個(gè)不太相關(guān)的信號在同一里面賦值,其可讀性極差,在組合邏輯中,還容易產(chǎn)生latch。
而前者賦值方式,綜合工具肯定會報(bào)錯(cuò),這到不用很擔(dān)心,因?yàn)槟軌驁?bào)的錯(cuò)誤時(shí)是最容易被發(fā)現(xiàn)的。俗語說:“咬人的狗不叫”,而對于FPGA設(shè)計(jì)來說“致命的BUG,從來不報(bào)錯(cuò)”。
(5)鎖存
FPGA中不要有鎖存器的產(chǎn)生。最容易產(chǎn)生的是在always(*)語句中,最后一定是所有分支條件都要描述并賦值,(一定要有最后的else)。狀態(tài)機(jī)中,同樣如此,不但需要有default的狀態(tài),每個(gè)狀態(tài)的都要有所有的分支都要賦值。
鎖存器,是FPGA設(shè)計(jì)的大敵,因?yàn)闀?dǎo)致非你想要的錯(cuò)誤功能的產(chǎn)生,并且導(dǎo)致時(shí)序分析錯(cuò)誤,就會產(chǎn)生前述的問題“所見不是所得”,并且綜合工具不會報(bào)錯(cuò)。
如果你設(shè)計(jì)的電路功能,仿真正確,而實(shí)際工作不正常,有一部分的原因是生成了鎖存器,如果設(shè)計(jì)很大,不容易查的話,可以打開綜合報(bào)告,搜索“LATCH”關(guān)鍵詞,查看是否有鎖存器的產(chǎn)生,一句話“鎖存器,必殺之”。
時(shí)序邏輯會產(chǎn)生鎖存器嗎?當(dāng)然不會,時(shí)序邏輯綜合結(jié)果必然是觸發(fā)器,因此不用檢查時(shí)序邏輯的分支條件。
綜上:這是寫在coding之前的話,編碼的主要功能應(yīng)該是用可靠的電路來描述FPGA功能和需求,不要試圖通過語言的特性來描述功能,設(shè)計(jì)的主要精力應(yīng)放在用已知的電路(組合邏輯,時(shí)序邏輯)描述未知功能。
fpga相關(guān)文章:fpga是什么
電路圖符號相關(guān)文章:電路圖符號大全
評論