數(shù)字邏輯系統(tǒng)的設(shè)計實際上包含兩個相關(guān)又獨立的領(lǐng)域:設(shè)計與測試。這套書重點是設(shè)計,因為老衲對于測試不在行,所謂“藏拙”者也。但是完全不介紹測試也不成:這樣設(shè)計出來的代碼不知道對錯了。所以,今晚給大伙兒講點皮毛。
但凡人造系統(tǒng)都是有輸入輸出的,要不然這個系統(tǒng)對設(shè)計者或者用戶沒有用場,沒人無聊去設(shè)計 ---- 即使是魯布·戈德堡機(jī)械 。前面介紹的數(shù)字邏輯電路是通過芯片的管腳實現(xiàn)輸入輸出的,無論進(jìn)出的信號都是數(shù)字信號。仿真代碼理論上實在計算機(jī)上面運行的,和程序語言類似;所以,仿真部分的輸入輸出也和程序語言類似,是各種計算機(jī)的輸入輸出設(shè)備,包括文件。
1. 時鐘復(fù)位,自己編程
當(dāng)設(shè)計的單元簡單、輸入輸出關(guān)系明確的時候,可以由設(shè)計者直接設(shè)計測試向量完成驗證。這也是大多數(shù)資料里面介紹的方法,其中內(nèi)容不必詳述。比如,你做一個計數(shù)器,然后你做一下 reset 和 clock 信號,于是在 reset 不生效之后,輸出就是 1-2-3…了,OK,恭喜啊,你得設(shè)計正確。再如,你設(shè)計一個加法器,你就需要做不同的加數(shù)和被加數(shù)的組合,如果得到 0 + 1 = 1,1 + 1 =2, 1 + 2= 3…那么就可以交貨了。
時鐘信號無疑是最重要,但是也是最簡單的信號之一了。50%占空比的時鐘信號的代碼見例 8.19。其他占空比的時鐘稍稍麻煩一些,forever 塊需要兩個時延和兩次賦值。實際上,占空比并不會影響數(shù)字邏輯系統(tǒng)的性能,所以盡量用 50%占空比的時鐘就好了。
【例 8.19】時鐘生成部分代碼
…
parameter DELAY;
…
reg clk;
…
begin
//Clock generation
initial
begin
clk = 0;
//Reset
forever
begin
#DELAY clk = !clk;
//Reverse the clock in each 10ns
end
end
…
據(jù)說時鐘產(chǎn)生有多種方法的,和“‘回’字的三種寫法”道理一樣。咱們不是孔乙己類似的腐儒,會一種用熟了就好了。
復(fù)位信號也不復(fù)雜,開機(jī)的復(fù)位代碼見例 8.20 所示(高電平復(fù)位)。其他時刻的復(fù)位,就是再外加一個時延的問題,施主們自便。
【例 8.20】開機(jī)復(fù)位的代碼
…
parameter RESET_PERIOD;
…
reg clk;
…
//Reset operation
initial
begin
reset = 0;
//Reset enable
# RESET_PERIOD reset = 1;
//Counter starts
end
…
2. 外部芯片,討要廠家
當(dāng)設(shè)計了一個對于復(fù)雜芯片的接口或者控制器,你就需要外部芯片的行為模型配合你得測試了。舉個例子,你需要設(shè)計一個 SDRAM 的控制器,用來控制大容量的 SDRAM 來存儲數(shù)據(jù)。首先,你會得到一個對應(yīng) RAM 的 datasheet(數(shù)據(jù)手冊),里面會有芯片的電氣特征啊什么的,數(shù)字邏輯設(shè)計不感興趣的內(nèi)容。大家需要注意的就是讀寫的時序,還有 SDRAM 的刷新要求(不懂這個?沒關(guān)系了,就是一個例子。等到你做的時候,自然就了解了。如果還是不懂,那么等著丟數(shù)據(jù)吧,別抱怨我沒說過)。這個控制器基本就是一個狀態(tài)機(jī)。在設(shè)計完之后,需要進(jìn)行驗證的時候,問題來了:“如何測試這種系統(tǒng)呢?”。豎起耳朵聽著,找供貨商要對應(yīng)芯片的行為模型,這個可以有的。
事實上,在選擇芯片的時候,有沒有這種模型是很重要的一個指標(biāo),必須向采購要求。叫甲方們在吃回扣之余,一定要選擇有行為模型的芯片。要不然,邏輯設(shè)計的工作量就是加倍了。而且即使我們按照自己的理解設(shè)計了外部芯片的行為,還不能保證我們做的模型是完全正確的。這就陷入了驗證 - 再驗證的死循環(huán)了,命苦啊。
這種驗證最簡單,除了時鐘、復(fù)位什么的以外,就需要構(gòu)建幾個線型變量,把我們的設(shè)計和片外芯片鏈接在一起就好了。簡單說,就是實例化外部新片、實例化設(shè)計的系統(tǒng)加上連線,三步走。
3. 復(fù)雜處理,文件讀取
在數(shù)字信號處理類的系統(tǒng)驗證的時候,一般需要算法工程師一起來聯(lián)合工作。例如:通訊里面的發(fā)射機(jī)、接收機(jī)等。和我們一樣辛勞的算法工程師們,需要給我們提供測試向量。這種向量一般是以文件形式提供的,里面有輸入和輸出數(shù)據(jù),一般是十六進(jìn)制的幾列。大家需要利用文件讀寫函數(shù),讀取輸入(可能也要輸出)數(shù)據(jù),輸入需要驗證的系統(tǒng),來考察設(shè)計是否正確。
這里主要涉及到了 Verilog 語言的文件操作的系統(tǒng)任務(wù),下面給大伙兒做一個介紹。Verilog 語言里面的文件操作基本是照搬 C 語言里面的,有打開文件、讀文件、寫文件和關(guān)閉文件等功能。最后啰嗦一句:全部文件操作都不能綜合。常用任務(wù)見表 8.10 所示。
表 8.10 常用文件有關(guān)的系統(tǒng)任務(wù)
功能 |
格式 |
打開文件 |
integer file_id = $fopen ( " file_name " ); integer file_id = $fopen ( " file_name ", type ); |
關(guān)閉文件 |
$fclose (file_id); |
寫文件 |
$fdisplay(file_id , list_of_arguments); $fwrite(file_id , list_of_arguments); $fstrobe(file_id , list_of_arguments); $fmonitor(file_id , list_of_arguments); |
寫數(shù)據(jù)的格式 |
$swrite(output_reg, list_of_arguments); $sformat(output_reg, format_string, list_of_arguments); |
讀一個字符 |
c = $fgetc ( file_id ); |
讀一行 |
integer code = $fgets ( str, file_id ); |
按照格式讀 |
integer code = $fscanf ( file_id, format, args ); |
讀二進(jìn)制文件 |
integer code = $fread( myreg, file_id); integer code = $fread( mem, file_id); integer code = $fread( mem, file_id, start); integer code = $fread( mem, file_id, start, count); integer code = $fread( mem, file_id, , count); |
文件位置操作 |
integer pos = $ftell ( fIle_id ); code = $fseek ( file_id, offset, operation ); code = $rewind ( file_id ); |
文件調(diào)入內(nèi)存 |
$readmemb ( " file_name " , memory_name , start_addr , finish_addr ) ; $readmemh ( " file_name " , memory_name , start_addr , finish_addr ) ; |
文件操作狀態(tài) |
integer errno = $ferror ( file_id, str ); |
表 8.11 打開文件中的打開方式
字符串 |
含義 |
字符串 |
含義 |
"r" 或者"rb" |
只讀 |
"w" 或者 "wb" |
調(diào)整長度為 0 或者建立新文件,用于寫 |
"a" 或者 "ab" |
讀,打開文件在末尾寫或者新文件,用于寫 |
"r+","r+b"或者 "rb+" |
更新 |
"w+","w+b"或者 "wb+" |
調(diào)整長度為 0 或者或者建立新文件,用于寫 |
"a+", "a+b"或者"ab+" |
讀,打開文件或者新建文件,在文件末尾寫 |
寫文件任務(wù)中 display、monitor 和 strobe 和顯示任務(wù)對應(yīng)功能類似。
雖然 Verilog 里面基本繼承了有關(guān)文件的全部操作,但是一般沒人會聰明到用 Verilog 語言來拷貝一個文件什么的,雖然這個真的可以做到。前面說了,這些任務(wù)中最常用的是有打開文件、讀文件、寫文件和關(guān)閉文件等功能。
使用 Verilog 語言進(jìn)行系統(tǒng)仿真或者驗證的時候,最常用的文件處理有兩種。其一是讀取需要輸入系統(tǒng)的或者用于系統(tǒng)輸出對比用的測試向量,在仿真環(huán)境中檢驗系統(tǒng)設(shè)計是否正確;還有就是把系統(tǒng)的輸出寫入文件里面,用外面的程序調(diào)用來測試性能。
讀取文件中測試向量數(shù)據(jù)一般使用 ---- 針對二進(jìn)制數(shù)值以 ASCII 碼存儲的文本文件的$readmemb,或者針對十六進(jìn)制數(shù)值以 ASCII 碼存儲的文本文件的$readmemh 。它們針對的文件中數(shù)據(jù)格式不同,但是基本功能相同。參數(shù)中,“file_name”為需要讀的文件名;“memory_name”是數(shù)值在 Verilog 代碼中的存儲位置,一般為數(shù)組;“start_addr ”和“finish_addr”為開始讀取和結(jié)束讀取的位置,可以忽略(此時從頭到尾地讀文件)。
讀取的文本文件中,只允許存在空白符號(空格、換行、tab 或者制表符)、注釋(兩種格式均可)和二進(jìn)制 / 十六進(jìn)制數(shù)值。
文件中的數(shù)據(jù)由開始到結(jié)束,存儲到以“memory_name”命名的存儲單元從最小標(biāo)號開始的順序空間中。如果存儲單元對應(yīng)的區(qū)域不夠存儲全部數(shù)據(jù)的時候,系統(tǒng)會給出警告。
4. 更加復(fù)雜,外部接口
Verilog 語言的基本語法部分,前面已經(jīng)竹筒倒豆子地交代給施主們了。在目前介紹過的語言體系下,完成上面所說的完全依賴機(jī)器進(jìn)行的測試和驗證,還是有難度的。所以,Verilog 語言標(biāo)準(zhǔn)里面最后面的大約三分之一的章節(jié),都在介紹一種 Verilog 和高級語言(標(biāo)準(zhǔn)里主要指 C 語言)的接口機(jī)制 ---- 這個機(jī)制就是 PLI。當(dāng)然靠這里的幾頁紙的介紹,不可能教會大伙兒如何使用這個高級玩意兒。貧道的目的是叫到家知道 PLI 這回事,萬一急需時候不會茫無頭緒。
PLI 提供一種接口,將用戶編寫的 C 或 C++程序連接到 Verilog 仿真器上,實現(xiàn) Verilog 仿真器的功能擴(kuò)展和定制。
PLI 接口主要提供以下三種功能。
首先, PLI 接口允許用戶編寫自定義的系統(tǒng)任務(wù)與函數(shù)。用戶寫出相應(yīng)的 PLI 程序并連接到仿真器后,就可以在自己寫的代碼中使用這些系統(tǒng)任務(wù)與函數(shù)了。一旦這些在仿真過程中被調(diào)用,仿真器就會找到對應(yīng)的用戶編寫的 PLI 程序來執(zhí)行,從而實現(xiàn)仿真 器的定制。
其次,PLI 接口還允許用戶在自己的 PLI 程序中與仿真器中實例化的 Verilog 硬件進(jìn)行交互,比如讀一個信號的值,給一組觸發(fā)器寫值,設(shè)置一個單元的時延等。對于 PLI 程序而言,仿真器中的 Verilog 實例完全是透明的。在不改變系統(tǒng)結(jié)構(gòu)的前提下。用戶想對這些硬件做什么 操作都可以。
最后,某些特定的操作需要對仿真過程中一些信號的變化做出響應(yīng)。雖然我們可以用 always 來監(jiān)控少量信號的變化,但如果需要監(jiān)測大量信號,這種機(jī)制并不現(xiàn)實。PLI 接口提供了一種函數(shù)回調(diào)機(jī)制解決這個問題。用戶可以將某個信號掛上一個 PLI 程序中的 C 函數(shù),以后每當(dāng)該 信號變化,這個 C 函數(shù)都會被調(diào)用,從而很方便地實現(xiàn)信號監(jiān)測。
除了上面所說的這些機(jī)制外,PLI 還能讓用戶控制仿真的過程,比如暫停,退出,往 log 文件里寫信息等。還可以采集仿真過程的數(shù)據(jù),比如當(dāng)前仿真時間等等。實際的 PLI 程序中這些功能同樣少不了。
PLI 的典型應(yīng)用主要包括:
第一, 實現(xiàn) Verilog 模型和 C 模型的共同仿真。對于比較復(fù)雜的系統(tǒng),開發(fā)者經(jīng)常需要首先制作一個能夠工作的 C 模型,然后逐模塊地將其改寫為 Verilog。這樣可以實現(xiàn)一個比較安全、漸進(jìn)的開發(fā)過程。還有一些比較復(fù)雜的硬件單元庫,尤其是定制的浮點單元庫,其仿真模型都是 C 模型。這時也必須 使用 PLI 來連接兩種模型。
其次,產(chǎn)生測試激勵,產(chǎn)生驗證矢量或直接進(jìn)行驗證。這是 PLI 程序最常用也是最主要的功能。比較復(fù)雜的系統(tǒng)經(jīng)常需要根 據(jù)上一個激勵的響應(yīng)決定下一個激勵,這就要求過程控制做得很好??墒?,大家用過 Verilog 的都知道,Verilog 的過程控制非常弱,很難在 測試代碼中寫出復(fù)雜的程序控制。而 C 在這一點上有絕對的優(yōu)勢。因此,使用 PLI 可以取長補(bǔ)短,實現(xiàn)一個高效的仿真系統(tǒng)。
第三,捕獲仿真過程和結(jié)果,并以用戶易于接受的方式輸出。舉一個典型的例子,打波形就是這種應(yīng)用。
第四,分析仿真過程,計算性能參數(shù)。例如功耗分析的實現(xiàn)方式,就是用 PLI 紀(jì)錄每個單元的翻轉(zhuǎn)情況,然后根據(jù)翻轉(zhuǎn)次數(shù)計算功耗。
最后, 軟硬件聯(lián)合仿真?,F(xiàn)在,純以硬件方式工作的芯片已經(jīng)不多了,大量的芯片工作時都需要軟件的參與和控制。離開這些控制軟件進(jìn)行仿真,工作環(huán)境不真實,可能造 成激勵模式的遺漏。如果不用 PLI,我們就只能用 Verilog 來在測試代碼中模擬軟件控制,但 Verilog 可能根本描述不了復(fù)雜的控制。使 用 PLI 連接控制軟件和仿真器,我們就能夠模擬實際的芯片工作環(huán)境和過程。另外還有一個額外的好處:控制軟件本身也能在這個過程中進(jìn)行調(diào)試。
到 1995 年 Verilog 成為 IEEE 標(biāo)準(zhǔn)時,就停止發(fā)展了,而 VPI 從此時開始誕生,到現(xiàn)在也還在演化。IEEE Verilog 工作組的本意是從那以后逐步使用 VPI 替代 PLI 1.0,但這個目標(biāo)到現(xiàn)在也沒有實現(xiàn),而且還看不到實現(xiàn)的希望。
在使用上,PLI 和 VPI 各有特點。PLI 的 API 又多又全又亂,而 VPI 的 API 就非常精煉,一個詞,漂亮。常用的 PLI 的函數(shù)集中恐怕至少有近百個,寫程序時不查手冊幾乎不可能;而且,很多 API 是重復(fù)的。而 VPI 是標(biāo)準(zhǔn)組織制訂出來的,而且融入了相當(dāng)多的面向思想,因此在精煉方面顯然遠(yuǎn)遠(yuǎn)超過 PLI。整個 VPI 的函數(shù)集才二十來個函數(shù)。但是,VPI 并不好 學(xué),因為它的結(jié)構(gòu)遠(yuǎn)比 PLI 復(fù)雜。VPI 最大的弱點還是仿真器對其支持不好。很多 03 年出的仿真器都還不敢聲稱自己很好地支持 VPI。
電子產(chǎn)品有關(guān)的市場早就進(jìn)入了白熱化的競爭階段,一個效果總會有很多不同的方案來競爭。說老實話,Verilog 語言用于測試和驗證不是十分方便,所以有了 SystemVerilog 語言來占領(lǐng)這個領(lǐng)域。PLI 在 Verilog 語言里面也是很麻煩的部分,對應(yīng) SystemVerilog 里面的 DPI(Direct Programming Interface,直接編程接口)就簡單多了。
SystemVerilog 是一種由 Verilog 發(fā)展而來的硬件描述、硬件驗證統(tǒng)一語言,前一部分基本上是 2005 年版 Verilog 的擴(kuò)展,而后一部分功能驗證特性則是一門面向?qū)ο蟪绦蛟O(shè)計語言。面向?qū)ο筇匦院芎玫貜浹a(bǔ)了傳統(tǒng) Verilog 在芯片驗證領(lǐng)域的缺陷,改善了代碼可重用性,同時可以讓驗證工程師在比寄存器傳輸級更高的抽象級別,以事務(wù)而非單個信號作為監(jiān)測對象,這些都大大提高了驗證平臺搭建的效率。
SystemVerilog DPI,是 SystemVerilog 與其他外來編程語言的接口。能夠使用的語言包括 C 語言、C++、SystemC 等。直接編程接口由兩個層次構(gòu)成:SystemVerilog 層和外來語言層。兩個層次相互分離。對于 SystemVerilog 方面,另一邊使用的編程語言是透明的,但它并不關(guān)注這一點。SystemVerilog 和外來語言的編譯器各自并不需要分析另一種語言的代碼。由于不觸及 SystemVerilog 層,因此支持使用不同的語言。不過,目前 SystemVerilog 僅為 C 語言定義了外來語言層。
凡是 PLI 可以做的事情,DPI 都可以做;凡是 PLI 可以應(yīng)用的場合,DPI 都可以應(yīng)用。比較而言,DPI 具有很大的優(yōu)勢:它允許用戶調(diào)用現(xiàn)有的 C 代碼,而不需要 Verilog 語言 PLI 或者 VPI 接口。它還提供了一種替代的、簡單的方式調(diào)用,雖然不能完成全部 PLI 的功能。但是 DPI 實現(xiàn)了最有用的部分。用 C 實現(xiàn)的函數(shù),在可以使用“引入 DPI”聲明后,在 SystemVerilog 代碼中調(diào)用。只需在 SystemVerilog 里面簡單聲明,即可使用這些函數(shù)作為輸入的任務(wù)和函數(shù)。
看到前面的說法,很多施主會認(rèn)為 DPI 比起 PLI 來簡單。這個結(jié)論對也不對。如果您老熟悉 SystemVerilog 這門語言,這句話沒錯:原來 PLI 里面幾十行的代碼,在 DPI 里面一般也就只要幾行就完成了。但是,如果貴客還沒學(xué)習(xí)過 SystemVerilog 語言,那就要重新學(xué)習(xí)了。而且,對于設(shè)計、驗證雙肩挑的工程師而言,這還意味著要在兩種語言之間不停切換。這個大概相當(dāng)于雙手互博的難度,很考驗功力的。
DPI 還有一個問題就是支持的軟件有限。例如:Windows 平臺下 Modelsim 對 DPI 支持就不好。這個情況也限制了它的推廣。
這正是:
“
凡人總有出錯時,代碼怎能無問題?欲要系統(tǒng)能跑起,仿真驗證是真諦。
測試系統(tǒng)自動機(jī),信號產(chǎn)生不離棄。文件讀寫取數(shù)值,外部模塊來聯(lián)系。
”
與非網(wǎng)原創(chuàng)內(nèi)容,謝絕轉(zhuǎn)載!
系列匯總:
之二:Verilog 編程無法一蹴而就,語言層次講究“名正則言順”
之三:數(shù)字邏輯不容小窺,電路門一統(tǒng)江湖
之五:Verilog 不難學(xué),聊聊時序邏輯那些事兒