www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > > ZYNQ

關(guān)注、星標公眾,直達精彩內(nèi)容




什么是UVM?

  • VMM(Verification Methodology Manual),Synopsys在2006年推出的,VMM當中集成了寄存器解決方案RAL(Register Abstraction Layer)。

  • OVM(Open Verification Methodology),Cadence和Mentor在2008年推出的,它引進了factory機制,功能非常強大,但是沒有寄存器解決方案。

  • UVM(Universal Verification Methodology),即通用驗證方法學,其正式版本在2011年2月由Accellera推出的,UVM幾乎完全繼承了OVM,同時又采納了VMM中的寄存器解決方案。

什么是UVM驗證平臺?

  • UVM是基于System Verilog的一種驗證方法學,也可以看成是一個庫,提供一系列的接口,可以利用UVM搭建驗證平臺,用于驗證數(shù)字邏輯電路的正確性。

  • 注意,UVM本身并不是一個驗證平臺,他只是一個庫,而一個驗證平臺引入了UVM相關(guān)庫,稱為基于UVM的驗證平臺,或者簡稱為UVM驗證平臺。

  • 支持UVM的EDA廠商:Cadence、Synopsys、Mentor…

UVM基礎(chǔ)

一個基于SV的簡單驗證平臺

注:這里“基于SV”指平臺僅僅使用SystemVerilog語言搭建。

一個基于UVM的簡單驗證平臺

基于UVM驗證平臺原則

  • 類:UVM中幾乎所有的東西都是用類(class)來實現(xiàn)的,所以,搭建uvm平臺第一條原則,所有的組件都用類來完成。

  • 基于UVM類:當要實現(xiàn)一個功能時,首先應該想到的是從UVM的某個類派生出一個新的類來實現(xiàn)期望功能,所以,搭建uvm平臺第二條原則,所有的組件應該派生自uvm類。

UVM中兩大最重要基類

  • uvm_object:它是UVM最基本的類,幾乎所有的類都派生自uvm_object,它的拓展性是最好的,當然能力也是最差的。它主要構(gòu)成了環(huán)境的屬性(例如配置)和數(shù)據(jù)傳輸。

  • uvm_component:它派生自uvm_object ,卻擁有自己獨有的強大特性,它有兩大獨有特點,一是通過new形成樹形結(jié)構(gòu),二是自動執(zhí)行phase。它主要構(gòu)成了環(huán)境的層次。

UVM中常用類的繼承關(guān)系

什么是UVM Factory

所謂Factory就是工廠,是通過一個字符串(類名)創(chuàng)建一個此字符串所代表的的類的一個實例,并且能夠自動調(diào)用其phase執(zhí)行的機制,也就相當于加工工廠。理解上可能比較抽象,我們舉例來說明。

如下圖,一個汽車工廠好比我們的驗證平臺,支持寶馬和奔馳兩條生產(chǎn)線,兩位老板只需下令生產(chǎn)奔馳還是寶馬,工廠按照生產(chǎn)線自動加工,最終產(chǎn)出汽車,這里指令(奔馳/寶馬)就是上述中的字符串,生產(chǎn)線就是調(diào)用其phase執(zhí)行的機制,整個驗證平臺也就相當于工廠,這就是UVM Factory。

我們的工廠可以支持生產(chǎn)哪些“汽車”,是由驗證平臺決定的,如果向驗證平臺輸入不支持的指令,比如指定工廠生產(chǎn)“永久自行車”,會被視為錯誤指令。

如果就想生產(chǎn)“永久自行車”,我們需要添加永久自行車生產(chǎn)線:

然后我們就可以向工廠輸入指令,生產(chǎn)“永久自行車”了。

總結(jié) :

  • run_test()語句會通過傳入的字符串,創(chuàng)建一個類名為輸入字符串的類的實例,并且會自動調(diào)用此類的phase,但是前提是你已經(jīng)注冊了這個類。
  • 對于uvm_component類,注冊是通過uvm_component_utils宏來進行的。所有派生自uvm_component以及其派生類都應使用uvm_component_utils來注冊
  • 對于uvm_object類,注冊是通過uvm_object_utils宏來進行的。所有派生自uvm_object以及其派生類(除uvm_component外)都應使用uvm_object_utils來注冊
  • 由上述例子可以看出:run_test()是啟動整個驗證平臺的UVM庫函數(shù)。

