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