日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【数字设计验证】System Verilog(sv)稍微进阶的笔记(一)

發(fā)布時(shí)間:2023/12/20 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【数字设计验证】System Verilog(sv)稍微进阶的笔记(一) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 1. EDA工具對(duì)代碼的處理與輸出
    • 1.1 Compile
    • 1.2 Simulation
    • 1.3 Synthesis
  • 2. System Verilog Coding Guide
    • 2.1 狀態(tài)機(jī)【Design】
    • 2.2 Behavorial Verilog再到Always模塊【Design】
    • 2.3 便捷寫(xiě)法【Design】
    • 2.4 變量運(yùn)算【Both DV】
    • 2.5 Verilog Stratified Event Queue【Verification】
    • 2.6 Fork methods【Verification】
    • 2.7 Assertion【Verification】
    • 2.8 import package & `include【Both DV】
    • 2.9 Random Testing【Verification】
    • 2.10 Fuction & Task【Verification】
    • 2.11 Coding for Synthesis【Design】
  • 3. Extension
    • 3.1 Automatic buildup/Test Script Cases
    • 3.2 Code Coverage
    • 3.3 TBD

接觸硬件描述語(yǔ)言(HDL)也有幾個(gè)年頭了,由于之后research會(huì)偏向Architecture,做偏軟件的活,算是走入一個(gè)新的階段,因此想寫(xiě)一篇關(guān)于SV的筆記進(jìn)行總結(jié)復(fù)習(xí)。選擇SV的原因在于它目前是業(yè)界主流。SV是Verilog的繼承擴(kuò)展版本,類(lèi)似于Cpp和C的關(guān)系,擴(kuò)展內(nèi)容可以分為Declaration Enhancement(多了變量類(lèi)型),和Programming Enhancement(一些寫(xiě)法的shortcut,硬件行為描述的支持,運(yùn)算符,directive等等),具體參見(jiàn)下圖,Coding會(huì)在Part 2詳細(xì)說(shuō)明。總之sv對(duì)design和verification的支持都很顯著,本文會(huì)挑個(gè)人覺(jué)得比較有用的點(diǎn)去記,所以這不是一個(gè)入門(mén)的指導(dǎo),我會(huì)跳過(guò)蠻多基礎(chǔ)的東西,Bear in mind!!!

1. EDA工具對(duì)代碼的處理與輸出

在講Coding前,還是稍微skim一下它的工作步驟,只想看sv的Coding Guide的話(huà)直接跳到Part 2,這里不細(xì)說(shuō)底層的運(yùn)行,因?yàn)檫@與不同EDA工具和FAB的lib有關(guān)系 (例如說(shuō)Synopsy和Cadence的綜合程序算法就不大一樣),目前還沒(méi)有能力和興趣去探究,主要還是說(shuō)一些已經(jīng)standardized的東西。我們使用HDL的最終目的在于生成可靠可知的IC layout,我們將layout及其設(shè)計(jì)步驟抽象為代碼,人寫(xiě)完代碼后借由一系列Computer Aided Design(CAD)工具(在EE領(lǐng)域我們特指它為EDA)再將代碼轉(zhuǎn)變?yōu)閘ayout的輸出文件。因?yàn)槲覀冃枰?strong>可靠可知,所以設(shè)計(jì)流程中的一些中間產(chǎn)物也是很重要的。因此這里將對(duì)HDL代碼的處理分步為:Compile, Simulation和Synthesis。對(duì)應(yīng)著不同類(lèi)的EDA工具對(duì)應(yīng)的功能目的和輸出結(jié)果。

PS: HDL可服務(wù)于不同類(lèi)型的DIC實(shí)現(xiàn):Full Custom, Semi-Custom和Programmable。這更分化了不同的EDA功用,但是大道至簡(jiǎn),殊途同歸,后文主要還是講general的部分和一些經(jīng)驗(yàn)tips方便回憶

1.1 Compile

第一步是編譯,實(shí)際上對(duì)于這一名詞不同工具我們也能看到不同的結(jié)果。比如說(shuō)對(duì)于vivado或者dc shell這類(lèi)需要后續(xù)制作netlist等的軟件,當(dāng)我們輸入filelist進(jìn)行編譯后除了進(jìn)行syntax analysis外,還會(huì)附帶生成對(duì)應(yīng)的RTL Schematic,也就是說(shuō)我們可以看到電路的hierarchy,甚至有些情況Compile就直接是Synthesis操作本身。而對(duì)于仿真器而言,Compile的含義很多只代指對(duì)與各個(gè)文件的代碼分析,當(dāng)要去執(zhí)行Simulation的時(shí)候才會(huì)load各個(gè)模塊生成Schematic和對(duì)應(yīng)的Hierarchy。

這里仿真軟件舉的例子為Modelsim(vsim這個(gè)軟件的組分也是比較多的,包括compiler, linter, simulator, waveform visioner等,支持GUI或者TCL對(duì)于各個(gè)模塊的調(diào)用)。它在compile worklib后點(diǎn)擊View–Schematic依舊是空的,在Run Simulation過(guò)后可以在Sim窗口中看到目標(biāo)top module 的Hierarchy結(jié)構(gòu)還有schematic。而Dc shell和vivado等可以直接set top module并查看這些東西。Anyway,在這里我還是想將對(duì)RTL代碼Compile的概念統(tǒng)一描述,就當(dāng)是Syntax Analyze 和 Linter,進(jìn)行糾錯(cuò)并整合信息方便后面的程序進(jìn)行處理。Compile是可以分辨一些directives(主要應(yīng)用于Syn),挑出基本的組分。此外,關(guān)于Tb層面的compile,默認(rèn)的timescale為1ns/xx(時(shí)間單位/仿真精度)。還有package和#include之于compile的區(qū)別,會(huì)在Part 2提及,Makefile的特性,我們重復(fù)compile不需要update沒(méi)有更改的成員。在編寫(xiě)code的時(shí)候利用文本插件(推薦Vim或者VSC等,能裝插件就行)的輔助可以很快通過(guò)第一次compile(減少很多syntax層面的typo)。

1.2 Simulation

當(dāng)然在通過(guò)1.1的Compile后我們只是獨(dú)自對(duì)各個(gè)file進(jìn)行檢查,想要把DUT作為一個(gè)整體,還需要各個(gè)模塊進(jìn)行完整的interconnection。因此當(dāng)我們?cè)贛odelsim跑某個(gè)testbench時(shí),即便Compile全部PASS,也會(huì)出現(xiàn)例如組件名字mismatch,端口連接mismatch或不能識(shí)別某個(gè)名字等的情況。總之,針對(duì)DUT部分,我們需要將它作為電路進(jìn)行編寫(xiě)并進(jìn)行嚴(yán)格的連接,這些組件都能在Simulation后的hierarchy中單獨(dú)顯示,合理地劃分組件和功能能更方便Debug,SV對(duì)于Simulation有非常多額外的支持。

這里標(biāo)記一些用Simulation Tool的小tips。

  • 首先跑Sim后,代碼和各組件建立了映射關(guān)系,因此我們可以通過(guò)看代碼添加所需的信號(hào),也可以郵件代碼中的一些信號(hào)trace driver或reference,這個(gè)對(duì)于debug一些陌生的代碼很有好處,可以快速地了解信號(hào),寄存器之間的關(guān)系,反之亦然-go to source。
  • 在寫(xiě)代碼的時(shí)候,除了一些需要用parameters customize Hardware,盡量添加信號(hào)位寬的標(biāo)注可以方便Debug發(fā)現(xiàn)忘記添加變量聲明的情況。或者說(shuō)直接添加以下代碼取消掉SV對(duì)default type的設(shè)置。
`default_nettype none