UVM驗證平臺啟動執(zhí)行流程

搭建UVM驗證平臺

UVM驗證平臺基本組成

一個只有driver的測試平臺

driver代碼示例:

class my_driver extends uvm_driver;     //繼承uvm庫中的uvm_driver類
`uvm_component_utils(my_driver) //將my_driver類注冊到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); //將頂層實例化的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實例化時可直接使用
dut my_dut(.clk(clk),
.rst_n(rst_n),
.rxd(input_if.data)); //dut實例化
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"); //實例化my_driver類,實例的名字是uvm_test_top,并執(zhí)行類my_driver中的main_phase任務
end
endmodule

注意uvm_test_top是run_test產(chǎn)生的my_driver類的實例化對象名字,run_test(“my_driver”)可以簡單的看成:

my_driver uvm_test_top;
uvm_test_top.main_phase();

這里,任何被run_test()實例化的類的對象的名字都會被UVM平臺默認成uvm_test_top,這個被實例化的類也就是整個uvm平臺的頂層,而且只允許有一個頂層,即一個驗證平臺只調(diào)用一個run_test()。

top_tb頂層與uvm樹形結(jié)構(gòu)的交互:

  • uvm_config_db
function void my_driver:: build_phase(uvm_phase phase);  //將頂層實例化的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去實現(xiàn)的,既然uvm將其封裝為uvm_config_db,建議統(tǒng)一使用此方法,使用uvm提供的函數(shù)也是最好的避免出錯的辦法。

  • top_tb頂層與uvm樹形結(jié)構(gòu):
  • 如果將top_tb中的第3點去掉,請回答以下問題:
    • top_tb如何獲取dut內(nèi)部信號?答:通過top_tb.my_dut.xxx可以獲取。
    • top_tb如何獲取右側(cè)樹形結(jié)構(gòu)的內(nèi)部信息?答:通過top_tb.uvm_test_top.xxx是不可行的,因為run_test實例化了一個脫離了top_tb層次結(jié)構(gòu)的實例對象,建立了一個新的層次,所以不能通過top_tb.uvm_test_top.xxx直接訪問。所以針對這種情況,UVM引入了config_db的機制,也就是前面分別在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)之間的交互用虛線,是因為my_driver.interface.output_xxx不是直接給top_tb.interface.input_xxx傳值,而是在采用了config_db機制后,類似于兩邊在操作同一個指針地址,即改變my_driver.interface.output_xxx的值,就等于直接改變了top_tb.interface.input_xxx中變量的值。

另外:uvm_config_db將top_tb頂層與uvm樹形結(jié)構(gòu)打通,我們可以將top_tb與樹形結(jié)構(gòu)任意組件進行交互,而不僅限于driver和monitor。

樹形結(jié)構(gòu)構(gòu)造

目前,一個只含有driver驅(qū)動的UVM驗證平臺已經(jīng)形成,那么接下來要考慮樹形結(jié)構(gòu)的構(gòu)造,即添加新部件并使其層次化。

driver、monitor、agent和env:

注意:此時樹形結(jié)構(gòu)的頂層變成了my_env, 所以在top_tb中run_test(“my_driver”)應改成run_test(“my_env”),之前講過,run_test(“my_driver”)實例化之后對象的名字是uvm_test_top, 那么run_test(“my_env”)實例化之后頂層對象的名字是什么?

答案是仍然為uvm_test_top。

樹形結(jié)構(gòu)發(fā)生了層次改變,此時top_tb怎么和my_driver交互?

如下top_tb代碼:

module tob_tb
my_if input_if(clk, rstn); //聲明top_tb的interface,在dut實例化時可直接使用
dut my_dut(.clk(clk),
.rst_n(rst_n),
.rxd(input_if.data)); //dut實例化
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"); //實例化my_env類,實例的名字是uvm_test_top,并執(zhí)行類my_env中的phase
end
endmodule

加入checker:

注意:這里的checker是將reference model和scoreboard統(tǒng)一看成一個整體。reference model和scoreboard的定義和其他component的方式一樣,這里不再贅述。

  • 樹形結(jié)構(gòu)通信通道

加入sequencer:

這里sequencer是一個固定組件,sequencer主要將激勵承接給driver,my_transaction是一個數(shù)據(jù)包,也就是測試激勵,這里還要有一個sequence概念,sequence里存放一組trans,提供給sequencer,可見sequencer屬于component類,sequence和trans屬于object類。

關(guān)于sequencer、sequence和trans,還有這樣一個比喻,trans好比子彈,sequence好比彈夾,而sequencer是槍。

加入transaction:

  • transaction使用`uvm_object_utils注冊。
  • 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”); //等價于my_transaction tr; tr = new(“tr”);
