證哲學(xué))
搞了這么多年驗(yàn)證從Verilog時(shí)代的端口列表一路寫過來再回頭看SystemVerilog的Interface我最大的感受是這不只是一個(gè)語法糖它實(shí)際上改變了我們從硬件連到驗(yàn)證環(huán)境的思維方式。如果說RTL設(shè)計(jì)里模塊之間靠的是端口和網(wǎng)表在物理層面連接那Interface就是第一次讓你能在語言層面把“連接”這件事本身當(dāng)成一個(gè)對象來管理。這篇文章我想從一個(gè)比較完整的視角來拆解SystemVerilog Interface——從最底層的物理連接語義到modport和clocking block這些具體機(jī)制再到虛擬接口和UVM驗(yàn)證哲學(xué)之間的關(guān)系最后把我實(shí)際項(xiàng)目中踩過的坑一并拿出來聊。無論你是剛接觸SystemVerilog的驗(yàn)證新人還是想給現(xiàn)有環(huán)境做重構(gòu)的老手這篇文章應(yīng)該都有值得你看的東西。1. 為什么需要Interface端口列表爆炸背后的連接困境1.1 傳統(tǒng)Verilog端口列表到底有多痛還記得早年在Verilog里寫一個(gè)AXI從設(shè)備端口的場景嗎光是通道就得分成寫地址、寫數(shù)據(jù)、寫響應(yīng)、讀地址、讀數(shù)據(jù)、讀響應(yīng)五六組每一組少說也有六七根線再算上時(shí)鐘復(fù)位一個(gè)模塊的端口列表能輕松超過五六十根信號。每次新建一個(gè)模塊光是復(fù)制粘貼端口聲明、保證方向和位寬不出錯(cuò)就要花掉不少時(shí)間。更痛苦的是一旦驗(yàn)證環(huán)境里某個(gè)總線的位寬從32位改成64位涉及的所有文件你可能都要過一遍漏改一處就是編譯或者比對錯(cuò)誤。Verilog時(shí)代的這套做法本質(zhì)上是把“連接關(guān)系”隱含在信號名和端口列表里。兩個(gè)模塊能通上信號靠的是同名同寬、方向互補(bǔ)、拼寫一致這種約定非常脆弱只要有一處配錯(cuò)工具只會(huì)報(bào)一個(gè)不知所云的連接錯(cuò)誤。人手一多、接口一復(fù)雜這種模式幾乎必然走向混亂。1.2 Interface想解決的核心問題把信號和相關(guān)操作打包SystemVerilog Interface不僅僅是把一組線捆在一起更關(guān)鍵的是它把與這組線相關(guān)的協(xié)議行為也帶了進(jìn)去。你可以把接口理解成一根“協(xié)議總線”的完整封裝——信號聲明在接口里方向由使用者通過modport定義訪問時(shí)序可以交給clocking block管理甚至握手邏輯、讀取事務(wù)這些操作都可以以task或function的形式放進(jìn)接口里。這樣一來模塊頂層看到的就不是幾十根散線而是一個(gè)有明確語義的接口實(shí)例。你需要連一個(gè)APB從設(shè)備就在模塊端口上放一個(gè)apb_if.slave你需要連一個(gè)APB主設(shè)備就在端口上用apb_if.master。信號的組織單位從“根”變成了“束”這個(gè)轉(zhuǎn)變在代碼維護(hù)上的收益非常大。驗(yàn)證環(huán)境里加一根新的狀態(tài)信號只要在接口定義里加所有掛這個(gè)接口的模塊和組件會(huì)自動(dòng)跟著更新再也不用滿項(xiàng)目做全局搜索替換了。1.3 Interface是SystemVerilog里的“第一類對象”有一點(diǎn)很多人剛開始容易忽略Interface不是模塊的附屬品它是和module、class、program并列的一種獨(dú)立聲明類型。這意味著它本身可以被參數(shù)化可以被例化多次可以出現(xiàn)在數(shù)組里也可以作為模塊的端口類型。接口自己內(nèi)部還能寫initial、always、assign、task、function這些內(nèi)容。這種“第一類對象”的身份給Interface帶來了遠(yuǎn)超信號集合的能力。你可以在接口內(nèi)部實(shí)現(xiàn)簡單的協(xié)議檢查比如寫地址和數(shù)據(jù)是否在同一拍對齊也可以實(shí)現(xiàn)覆蓋率相關(guān)的采樣邏輯還可以在接口里直接提供一組高層次操作讓驗(yàn)證環(huán)境里的driver不用再關(guān)心具體波形。從這個(gè)角度看Interface從誕生那天起就不只是給RTL連信號用的它天然帶著驗(yàn)證領(lǐng)域的基因。2. Interface連的到底是什么信號封裝、modport與clocking block的硬件視角2.1 接口里的信號沒有方向這把許多人的方向感弄反了我見過不少新手第一次寫Interface時(shí)的反應(yīng)在一個(gè)接口里聲明logic [31:0] addr;然后很自然地追問這個(gè)addr到底是輸入還是輸出Interface在設(shè)計(jì)上的一個(gè)核心思想是信號方向不是由接口本身決定的而是由連接它的端口方向決定的。同一根addr在master視角里是輸出在slave視角里就是輸入。這種“無方向”的設(shè)計(jì)一開始確實(shí)反直覺但想通之后會(huì)發(fā)現(xiàn)它非常合理——信號本身只是一根導(dǎo)線方向取決于從哪個(gè)設(shè)備往外看。這種設(shè)計(jì)帶來的直接好處是同一個(gè)接口定義可以同時(shí)服務(wù)master和slave兩端的模塊你不需要為兩端各寫一套接口。而modport要解決的問題正是如何在不同視角下向使用者呈現(xiàn)同一組信號。2.2 modport同一捆線的不同視角modport用一句話解釋就是給不同角色提供不同的信號視圖。以一個(gè)簡單的APB接口為例interface apb_if(input logic clk, input logic rst_n); logic [31:0] paddr; logic psel; logic penable; logic pwrite; logic [31:0] pwdata; logic [31:0] prdata; logic pready; modport master( output paddr, psel, penable, pwrite, pwdata, input prdata, pready, output pslverr ); modport slave( input paddr, psel, penable, pwrite, pwdata, output prdata, pready ); endinterfacemaster視角里paddr是outputslave視角里paddr是input但它們在物理上就是同一根線。這種聲明方式不只是“看著清楚”它還把連接關(guān)系里的方向約束交給了編譯器。你在模塊端口里寫了apb_if.master工具就知道該把哪些信號當(dāng)輸入哪些當(dāng)輸出連接錯(cuò)誤在編譯階段就能暴露而不是等仿真波形一片混亂之后再回頭排查。modport還有一個(gè)被低估的用途隱藏信號。比如在某個(gè)從設(shè)備視角里根本不需要看到的一些內(nèi)部狀態(tài)線可以不在該modport里暴露這樣閱讀代碼的人一眼就能看出這個(gè)角色關(guān)心的信號面是什么信息過載的問題會(huì)輕很多。2.3 clocking block把采樣和驅(qū)動(dòng)的時(shí)序變成語言特性如果只是把線捆起來Interface不過是個(gè)“高級結(jié)構(gòu)體”。真正讓Interface在驗(yàn)證領(lǐng)域大放異彩的是clocking block。驗(yàn)證環(huán)境里最經(jīng)典的時(shí)序問題就是采樣和驅(qū)動(dòng)的競爭。如果測試平臺(tái)在時(shí)鐘上升沿的同一時(shí)刻去采樣DUT輸出采到的是變化前還是變化后的值這是個(gè)一直讓驗(yàn)證工程師頭疼的問題。clocking block通過聲明采樣邊沿和驅(qū)動(dòng)邊沿將這類時(shí)序關(guān)系顯式固化下來interface apb_if(input logic clk, input logic rst_n); logic [31:0] paddr; logic psel; logic penable; logic pwrite; logic [31:0] pwdata; logic [31:0] prdata; logic pready; clocking cb (posedge clk); default input #1step output #0; output paddr, psel, penable, pwrite, pwdata; input prdata, pready; endclocking modport master_mp(clocking cb); endinterface這里的關(guān)鍵是default input #1step。1step指時(shí)鐘事件之前的一個(gè)時(shí)間步它保證采樣發(fā)生在時(shí)鐘沿之前相對穩(wěn)定的一個(gè)時(shí)刻從而避開時(shí)鐘沿同步事件引起的競爭輸出#0則讓驅(qū)動(dòng)操作在時(shí)鐘沿之后的零延時(shí)被調(diào)度確保和DUT的時(shí)序語義匹配。沒有這個(gè)機(jī)制你就要在環(huán)境里手工設(shè)計(jì)各種(posedge clk); #1;之類的延時(shí)既繁瑣又容易出現(xiàn)仿真和綜合不一致的風(fēng)險(xiǎn)。2.4 接口里寫initial、assign和always允許但要克制接口既然擁有完整的硬件模擬語義自然可以在里面寫initial、assign和always塊。最典型的場景是在接口里給某些信號提供默認(rèn)驅(qū)動(dòng)或者直接用手寫波形的方式做一個(gè)小型BFM。但我的建議是克制。接口里放太多行為代碼會(huì)讓接口從“連接契約”演變成“一個(gè)隱藏的模塊”這恰恰違背了Interface作為連接抽象的初衷。協(xié)議握手行為盡量還是放到驗(yàn)證環(huán)境的driver/BFM里通過task封裝后供上層調(diào)用。接口里只保留與信號組織、時(shí)序采樣密切相關(guān)的結(jié)構(gòu)這樣無論是設(shè)計(jì)側(cè)還是驗(yàn)證側(cè)看接口文件時(shí)都能很快理解它的邊界在哪里。3. 從物理連線到事務(wù)抽象Interface把驗(yàn)證環(huán)境拉進(jìn)了協(xié)議思維3.1 一次APB讀事務(wù)的物理波形圖景先看一個(gè)具體場景APB總線上master要向slave讀取地址0x100處的數(shù)據(jù)。物理時(shí)序上大概是這樣master先把paddr驅(qū)動(dòng)成0x100把pwrite拉低表示讀然后拉高psel進(jìn)入setup phase下一拍拉高penable進(jìn)入access phaseslave把prdata驅(qū)動(dòng)到總線上并拉高preadymaster采樣到pready后一拍之后拉低psel和penable結(jié)束整個(gè)傳輸。如果讓驗(yàn)證環(huán)境直接對著這些信號來寫激勵(lì)代碼會(huì)極其啰嗦而且每一個(gè)測試用例里的driver代碼都在重復(fù)處理這套底層握手。更麻煩的是如果協(xié)議時(shí)序稍有改動(dòng)所有用例里的相關(guān)驅(qū)動(dòng)代碼都要跟著調(diào)整維護(hù)成本直線上升。3.2 在接口里用task封裝協(xié)議操作Interface的一個(gè)經(jīng)典用法是把“完成一次讀傳輸”這個(gè)協(xié)議行為用task直接封裝在接口內(nèi)部。以APB為例interface apb_if(input logic clk, input logic rst_n); logic [31:0] paddr; logic psel; logic penable; logic pwrite; logic [31:0] pwdata; logic [31:0] prdata; logic pready; clocking cb (posedge clk); default input #1step output #0; output paddr, psel, penable, pwrite, pwdata; input prdata, pready; endclocking task automatic read(input logic [31:0] addr, output logic [31:0] data); (posedge clk); cb.paddr addr; cb.pwrite 1b0; cb.psel 1b1; cb.penable 1b0; (posedge clk); cb.penable 1b1; do (posedge clk); while (!cb.pready); data cb.prdata; cb.psel 1b0; cb.penable 1b0; endtask endinterface有了這個(gè)readtaskBFM或者測試用例里只需要一行if.read(0x100, data);就能完成整個(gè)讀操作。握手細(xì)節(jié)、時(shí)序?qū)R、數(shù)據(jù)捕獲全部被封裝在接口里。3.3 為什么這讓驗(yàn)證環(huán)境脫胎換骨這一步從“信號級”到“事務(wù)級”的抽象本質(zhì)上是把驗(yàn)證工程師的工作重心從“如何拉好每一根線”轉(zhuǎn)移到了“如何編排事務(wù)序列”。事務(wù)序列關(guān)注的是協(xié)議場景比如連續(xù)讀、寫后讀、讀同一地址十次這些才是測試用例真正想表達(dá)的意思。沒有接口這一層封裝你只能在driver里對著信號一個(gè)一個(gè)地打既容易出錯(cuò)又難以理解。更重要的是這種封裝讓BFM具備了可重用性。同一個(gè)APB接口項(xiàng)目A里讀地址和數(shù)據(jù)位寬都是32位項(xiàng)目B里數(shù)據(jù)位寬變成64位你只需要修改接口內(nèi)部的信號聲明和task實(shí)現(xiàn)上層所有調(diào)用if.read()的代碼幾乎不用動(dòng)。這種隔離效應(yīng)在跨項(xiàng)目復(fù)用驗(yàn)證IP時(shí)非常值錢。3.4 驗(yàn)證組件如何與Interface解耦虛擬接口登場封裝好task之后緊接著會(huì)遇到一個(gè)語言層面的問題UVM里的driver是一個(gè)class而class不能像module那樣直接聲明一個(gè)接口類型的端口。SystemVerilog為class提供的是一根“引用”性質(zhì)的指針叫虛擬接口virtual interface。虛擬接口本質(zhì)上是物理接口的引用它的價(jià)值在于打破class和module之間的類型隔離。你可以在new()或者build_phase里從外部配置里拿到一個(gè)virtual apb_if然后像使用真實(shí)接口一樣調(diào)用其中的信號和task。后面專門有一節(jié)展開講虛擬接口和UVM的關(guān)系這里先記住結(jié)論沒有虛擬接口Interface再強(qiáng)大也進(jìn)不了面向?qū)ο蟮尿?yàn)證環(huán)境。4. 虛擬接口與UVM連接驗(yàn)證哲學(xué)的最后一塊拼圖4.1 為什么不能直接在uvm_component里放物理接口UVM是基于SystemVerilog的class體系搭建的而物理接口interface本質(zhì)上是一個(gè)硬件模塊層面的類型二者位于不同的數(shù)據(jù)類型空間。SystemVerilog不允許在class里聲明一個(gè)物理接口類型的成員變量因?yàn)閏lass是軟件對象interface是硬件實(shí)例。為了解決這個(gè)問題語言引入了虛擬接口的概念。簡單說虛擬接口就是一個(gè)“句柄”它指向某個(gè)物理接口實(shí)例。class通過虛擬接口訪問到的是真實(shí)硬件信號的驅(qū)動(dòng)和采樣能力但在類型上不會(huì)把硬件和軟件強(qiáng)行糅在一起。class apb_driver extends uvm_driver #(apb_transaction); virtual apb_if vif; function void build_phase(uvm_phase phase); super.build_phase(phase); if (!uvm_config_db#(virtual apb_if)::get(this, , vif, vif)) uvm_fatal(NOVIF, virtual interface not found) endfunction task run_phase(uvm_phase phase); apb_transaction tr; forever begin seq_item_port.get_next_item(tr); vif.read(tr.addr, tr.data); seq_item_port.item_done(); end endtask endclass4.2 虛擬接口的獲取方式和配置鏈路虛擬接口從物理接口到class環(huán)境一般要經(jīng)過兩條路徑。最常見的是通過uvm_config_db在testbench頂層把物理接口set到某個(gè)路徑下然后在driver的build_phase里get出來。這里有個(gè)常見的錯(cuò)誤set的路徑和get的路徑對不上或者uvm_config_db#(virtual apb_if)::set放到了build_phase之后執(zhí)行導(dǎo)致get返回空指針。第二條路徑是在new()的時(shí)候直接傳參。這種方式更直接但會(huì)讓組件的構(gòu)造和外部環(huán)境耦合得比較緊不利于復(fù)用。我更推薦config_db方式它保持了UVM“配置與使用分離”的風(fēng)格也方便后續(xù)通過factory override替換接口。4.3 虛擬接口使用中的幾個(gè)關(guān)鍵注意點(diǎn)第一虛擬接口在build_phase里獲取之后在run_phase里使用時(shí)才真正去索引物理信號。如果這兩個(gè)phase之間物理接口被重建比如某些仿真支持熱復(fù)位重建接口層次需要注意句柄失效的問題。大多數(shù)標(biāo)準(zhǔn)仿真流程不會(huì)有這種事但如果你在做重啟、重配置這類場景要提前想清楚虛擬接口的生命周期管理。第二一個(gè)物理接口可以被多個(gè)虛擬接口引用。這在保持驗(yàn)證環(huán)境分層時(shí)很有用比如monitor和driver各持有一個(gè)虛擬接口引用它們共享同一份物理信號但分別承擔(dān)采樣和驅(qū)動(dòng)的職責(zé)。第三虛擬接口指向的是物理接口的實(shí)例不指向modport。modport的選擇在物理連接處就已經(jīng)確定了虛擬接口能訪問到的信號集合取決于物理接口的定義而不是你拿虛擬接口時(shí)再臨時(shí)指定的。這一點(diǎn)容易產(chǎn)生誤解以為虛擬接口可以按modport改變訪問權(quán)限實(shí)際并非如此。4.4 interface class另一種抽象思路SystemVerilog 2012引入了interface class不少剛接觸的人會(huì)和interface混淆。interface class是純粹的面向?qū)ο蟪橄罄锩嬷挥蟹椒ㄔ吐暶鳑]有任何信號和實(shí)現(xiàn)功能上接近Java里的接口。它解決的是“多個(gè)類之間如何約定共同行為”的問題與硬件信號沒有關(guān)系。在實(shí)際項(xiàng)目中interface和interface class有時(shí)會(huì)配合出現(xiàn)物理連接交給interface行為協(xié)議約定交給interface class。理解兩者的區(qū)別對閱讀較新的驗(yàn)證IP代碼很有幫助看到interface關(guān)鍵字思考的是信號與連接看到interface class思考的是類與類之間的契約。5. 真實(shí)項(xiàng)目中踩過的Interface的坑5.1 端口方向不匹配仿真過了上板卻失敗有一次我們做一個(gè)跨時(shí)鐘域模塊的驗(yàn)證模塊端口用的是某個(gè)接口的modport驗(yàn)證環(huán)境里的監(jiān)測組件也引用了同一個(gè)接口。功能仿真完全正常但到了FPGA原型驗(yàn)證階段上板后用邏輯分析儀抓波形發(fā)現(xiàn)數(shù)據(jù)始終對不上。最后定位到原因是仿真環(huán)境里monitor連接接口時(shí)用了一個(gè)不帶modport的裸接口引用工具把信號方向按默認(rèn)規(guī)則推斷導(dǎo)致monitor某根信號是輸入而不是輸出仿真時(shí)由于testbench的強(qiáng)驅(qū)動(dòng)把問題掩蓋了上板后真實(shí)IO方向錯(cuò)誤就暴露了。這個(gè)教訓(xùn)是接口的modport一定要作為一種規(guī)范性約束貫穿所有連接點(diǎn)不要因?yàn)椤胺抡婧孟衲芘堋本吞^它。modport不只是文檔注釋它是工具能做方向檢查的關(guān)鍵依據(jù)。5.2 clocking block的驅(qū)動(dòng)和直接驅(qū)動(dòng)同時(shí)存在在接口里定義clocking block之后代碼中同一個(gè)信號有可能既被clocking block驅(qū)動(dòng)又被普通的assign驅(qū)動(dòng)。很多人會(huì)犯一個(gè)錯(cuò)誤在class里通過虛擬接口調(diào)用接口里封裝的task同時(shí)在testbench頂層又用assign給同一根信號賦了初值。仿真時(shí)初值一直壓著task驅(qū)動(dòng)導(dǎo)致波形數(shù)據(jù)不更新而且這種問題不會(huì)報(bào)錯(cuò)只會(huì)讓波形看起來“怪怪的”。一個(gè)比較可靠的規(guī)則是同一根信號在一個(gè)仿真域里只允許一種驅(qū)動(dòng)方式。如果用了clocking block接口里的信號就不要再用assign去做持續(xù)驅(qū)動(dòng)如果需要置初值放在initial塊里、或者干脆通過clocking block的驅(qū)動(dòng)來完成。5.3 接口例化位置、作用域與全局接口的隱患接口作為端口傳遞和在testbench內(nèi)部例化作用范圍不同。有的項(xiàng)目圖省事會(huì)把接口定義成全局級別的實(shí)例結(jié)果不同測試用例之間相互影響非常難排查。我的建議是接口實(shí)例化在testbench頂層通過模塊端口或者config_db往下傳避免“全局接口”這種設(shè)計(jì)。全局接口在小型單用例驗(yàn)證時(shí)問題不大但用例一多彼此之間共享狀態(tài)很容易導(dǎo)致case隔離性被破壞。驗(yàn)證環(huán)境的第一原則是case之間互相隔離接口也要遵守這條原則。5.4 參數(shù)化接口與接口數(shù)組的邊界情況參數(shù)化接口挺實(shí)用比如定義位寬可配的AXI接口interface axi_if #(parameter int ADDR_W 32, parameter int DATA_W 64) (...); logic [ADDR_W-1:0] awaddr; logic [DATA_W-1:0] wdata; // ... endinterface但參數(shù)化接口在使用時(shí)有一個(gè)容易踩的點(diǎn)如果你在某個(gè)module端口里用axi_if作為端口類型而這個(gè)module的位寬參數(shù)也來自上層的參數(shù)傳遞那么接口參數(shù)和模塊參數(shù)需要嚴(yán)格匹配。工具對參數(shù)匹配的檢查是編譯期行為不匹配直接報(bào)錯(cuò)這個(gè)倒還好。真正麻煩的是接口數(shù)組和generate塊配合時(shí)你用genvar例化一堆接口再用虛擬接口的數(shù)組往環(huán)境里傳此時(shí)uvm_config_db傳的是virtual axi_if的數(shù)組如果在仿真中途重新生成了某個(gè)接口實(shí)例原先指向它的虛擬接口句柄可能不再可靠引用之前要確認(rèn)層次結(jié)構(gòu)沒有變化。5.5 綜合視角接口里的行為代碼不會(huì)被綜合成期望的邏輯如果設(shè)計(jì)側(cè)也想用Interface有一個(gè)容易被忽略的現(xiàn)實(shí)問題不是所有綜合工具都支持把接口內(nèi)的task/function/clocking block完整綜合成等價(jià)門級邏輯。對RTL設(shè)計(jì)來說接口更多是作為信號組織的層次化手段內(nèi)部盡量不要放行為級代碼否則綜合工具對接口內(nèi)部語義的解釋不同很容易產(chǎn)生前后仿真不一致。我的做法是設(shè)計(jì)側(cè)只使用接口的信號聲明和modport所有行為級代碼放到驗(yàn)證側(cè)的接口包裝類或UVM組件中。即使工具支持接口內(nèi)部的行為綜合我也會(huì)保持這個(gè)邊界因?yàn)轵?yàn)證環(huán)境的語義和設(shè)計(jì)綜合的語義本來就該分開。下面用一個(gè)表格總結(jié)一下常見問題、原因和應(yīng)對思路常見現(xiàn)象可能原因處理方式仿真正常但上板方向錯(cuò)誤modport約束沒有被嚴(yán)格遵守所有連接點(diǎn)統(tǒng)一使用modport禁止裸接口引用波形數(shù)據(jù)不更新clocking block與其他assign雙重驅(qū)動(dòng)同一信號始終保持單一驅(qū)動(dòng)源不同case相互影響全局接口實(shí)例共享狀態(tài)接口在testbench頂層例化通過config_db傳遞虛擬接口get到nullset路徑與get路徑不匹配或時(shí)機(jī)錯(cuò)誤檢查時(shí)序set必須在build_phase之前完成綜合前后行為不一致接口內(nèi)部包含行為級代碼設(shè)計(jì)側(cè)只用信號和modport行為代碼留在驗(yàn)證側(cè)6. Interface與整個(gè)驗(yàn)證方法學(xué)的融合重構(gòu)環(huán)境時(shí)的收益評估6.1 從“改一點(diǎn)動(dòng)全身”到“改一處全局生效”引入Interface之后驗(yàn)證環(huán)境重構(gòu)最大的收益在于信號變更的“局部化”。比如某個(gè)總線上新增了一根用于指示錯(cuò)誤狀態(tài)的信號放在過去你要改DUT端口、改BFM、改monitor、改斷言、改scoreboard里的引用。用了Interface你只需要在接口定義和對應(yīng)modport里加上這根信號然后處理真正關(guān)心它的事務(wù)邏輯即可。這種“局部化”效應(yīng)在項(xiàng)目后期尤其明顯。項(xiàng)目快收斂時(shí)協(xié)議的小改動(dòng)非常頻繁如果環(huán)境架構(gòu)還停留在信號級連接每一次改動(dòng)都是一次全局排查。Interface在這個(gè)階段幫忙省下的時(shí)間往往比前期寫接口定義的時(shí)間多一個(gè)數(shù)量級。6.2 接口內(nèi)的斷言和協(xié)議檢查把驗(yàn)證前移接口內(nèi)部天然適合放置與信號協(xié)議相關(guān)的斷言。因?yàn)閿嘌孕枰^察的都是接口內(nèi)聲明的信號寫在接口里可以直接引用不需要額外跨層次引用其他模塊的線。比如APB協(xié)議要求psel拉高時(shí)penable不能同時(shí)為高setup phase不允許penable有效這條規(guī)則可以作為立即斷言或并發(fā)斷言放進(jìn)接口property p_enable_not_during_setup; (posedge clk) disable iff (!rst_n) (psel !penable) | (!penable || pready); endproperty ap_enable_not_during_setup: assert property(p_enable_not_during_setup);這樣的斷言跟著接口走接口被復(fù)用到哪里斷言就自動(dòng)保護(hù)到哪里。和把斷言寫在獨(dú)立的斷言模塊里相比它減少了路徑引用錯(cuò)誤也讓接口文件成為“協(xié)議自包含”的單元。6.3 事務(wù)級建模與覆蓋率收集的銜接接口不只是把信號變成事務(wù)還可以在事務(wù)級建模和覆蓋率收集之間當(dāng)好“橋梁”。覆蓋率組如果要采樣某個(gè)信號的狀態(tài)通常是直接引用接口內(nèi)的信號。因?yàn)榻涌趦?nèi)信號的層次路徑比從testbench逐層往下指要短得多也穩(wěn)定得多。covergroup apb_cov (posedge clk); option.per_instance 1; coverpoint paddr; coverpoint pwrite; coverpoint pready; cross pwrite, pready; endgroup如果你有一整套基于虛擬接口的driver和monitor再配上接口內(nèi)的斷言和覆蓋組一個(gè)協(xié)議子系統(tǒng)的驗(yàn)證環(huán)境就基本“自治”了。這也是我在多個(gè)項(xiàng)目里感覺最舒服的狀態(tài)環(huán)境的變化被接口吸收掉了剩下真正需要人去思考的是場景和序列本身。6.4 跨團(tuán)隊(duì)協(xié)作中的規(guī)范化接口定義本質(zhì)上是一份“可執(zhí)行的協(xié)議文檔”。設(shè)計(jì)團(tuán)隊(duì)和驗(yàn)證團(tuán)隊(duì)都引用同一個(gè)接口文件雙方對信號名、方向、時(shí)序的理解就天然一致。協(xié)議改動(dòng)時(shí)只要接口文件改了設(shè)計(jì)側(cè)和驗(yàn)證側(cè)編譯時(shí)能第一時(shí)間感知到不匹配。相比口頭約定、EXCEL表格維護(hù)信號列表的舊方式接口文件帶來的規(guī)范化價(jià)值很直接。我參與過的項(xiàng)目里接口文件通常是要走評審的它和協(xié)議文檔擁有同等重要的地位。有了接口這個(gè)載體協(xié)議討論可以落到具體代碼邊界上而不是停留在文字描述層面這種溝通效率的提升用過的人都會(huì)有體會(huì)。7. 幾個(gè)值得固化的使用習(xí)慣接口文件保持單一職責(zé)。一個(gè)接口最好只描述一種協(xié)議或者一組強(qiáng)相關(guān)的信號。不要把AXI、APB、自定義控制信號全塞進(jìn)同一個(gè)接口里否則modport會(huì)變得非常臃腫復(fù)用性反而下降。不要濫用task封裝。接口里的task適合封裝中低頻、相對固定的協(xié)議操作比如讀、寫。如果每個(gè)用例都想自定義一種完全不同的驅(qū)動(dòng)行為task的參數(shù)會(huì)變得非常復(fù)雜不如把這種靈活性放在UVM sequence層。接口的定位是“協(xié)調(diào)物理信號”sequence的定位是“編排激勵(lì)場景”不要越界。給modport命名時(shí)帶上角色后綴。比如master_mp、slave_mp、monitor_mp一目了然。否則看代碼時(shí)還要回過去查這個(gè)modport是給誰用的增加不必要的閱讀負(fù)擔(dān)。盡可能把接口與斷言、覆蓋組放在一起管理。每次修改協(xié)議時(shí)只看一個(gè)文件就能知道信號、時(shí)序、約束、覆蓋目標(biāo)整體發(fā)生了什么變化這對代碼評審非常有幫助。虛擬接口的獲取統(tǒng)一封裝。每個(gè)driver/monitor里都寫一套u(yù)vm_config_db的get邏輯很容易出錯(cuò)。比較穩(wěn)妥的做法是在一個(gè)公共基類里封裝好虛擬接口的set/get或者用uvm_object_wrapper創(chuàng)建一個(gè)小配置對象子類只負(fù)責(zé)聲明自己需要什么接口不負(fù)責(zé)具體配置路徑。8. 從一個(gè)由接口驅(qū)動(dòng)的時(shí)代回看Verilog連接方式站在今天的視角回看Interface很大程度上解決了傳統(tǒng)Verilog連接方式里兩個(gè)根深蒂固的問題一是信號組織以“根”為單位連接關(guān)系分散難維護(hù)二是驗(yàn)證環(huán)境和設(shè)計(jì)環(huán)境之間缺少一個(gè)“協(xié)議級”的共享語言。Interface把信號、方向、時(shí)序、行為封裝進(jìn)同一個(gè)單元讓連接這件事第一次有了結(jié)構(gòu)化的表達(dá)能力。從物理連接到驗(yàn)證哲學(xué)Interface其實(shí)是同一種抽象能力在兩個(gè)方向上的延伸。物理方向上它用modport讓同一組信號對不同角色呈現(xiàn)不同方向讓連接的可檢查性大幅提升驗(yàn)證方向上它用clocking block和虛擬接口讓面向?qū)ο蟮尿?yàn)證世界與硬件世界對接把事務(wù)級抽象落到實(shí)處。理解了這兩層再回頭看Interface的每一個(gè)語法細(xì)節(jié)都會(huì)更有方向感。我在實(shí)際使用里最深的一個(gè)體會(huì)是不要一上來就追求接口里塞滿task、斷言、覆蓋組這些“高級功能”。先用好最基礎(chǔ)的信號封裝和modport把一個(gè)協(xié)議跑通再逐步往里加時(shí)序、加行為、加斷言。接口的邊界感是在項(xiàng)目推進(jìn)中逐漸打磨出來的它對環(huán)境的塑造力很大程度上取決于你怎么用它而不是它有多少特性。