這樣的話(huà)如果我們不小心用了未聲明的變量就會(huì)直接報(bào)錯(cuò),而非默認(rèn)為logic[0:0],在一些現(xiàn)有的代碼中,有人習(xí)慣直接用未聲明的變量做interconnection,個(gè)人認(rèn)為這不是個(gè)好習(xí)慣,盡管說(shuō)這些變量不具備其他的功能,但標(biāo)注出來(lái)對(duì)debug而言會(huì)更方便。

  • 添加Divider在waveform中可以方便區(qū)分不同module的信號(hào),也可以在preference設(shè)置代碼名稱(chēng)的path深度來(lái)簡(jiǎn)化信號(hào)來(lái)源的描述。信號(hào)的默認(rèn)名字一般就是main/sub/signal name。我一般習(xí)慣設(shè)置max path長(zhǎng)度為2~3,一般來(lái)講記性越差,工程越大,所需要的path長(zhǎng)度更大吧。一次性拉了過(guò)多的信號(hào)的話(huà),可以通過(guò)set property改變wave的顏色使重要成員顯著

  • 做self check的時(shí)候可以多使用 $stop or $fatal(“l(fā)og”)并fork timeout thread來(lái)做debug,step by step的debug是我們所喜歡的方式

fork//Error occurs when pwr_up is never assertedbegin: timeoutrepeat(30000) @(posedge clk);$fatal("Timeout for waiting pwr_up");end: timeout1//If pwr_up is asserted, disable timeoutbegin@(posedge iDUT.iAuth.pwr_up);disable timeout; //Once pwr_up is asserted, disable timeout and keep testingendjoin
  • $strobe $display $monitor 在event-driven simulation下發(fā)生在哪個(gè)queue呢(時(shí)刻),參見(jiàn)Stratified Event Queue,它詳細(xì)描述了Simulation的運(yùn)行規(guī)范,這里就記住strobe和monitor能發(fā)生在非阻塞賦值后就行。
  • $stop $finish stop類(lèi)似于斷點(diǎn),可以continue。finish類(lèi)似于terminate當(dāng)前thread,在GUI中它通常會(huì)問(wèn)我們finish后要不要exit。Debug還是stop用的比較多
  • class 中的變量無(wú)法生成add到wave,我們需要將它轉(zhuǎn)為interface才可以拉出來(lái)看到波形。這不太方便,因此要注意信號(hào)盡量例化或連接到外面。
  • 后仿真需要注意精度的問(wèn)題,會(huì)對(duì)結(jié)果產(chǎn)生出入。而且對(duì)Netlist仿真有時(shí)候就很玄學(xué),應(yīng)該和Lib有關(guān)系,個(gè)人看法還是圖個(gè)樂(lè)呵。除此之外盡量也不要依靠?jī)?nèi)部信號(hào),很容易出現(xiàn)X value,內(nèi)部信號(hào)名很多也會(huì)被優(yōu)化或者處理掉,尤其是synthesize flatten的情況,DUT變成了一層。如果一定要refer一個(gè)內(nèi)部信號(hào),可以在綜合時(shí)加入一下代碼,或者一些軟件可以識(shí)別RTL代碼中的dont touch,下面一些代碼是dc shell一些代碼是vivado的,使用的時(shí)候參見(jiàn)其TCL文檔即可。
# in scripts set_dont_touch [find pin main/sub/sig_name] set_property DONT_TOUCH TRUE [~] set_property MARK_DEBUG TRUE [[get_nets –of [get_pins hier1/hier2/<flop_name>/Q]]] # or in the RTL codes (*DONT_TOUCH = "TRUE"*) wire xxx... (* MARK_DEBUG = "{TRUE|FALSE}" *) logic yyy...

1.3 Synthesis