req.data = 8’b1; //等價于tr.data = 8’b1;
vif.data <= req.data; //等價于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:

前面所示代碼中激勵都是在driver產(chǎn)生的,正常情況driver只是傳遞激勵,而不是產(chǎn)生激勵,所以要將激勵產(chǎn)生從driver中移除,從外界獲得激勵,那driver的激勵應該從哪里產(chǎn)生呢?

這就是sequencer要做的事情,也就是說sequencer要提供req(這里為my_transaction)給my_driver。

那么sequencer的transaction從哪里來?

`uvm_do(my_trans)實現(xiàn)了以下操作:

  • 創(chuàng)建一個my_transaction的實例my_trans
  • 將其隨機化
  • 最終將其傳送給my_sequencer(此宏不能做到自動連接my_sequencer并將my_trans直接傳送給my_sequencer)

sequence工作機制:

  • 對于1, my_sequencer等待。
  • 對于2, my_sequencer等待。
  • 對于3, my_sequencer將my_sequence中的my_trans發(fā)送給my_driver。

待解決問題

  • my_driver如何向my_sequencer發(fā)送transaction接收請求?
  • my_sequence如何向my_sequencer發(fā)送transaction?

my_driver向my_sequencer發(fā)送申請:

注意

  • seq_item_port是uvm_driver中的成員變量。
  • seq_item_export是uvm_sequencer中的成員變量。

my_sequence向my_sequencer發(fā)送my_transaction:

前面提到, my_sequence中的`uvm_do不能做到自動連接my_sequencer并將my_trans直接傳送給my_sequencer,所以需要額外啟動連接,以在當前頂層my_env中手工啟動為例(一般都是在頂層啟動):

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自動啟動機制default_sequence:

前面my_sequence是在my_env中手工啟動的,default_sequence可以自動啟動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)在:

