芯片驗(yàn)證|UVM與驗(yàn)證環(huán)境一文通
掃描二維碼
隨時(shí)隨地手機(jī)看文章
關(guān)注、星標(biāo)公眾號(hào),直達(dá)精彩內(nèi)容
什么是UVM?
VMM(Verification Methodology Manual),Synopsys在2006年推出的,VMM當(dāng)中集成了寄存器解決方案RAL(Register Abstraction Layer)。
OVM(Open Verification Methodology),Cadence和Mentor在2008年推出的,它引進(jìn)了factory機(jī)制,功能非常強(qiáng)大,但是沒有寄存器解決方案。
UVM(Universal Verification Methodology),即通用驗(yàn)證方法學(xué),其正式版本在2011年2月由Accellera推出的,UVM幾乎完全繼承了OVM,同時(shí)又采納了VMM中的寄存器解決方案。
什么是UVM驗(yàn)證平臺(tái)?
UVM是基于System Verilog的一種驗(yàn)證方法學(xué),也可以看成是一個(gè)庫,提供一系列的接口,可以利用UVM搭建驗(yàn)證平臺(tái),用于驗(yàn)證數(shù)字邏輯電路的正確性。
注意,UVM本身并不是一個(gè)驗(yàn)證平臺(tái),他只是一個(gè)庫,而一個(gè)驗(yàn)證平臺(tái)引入了UVM相關(guān)庫,稱為基于UVM的驗(yàn)證平臺(tái),或者簡(jiǎn)稱為UVM驗(yàn)證平臺(tái)。
支持UVM的EDA廠商:Cadence、Synopsys、Mentor…
UVM基礎(chǔ)
一個(gè)基于SV的簡(jiǎn)單驗(yàn)證平臺(tái)
注:這里“基于SV”指平臺(tái)僅僅使用SystemVerilog語言搭建。
一個(gè)基于UVM的簡(jiǎn)單驗(yàn)證平臺(tái)
基于UVM驗(yàn)證平臺(tái)原則
類:UVM中幾乎所有的東西都是用類(class)來實(shí)現(xiàn)的,所以,搭建uvm平臺(tái)第一條原則,所有的組件都用類來完成。
基于UVM類:當(dāng)要實(shí)現(xiàn)一個(gè)功能時(shí),首先應(yīng)該想到的是從UVM的某個(gè)類派生出一個(gè)新的類來實(shí)現(xiàn)期望功能,所以,搭建uvm平臺(tái)第二條原則,所有的組件應(yīng)該派生自u(píng)vm類。
UVM中兩大最重要基類
uvm_object:它是UVM最基本的類,幾乎所有的類都派生自u(píng)vm_object,它的拓展性是最好的,當(dāng)然能力也是最差的。它主要構(gòu)成了環(huán)境的屬性(例如配置)和數(shù)據(jù)傳輸。
uvm_component:它派生自u(píng)vm_object ,卻擁有自己獨(dú)有的強(qiáng)大特性,它有兩大獨(dú)有特點(diǎn),一是通過new形成樹形結(jié)構(gòu),二是自動(dòng)執(zhí)行phase。它主要構(gòu)成了環(huán)境的層次。
UVM中常用類的繼承關(guān)系
什么是UVM Factory
所謂Factory就是工廠,是通過一個(gè)字符串(類名)創(chuàng)建一個(gè)此字符串所代表的的類的一個(gè)實(shí)例,并且能夠自動(dòng)調(diào)用其phase執(zhí)行的機(jī)制,也就相當(dāng)于加工工廠。理解上可能比較抽象,我們舉例來說明。
如下圖,一個(gè)汽車工廠好比我們的驗(yàn)證平臺(tái),支持寶馬和奔馳兩條生產(chǎn)線,兩位老板只需下令生產(chǎn)奔馳還是寶馬,工廠按照生產(chǎn)線自動(dòng)加工,最終產(chǎn)出汽車,這里指令(奔馳/寶馬)就是上述中的字符串,生產(chǎn)線就是調(diào)用其phase執(zhí)行的機(jī)制,整個(gè)驗(yàn)證平臺(tái)也就相當(dāng)于工廠,這就是UVM Factory。
我們的工廠可以支持生產(chǎn)哪些“汽車”,是由驗(yàn)證平臺(tái)決定的,如果向驗(yàn)證平臺(tái)輸入不支持的指令,比如指定工廠生產(chǎn)“永久自行車”,會(huì)被視為錯(cuò)誤指令。
如果就想生產(chǎn)“永久自行車”,我們需要添加永久自行車生產(chǎn)線:
然后我們就可以向工廠輸入指令,生產(chǎn)“永久自行車”了。
總結(jié) :
run_test()語句會(huì)通過傳入的字符串,創(chuàng)建一個(gè)類名為輸入字符串的類的實(shí)例,并且會(huì)自動(dòng)調(diào)用此類的phase,但是前提是你已經(jīng)注冊(cè)了這個(gè)類。 對(duì)于uvm_component類,注冊(cè)是通過uvm_component_utils宏來進(jìn)行的。所有派生自u(píng)vm_component以及其派生類都應(yīng)使用uvm_component_utils來注冊(cè) 對(duì)于uvm_object類,注冊(cè)是通過uvm_object_utils宏來進(jìn)行的。所有派生自u(píng)vm_object以及其派生類(除uvm_component外)都應(yīng)使用uvm_object_utils來注冊(cè) 由上述例子可以看出:run_test()是啟動(dòng)整個(gè)驗(yàn)證平臺(tái)的UVM庫函數(shù)。
UVM驗(yàn)證平臺(tái)啟動(dòng)執(zhí)行流程
搭建UVM驗(yàn)證平臺(tái)
UVM驗(yàn)證平臺(tái)基本組成
一個(gè)只有driver的測(cè)試平臺(tái)
driver代碼示例:
class my_driver extends uvm_driver; //繼承uvm庫中的uvm_driver類
`uvm_component_utils(my_driver) //將my_driver類注冊(cè)到factory
virtual my_if vif; //聲明driver的interface, interface my_if的定義這里不再介紹
extern virtual function void build_phase(uvm_phase phase);
extern virtual task void main_phase(uvm_phase phase);
endclass
function void my_driver:: build_phase(uvm_phase phase); //將頂層實(shí)例化的interface指針傳遞給driver的insterface
super.build_phase(phase);
if(!uvm_config_db# (virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("my_driver", "virtual interface must be set for vif !!!")
endfunction
task void my_driver:: main_phase(uvm_phase phase);
//略去
endtask
top_tb代碼示例:
module tob_tb
my_if input_if(clk, rstn); //聲明top_tb的interface,在dut實(shí)例化時(shí)可直接使用
dut my_dut(.clk(clk),
.rst_n(rst_n),
.rxd(input_if.data)); //dut實(shí)例化
initial begin
uvm_config_db# (virtual my_if)::set(null, "uvm_test_top", "vif", input_if) //與driver的interface建立連接,將top_tb.input_if與my_driver.vif連接起來
run_test("my_driver"); //實(shí)例化my_driver類,實(shí)例的名字是uvm_test_top,并執(zhí)行類my_driver中的main_phase任務(wù)
end
endmodule
注意: uvm_test_top是run_test產(chǎn)生的my_driver類的實(shí)例化對(duì)象名字,run_test(“my_driver”)可以簡(jiǎn)單的看成:
my_driver uvm_test_top;
uvm_test_top.main_phase();
這里,任何被run_test()實(shí)例化的類的對(duì)象的名字都會(huì)被UVM平臺(tái)默認(rèn)成uvm_test_top,這個(gè)被實(shí)例化的類也就是整個(gè)uvm平臺(tái)的頂層,而且只允許有一個(gè)頂層,即一個(gè)驗(yàn)證平臺(tái)只調(diào)用一個(gè)run_test()。
top_tb頂層與uvm樹形結(jié)構(gòu)的交互:
uvm_config_db
function void my_driver:: build_phase(uvm_phase phase); //將頂層實(shí)例化的interface指針傳遞給driver的insterface
super.build_phase(phase);
if(!uvm_config_db# (virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("my_driver", "virtual interface must be set for vif !!!")
endfunction
top_tb頂層與uvm樹形結(jié)構(gòu)的交互為什么要用這種看起來很怪的uvm_config_db方式獲得top_tb的interface,不能直接調(diào)用得到嗎?
按道理來講,是可以的,uvm本來就是基于sv的函數(shù)庫,其底層肯定也是有sv去實(shí)現(xiàn)的,既然uvm將其封裝為uvm_config_db,建議統(tǒng)一使用此方法,使用uvm提供的函數(shù)也是最好的避免出錯(cuò)的辦法。
top_tb頂層與uvm樹形結(jié)構(gòu):
如果將top_tb中的第3點(diǎn)去掉,請(qǐng)回答以下問題: top_tb如何獲取dut內(nèi)部信號(hào)?答:通過top_tb.my_dut.xxx可以獲取。 top_tb如何獲取右側(cè)樹形結(jié)構(gòu)的內(nèi)部信息?答:通過top_tb.uvm_test_top.xxx是不可行的,因?yàn)閞un_test實(shí)例化了一個(gè)脫離了top_tb層次結(jié)構(gòu)的實(shí)例對(duì)象,建立了一個(gè)新的層次,所以不能通過top_tb.uvm_test_top.xxx直接訪問。所以針對(duì)這種情況,UVM引入了config_db的機(jī)制,也就是前面分別在top_tb和my_driver類中build_phase提到的:
uvm_config_db# (virtual my_if)::set(null, "uvm_test_top", "vif", input_if)
uvm_config_db# (virtual my_if)::get(this, "", "vif", vif)
這樣我們通過uvm_config_db將top_tb頂層與uvm樹形結(jié)構(gòu)打通。
top_tb與樹形結(jié)構(gòu)中driver的交互流:
注意:top_tb與樹形結(jié)構(gòu)之間的交互用虛線,是因?yàn)閙y_driver.interface.output_xxx不是直接給top_tb.interface.input_xxx傳值,而是在采用了config_db機(jī)制后,類似于兩邊在操作同一個(gè)指針地址,即改變my_driver.interface.output_xxx的值,就等于直接改變了top_tb.interface.input_xxx中變量的值。
另外:uvm_config_db將top_tb頂層與uvm樹形結(jié)構(gòu)打通,我們可以將top_tb與樹形結(jié)構(gòu)任意組件進(jìn)行交互,而不僅限于driver和monitor。
樹形結(jié)構(gòu)構(gòu)造
目前,一個(gè)只含有driver驅(qū)動(dòng)的UVM驗(yàn)證平臺(tái)已經(jīng)形成,那么接下來要考慮樹形結(jié)構(gòu)的構(gòu)造,即添加新部件并使其層次化。
driver、monitor、agent和env:
注意:此時(shí)樹形結(jié)構(gòu)的頂層變成了my_env, 所以在top_tb中run_test(“my_driver”)應(yīng)改成run_test(“my_env”),之前講過,run_test(“my_driver”)實(shí)例化之后對(duì)象的名字是uvm_test_top, 那么run_test(“my_env”)實(shí)例化之后頂層對(duì)象的名字是什么?
答案是仍然為uvm_test_top。
樹形結(jié)構(gòu)發(fā)生了層次改變,此時(shí)top_tb怎么和my_driver交互?
如下top_tb代碼:
module tob_tb
my_if input_if(clk, rstn); //聲明top_tb的interface,在dut實(shí)例化時(shí)可直接使用
dut my_dut(.clk(clk),
.rst_n(rst_n),
.rxd(input_if.data)); //dut實(shí)例化
initial begin
// uvm_config_db# (virtual my_if)::set(null, "uvm_test_top", "vif", input_if)
uvm_config_db# (virtual my_if)::set(null, "uvm_test_top.agt.drv", "vif", input_if) //與driver的interface建立連接,將top_tb.input_if與my_driver.vif連接起來
// run_test("my_driver")
run_test("my_env"); //實(shí)例化my_env類,實(shí)例的名字是uvm_test_top,并執(zhí)行類my_env中的phase
end
endmodule
加入checker:
注意:這里的checker是將reference model和scoreboard統(tǒng)一看成一個(gè)整體。reference model和scoreboard的定義和其他component的方式一樣,這里不再贅述。
樹形結(jié)構(gòu)通信通道
加入sequencer:
這里sequencer是一個(gè)固定組件,sequencer主要將激勵(lì)承接給driver,my_transaction是一個(gè)數(shù)據(jù)包,也就是測(cè)試激勵(lì),這里還要有一個(gè)sequence概念,sequence里存放一組trans,提供給sequencer,可見sequencer屬于component類,sequence和trans屬于object類。
關(guān)于sequencer、sequence和trans,還有這樣一個(gè)比喻,trans好比子彈,sequence好比彈夾,而sequencer是槍。
加入transaction:
transaction使用`uvm_object_utils注冊(cè)。 transaction可以看成是數(shù)據(jù)包,把數(shù)據(jù)打包傳輸,便于交互。 在組件之間(driver,checker,monitor等)的信息傳遞都是基于transaction。
加入transaction后driver的變化:
class my_driver extends uvm_driver #(my_transaction); //uvm_driver是參數(shù)化類,參數(shù)類型為my_transaction
`uvm_component_utils(my_driver)
virtual my_if vif;
……
endclass
task void my_driver:: main_phase(uvm_phase phase);
將:
vif.data <= 8'b1;
改成:
req = new(“req”); //等價(jià)于my_transaction tr; tr = new(“tr”);
req.data = 8’b1; //等價(jià)于tr.data = 8’b1;
vif.data <= req.data; //等價(jià)于vif.data <= tr.data;
endtask
req是父類uvm_driver中變量,類型是傳遞給uvm_driver的參數(shù),這里傳遞的參數(shù)是my_transaction,所以父類uvm_driver中的req類型就是my_transaction。 my_driver繼承了uvm_driver類,所以可以直接使用uvm_driver中的req,而不需要在my_driver中聲明定義req。
加入sequence:
前面所示代碼中激勵(lì)都是在driver產(chǎn)生的,正常情況driver只是傳遞激勵(lì),而不是產(chǎn)生激勵(lì),所以要將激勵(lì)產(chǎn)生從driver中移除,從外界獲得激勵(lì),那driver的激勵(lì)應(yīng)該從哪里產(chǎn)生呢?
這就是sequencer要做的事情,也就是說sequencer要提供req(這里為my_transaction)給my_driver。
那么sequencer的transaction從哪里來?
`uvm_do(my_trans)實(shí)現(xiàn)了以下操作:
創(chuàng)建一個(gè)my_transaction的實(shí)例my_trans 將其隨機(jī)化 最終將其傳送給my_sequencer(此宏不能做到自動(dòng)連接my_sequencer并將my_trans直接傳送給my_sequencer)
sequence工作機(jī)制:
對(duì)于1, my_sequencer等待。 對(duì)于2, my_sequencer等待。 對(duì)于3, my_sequencer將my_sequence中的my_trans發(fā)送給my_driver。
待解決問題:
my_driver如何向my_sequencer發(fā)送transaction接收請(qǐng)求? my_sequence如何向my_sequencer發(fā)送transaction?
my_driver向my_sequencer發(fā)送申請(qǐng):
注意:
seq_item_port是uvm_driver中的成員變量。 seq_item_export是uvm_sequencer中的成員變量。
my_sequence向my_sequencer發(fā)送my_transaction:
前面提到, my_sequence中的`uvm_do不能做到自動(dòng)連接my_sequencer并將my_trans直接傳送給my_sequencer,所以需要額外啟動(dòng)連接,以在當(dāng)前頂層my_env中手工啟動(dòng)為例(一般都是在頂層啟動(dòng)):
task void my_env:: main_phase(uvm_phase phase);
my_sequence seq;
seq = my_sequence::type_id::create("seq");
seq.start(agt.sqr); //將此sequence產(chǎn)生的transaction發(fā)送給agent中的my_sequencer.
endtask
my_sequence自動(dòng)啟動(dòng)機(jī)制default_sequence:
前面my_sequence是在my_env中手工啟動(dòng)的,default_sequence可以自動(dòng)啟動(dòng)my_sequence 。
task void my_env:: main_phase(uvm_phase phase);
將下列代碼去掉:
my_sequence seq;
seq = my_sequence::type_id::create("seq");
seq.start(agt.sqr); //將此sequence產(chǎn)生的transaction發(fā)送給agent中的my_sequencer.
endtask
virtual function void my_env::build_phase(uvm_phase phase);
uvm_config_db# (uvm_object_wrapper)::set(this, “agt.sqr.main_pahse”, “default_sequence”, my_sequence::type_id::get());
endtask
樹形結(jié)構(gòu)通信通道變化:
原始:
現(xiàn)在:
注意:
藍(lán)色部分: top_tb與樹形結(jié)構(gòu)的連接 紫色部分: transaction在my_checker和monitor之間的通信通道 紅色部分: transaction在my_sequence,my_sequencer和my_driver之間的通信通道 單箭頭虛線部分: 實(shí)際沒有顯性直接通道,均通過上一層connect實(shí)現(xiàn) 雙箭頭虛線部分: 代表同一模塊
截至目前的樹形結(jié)構(gòu):
目前,樹形結(jié)構(gòu)除my_case0頂層之后,均已構(gòu)建完成,并全部打通。后面要添加my_case0。
加入base_test:
base_test.sv代碼:
class base_test extends uvm_test;
`uvm_component_utils(base_test)
my_env env; //my_env會(huì)在base_test中實(shí)例化,base_test取代env稱為頂層。
endclass
function void base_test::build_phase(uvm_phase phase);
//my_sequence需要在頂層啟動(dòng)連接,因?yàn)閎ase_test變成頂層,所以從my_env中移到此處
uvm_config_db# (uvm_object_wrapper)::set(this, "env.agt.sqr.main_pahse",
"default_sequence",
my_sequence::type_id::get());
endfunction
top_tb.sv代碼:
module tob_tb
initial begin
// 將:
// uvm_config_db# (virtual my_if)::set(null, "uvm_test_top.agt.drv", "vif", input_if)
// run_test("my_env");
// 改成:
uvm_config_db# (virtual my_if)::set(null, "uvm_test_top.env.agt.drv", "vif", input_if)
run_test("base_test");
end
endmodule
加入my_case0:
class my_case0 extends base_test;
`uvm_component_utils(my_case0)
endclass
function void my_case0::build_phase(uvm_phase phase);
//以下重載了base_test中的my_sequence的啟動(dòng),注意bast_test中的仍保留。
uvm_config_db# (uvm_object_wrapper)::set(this, "env.agt.sqr.main_pahse", "default_sequence", my_sequence::type_id::get());
endfunction
注意:
my_case0是繼承了base_test的一個(gè)子類, 是base_test的一個(gè)更具體的實(shí)現(xiàn),也就從這里形成了testcase的概念,這個(gè)testcase的名字就是my_case0。 my_case0沒有改變激勵(lì)產(chǎn)生的方式,即仍然是啟動(dòng)了my_sequence,并利用my_sequence中`uvm_do(my_trans)來全隨機(jī)產(chǎn)生激勵(lì)。
加入my_case0_sequence:
加入my_case0_sequence:
class my_case0_sequence extends my_sequence #(my_transaction);
`uvm_object_utils(my_sequence)
my_transaction my_trans;
virtual task body();
`uvm_do_with(my_trans, {my_trans.data == 8'hff;})
endtask
endclass
注意:
my_case0_sequence是繼承了my_sequence的一個(gè)子類, 是my_sequence的一個(gè)更具體的實(shí)現(xiàn)。 my_case0_sequence重載了my_sequence 的body()任務(wù),并利用`uvm_do_with()來產(chǎn)生次數(shù)值都為8’hff的數(shù)據(jù)激勵(lì)。
my_case0.sv代碼:
class my_case0 extends base_test;
`uvm_component_utils(my_case0)
endclass
function void my_case0::build_phase(uvm_phase phase);
//以下將my_case0_sequence中的trans傳遞給了sequencer,最終傳給driver。
uvm_config_db# (uvm_object_wrapper)::set(this, "agt.sqr.main_pahse", "default_sequence", my_case0_sequence::type_id::get());
endfunction
注:
這里制造了一個(gè)用例my_case0,此用例每個(gè)transaction產(chǎn)生8’hff數(shù)據(jù)的激勵(lì)。 這里my_case0成為新的頂層。
將頂層base_test替換成my_case0:
module tob_tb
initial begin
// 將:
// run_test("base_test");
// 改成:
run_test("my_case0");
end
endmodule
run_test()作用:它會(huì)通過傳入的字符串,創(chuàng)建一個(gè)類名為輸入字符串的類的實(shí)例,并且會(huì)自動(dòng)調(diào)用此類的main_phase。
run_test給我們最初的印象是構(gòu)建了一個(gè)脫離了top_tb的樹形結(jié)構(gòu),并完成了所有內(nèi)部需要交互部件的打通。
現(xiàn)在我們需要改變一下思維,這里當(dāng)調(diào)用run_test(“my_case0”)時(shí),不再考慮樹形結(jié)構(gòu),我們用一個(gè)更抽象的概念來描述run_test的行為,那就是:它執(zhí)行一個(gè)用例my_case0,而且只有一個(gè)用例在執(zhí)行,這條用例每個(gè)transaction都在產(chǎn)生數(shù)值為8’hff的數(shù)據(jù)。我們平時(shí)所謂的跑各種各樣的用例,這些用例其實(shí)都是基于這個(gè)去構(gòu)造和命名的(在uvm平臺(tái)中)。
用例構(gòu)造
構(gòu)造另一個(gè)用例my_case1:
my_case1_sequence.sv:
class my_case1_sequence extends my_sequence #(my_transaction);
`uvm_object_utils(my_sequence)
my_transaction my_trans;
virtual task body();
`uvm_do_with(my_trans, {my_trans.data == 8'haa;})
endtask
endclass
注意:my_case1_sequence重載了my_sequence 的body()任務(wù),并利用`uvm_do_with()來產(chǎn)生次數(shù)值都為8’haa的數(shù)據(jù)激勵(lì)。
my_case1.sv:
class my_case1 extends base_test;
`uvm_component_utils(my_case1)
endclass
function void my_case1::build_phase(uvm_phase phase);
//以下將my_case1_sequence中的trans傳遞給了sequencer,最終傳給driver。
uvm_config_db# (uvm_object_wrapper)::set(this, "agt.sqr.main_pahse",
"default_sequence",
my_case1_sequence::type_id::get());
endfunction
注意:這里制造了一個(gè)用例my_case1,此用例每個(gè)transaction產(chǎn)生8’haa數(shù)據(jù)的激勵(lì);如果想要運(yùn)行這個(gè)用例,那my_case1將成為新的頂層,也就是將top_tb中的 run_test(“my_case0”) 改為 run_test(“my_case1”)
UVM測(cè)試用例啟動(dòng)
由于run_test在top_tb中只能調(diào)用一次,所以每次跑新的用例,都要手動(dòng)改一下run_test()的參數(shù)名字,試想我們有10000個(gè)用例,如果都手動(dòng)改,那肯定是不可行的,所以UVM提供了另外一種啟動(dòng)方式。
module top_tb
initial begin
run_test();
end
endmodule
此方式將run_test()中的參數(shù)去掉,并利用UVM_TESTNAME從命令行中獲得測(cè)試用例的名字,例如:
<sim command> … + UVM_TESTNAME=my_case0
<sim command> … + UVM_TESTNAME=my_case1
注:sim command為eda廠商提供的仿真命令,后面會(huì)有介紹。
UVM驗(yàn)證平臺(tái)啟動(dòng)和封裝
非基于uvm驗(yàn)證平臺(tái)仿真啟動(dòng)
注意:.f文件里分別是驗(yàn)證環(huán)境和設(shè)計(jì)的代碼文件列表。
sysnopsys: 編譯:vcs –f env_vcs.f –f design_vcs.f –verdi_compile_option –coverage_compile_option …… 仿真:./simv –verdi_rrun_option –coverage_run_option …… cadence: 編譯:irun –f env_irun.f –f design_irun.f –verdi_compile_option –coverage_compile_option …… 仿真:irun –verdi_irun_option –coverage_run_option …… 編譯:xrun –f env_xrun.f –f design_xrun.f –verdi_compile_option –coverage_compile_option …… 仿真:xrun –verdi_run_option –coverage_run_option ……
基于uvm驗(yàn)證平臺(tái)仿真啟動(dòng)
sysnopsys: 編譯:vcs –f env_vcs.vf –f design_vcs.f –verdi_compile_option –coverage_compile_option –ntb_opts uvm …… 仿真:./simv –verdi_rrun_option –coverage_run_option +UVM_TESTNAME=my_case0 …… cadence: 編譯:irun –f env_irun.vf –f design_irun.f –verdi_compile_option –coverage_compile_option –uvm +UVM_TESTNAME=my_case0 …… 仿真:irun –verdi_irun_option –coverage_run_option +UVM_TESTNAME=my_case0 …… 編譯:xrun –f env_xrun.vf –f design_xrun.f –verdi_compile_option –coverage_compile_option –uvm +UVM_TESTNAME=my_case0 …… 仿真:xrun –verdi_run_option –coverage_run_option +UVM_TESTNAME=my_case0 ……
注意:需要在env.vf中包含uvm的庫文件:
$UVM_HOME/src/uvm_macros.svh
$UVM_HOME/src/uvm.sv
$UVM_HOME/src/uvm_pkg.sv
$UVM_HOME/dpi/uvm_dpi.sv
+incdir+$UVM_HOME/src
對(duì)于vcs和irun/xrun還有一點(diǎn)需要注意:
對(duì)于vcs,使用uvm庫時(shí)需: include ”uvm_pkg.sv” 對(duì)于irun/xrun,使用uvm庫時(shí)需: import uvm_pkg::*
基于uvm驗(yàn)證平臺(tái)的封裝
為什么要對(duì)驗(yàn)證平臺(tái)封裝?
到目前為止,我們就可以利用基于uvm的驗(yàn)證平臺(tái)跑用例進(jìn)行驗(yàn)證了。
經(jīng)歷了漫長(zhǎng)痛苦的uvm環(huán)境開發(fā)之后,當(dāng)我們?cè)谧约邯?dú)立開發(fā)的uvm驗(yàn)證環(huán)境中,成功跑完第一條用例my_case0仿真用例的那一刻,發(fā)現(xiàn)之前的付出都是值得的,當(dāng)我們利用自己制造的人生第一條用例my_case0找到人生第一個(gè)設(shè)計(jì)bug的那一刻,發(fā)現(xiàn)人生已經(jīng)達(dá)到了巔峰。
但是作為一名優(yōu)秀的驗(yàn)證工程師,我們的成就不僅如此,因?yàn)槲覀兊哪康牟粌H僅是找到bug,而是快速高效的找到bug。
當(dāng)基本驗(yàn)證平臺(tái)可以使用,進(jìn)入初期驗(yàn)證階段之后,你會(huì)發(fā)現(xiàn),可能會(huì)有不同的驗(yàn)證工程師在此驗(yàn)證環(huán)境中開發(fā)新的功能,可能會(huì)有不同的設(shè)計(jì)人員在此驗(yàn)證環(huán)境中復(fù)現(xiàn)bug,也可能包括自己在內(nèi)的工程師需要在此驗(yàn)證環(huán)境中運(yùn)行各種各樣配置的用例,如果每次都需要自己去改變底層仿真命令或者告訴其它人怎么改底層仿真命令,你會(huì)發(fā)現(xiàn)整個(gè)人都不好了。
而且如果所有人都自己去手動(dòng)改環(huán)境底層代碼跑用例,到最后整個(gè)驗(yàn)證平臺(tái)也會(huì)變得非常雜亂,非常不好維護(hù),并且就實(shí)際情況,大部分設(shè)計(jì)工程師是不接受每次跑用例都需要自己手動(dòng)改代碼的。
為了解決這些問題,使環(huán)境變得更加整潔高效,維護(hù)簡(jiǎn)單,便于擴(kuò)展,我們將環(huán)境進(jìn)行封裝。
封裝的原則是什么?
對(duì)自己白盒:所有的底層運(yùn)行驗(yàn)證環(huán)境的編譯選項(xiàng)和仿真選項(xiàng)都要自己維護(hù)開發(fā);所有的新的開發(fā)需求需要自己來指定結(jié)構(gòu)和位置;所有的內(nèi)部uvm固有部件骨架都要自己維護(hù)開發(fā)。 對(duì)驗(yàn)證工程師灰盒:熟悉整體環(huán)境運(yùn)行原理;熟悉各編譯和仿真選項(xiàng)含義,驗(yàn)證過程中知道如何增減選項(xiàng);在指定的結(jié)構(gòu)和位置開發(fā)新的功能;不需要對(duì)此uvm環(huán)境固有骨架進(jìn)行全面掌握。 對(duì)設(shè)計(jì)工程師黑盒:不需要知道任何環(huán)境內(nèi)部構(gòu)造,只需要按照驗(yàn)證工程師提供的腳本命令運(yùn)行用例即可。
如何對(duì)驗(yàn)證平臺(tái)封裝?
環(huán)境采用腳本封裝,一般只需提供命令和接口選項(xiàng),腳本可以使用Makefile,python,perl,shell等。以python腳本封裝為例。
仿真命令:./runtest.py testname=my_case0 seed=123456 –dump –cov –funcov –debug –covmerge simpath=./sim seed : 提供仿真種子號(hào) -dump : 產(chǎn)生波形 -cov : 打開code coverage收集 -funcov : 打開function coverage收集 -debug : 仿真結(jié)束后自動(dòng)彈出波形 -covermerge : 仿真結(jié)束后自動(dòng)merge coverage數(shù)據(jù) simpath : 指定生成log的文件夾
上述接口選項(xiàng)最終都會(huì)呈現(xiàn)在底層編譯和仿真命令中,使用者可以根據(jù)需求打開關(guān)閉提供選項(xiàng),而不必需要知道環(huán)境內(nèi)部的細(xì)節(jié)。
如果有新的需求,開發(fā)完畢后提供對(duì)應(yīng)的接口即可,大大減少了使用的低效性。
封裝腳本的構(gòu)造
封裝腳本一般分為兩部分:?jiǎn)螚l用例運(yùn)行腳本 和 回歸用例運(yùn)行腳本。
單條用例運(yùn)行腳本:
用戶提供testcase list。 根據(jù)腳本提供用例名字在tclist找到對(duì)應(yīng)的用例屬性,包括uvm中提供的test名字(如my_case0),用例所在路徑等。 在sim下創(chuàng)建以用例名字+種子號(hào)命名的文件夾,后面生成的這條用例的所有相關(guān)信息,包括log和波形等都會(huì)存放在這里。 根據(jù)腳本提供選項(xiàng)進(jìn)行整個(gè)環(huán)境的編譯工作。 根據(jù)腳本提供選項(xiàng)進(jìn)行整個(gè)環(huán)境的仿真運(yùn)行。 用例運(yùn)行結(jié)束后,根據(jù)仿真產(chǎn)生的log,得到并打印出pass還是fail的信息,方便使用者進(jìn)行快速判斷。 使用者到對(duì)應(yīng)的sim/testcase_name_seed/下查找所有相關(guān)log和波形,進(jìn)行相關(guān)debug。 添加其它功能,例如coverage的merge,但是一般只有回歸才會(huì)涉及到merge的工作,所以這部分功能可以放到回歸腳本中。 回歸用例運(yùn)行腳本:
識(shí)別腳本提供的tclist中提供的回歸組名,將所有指定組的用例進(jìn)行仿真回歸。 內(nèi)部調(diào)用單條用例腳本,并將回歸用例腳本輸入?yún)?shù)全部轉(zhuǎn)換為單條用例腳本輸入?yún)?shù)需要的格式。 監(jiān)測(cè)每條用例的仿真結(jié)果,并累加計(jì)數(shù)得到總的tc數(shù),總pass tc數(shù),總fail tc數(shù),和總的沒有產(chǎn)生仿真log的tc數(shù)。 判斷是否所有用例運(yùn)行結(jié)束,并打印最終回歸報(bào)告。 如果打開coverge merge選項(xiàng),會(huì)自動(dòng)merge所有回歸用例的coverage數(shù)據(jù)。 回歸結(jié)束后在sim下生成pass,fail的tclist, 同樣每條用例的結(jié)果也都存在以用例名字+種子號(hào)命名的文件夾中,可以實(shí)時(shí)查看。 跑單條用例命令:./rt.py testname=my_case0 seed=123456 –dump –cov –funcov –debug –covmerge simpath=./sim
跑回歸用例命令:./rt.py –regress –rgr_group=my_regression -seedrand –dump –cov –funcov –covmerge simpath=./sim
UVM驗(yàn)證平臺(tái)的優(yōu)化
優(yōu)化目的是什么?
針對(duì)平臺(tái)結(jié)構(gòu)優(yōu)化:使環(huán)境結(jié)構(gòu)簡(jiǎn)潔,清晰明了,重用性好,移植性強(qiáng),拓展性高,讓新的用戶和開發(fā)者能夠快速切入。 針對(duì)平臺(tái)性能優(yōu)化:使環(huán)境運(yùn)行速度提升,提高驗(yàn)證效率
如何優(yōu)化驗(yàn)證平臺(tái)?
針對(duì)平臺(tái)結(jié)構(gòu)優(yōu)化:
加入readme,包含運(yùn)行方法等必要信息。 加入setup腳本,所需一切配置均在此一鍵完成。 環(huán)境中用到所有路徑宏均統(tǒng)一管理,方便更改和移植。 運(yùn)行中間文件統(tǒng)一管理,方便查看和刪除。 目錄結(jié)構(gòu)清晰,文件夾命名需簡(jiǎn)潔易懂。 刪除開發(fā)過程中的無效代碼和目錄。 加入必要信息打印開關(guān),方便讀取層次結(jié)構(gòu)。 撰寫平臺(tái)使用手冊(cè),進(jìn)行環(huán)境詳細(xì)說明。 針對(duì)平臺(tái)性能優(yōu)化:
檢查變量定義,減少存儲(chǔ)空間占用,如需要超大容量數(shù)組時(shí),需使用關(guān)聯(lián)數(shù)組。 檢查哪些任務(wù)可以并行執(zhí)行,改成fork_join*多線程機(jī)制。 檢查是否transaction約束過多,如過多速度會(huì)明顯變慢。 檢查dut文件列表,去掉不必要文件,減少環(huán)境編譯時(shí)間。 檢查是否有不必要打印,關(guān)掉以減少log輸出占用的時(shí)間。 檢查model,checker,driver中是否有過多占用時(shí)間的函數(shù),重新考慮是否有高效替代方案。 利用第三方工具得到平臺(tái)各個(gè)部件的時(shí)間占用分布,分析占用最多的幾個(gè)部件原因,尋找解決方案。
uvm驗(yàn)證平臺(tái)目錄組織結(jié)構(gòu)
verif/my_ut/env下文件:
agents/ :agent文件目錄,內(nèi)含一個(gè)或多個(gè)agent,agent包含driver、sequencer、monitor組件,agent從行為上可以理解為與dut交互的模塊或組織。 coverage/ :coverage文件目錄,內(nèi)含一個(gè)或多個(gè)模塊的覆蓋率文件,也就是covergroup。 checker/ :checker文件目錄,也就是reference model和scoreboard文件,不過有時(shí)候會(huì)將兩者融合成一個(gè)checker文件。 include/ :include文件,一些環(huán)境路徑的宏定義以及其他include文件存放在這里。 interface/ :interface文件目錄,內(nèi)含一個(gè)或多個(gè)接口文件。 script/ :script文件目錄,一些驗(yàn)證平臺(tái)封裝的腳本文件存放在這里。 setup/ :setup文件目錄,一些平臺(tái)初始化文件存放在這里,如環(huán)境變量的初始化和工具的配置。 tests/ :tests文件目錄,所有測(cè)試用例存放在這里。 readme :readme文件,對(duì)環(huán)境和使用的說明文件。 tb.v :tb文件,頂層testbench。 env_vcs.f :env_vcs.f文件,針對(duì)vcs工具的filelist文件,包含設(shè)計(jì)和驗(yàn)證平臺(tái)的文件列表。 env_xrun.f :env_xrun.f文件,針對(duì)xrun工具的filelist文件,包含設(shè)計(jì)和驗(yàn)證平臺(tái)的文件列表。 tc.list :tc.list文件,包含所有測(cè)試用例信息,可為測(cè)試用例設(shè)定分組,方便回歸測(cè)試。 my_env_cfg.sv :my_env_cfg.sv文件,整個(gè)驗(yàn)證平臺(tái)的配置文件,內(nèi)含靜態(tài)變量,可傳遞至平臺(tái)任意模塊,完成驗(yàn)證平臺(tái)的配置。
參考資料
Wenhui's Rotten Pen SystemVerilog chipverify