這里只講Code->Netlist這一步也就是,后續(xù)Netlist->layout的APR部分不在此列,以DC shell為例子。在Compile并進(jìn)行完成前仿后,我們將RTL代碼mapping到對(duì)應(yīng)庫(kù)的門(mén)級(jí)網(wǎng)標(biāo)中,這一步我們實(shí)際上無(wú)論是Semi-Custom,Full Custom抑或是FPGA,都是必要的,因?yàn)閺腃ode到Gate List是比較外層的由抽象到具象的程序,到了具體的implementation再分化。但無(wú)論如何,Netlist所依賴(lài)的Lib各不相同,對(duì)于Full Custom而言,那就不是單純依賴(lài)Fab提供的Lib了,而是需要更多人工的設(shè)計(jì)。以DC shell為例子的話(huà),Synthesis在以下方面可以額外注意下,基礎(chǔ)共通部分我不贅述,隨便看一個(gè)完整的腳本代碼再參照資料就可以理解:

  • Cost Function = -Slack + Area + power 此為綜合trade off的標(biāo)準(zhǔn),往往不可兼得
  • Timing Constraints: 就是數(shù)電的基礎(chǔ)時(shí)序約束,Setup和Hold。Tsu+Td+Tco+Tuncertainty < Tclk; Tco + Td-Tuncertainty > Thold。Tuncertainty來(lái)源于Skew也就是Clock Tree的不均衡
  • 一般在DC shell中我們不會(huì)touch clk。會(huì)在CTS(Clock Tree Syn)中單獨(dú)去解決它。
  • Input delay 和 Output delay一般默認(rèn)是同步的環(huán)境,可當(dāng)作Td的一部分,針對(duì)異步的模塊我們一般手動(dòng)去設(shè)置,不對(duì)它納入時(shí)序分析的整體和優(yōu)化。
  • set_multicycle_path 2 -setup -from [find pin Main/Sub/ptch_*_reg*/CLK] set_multicycle_path 2 -setup -from [find pin Main/Sub/AZ*_reg*/CLK]set_clock_groups -name async_clk0_clk1 -asynchronous -group {clk0 usrclk itfclk} \ -group {clk1 gtclkrx gtclktx} ...
  • drive以及Capacitive load決定了門(mén)的延時(shí),可以參照那個(gè)Ids的方程,信號(hào)的傳輸就是經(jīng)歷一個(gè)又一個(gè)transition,對(duì)應(yīng)的Slope自然就一定上決定了速度。舉個(gè)例子,Fanout過(guò)大drive弱,因此消耗的delay也就多,我們一般也會(huì)加以約束,添加buffer,只不過(guò)說(shuō)這又要增加Area了,正好也對(duì)應(yīng)了1的tradeoff
  • 多次綜合的優(yōu)勢(shì):我們之前說(shuō)過(guò)compile定義的區(qū)分,對(duì)于綜合工具而言,Compile有時(shí)可以定義為綜合操作本身,這一點(diǎn)可以在對(duì)應(yīng)腳本命令中看出,map effort越高,所用以fix timing的area消耗越大。這里我們統(tǒng)一描述為綜合Syn,但是tcl還是照常。多次綜合可以把Netlist做Flatten,取締掉原版設(shè)計(jì)的hierarchy,盡力去服用Gate本身。也能添加一些額外的約束到Netlist以便于重新組織,例如說(shuō)之前提到的asynchronous和hold time fix。示例代碼如下:
  • compile -map_effort high set_multicycle_path 2 ... ungroup -all -flatten set_fix_hold clk compile -map_effort medium
  • 還有一個(gè)小tip,有些人會(huì)告訴你GUI is just children’s work,cmd mode才是硬核。然而我個(gè)人的看法是工具不分高低貴賤,好用就行,至少我認(rèn)為一些GUI的terminal要比直接調(diào)用的shell>要好用多了(我說(shuō)的就是Synopsy!)。而且GUI兼顧腳本的運(yùn)行,又能夠方便調(diào)用到一些直觀的功能,比如說(shuō)hierarchy和schematic。所以以偏概全地跟風(fēng)并不好,自己實(shí)踐可以得到更優(yōu)的選擇吧,大概!
  • 2. System Verilog Coding Guide

    扯了那么多有的沒(méi)的,終于到正題了,也就是Coding的部分,如前文所說(shuō),SV是Verilog的擴(kuò)展,因此這個(gè)PART的內(nèi)容也會(huì)夾雜Verilog本身的東西,只挑選我認(rèn)為很重要或者比較有意思的內(nèi)容。開(kāi)頭我先列一點(diǎn)優(yōu)秀的文字資料,雖說(shuō)coding是一門(mén)實(shí)踐的技術(shù),但就我的感覺(jué)來(lái)看,實(shí)踐過(guò)后回歸理論,能夠很有效的補(bǔ)全知識(shí)漏洞。

    • IEEE SV官方指南:IEEE 1800-2017 SV官方鏈接 有些東西網(wǎng)上搜的不太明白可以查一查。
    • 2013 SNUG會(huì)議文章 “Synthesizing systemverilog busting the myth that systemverilog is only for verification”,關(guān)于System Verilog的使用,例子很豐富,講解很詳細(xì):PDF link
    • 2000 SNUG會(huì)議文章 “Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill!”,Verilog的Coding Guideline:PDF link

    2.1 狀態(tài)機(jī)【Design】

    從結(jié)構(gòu)上來(lái)看,數(shù)電狀態(tài)機(jī)就是時(shí)序邏輯+組合邏輯形成的環(huán)路,最簡(jiǎn)單的狀態(tài)機(jī)例子就是計(jì)數(shù)器,counter即是它的狀態(tài),一般來(lái)講寄存器保有current state,Comb的輸出決定next state。Moore SM和Mealy SM的區(qū)別就在于狀態(tài)機(jī)輸出的構(gòu)成,Moore的輸出只由current state決定,比如說(shuō)計(jì)數(shù)器的counter,告之當(dāng)前時(shí)刻。而Mealy的輸出還由外部輸入決定,比如說(shuō)給計(jì)數(shù)器輸出添加一個(gè)mask,這樣就可以選定時(shí)間的scale,從輸出性質(zhì)來(lái)講,Moore為同步,Mealy可能為異步,如果外部輸入的是同步信號(hào),那其實(shí)輸出的也就同步了。

    從功能上來(lái)看,我認(rèn)為狀態(tài)機(jī)就是時(shí)序邏輯的一個(gè)拓展的實(shí)例。在數(shù)電中,我們用時(shí)鐘和寄存器來(lái)進(jìn)行timing control,規(guī)范在不同時(shí)間(CLK)電路進(jìn)行不同的配置和操作。重點(diǎn)在于規(guī)范,這就是我們除開(kāi)FF和latch以外禁止做comb環(huán)路的原因,在組合環(huán)路中我們很難準(zhǔn)確預(yù)測(cè)什么時(shí)刻電路會(huì)做什么,因此引入時(shí)鐘信號(hào)和時(shí)序單元來(lái)規(guī)范一切。而狀態(tài)機(jī)也是如此,這個(gè)環(huán)路使我們能夠設(shè)計(jì)電路什么時(shí)刻會(huì)干什么,下一時(shí)刻又會(huì)發(fā)生什么,所以我說(shuō)他是時(shí)序功能的延續(xù),是電路時(shí)序控制的一個(gè)抽象,抽象為Bubble Diagram能更讓人理解功能。Bubble Diagram的規(guī)范寫(xiě)法如下圖所示。

    從Coding來(lái)看,我要說(shuō)的無(wú)非就是兩段/三段式,Enum type,Latch規(guī)避以及Case語(yǔ)法變種。

  • 二/三段式:只是把輸出信號(hào)放在Case段里還是外的區(qū)別。我個(gè)人還是喜歡在case里加中間信號(hào),然后最后assign輸出信號(hào)的,感覺(jué)結(jié)構(gòu)比較清晰一點(diǎn)。第一段就是時(shí)序部分current state <= next_state; 下面給一個(gè)2-3段的簡(jiǎn)單代碼,雖簡(jiǎn)單但也有點(diǎn)長(zhǎng),會(huì)在之后的敘述中再次提到。
  • ///// SM 2nd part: decide next state(maybe combine some outputs)/////always_comb beginnext_state = state;clr_tmr = 1'b0; //default to avoild latchcase (state)INIT: beginif(sum_gt_min) begin //ensure that rider is absolutely on (higher than threshold + hysteresis), go to waiting balancenext_state = WAITING;clr_tmr = 1'b1;endendWAITING: beginclr_tmr = 1'b0;if (sum_lt_min) begin //rider totally step off, go back to INITnext_state = INIT;end else if(diff_gt_1_4) begin //balance wrong, clear the counter, waiting for balanceclr_tmr = 1'b1;end else if (tmr_full) begin //1.3s for balance is up, we can enable steernext_state = NORMAL;endendNORMAL: beginif(~sum_gt_min) begin //CAUSE: first check rider may get knock off the device - high prioritynext_state = INIT;end else if(diff_gt_15_16) begin //rider is steppingg off go to waiting for balance(clr counter)next_state = WAITING;clr_tmr = 1'b1;endenddefault: beginnext_state = INIT;endendcaseend///// SM 3rd part: assign some outputs by sm&input /////assign rider_off = (sum_lt_min) ? 1 : ((state == INIT) & (~sum_gt_min)) ? 1 : 0;assign en_steer = (state == NORMAL) ? 1 : 0;
  • Enum Type: 在2-3段的代碼我們看到next_state = INIT看似奇怪的語(yǔ)句,實(shí)際上就是引入了eunm變量,enum變量方便在simulation中Debug,但是readability一般,特別是寫(xiě)在package里的時(shí)候,enum的值默認(rèn)為0-n,可以手動(dòng)賦值。在waveform中enum為顯示為其名稱(chēng)而非具體的binary value
  • typedef enum reg[1:0] {INIT, WAITING, NORMAL} state_t; //0,1,2typedef enum reg[1:0] {INIT = 1, WAITING = 2, NORMAL = 0} state_t; //1,2,0x = state_t'(num) //type transferstate_t state, next_state;
  • 規(guī)避latch:在2-3段的代碼中我們看到在comb段開(kāi)頭寫(xiě)了next_state = state; clr_tmr = 1’b0; 這個(gè)的目的就是讓case中涉及的變量能夠有個(gè)default value,防止有些situation這些變量沒(méi)有cover到,編譯器默認(rèn)把這部分當(dāng)作latch了,從行為上來(lái)看就是所有case中的output我們都會(huì)先給個(gè)值,根據(jù)阻塞賦值的定義,這個(gè)值會(huì)在后面的語(yǔ)句覆蓋掉(如果有的話(huà)),值得一提的是always模塊內(nèi)的輸出變量得是reg,但實(shí)際上在硬件中它可以是個(gè)wire(比如這里的case塊),因此SV引入了logic變量unify了這兩者。總之規(guī)避latch是很重要的,它可能會(huì)引發(fā)一些synthesis工具的報(bào)錯(cuò)。而默認(rèn)next = current 也給了next 同current rst的情況。這套思路實(shí)際在if else語(yǔ)句中也適用,我們填充default情況的判定來(lái)規(guī)避自動(dòng)綜合出latch。
  • case語(yǔ)法的變種casex(),casez(),case() inside,unique case,priority case。前三者,雖然花里胡哨又是xz inside,實(shí)際上就是引入了wildcards,也就是dont care。case inside 只能在case item(xx: begin end)里添加z,x或?作為wildcards,若是輸入expression中有xz,不認(rèn)為是wild card。而對(duì)于casez 而言,exp中的z設(shè)為wildcards而casex則在casez基礎(chǔ)上將exp中的zx都設(shè)為wildcards。wild card在decoder中還是挺好用的,尤其是那種可能會(huì)發(fā)生x z信號(hào)的情況,一個(gè)wild card能讓他們避免進(jìn)入default,case inside還擴(kuò)展了range作為case item的選擇。但對(duì)于綜合而言,結(jié)果就和sim有出入了,實(shí)際上這三者xz inside綜合出的結(jié)果都是一樣的,casex 和 casez對(duì)于exp的wildcard無(wú)法在synthesis后的電路適用,因?yàn)樵趯?shí)際電路中我們無(wú)法判別輸入是否有x或z,因此我們不難看出,之所以在SV引入case inside,其中一個(gè)原因就是統(tǒng)一Sim到Syn的功能規(guī)范,casex和casez因exp可能產(chǎn)生的sim&syn不一致理應(yīng)舍去。因此,個(gè)人推薦做DUT的話(huà)那就只能用case inside,若是testbench代碼則愛(ài)用啥用啥。priority和unique也直接影響Synthesis,顧名思義,priority所映射的電路就是有優(yōu)先級(jí)的,如果滿(mǎn)足第一個(gè)就不會(huì)去管第二個(gè)了。而unique指的是the one and the only one,他的結(jié)構(gòu)會(huì)呈現(xiàn)出Flat mux的形態(tài)(flat會(huì)更節(jié)省,因?yàn)椴恍枰槾蝹鬟f,Td也小),若是實(shí)際與這種directive不符合,syn的時(shí)候會(huì)報(bào)warning,但對(duì)于Sim來(lái)講,他們皆相同,都是有priority的順序比較。總之減少didnt cover的情況,并確保code和RTL電路的mapping一致性是很重要的,SV給我們提供了很多幫助
    下面是一個(gè)case的代碼例子:
  • always_comb begincasex(case_exp)3'b0??: $display("one");3'b10?: $display("two");3'b111: $display("three");default:$display("four");endcase end always_comb begincase(case_exp) inside3'b0??: $display("one");3'b10?: $display("two");3'b111: $display("three");default:$display("four");endcase end //distinguish these two different results

    當(dāng)case_exp為000,11x和11z時(shí),x輸出的為133,inside輸出的結(jié)果為144,如是casez則結(jié)果為143。從這可以看出Sim中這三者的差異,實(shí)際就是exp判定xz的差異,但Post Sim的話(huà)他們就全部一樣了。

    2.2 Behavorial Verilog再到Always模塊【Design】

    我們將Verilog代碼區(qū)分為BehavioralStructuralStructural代碼就是模塊元件之間的連接的形式,比如說(shuō)Netlist就是純純的structural,是比較底層的電路抽象,除此之外的都是Behavioral代碼,我們描述具體行為來(lái)生成電路,是一種更高的抽象方法,其中always模塊便是Behavioral的最大組成部分,我們用它來(lái)表示時(shí)序和綜合電路,包括latch。此外的話(huà)多為輔助,比如說(shuō)用assign表示組合邏輯,是種方便的alternative。下面說(shuō)一些always模塊編寫(xiě)的guideline。

  • always_ff,always_comb和always_latch:之前我們提到code和RTL電路的mapping一致性,這里SV的拓展也是為它而生,我們用不同的always_來(lái)map想要的電路類(lèi)型,倘若我們的代碼行為與硬件mapping不一致,它同理也會(huì)報(bào)warning,非綜合型的代碼也無(wú)法在這個(gè)block仿真,但無(wú)法說(shuō)通過(guò)這個(gè)directive來(lái)決定想要綜合出的元件類(lèi)型,它能做到的也只是輔助。

  • posedge和negedge:他們并不止是1-0 0-1,在tb中適用更多。

  • 不同變量的行為,我們盡可能將對(duì)應(yīng)always分離,這樣可以規(guī)避不需要的situation判定,從而減少潛在bug和資源消耗。下面舉一個(gè)最簡(jiǎn)單的復(fù)位打拍電路為例,對(duì)應(yīng)他們的電路圖,我們可以看到Synthesis的差異

  • // circuit 1 mixed always_ff@(posedge clk or negedge rst_n) beginif(~rst_n)q1 <= 1'b0;else beginq1 <= D;q2 <= q1;end end // circuit 2 seperate always_ff@(posedge clk or negedge rst_n) beginif(~rst_n)q1 <= 1'b0;else beginq1 <= D;end endalways_ff@(posedge clk) beginq2 <= q1; end

    可以看到在復(fù)位的情況我們沒(méi)有定義q2的行為,所以綜合工具會(huì)給他自動(dòng)保持q2<=q2,因此除非我們知道要做這種回路,否則的話(huà)還是分離或者是定義其對(duì)應(yīng)的行為。

  • 異步信號(hào)輸入的亞穩(wěn)態(tài)處理:Meta-Stability的產(chǎn)生來(lái)源于異步信號(hào)輸入到FF,就拿上面circuit2 代碼的下面一個(gè)FF舉例,若輸入q1是個(gè)相對(duì)于clk的異步信號(hào),那我們?cè)诓蓸拥倪^(guò)程中可能得到的Z值。原因的話(huà)從粗略角度講,這個(gè)異步信號(hào)可能不滿(mǎn)足setup或hold time,因此在輸入的跳變中寄存器采到了一個(gè)中間態(tài),即為Z高阻態(tài)。細(xì)致點(diǎn)講的話(huà),一般來(lái)講我們的FF由兩個(gè)鎖存器組成,我們會(huì)發(fā)生亞穩(wěn)態(tài)的原因就是當(dāng)?shù)谝粋€(gè)latch采到Z的時(shí)候恰好時(shí)鐘跳變,第一個(gè)latch的值傳遞到了第二個(gè)上,因此我們最后得到了Z(這是setup不滿(mǎn)足的情況,hold也類(lèi)似)。所以說(shuō),為了規(guī)避采到Z,會(huì)多放置一個(gè)FF來(lái)進(jìn)行采樣(像是異步復(fù)位的同步釋放),這樣的話(huà)即便是第一個(gè)FF傳遞出了Z,經(jīng)由1個(gè)clk的調(diào)整,它也基本上就會(huì)恢復(fù)狀態(tài),由第二個(gè)FF得到正常的同步信號(hào)。關(guān)于亞穩(wěn)態(tài)為什么維持時(shí)間比較短的原因,籠統(tǒng)地說(shuō),這個(gè)寄存器器件的工藝和環(huán)境等有關(guān),在寄存器中維持高阻態(tài)這個(gè)平衡并不容易,一點(diǎn)串?dāng)_和毛刺都可能打破這個(gè)平衡。具體的說(shuō),如圖所示,鎖存器有一強(qiáng)一弱兩個(gè)反相器頭尾相連組成,倘若此時(shí)亞穩(wěn)態(tài)發(fā)生了,也就是第一個(gè)latch頭尾保持Z值,已知inverter一強(qiáng)一弱,那么所謂的平衡的電壓值也就有了偏斜(上下電流是有不同的,若inverter的strength來(lái)源于transistor寬長(zhǎng)比),再加上器件電流自身的擾動(dòng),那么很快,電壓就會(huì)恢復(fù)到一高一低的電平,也就是亞穩(wěn)態(tài)的恢復(fù),傳遞到下一級(jí)的latch上。因此只要恢復(fù)時(shí)間小于一個(gè)CLK,那么加一級(jí)同步器就能有效的抵消掉亞穩(wěn)態(tài)阻止它傳播。
  • 2.3 便捷寫(xiě)法【Design】

    在原有的印象中,RTL代碼相對(duì)于高級(jí)編程語(yǔ)言是非常冗余且傳統(tǒng)的,但實(shí)際上隨著版本的更新迭代,SV如今也是提供了豐富的便捷的代碼編寫(xiě)用法。SV支持的Oop涵蓋很廣且通用就不在這個(gè)子Part提及了。

    • vector和generate:當(dāng)需要重復(fù)例化模塊或者重復(fù)一些行為語(yǔ)句時(shí),可以使用向量或者generate語(yǔ)句來(lái)實(shí)現(xiàn)。若是比較規(guī)范的需求,則可以直接用vector來(lái)做,若是一些需要不同situation控制的地方,則需要用generate語(yǔ)句。
    wire[7:0] A, B, S; wire[6:0] Carrybits; fulladder adders[0:7] (A, B, {Carrybits,1'b0}, S) //如果要用half adder + full adder的形式,generate會(huì)更加方便,加個(gè)判斷index的情況即可
    • 變量拼接(Concatenation):拼接我們經(jīng)常用,就是利用{},高位到低位,下面就列舉它的一些handy usecases
    // add signed and unsigned with ov wire signed[15:0] addend1; wire [7:0] addend2; wire [15:0] sum; wire ov; assign {ov, addend2} = addend1 + {8'h00, addend2};// sign extension assign {ov, addend2} = addend1 + {{8{addend2[7]}}, addend2[6:0]};// signed shift assign signedDiv8 = {{3{value[15]}},value[15:3]}; assign signedDiv8 = value <<< 3; // keep value as signed assign rotated = {value[0],value[15:1]}; // shift registers are quite prevalent and we can reuse the bits in interesting ways// struct assignment struct { logic [ 7:0] opcode; logic [31:0] data; logic status; } operation; operation = ’{8’h55, 1024, 1’b0}; // entire assignment operation.data = 32'd1024; // seperate assignment// vector assignment logic [7:0] a1 [0:1][0:3]; logic [7:0] a2 [2][4]; // C like declaration a1 = ’{’{7,3,0,5},’{default:’1}}; // first range as the most external {} a2 = a1;
    • Instantiation:例化模塊時(shí),我們需要添加對(duì)應(yīng)模塊的IO和parameters,為了方便有這幾種方法:
      使用插件直接生成對(duì)應(yīng)例化代碼,如VSC Open Command Palette --> system verilog auto instantiate…看具體不同插件的使用方法了,在vsc一般就是在ctrl+shift+p(command palette shortcut)后調(diào)用即可。推薦將parameter寫(xiě)在module內(nèi)部,因?yàn)橛袝r(shí)候插件沒(méi)那么聰明哈哈,不過(guò)這種生成的腳本挺好寫(xiě)的,自己弄個(gè)也不麻煩就是了,還能把變量declaration加上去了。
    // 另一種方法就是用sv支持的語(yǔ)法 dff dff1 (.*, .q(q[0]), .d(d[0]), .qb()); // 等同于 dff dff1 (.clk, .rst, .pre, .d(d[0]), .q(q[0])); // 這種方法就是自動(dòng)與同名變量進(jìn)行連接,但是私以為

    2.4 變量運(yùn)算【Both DV】

    SV提供了豐富的Arithmetic Operator,除了介紹基本的符號(hào)和規(guī)則外,我同樣也會(huì)寫(xiě)一些handy usecases

  • 4-value logic:包含了XZ后,AND OR等波爾運(yùn)算會(huì)稍微復(fù)雜一點(diǎn)點(diǎn),X就是Unknown,如01沖突會(huì)給X。Z是高阻態(tài),懸浮信號(hào),易被外來(lái)的下拉或者上拉信號(hào)改變,因此總線(xiàn)中很常用。

  • Strength Value:這個(gè)一般也就在TB中會(huì)用,實(shí)際的driving force肯定和器件相關(guān),我們可以給primitive添加這些directives來(lái)給定輸出拉高拉低的strength。

  • not (weak0,weak1) #(1;2;3, 3:2:1, ...) inv2 (Q,n1) // 1 ns min, 2ns typical, 3ns max delay; rise, fall, to Z delay
  • 符號(hào)數(shù): SV默認(rèn)使用的是2s補(bǔ)碼,我們可以用快捷方式表達(dá)負(fù)數(shù)如-16‘h3A,也可以直接用對(duì)應(yīng)的補(bǔ)碼形式16’FFC6,通過(guò)內(nèi)置函數(shù)$signed()來(lái)講值當(dāng)作符號(hào)數(shù)來(lái)處理,在乘法中運(yùn)用更加必要一些。
  • 運(yùn)算符Arithmetic Operators:就列舉一些可能不太熟的運(yùn)算符
    表1:運(yùn)算符補(bǔ)充
  • Operator NameOperatorDescription
    exponent**指數(shù)運(yùn)算符 Range of unsigned = 2 ** width-1 或 $pow(2,width)-1
    modulus%求余運(yùn)算符
    Shift<<< >>>>>> 符號(hào)右移only works of operand and dest declared as signed. 等同于加了個(gè)sign extension
    Equality=== !===== and !== compare x’s and z’s explicitly not as don’t care. Use in testbench
    Bitwise& | ^ ~Applies bit by bit, ~ is unary ^ is xor,按位與或…就是把dest所有bit用operator連接
    set membershipinside()if (data inside {[0:255}) … if (data inside {3’b1?1}) … if (data inside array) …
    streaming<< >>bitstream operator, 會(huì)將目標(biāo)變量當(dāng)作比特流來(lái)處理,>>正序 <<倒序 a = { << byte { b }}; reverse by byte
    Wild equality==? !=?deem xz as wildcards if (data =?= 8’b1xxx_z1xz) begin
    assignment*= +=similar to cpp like a+=1 ---- a = a + 1

    看著比較高級(jí)的運(yùn)算符(斜體)通常就只在TB或parameter中用,也不是禁止在DUT用,但和design規(guī)范相關(guān)。運(yùn)算符優(yōu)先級(jí)和其他語(yǔ)言比較類(lèi)似,不在這贅述,但是我個(gè)人的習(xí)慣就是加括號(hào),增加可讀性,明確優(yōu)先度。

  • handy cases:這里就結(jié)合代碼給一些slick的運(yùn)算操作,順帶解釋一些3中的運(yùn)算符
  • // Reduction Operator assign all_one = &accumulator; // are all bits set? all 1 leads to res 1 assign not_zero = |accumulator; // are any bits set? any 1 leads to res 1 assign zero_exist = ~&accumulator; // are any bits rst? any 0 leads to res 1 assign all_zero = ~|accumulator; // are all bits rst? all 0 leads to res 1 assign parity = ^data_out; // is number of 1 odd? odd 1s leads to res 1// saturation 16bit signed to 12bit signed assign sat12 = (~sum[15] & |sum[14:11]) ? 12’h7FF : //positive sat (sum[15] & ~&sum[14:11]) ? 12’h800 : //negative sat sum[11:0]; //normal// decide if (data inside {4'b1??1}) if (data =?= 4'b1zx1) // same condition effect match with format//PACK and UNPACK data = {<< 8{data}}; // reverse data by byte int a, b, c; {>>{ a, b, c }} = 96'b1; // left to right a, b, c = 0,0,1 fit 96bits {>>{ a, b, c }} = 97'b1; //left to right a, b, c = 0, 0, 0 fit the MSB 96bits(0) bit [99:0] d; d = {>> { a, b, c }}; //d become all zero ,if d narrower than 96, compile error

    2.5 Verilog Stratified Event Queue【Verification】

    這部分來(lái)源于SV官方文檔 IEEE Standard,定義了軟件的Simulation Spec,已知我們的simulation是event driven的,這個(gè)Queue就定義了各類(lèi)event與它們的判定優(yōu)先級(jí)。當(dāng)我們編寫(xiě)SV時(shí),或多或少會(huì)遇到race的情況,因此了解這個(gè)優(yōu)先級(jí)和組成有益于我們編寫(xiě)和解讀代碼,避免看到仿真結(jié)果一臉懵的情況。為了詳細(xì)說(shuō)明,這里再列舉了需要的Simulation Terminology

    • Processes:我們可以在仿真軟件的Sim窗口中查看到這些元素。
      Objects that can be evaluated,包括modules tasks functions primitives,tb的inital和always blocks還有procedural assignments
    • Update Event
      LHS assignment,對(duì)應(yīng)變量值的變化更新,可以是net可以是reg
    • Evaluation Event
      RHS computation,計(jì)算右邊的式子。
    • Scheduling an event
      將Event(上述)放入Event Queue
    • Simulation Time
      仿真用的時(shí)間系,不是說(shuō)我們現(xiàn)實(shí)的時(shí)間
    • Simulation Cycle
      Complete processing all currently active events。一次simulation time 可以有多個(gè)cycle
    • Explicit zero delay #0
      將當(dāng)前sim time后面event強(qiáng)行放入inactive狀態(tài)

    列舉完了Terminologies,下面我們解釋這個(gè)stratified event queue,event分五個(gè)Region,隨機(jī)執(zhí)行active region中的events,當(dāng)一次simulation cycle結(jié)束后(當(dāng)前simulation time的所有active events finish),會(huì)依次將其他Region的events加入到active中(例如跳轉(zhuǎn)到下一時(shí)刻,將future events加入active并執(zhí)行,自然是最后去做的,也就是Region 5)。下面我們對(duì)各個(gè)region的events進(jìn)行描述。

    • Region 1 Active Events:這個(gè)其實(shí)就是當(dāng)前時(shí)刻要率先執(zhí)行的任務(wù),包括內(nèi)置函數(shù)$display,RHS的計(jì)算,阻塞賦值,continuous assignment(也就是assign語(yǔ)句),primitive的輸入到輸出等,Region內(nèi)部的events執(zhí)行并不存在固定順序,但在同一個(gè)block中時(shí),阻塞賦值存在順序,這個(gè)我認(rèn)為也可以延伸到continuous assignment上。
    • Region 2 Inactive Events:#0 的阻塞賦值會(huì)放在這里。
    • Region 3 Nonblocking Events:阻塞賦值中更新LHS。
    • Region 4 Monitor Events:$monitor $strobe。
    • Region 5 Future Events:接下時(shí)刻的events。

    事實(shí)上,我們比較在意initial和always模塊中的的先后順序,我一般將組合邏輯作為對(duì)應(yīng)順序的非阻塞賦值,有單向依賴(lài)關(guān)系的情況是好排序的,但是遇到兩個(gè)block又雙向依賴(lài)的情況,race condition一般就不好判定了。而對(duì)于那種trigger events,它在Active的位次處于同一block blocking assignment的后列,當(dāng)他去monitor trigger的時(shí)候內(nèi)部block的events事實(shí)上已經(jīng)執(zhí)行完成了,因此阻塞賦值不能夠出現(xiàn)自己self-trigger的情況,但非阻塞可以,因?yàn)樗黸pdate LHS是在Active后

    initial clk = 0;always@(clk) ```#5 clk = ~clk; //不成功!只有在clk完成blocking assignment后,always模塊才會(huì)開(kāi)啟trigger。always@(clk)#5 clk <= ~clk; //成功!trigger先在Active中開(kāi)啟,在后才會(huì)把Non blocking中的賦值放入Active,此時(shí)trigger events可以檢測(cè)到非阻塞賦值的變化

    2.6 Fork methods【Verification】

    我們使用fork方法來(lái)實(shí)現(xiàn)TB中的parallel thread。除了方法本身,這里還會(huì)介紹一些配合named blocks的usecases

    • fork join:該block下的子線(xiàn)程需要全部完成后,fork join才會(huì)結(jié)束并返回主線(xiàn)程。
    • fork join_any:任何一個(gè)子線(xiàn)程完成后,主線(xiàn)程繼續(xù)往下跑,但這并不會(huì)結(jié)束其他的子進(jìn)程的運(yùn)行,它們依舊會(huì)在后臺(tái)繼續(xù)運(yùn)行。
    • fork join_none:相較于join_any,join_none會(huì)直接讓主線(xiàn)程繼續(xù)跑無(wú)論是否有子進(jìn)程結(jié)束,同樣的子進(jìn)程會(huì)在后臺(tái)繼續(xù)運(yùn)行。
    • wait fork:針對(duì)join_any和join_none兩種情況,我們可以在主進(jìn)程中添加wait fork語(yǔ)句,方便在后面等待子進(jìn)程的完成加以控制。
      下面的代碼案例就使用了wait fork 組合 fork join_any。當(dāng)完成Thread1時(shí)它會(huì)繼續(xù)執(zhí)行join下的語(yǔ)句,并在wait fork時(shí)等待其他thread完成,通常我們會(huì)給一個(gè)timeout的限制,一般報(bào)timeout錯(cuò)就$stop。
    initial begin// Main Thread$display("Proc Starts at %0t ns!", $time());fork//Thread 1$display("Thread 1 hits at %0t ns!", $time());//Thread 2begin: breakwhile(1) begin : continue@(posedge clk);cnt += 1;if(cnt >= 1000) begincnt = 0disable continue; // == continueendif(rdy == 1'b1 && cnt == 1) begindisable timeout_check; // == breakdisable break;endend : continue//disable timeout_checkend: break//Thread 3begin: timeout_checkrepeat(50000) @(posedge clk);$display("time out error at %0t", $time);disable break;end: timeout_checkjoin_any$display("Wait Fork threads");wait fork;$display("fork join is over at %0t", $time()) end

    2.7 Assertion【Verification】

    在testbench中我們經(jīng)常會(huì)進(jìn)行self-check,而Assertion則是SV支持的一種簡(jiǎn)化的self-check寫(xiě)法。具體來(lái)講它就是Assert一個(gè)condition,這是申明Assertion的系統(tǒng)下所需要遵循的一個(gè)條件狀態(tài),可以是一個(gè)即時(shí)或者長(zhǎng)期的固有屬性。因此有Immediate Assertion和Concurrent Assertion的區(qū)分,當(dāng)判定的條件是永久性觸發(fā)(如posedge clk)時(shí),我們就用Concurrent Assertion。當(dāng)Assertion只需判定一次(形同if語(yǔ)句)時(shí),我們就用Immediate Assertion。從可讀性和規(guī)范上Assertion是有增色的,它統(tǒng)一了self-check的格式。值得詳述的還是Concurrent Assertion,我們可以用Keyword “property”來(lái)聲明concurrent的condition。如果我們沒(méi)有去指定觸發(fā)check property的tick,則默認(rèn)會(huì)用simulation的sys tick進(jìn)行周期性check,這過(guò)于頻繁。因此我們通常會(huì)使用clk來(lái)作為property的checking tick。此外若是比較復(fù)雜的property,我們可以用sequence組合property blocks來(lái)進(jìn)行層次化, sequence->property->directive。下面就放一些例子,其中也涉及到一些不太常用的operator。

  • Immediate Assertion
  • [assertion name] : assert (res == expected) $pass_statement else $fail_statement // equal to if (res == expected) $pass_statement else $fail_statementassert(class.func1()); //默認(rèn)不加fail success statement的話(huà),assert函數(shù)會(huì)判定這個(gè)返回值,若為0則爆error
  • Simple Concurrent Assertion
  • assert property (@(posedge clk) !(rd & wrt)) else $fatal("WRT and RD should not assert at same time") // property的判定實(shí)際上是先于trigger的那個(gè)時(shí)刻的,因此若有同步信號(hào)進(jìn)來(lái),則只會(huì)在下一拍check到// when req is asserted ack should /// // be asserted 1 to 2 clocks later /// assert property (@(posedge clk) req |-> ##[1:2] ack);

    operator |-> 是一個(gè)implication operator,代指了信號(hào)assert的先后關(guān)系,這個(gè)是LHS先RHS后。
    operator ## 表示前后時(shí)間間隔的operator,可以是constant或者range,無(wú)|->直接使用的話(huà)就是前后的范圍了。

  • Multiple level Assertion
  • //Sequence layer sequence : reg_gnt_seq(~reg & gnt) |-> ##[1:4] (~reg & ~gnt); endsequencesequence : rdy_ack_seqrdy ##[1:3] ack; endsequence//Property layer property : reg_prop@(posedge clk)disable iff(~rst_n) set[->3] |-> ##1 rdy_ack_seq.ended ##1 reg_gnt_seq.ended; endpropertyassert property(reg_prop) else $fatal("ERROR!");

    這里property調(diào)用了一個(gè)變量和兩個(gè)sequence。其中set需要拉高3個(gè)clk而后第二時(shí)間觸發(fā)rdy_ack_seq,第三時(shí)間出發(fā)reg_gnt_seq,ended是將他們同步為結(jié)束后執(zhí)行下一步。相對(duì)復(fù)雜的時(shí)序組合通過(guò)不同seq制作property是很好的self check編寫(xiě)方法。我們也會(huì)通過(guò)disable iff語(yǔ)句來(lái)對(duì)self check進(jìn)行關(guān)停與否。

  • Event
    self check中我們有時(shí)想要手動(dòng)控制步驟的執(zhí)行,一種方法是直接定義一個(gè)變量如logic flag,通過(guò)其他thread對(duì)他賦值來(lái)控制check。這里介紹event變量,作用類(lèi)似,我們可以通過(guò)監(jiān)視他的triggered成員,這個(gè)成員是一個(gè)狀態(tài)量,也就是說(shuō)不會(huì)因?yàn)閞ace condition而不被觸發(fā)。而倘若我們用類(lèi)似@()方法,我們?cè)贓vent Queue中已知trigger方法是在后觸發(fā)的,因此race condition下不能檢測(cè)到pulse。wait_order 顧名思義,它會(huì)等待event的順序觸發(fā)。
  • event a, b, c; initial begin#20;->a;#10 ->b;#10 ->c; endinitial#20 wait(a.triggered); //it works triggered is like a stateinitial#20 @(a); //it cannot be triggered out of race “a” is like a pulse in that wayinitialwait_order(a,b,c);

    2.8 import package & `include【Both DV】

    `include可以理解為直接將文件的文本copy到對(duì)應(yīng)位置,它的好處是合并編譯,include的內(nèi)容可以依賴(lài)目標(biāo)模塊的成員,比如說(shuō)要refer內(nèi)部信號(hào),include是可以做到的,壞處就在于冗余不方便多文件共享了,他例化的都是各自獨(dú)立的存在,public variable不好實(shí)現(xiàn)。而package是單獨(dú)編譯的,當(dāng)模塊import package的時(shí)候,實(shí)際上它是會(huì)look up這個(gè)文件,所有import的都依賴(lài)一個(gè)package,我們也能指定import的內(nèi)容,者方便了多文件共享和通信,缺點(diǎn)就是不能反過(guò)來(lái)依賴(lài)模塊文件,總體來(lái)講就是1對(duì)1和1對(duì)多的區(qū)別。package是SV特有的extension,搭配面對(duì)對(duì)象特性,可以做到方便快捷的多文件泛用。

    2.9 Random Testing【Verification】

    這里主要講CRV (Constrained Random Verification)。它會(huì)調(diào)用一些SV的功能對(duì)生成的隨機(jī)變量加以約束以生成我們想要的數(shù)據(jù)分布,從而實(shí)施更有針對(duì)性的測(cè)試。

    • rand/randc bit[7:0] rand_byte; 這個(gè)type有一個(gè)built-in method randomize(),可以自己生成隨機(jī)數(shù)。randc的特點(diǎn)是他生成的隨機(jī)數(shù)會(huì)耗盡后再出現(xiàn)重復(fù),也就是說(shuō)相鄰的同樣隨機(jī)值之間一定包含該變化域的所有可能值,像這里就有255個(gè)。
    • 利用constraint{} directive,我們可以限制rand bits的隨機(jī)生成,constraint塊的分層是{}不同于通常的begin end。
    rand bit [31:0] len, src, dst; rand bit congestion_test;constraint c_stim{len<3000;len>20;if(congestion_test){dst inside {[32,128]};src = 233;} else {src inside {0, [2:10], [100:107] }} } //根據(jù)congestion test 生成不同限制的隨機(jī)值rand bit[7:0] low, med, high;constraint relate{low < med;high > med; } //constraint可以是相對(duì)大小限制
    • control the distribution,用dist directive來(lái)控制隨機(jī)數(shù)的分布。通過(guò)class的構(gòu)造我們也能生成隨機(jī)數(shù),成員也可以通過(guò)randomize()變成對(duì)應(yīng)的隨機(jī)數(shù)。
    class stim_trand bit[15:0] RF;constraint{RF dist {[16'h0 : 16'd99]:=99, [16'd99 : 16'hffff]:=1 }; // 99:1 distribution} endclassinitial beginstim_t stim = new();for (int i = 0; i < 10; i++) beginstim.randomize();end end // we can give assignment to feed the value to DUT, otherwise using virtual interface is also advisable.

    2.10 Fuction & Task【Verification】

    這里標(biāo)注了function和task的一些區(qū)別,有時(shí)候function還是會(huì)用在design里的(作為comb logic)。

    這里再額外說(shuō)一下task/func automatic,它的主要作用有兩個(gè),一個(gè)是迭代函數(shù),另一個(gè)是引入ref輸入(類(lèi)似指針,但只可讀),ref輸入的作用就是可以輸入一些含時(shí)的信號(hào),以作monitor。

    //Define the factorial function function automatic integer factorial; input[31:0] oper; beginif (oper>=2)factorial = factorial(oper-1)*oper; elsefactorial = 1; end //這里可以看到func的返回可以直接用它的函數(shù)名來(lái)賦值task automatic uart_tx_case(input[7:0] tx_in, ref clk);@(negedge clk);vif.send_cmd = 1;vif.cmd = tx_in;@(negedge clk);vif.send_cmd = 0; // 1clk trmt, to capture and set txdata;wait(vif.cmd_sent == 1'b1); // serial data done, otherwise uart_tx is not sent$display("tx data, %h, is sent",tx_in);wait_bauds(12); endtask

    2.11 Coding for Synthesis【Design】

    雖然說(shuō)根據(jù)script,EDA工具會(huì)自動(dòng)去優(yōu)化生成對(duì)應(yīng)的Netlist和Layout,但還是需要了解一些與代碼相關(guān)的部分,和設(shè)計(jì)的trade off息息相關(guān)。

  • Synthesis of Z and X. 我們可以用高阻態(tài)來(lái)實(shí)現(xiàn)三態(tài)門(mén),比如說(shuō)I2C總線(xiàn)中的開(kāi)漏結(jié)構(gòu)。而X則代表了dont care,他不會(huì)實(shí)際出現(xiàn)在電路中,但在表示電路時(shí)可以節(jié)省area。默認(rèn)的話(huà)它會(huì)做成環(huán)路,在sequential最多就增加MUX的輸入,但在COMB中的話(huà)就是latch了。因此手動(dòng)賦X的話(huà)就會(huì)直接擯棄掉一些不需要的邏輯判定和資源。總體來(lái)講,在comb中,Area Latch>default 0>default x。
  • Datapath Chain:這里先說(shuō)沒(méi)有conditional control的datapath,比如說(shuō)Res = abc*d。實(shí)際上區(qū)別就在是unique還是priority的結(jié)構(gòu),后者會(huì)比前者的delay更大,但是因?yàn)閘ogic effort的區(qū)別使得priority的Area稍微少一點(diǎn)。當(dāng)然如若是shrinking后的自然是資源最少的,加入時(shí)序模塊。
  • Conditional Datapath。加入控制邏輯后就有不同的說(shuō)法了,比如說(shuō)Res = (sel) ? ab : cd;它可以是雙multiplier或者單的結(jié)構(gòu)。顯然我們從資源角度考慮是希望放兩個(gè)MUX作為單個(gè)multiplier的輸入的。但是,具體情況需要具體分析,我們不清楚這里Sel的來(lái)源,若是一個(gè)同步信號(hào)可能帶有比較長(zhǎng)的input delay,那么出于Critical Path的考慮就要權(quán)衡這個(gè)conditional control放在哪里了,他可以放在兩個(gè)Multiplier的后面的MUX選通,這樣的話(huà)timing問(wèn)題就會(huì)減緩。因此需要根據(jù)具體情況去trade off,需要去分析是否有l(wèi)ate arriving signal。
  • 在底層的話(huà)EDA工具會(huì)應(yīng)用不同的算法和對(duì)應(yīng)的綜合策略來(lái)進(jìn)行優(yōu)化,這里就不過(guò)多地贅述了。
  • 3. Extension

    眾所周知,Digital Design & Verification的工作流程比較長(zhǎng),不同EDA工具不同的Programming再到Layout等。因此這里再額外塞一些比較有意義的思路或方法,提高設(shè)計(jì)流程的質(zhì)量和速度。

    3.1 Automatic buildup/Test Script Cases

    工業(yè)化設(shè)計(jì)下,使用腳本將作業(yè)自動(dòng)化使我們所希望的,因此這里就列舉一些工具下自動(dòng)化腳本的案例。Digital部分我用的比較多的還是Shell或者Tcl,這兩者比較方便與Software和OS進(jìn)行交互。而若是數(shù)據(jù)處理等更high level的需求,Python C#等會(huì)更加好用。不同腳本語(yǔ)言也是可以混合運(yùn)行的。通常GUI會(huì)把使用的CMD interface打印在terminal里,是我們腳本仿照使用的一個(gè)比較好的學(xué)習(xí)窗口。

    // ModelSim sim.tcl proc compile_files {args} {#for .do script 's filelistvlog /filespace/demo/Segway/testbench/define.svh... }set fp [open "/filespace/demo/Segway/testbench/define.svh" w] puts $fp "`define TP1" close $fpvlib work vmap work work compile_filesvsim -t 1ns work.Segway_tb_seq -voptargs=+accadd wave -position insertpoint sim:/Segway_tb_seq/Seg_intf/* add wave -position insertpoint sim:/Segway_tb_seq/iPHYS/theta_* add wave -position insertpoint sim:/Segway_tb_seq/iDUT/ptch ...run -all dataset save sim wave1.wlf write format wave wave1.do

    還有vivado cadence和synopsis工具的case。等之后找到了再更新。

    3.2 Code Coverage

    根據(jù)變量值的變化情況(如SM覆蓋率),仿真工具可以生成Code Coverage的報(bào)告來(lái)評(píng)估仿真是否全面,我們?cè)俑鶕?jù)報(bào)告反推測(cè)試代碼沒(méi)有cover的模塊,優(yōu)化測(cè)試質(zhì)量和覆蓋率,這是Code Coverage的作用。可以通過(guò)GUI或者define對(duì)應(yīng)的directive來(lái)使能這個(gè)功能。
    不同工具雖然界面不同,但基本都遵從Compile Configuration – Enable Code Coverage 和Sim Config 同理,兩者都需要去設(shè)置。
    得到的結(jié)果粗糙來(lái)看如下所示:

    ================================================================================= === File: /filespace/Segway/rtl_src/A2D_Intf.sv =================================================================================Enabled Coverage Bins Hits Misses Coverage---------------- ---- ---- ------ --------Toggles 180 130 50 72.22%

    具體的話(huà)還能在代碼界面中查看各個(gè)變量,branch等是否cover到。

    3.3 TBD

    感覺(jué)具體的案例還是單獨(dú)開(kāi)blog寫(xiě)好點(diǎn),太臃腫了可讀性還是差了點(diǎn),吃了經(jīng)驗(yàn)的虧呀<_<

    總結(jié)

    以上是生活随笔為你收集整理的【数字设计验证】System Verilog(sv)稍微进阶的笔记(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。