注意:

  • 藍色部分: top_tb與樹形結(jié)構(gòu)的連接
  • 紫色部分: transaction在my_checker和monitor之間的通信通道
  • 紅色部分: transaction在my_sequence,my_sequencer和my_driver之間的通信通道
  • 單箭頭虛線部分: 實際沒有顯性直接通道,均通過上一層connect實現(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會在base_test中實例化,base_test取代env稱為頂層。
endclass
function void base_test::build_phase(uvm_phase phase);
//my_sequence需要在頂層啟動連接,因為base_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的啟動,注意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的一個子類, 是base_test的一個更具體的實現(xiàn),也就從這里形成了testcase的概念,這個testcase的名字就是my_case0。
  • my_case0沒有改變激勵產(chǎn)生的方式,即仍然是啟動了my_sequence,并利用my_sequence中`uvm_do(my_trans)來全隨機產(chǎn)生激勵。

加入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的一個子類, 是my_sequence的一個更具體的實現(xiàn)。
  • my_case0_sequence重載了my_sequence 的body()任務,并利用`uvm_do_with()來產(chǎn)生次數(shù)值都為8’hff的數(shù)據(jù)激勵。

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

注:

  • 這里制造了一個用例my_case0,此用例每個transaction產(chǎn)生8’hff數(shù)據(jù)的激勵。
  • 這里my_case0成為新的頂層。

將頂層base_test替換成my_case0:

module tob_tb
initial begin
// 將:
// run_test("base_test");
// 改成:
run_test("my_case0");
end
endmodule
  • run_test()作用:它會通過傳入的字符串,創(chuàng)建一個類名為輸入字符串的類的實例,并且會自動調(diào)用此類的main_phase。

  • run_test給我們最初的印象是構(gòu)建了一個脫離了top_tb的樹形結(jié)構(gòu),并完成了所有內(nèi)部需要交互部件的打通。

  • 現(xiàn)在我們需要改變一下思維,這里當調(diào)用run_test(“my_case0”)時,不再考慮樹形結(jié)構(gòu),我們用一個更抽象的概念來描述run_test的行為,那就是:它執(zhí)行一個用例my_case0,而且只有一個用例在執(zhí)行,這條用例每個transaction都在產(chǎn)生數(shù)值為8’hff的數(shù)據(jù)。我們平時所謂的跑各種各樣的用例,這些用例其實都是基于這個去構(gòu)造和命名的(在uvm平臺中)。

用例構(gòu)造

構(gòu)造另一個用例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()任務,并利用`uvm_do_with()來產(chǎn)生次數(shù)值都為8’haa的數(shù)據(jù)激勵。

  • 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

注意:這里制造了一個用例my_case1,此用例每個transaction產(chǎn)生8’haa數(shù)據(jù)的激勵;如果想要運行這個用例,那my_case1將成為新的頂層,也就是將top_tb中的 run_test(“my_case0”) 改為 run_test(“my_case1”)

UVM測試用例啟動

由于run_test在top_tb中只能調(diào)用一次,所以每次跑新的用例,都要手動改一下run_test()的參數(shù)名字,試想我們有10000個用例,如果都手動改,那肯定是不可行的,所以UVM提供了另外一種啟動方式。

module top_tb
initial begin
run_test();
end
endmodule

此方式將run_test()中的參數(shù)去掉,并利用UVM_TESTNAME從命令行中獲得測試用例的名字,例如:

<sim command> … + UVM_TESTNAME=my_case0
<sim command> … + UVM_TESTNAME=my_case1

注:sim command為eda廠商提供的仿真命令,后面會有介紹。

UVM驗證平臺啟動和封裝

非基于uvm驗證平臺仿真啟動

注意:.f文件里分別是驗證環(huán)境和設計的代碼文件列表。

  • 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驗證平臺仿真啟動

  • 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

對于vcs和irun/xrun還有一點需要注意:

  • 對于vcs,使用uvm庫時需: include ”uvm_pkg.sv”
  • 對于irun/xrun,使用uvm庫時需: import uvm_pkg::*

基于uvm驗證平臺的封裝

為什么要對驗證平臺封裝?

到目前為止,我們就可以利用基于uvm的驗證平臺跑用例進行驗證了。

經(jīng)歷了漫長痛苦的uvm環(huán)境開發(fā)之后,當我們在自己獨立開發(fā)的uvm驗證環(huán)境中,成功跑完第一條用例my_case0仿真用例的那一刻,發(fā)現(xiàn)之前的付出都是值得的,當我們利用自己制造的人生第一條用例my_case0找到人生第一個設計bug的那一刻,發(fā)現(xiàn)人生已經(jīng)達到了巔峰。

但是作為一名優(yōu)秀的驗證工程師,我們的成就不僅如此,因為我們的目的不僅僅是找到bug,而是快速高效的找到bug。

當基本驗證平臺可以使用,進入初期驗證階段之后,你會發(fā)現(xiàn),可能會有不同的驗證工程師在此驗證環(huán)境中開發(fā)新的功能,可能會有不同的設計人員在此驗證環(huán)境中復現(xiàn)bug,也可能包括自己在內(nèi)的工程師需要在此驗證環(huán)境中運行各種各樣配置的用例,如果每次都需要自己去改變底層仿真命令或者告訴其它人怎么改底層仿真命令,你會發(fā)現(xiàn)整個人都不好了。

而且如果所有人都自己去手動改環(huán)境底層代碼跑用例,到最后整個驗證平臺也會變得非常雜亂,非常不好維護,并且就實際情況,大部分設計工程師是不接受每次跑用例都需要自己手動改代碼的。

為了解決這些問題,使環(huán)境變得更加整潔高效,維護簡單,便于擴展,我們將環(huán)境進行封裝。

封裝的原則是什么?

  • 對自己白盒:所有的底層運行驗證環(huán)境的編譯選項和仿真選項都要自己維護開發(fā);所有的新的開發(fā)需求需要自己來指定結(jié)構(gòu)和位置;所有的內(nèi)部uvm固有部件骨架都要自己維護開發(fā)。
  • 對驗證工程師灰盒:熟悉整體環(huán)境運行原理;熟悉各編譯和仿真選項含義,驗證過程中知道如何增減選項;在指定的結(jié)構(gòu)和位置開發(fā)新的功能;不需要對此uvm環(huán)境固有骨架進行全面掌握。
  • 對設計工程師黑盒:不需要知道任何環(huán)境內(nèi)部構(gòu)造,只需要按照驗證工程師提供的腳本命令運行用例即可。

如何對驗證平臺封裝?

環(huán)境采用腳本封裝,一般只需提供命令和接口選項,腳本可以使用Makefile,python,perl,shell等。以python腳本封裝為例。

  • 仿真命令:./runtest.py testname=my_case0 seed=123456 –dump –cov –funcov –debug –covmerge simpath=./sim
    • seed : 提供仿真種子號
    • -dump : 產(chǎn)生波形
    • -cov : 打開code coverage收集
    • -funcov : 打開function coverage收集
    • -debug : 仿真結(jié)束后自動彈出波形
    • -covermerge : 仿真結(jié)束后自動merge coverage數(shù)據(jù)
    • simpath : 指定生成log的文件夾

上述接口選項最終都會呈現(xiàn)在底層編譯和仿真命令中,使用者可以根據(jù)需求打開關(guān)閉提供選項,而不必需要知道環(huán)境內(nèi)部的細節(jié)。

如果有新的需求,開發(fā)完畢后提供對應的接口即可,大大減少了使用的低效性。

封裝腳本的構(gòu)造

封裝腳本一般分為兩部分:單條用例運行腳本 和 回歸用例運行腳本。

  • 單條用例運行腳本:

    • 用戶提供testcase list。
    • 根據(jù)腳本提供用例名字在tclist找到對應的用例屬性,包括uvm中提供的test名字(如my_case0),用例所在路徑等。
    • 在sim下創(chuàng)建以用例名字+種子號命名的文件夾,后面生成的這條用例的所有相關(guān)信息,包括log和波形等都會存放在這里。
    • 根據(jù)腳本提供選項進行整個環(huán)境的編譯工作。
    • 根據(jù)腳本提供選項進行整個環(huán)境的仿真運行。
    • 用例運行結(jié)束后,根據(jù)仿真產(chǎn)生的log,得到并打印出pass還是fail的信息,方便使用者進行快速判斷。
    • 使用者到對應的sim/testcase_name_seed/下查找所有相關(guān)log和波形,進行相關(guān)debug。
    • 添加其它功能,例如coverage的merge,但是一般只有回歸才會涉及到merge的工作,所以這部分功能可以放到回歸腳本中。
  • 回歸用例運行腳本:

    • 識別腳本提供的tclist中提供的回歸組名,將所有指定組的用例進行仿真回歸。
    • 內(nèi)部調(diào)用單條用例腳本,并將回歸用例腳本輸入?yún)?shù)全部轉(zhuǎn)換為單條用例腳本輸入?yún)?shù)需要的格式。
    • 監(jiān)測每條用例的仿真結(jié)果,并累加計數(shù)得到總的tc數(shù),總pass tc數(shù),總fail tc數(shù),和總的沒有產(chǎn)生仿真log的tc數(shù)。
    • 判斷是否所有用例運行結(jié)束,并打印最終回歸報告。
    • 如果打開coverge merge選項,會自動merge所有回歸用例的coverage數(shù)據(jù)。
    • 回歸結(jié)束后在sim下生成pass,fail的tclist, 同樣每條用例的結(jié)果也都存在以用例名字+種子號命名的文件夾中,可以實時查看。
  • 跑單條用例命令:./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ōu)化

優(yōu)化目的是什么?

  • 針對平臺結(jié)構(gòu)優(yōu)化:使環(huán)境結(jié)構(gòu)簡潔,清晰明了,重用性好,移植性強,拓展性高,讓新的用戶和開發(fā)者能夠快速切入。
  • 針對平臺性能優(yōu)化:使環(huán)境運行速度提升,提高驗證效率

如何優(yōu)化驗證平臺?

  • 針對平臺結(jié)構(gòu)優(yōu)化:

    • 加入readme,包含運行方法等必要信息。
    • 加入setup腳本,所需一切配置均在此一鍵完成。
    • 環(huán)境中用到所有路徑宏均統(tǒng)一管理,方便更改和移植。
    • 運行中間文件統(tǒng)一管理,方便查看和刪除。
    • 目錄結(jié)構(gòu)清晰,文件夾命名需簡潔易懂。
    • 刪除開發(fā)過程中的無效代碼和目錄。
    • 加入必要信息打印開關(guān),方便讀取層次結(jié)構(gòu)。
    • 撰寫平臺使用手冊,進行環(huán)境詳細說明。
  • 針對平臺性能優(yōu)化:

    • 檢查變量定義,減少存儲空間占用,如需要超大容量數(shù)組時,需使用關(guān)聯(lián)數(shù)組。
    • 檢查哪些任務可以并行執(zhí)行,改成fork_join*多線程機制。
    • 檢查是否transaction約束過多,如過多速度會明顯變慢。
    • 檢查dut文件列表,去掉不必要文件,減少環(huán)境編譯時間。
    • 檢查是否有不必要打印,關(guān)掉以減少log輸出占用的時間。
    • 檢查model,checker,driver中是否有過多占用時間的函數(shù),重新考慮是否有高效替代方案。
    • 利用第三方工具得到平臺各個部件的時間占用分布,分析占用最多的幾個部件原因,尋找解決方案。

uvm驗證平臺目錄組織結(jié)構(gòu)

verif/my_ut/env下文件:

  • agents/ :agent文件目錄,內(nèi)含一個或多個agent,agent包含driver、sequencer、monitor組件,agent從行為上可以理解為與dut交互的模塊或組織。
  • coverage/ :coverage文件目錄,內(nèi)含一個或多個模塊的覆蓋率文件,也就是covergroup。
  • checker/ :checker文件目錄,也就是reference model和scoreboard文件,不過有時候會將兩者融合成一個checker文件。
  • include/ :include文件,一些環(huán)境路徑的宏定義以及其他include文件存放在這里。
  • interface/ :interface文件目錄,內(nèi)含一個或多個接口文件。
  • script/ :script文件目錄,一些驗證平臺封裝的腳本文件存放在這里。
  • setup/ :setup文件目錄,一些平臺初始化文件存放在這里,如環(huán)境變量的初始化和工具的配置。
  • tests/ :tests文件目錄,所有測試用例存放在這里。
  • readme :readme文件,對環(huán)境和使用的說明文件。
  • tb.v :tb文件,頂層testbench。
  • env_vcs.f :env_vcs.f文件,針對vcs工具的filelist文件,包含設計和驗證平臺的文件列表。
  • env_xrun.f :env_xrun.f文件,針對xrun工具的filelist文件,包含設計和驗證平臺的文件列表。
  • tc.list :tc.list文件,包含所有測試用例信息,可為測試用例設定分組,方便回歸測試。
  • my_env_cfg.sv :my_env_cfg.sv文件,整個驗證平臺的配置文件,內(nèi)含靜態(tài)變量,可傳遞至平臺任意模塊,完成驗證平臺的配置。

參考資料

  • Wenhui's Rotten Pen
  • SystemVerilog
  • chipverify

*免責聲明:本文由作者原創(chuàng)。文章內(nèi)容系作者個人觀點,轉(zhuǎn)載僅為了傳達一種不同的觀點,不代表對該觀點贊同或支持,如果有任何異議,歡迎聯(lián)系。

????????????????  END  ????????????????
關(guān)注微信公眾號『ZYNQ』,回復“加群”加入fpga/zynq技術(shù)交流群。后臺回復“m”,查看更多精彩內(nèi)容。

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