uvm 形式验证_UVM基础
uvm_component與uvm_object
1.
幾乎所有的類都派生于uvm_object,包括uvm_component。
uvm_component有兩大特性是uvm_object所沒有的:
- 一是通過在new的時候指定parent參數(shù)來形成一種樹形的組織結(jié)構(gòu);
- 二是有phase的自動執(zhí)行特點。
下圖是常用的UVM繼承關(guān)系:
從圖中可以看出,從uvm_object派生出了兩個分支,所有的UVM樹的結(jié)點都是由uvm_component組成的,只有基于uvm_component派生的類才可能成為UVM樹的結(jié)點;最左邊分支的類或者直接派生自uvm_object的類,是不可能以結(jié)點的形式出現(xiàn)在UVM樹上的。
2.
常用的派生自uvm_object類的有:
- uvm_sequence_item:讀者定義的所有的transaction要從uvm_sequence_item派生。
- uvm_sequence:所有的sequence要從uvm_sequence派生。
- config:所有的config一般直接從uvm_object派生。config的主要功能就是規(guī)范驗證平臺的行為方式。之前我們已經(jīng)見識了使用config_db進行參數(shù)配置,這里的config其實指的是把所有的參數(shù)放在一個object中。
- uvm_reg_item:它派生自uvm_sequence_item,用于register model中。
- uvm_reg_map、uvm_mem、uvm_reg_field、uvm_reg、uvm_reg_file、uvm_reg_block等與寄存器相關(guān)的眾多的類都是派生自uvm_object,它們都是用于register model。
- uvm_phase:它派生自uvm_object,其主要作用為控制uvm_component的行為方式,使uvm_component平滑地在各個不同的phase之間依次運轉(zhuǎn)。
常用的派生自uvm_component類的有:
- uvm_driver:所有的driver都要派生自uvm_driver。driver的功能主要就是向sequencer索要sequence_item(transaction),并且將sequence_item里的信息驅(qū)動到DUT的端口上,這相當于完成了從transaction級別到DUT能夠接受的端口級別信息的轉(zhuǎn)換。
- uvm_monitor:所有的monitor都要派生自uvm_monitor。monitor做的事情與driver相反,driver向DUT的pin上發(fā)送數(shù)據(jù),而monitor則是從DUT的pin上接收數(shù)據(jù),并且把接收到的數(shù)據(jù)轉(zhuǎn)換成transaction級別的sequence_item,再把轉(zhuǎn)換后的數(shù)據(jù)發(fā)送給scoreboard,供其做比較。
- uvm_scoreboard:一般的scoreboard都要派生自uvm_scoreboard。scoreboard的功能就是比較reference model和monitor分別發(fā)送來的數(shù)據(jù),根據(jù)比較結(jié)果判斷DUT是否正確工作。
- reference model:UVM中并沒有針對reference model定義一個類。所以通常來說,reference model都是直接派生自uvm_component。reference model的作用就是模仿DUT,完成與DUT相同的功能。
- uvm_agent:所有的agent要派生自uvm_agent。與前面幾個比起來,uvm_agent的作用并不是那么明顯。它只是把driver和monitor封裝在一起,根據(jù)參數(shù)值來決定是只實例化monitor還是要同時實例化driver和monitor。
- uvm_env:所有的env(environment的縮寫)要派生自uvm_env。env將驗證平臺上用到的固定不變的component都封裝在一起。這樣,當要運行不同的測試用例時,只要在測試用例中實例化此env即可。
3.
在UVM中與uvm_object相關(guān)的factory宏有如下幾個:
- uvm_object_utils:它用于把一個直接或間接派生自uvm_object的類注冊到factory中。
- uvm_object_param_utils:它用于把一個直接或間接派生自uvm_object的參數(shù)化的類注冊到factory中。
- uvm_object_utils_begin:這個宏在第2章介紹my_transaction時出現(xiàn)過,當需要使用field_automation機制時,需要使用此宏。
- uvm_object_param_utils_begin:與uvm_object_utils_begin宏一樣,只是它適用于參數(shù)化的且其中某些成員變量要使用field_automation機制實現(xiàn)的類。
- uvm_object_utils_end:它總是與uvm_object_*_begin成對出現(xiàn),作為factory注冊的結(jié)束標志。
在UVM中與uvm_component相關(guān)的factory宏有如下幾個:
- uvm_component_utils:它用于把一個直接或間接派生自uvm_component的類注冊到factory中。
- uvm_component_param_utils:它用于把一個直接或間接派生自uvm_component的參數(shù)化的類注冊到factory中。
- uvm_component_utils_begin:這個宏與uvm_object_utils_begin相似,它用于同時需要使用factory機制和field_automation機制注冊的類。注意主要為了可以自動地使用config_db來得到某些變量的值,而不是在component中使用field_automation機制。
- uvm_component_param_utils_begin:與uvm_component_utils_begin宏一樣,只是它適用于參數(shù)化的,且其中某些成員變量要使用field_automation機制實現(xiàn)的類。
- uvm_component_utils_end:它總是與uvm_component_*_begin成對出現(xiàn),作為factory注冊的結(jié)束標志。
4.
雖說uvm_component是從uvm_object派生來的,但作為UVM的結(jié)點,這使得其失去了某些uvm_object的特性。
比如在uvm_object中有clone函數(shù),它用于分配一塊內(nèi)存空間,并把另一個實例復(fù)制到這塊新的內(nèi)存空間中。clone函數(shù)的使用方式如下:
class A extends uvm_object;… endclassclass my_env extends uvm_env;virtual function void build_phase(uvm_phase phase);A a1;A a2;a1 = new("a1");a1.data = 8'h9;$cast(a2, a1.clone());endfunction endclass上述的clone函數(shù)無法用于uvm_component中,因為一旦使用后,新clone出來的類,其parent參數(shù)無法指定。
雖然uvm_component無法使用clone函數(shù),但是可以使用copy函數(shù)。因為在調(diào)用copy之前,目標實例已經(jīng)完成了實例化,其parent參數(shù)已經(jīng)指定了。
UVM的樹形結(jié)構(gòu)
1.
一般在使用時,parent通常都是this。假設(shè)A和B均派生自uvm_component,在A中實例化一個B:
class B extends uvm_component;… endclass class A extends uvm_component;B b_inst;virtual function void build_phase(uvm_phase phase);b_inst = new("b_inst", this);endfunction endclass在b_inst實例化的時候,把this指針傳遞給了它,代表A是b_inst的parent。一種常見的觀點是,b_inst是A的成員變量,自然而然的,A就是b_inst的parent了。
當b_inst實例化的時候,指定一個parent的變量,同時在每一個component的內(nèi)部維護一個數(shù)組 m_children,當b_inst實例化時,就把b_inst的指針加入到A的m_children數(shù)組中。只有這樣才能讓A知道b_inst是自己的孩子,同時也才能讓b_inst知道A是自己的父母。當b_inst有了自己的孩子時,即在b_inst的m_children中加入孩子的指針。
2.
樹根應(yīng)該就是uvm_test。在測試用例里實例化env,在env里實例化scoreboard、reference model、agent、在agent里面實例化sequencer、driver和monitor。
UVM中真正的樹根是一個稱為uvm_top的東西,完整的UVM樹如下圖所示。
uvm_top是一個全局變量,它是uvm_root的一個實例。而uvm_root派生自uvm_component,所以uvm_top本質(zhì)上是一個uvm_component。uvm_test_top的parent是uvm_top,而uvm_top的parent則是null。
如果一個component在實例化時,其parent被設(shè)置為null,那么這個component的parent將會被系統(tǒng)設(shè)置為系統(tǒng)中唯一的uvm_root的實例uvm_top。如下圖所示:
可見,uvm_root的存在可以保證整個驗證平臺中只有一棵樹,所有結(jié)點都是uvm_top的子結(jié)點。
而在之前我們的驗證平臺中這個parent被設(shè)置為null的節(jié)點為my_casen,因此其被設(shè)置為系統(tǒng)中唯一的uvm_root的實例uvm_top,而我們可以在仿真編譯時通過UVM_TESTNAME來指定選擇哪個case來進行仿真測試。
另外這里my_casen派生自base_test,但并不因此增加UVM的層級,因此其parent為null。
在驗證平臺中,有時候需要得到uvm_top,由于uvm_top是一個全局變量,可以直接使用uvm_top。除此之外,還可以使用如下的方式得到它的指針:
uvm_root top; top=uvm_root::get();3.
UVM提供了一系列的接口函數(shù)用于訪問UVM樹中的結(jié)點。這其中最主要的是以下幾個:
- get_parent函數(shù),用于得到當前實例的parent。
- 與get_parent相對的就是get_child函數(shù)。與get_parent不同的是,get_child需要一個string類型的參數(shù)name,表示此child實例在實例化時指定的名字。因為一個component只有一個parent,所以get_parent不需要指定參數(shù);而可能有多個child,所以必須指定name參數(shù)。
- 為了得到所有的child,可以使用get_children函數(shù):
使用方法為:
uvm_component array[$]; my_comp.get_children(array); foreach(array[i])do_something(array[i]);//自定義函數(shù)- 除了一次性得到所有的child外,還可以使用get_first_child和get_next_child的組合依次得到所有的child:
這兩個函數(shù)的使用依賴于一個string類型的name。在這兩個函數(shù)的原型中,name是作為ref類型傳遞的
extern function int get_first_child (ref string name); extern function int get_next_child (ref string name);- get_num_children函數(shù)用于返回當前component所擁有的child的數(shù)量:
field automation機制
1.
最簡單的uvm_field系列宏有如下幾種:
`define uvm_field_int(ARG,FLAG) `define uvm_field_real(ARG,FLAG) `define uvm_field_enum(T,ARG,FLAG) `define uvm_field_object(ARG,FLAG) `define uvm_field_event(ARG,FLAG) `define uvm_field_string(ARG,FLAG)上述幾個宏分別用于要注冊的字段是整數(shù)、實數(shù)、枚舉類型、直接或間接派生自uvm_object的類型、事件及字符串類型。
與動態(tài)數(shù)組有關(guān)的uvm_field系列宏有:
`define uvm_field_array_enum(ARG,FLAG) `define uvm_field_array_int(ARG,FLAG) `define uvm_field_array_object(ARG,FLAG) `define uvm_field_array_string(ARG,FLAG)與靜態(tài)數(shù)組相關(guān)的uvm_field系列宏有:
`define uvm_field_sarray_int(ARG,FLAG) `define uvm_field_sarray_enum(ARG,FLAG) `define uvm_field_sarray_object(ARG,FLAG) `define uvm_field_sarray_string(ARG,FLAG)與隊列相關(guān)的uvm_field系列宏有:
`define uvm_field_queue_enum(ARG,FLAG) `define uvm_field_queue_int(ARG,FLAG) `define uvm_field_queue_object(ARG,FLAG) `define uvm_field_queue_string(ARG,FLAG)與聯(lián)合數(shù)組相關(guān)的uvm_field宏有:
`define uvm_field_aa_int_string(ARG, FLAG) `define uvm_field_aa_string_string(ARG, FLAG) `define uvm_field_aa_object_string(ARG, FLAG) `define uvm_field_aa_int_int(ARG, FLAG) `define uvm_field_aa_int_int_unsigned(ARG, FLAG) `define uvm_field_aa_int_integer(ARG, FLAG) `define uvm_field_aa_int_integer_unsigned(ARG, FLAG) `define uvm_field_aa_int_byte(ARG, FLAG) `define uvm_field_aa_int_byte_unsigned(ARG, FLAG) `define uvm_field_aa_int_shortint(ARG, FLAG) `define uvm_field_aa_int_shortint_unsigned(ARG, FLAG) `define uvm_field_aa_int_longint(ARG, FLAG) `define uvm_field_aa_int_longint_unsigned(ARG, FLAG) `define uvm_field_aa_string_int(ARG, FLAG) `define uvm_field_aa_object_int(ARG, FLAG)聯(lián)合數(shù)組有兩大識別標志,一是索引的類型,二是存儲數(shù)據(jù)的類型。在這一系列uvm_field宏中,出現(xiàn)的第一個類型是存儲數(shù)據(jù)類型,第二個類型是索引類型,如uvm_field_aa_int_string用于聲明那些存儲的數(shù)據(jù)是int,而其索引是string類型的聯(lián)合數(shù)組。
2.
field automation機制的常用函數(shù)有:
- copy函數(shù)用于實例的復(fù)制,其原型為:
如果要把某個A實例復(fù)制到B實例中,那么應(yīng)該使用B.copy(A)。在使用此函數(shù)前,B實例必須已經(jīng)使用new函數(shù)分配好了內(nèi)存空間。
- compare函數(shù)用于比較兩個實例是否一樣,其原型為:
如果要比較A與B是否一樣,可以使用A.compare(B),也可以使用B.compare(A)。當兩者一致時,返回1;否則為0。
- pack_bytes函數(shù)用于將所有的字段打包成byte流,其原型為:
- unpack_bytes函數(shù)用于將一個byte流逐一恢復(fù)到某個類的實例中,其原型為:
- pack函數(shù)用于將所有的字段打包成bit流,其原型為:
- pack函數(shù)的使用與pack_bytes類似。unpack函數(shù)用于將一個bit流逐一恢復(fù)到某個類的實例中,unpack的使用與unpack_bytes類似。
- pack_ints函數(shù)用于將所有的字段打包成int(4個byte,或者dword)流。
- unpack_ints函數(shù)用于將一個int流逐一恢復(fù)到某個類的實例中
- print函數(shù)用于打印所有的字段。
- clone函數(shù)
- 除了上述函數(shù)之外,field automation機制還提供自動得到使用config_db::set設(shè)置的參數(shù)的功能,之前講過,這里不再贅述。
3.
在post_randomize中計算CRC前先檢查一下crc_err字段,如果為1,那么直接使用隨機值,否則使用真實的CRC。
class my_transaction extends uvm_sequence_item;rand bit[47:0] dmac;rand bit[47:0] smac;rand bit[15:0] ether_type;rand byte pload[];rand bit[31:0] crc;rand bit crc_err;function void post_randomize();if(crc_err);//do nothingelsecrc = calc_crc;endfunction endclass在sequence中可以使用如下方式產(chǎn)生CRC錯誤的激勵:
`uvm_do_with(tr, {tr.crc_err == 1;})這里在后面的控制域中加入UVM_NOPACK的形式來實現(xiàn)對crc的控制。
`uvm_object_utils_begin(my_transaction) `uvm_field_int(dmac, UVM_ALL_ON) `uvm_field_int(smac, UVM_ALL_ON) `uvm_field_int(ether_type, UVM_ALL_ON) `uvm_field_array_int(pload, UVM_ALL_ON) `uvm_field_int(crc, UVM_ALL_ON) `uvm_field_int(crc_err, UVM_ALL_ON | UVM_NOPACK) `uvm_object_utils_endUVM的這些標志位本身其實是一個17bit的數(shù)字:
parameter UVM_ALL_ON = 'b000000101010101; parameter UVM_COPY = (1<<0); parameter UVM_NOCOPY = (1<<1); parameter UVM_COMPARE = (1<<2); parameter UVM_NOCOMPARE = (1<<3); parameter UVM_PRINT = (1<<4); parameter UVM_NOPRINT = (1<<5); parameter UVM_RECORD = (1<<6); parameter UVM_NORECORD = (1<<7); parameter UVM_PACK = (1<<8); parameter UVM_NOPACK = (1<<9);在這個17bit的數(shù)字中,bit0表示copy,bit1表示no_copy,bit2表示compare,bit3表示no_compare,bit4表示print,bit5表示no_print,bit6表示record,bit7表示no_record,bit8表示pack,bit9表示no_pack。剩余的7bit則另有它用,這里不做討論。UVM_ALL_ON的值是’b000000101010101,表示打開copy、compare、print、record、pack功能。UVM_ALL_ON|UVM_NOPACK的結(jié)果就是‘b000001101010101。這樣UVM 在執(zhí)行pack操作時,首先檢查bit9,發(fā)現(xiàn)其為1,直接忽略bit8所代表的UVM_PACK。
4.
class my_transaction extends uvm_sequence_item;rand bit[47:0] smac;rand bit[47:0] dmac;rand bit[31:0] vlan[];rand bit[15:0] eth_type;rand byte pload[];rand bit[31:0] crc;`uvm_object_utils_begin(my_transaction)`uvm_field_int(smac, UVM_ALL_ON)`uvm_field_int(dmac, UVM_ALL_ON)`uvm_field_array_int(vlan, UVM_ALL_ON)`uvm_field_int(eth_type, UVM_ALL_ON)`uvm_field_array_int(pload, UVM_ALL_ON)`uvm_object_utils_end endclass在隨機化普通以太網(wǎng)幀時,可以使用如下的方式:
my_transaction tr; tr = new(); assert(tr.randomize() with {vlan.size() == 0;});協(xié)議中規(guī)定vlan的字段固定為4個字節(jié),所以在隨機化VLAN幀時,可以使用如下的方式:
my_transaction tr; tr = new(); assert(tr.randomize() with {vlan.size() == 1;});協(xié)議中規(guī)定vlan的4個字節(jié)各自有其不同的含義,這4個字節(jié)分別代表4個不同的字段。如果使用上面的方式,問題雖然解決了,但是這4個字段的含義不太明確。 一個可行的解決方案是:
class my_transaction extends uvm_sequence_item;rand bit[47:0] dmac;rand bit[47:0] smac;rand bit[15:0] vlan_info1;rand bit[2:0] vlan_info2;rand bit vlan_info3;rand bit[11:0] vlan_info4;rand bit[15:0] ether_type;rand byte pload[];rand bit[31:0] crc;rand bit is_vlan;`uvm_object_utils_begin(my_transaction)`uvm_field_int(dmac, UVM_ALL_ON)`uvm_field_int(smac, UVM_ALL_ON)if(is_vlan)begin`uvm_field_int(vlan_info1, UVM_ALL_ON)`uvm_field_int(vlan_info2, UVM_ALL_ON)`uvm_field_int(vlan_info3, UVM_ALL_ON)`uvm_field_int(vlan_info4, UVM_ALL_ON)end`uvm_field_int(ether_type, UVM_ALL_ON)`uvm_field_array_int(pload, UVM_ALL_ON)`uvm_field_int(crc, UVM_ALL_ON | UVM_NOPACK)`uvm_field_int(is_vlan, UVM_ALL_ON | UVM_NOPACK)`uvm_object_utils_end endclass在隨機化普通以太網(wǎng)幀時,可以使用如下的方式:
my_transaction tr; tr = new(); assert(tr.randomize() with {is_vlan == 0;});在隨機化VLAN幀時,可以使用如下的方式:
my_transaction tr; tr = new(); assert(tr.randomize() with {is_vlan == 1;});使用這種方式的VLAN幀,在執(zhí)行print操作時,4個字段的信息將會非常明顯;在調(diào)用compare函數(shù)時,如果兩個transaction不同,將會更加明確地指明是哪個字段不一樣。
UVM中打印信息的控制
1.
UVM通過冗余度級別的設(shè)置提高了仿真日志的可讀性。在打印信息之前,UVM會比較要顯示信息的冗余度級別與默認的冗余度閾值,如果小于等于閾值,就會顯示,否則不會顯示。默認的冗余度閾值是UVM_MEDIUM,所有低于等于UVM_MEDIUM(如UVM_LOW)的信息都會被打印出來。
可以通過get_report_verbosity_level函數(shù)得到某個component的冗余度閾值:
virtual function void connect_phase(uvm_phase phase);$display("env.i_agt.drv's verbosity level is %0d", env.i_agt.drv.get_report_verbosity_level()); endfunction這個函數(shù)得到的是一個整數(shù),它代表的含義如下所示:
typedef enum {UVM_NONE = 0,UVM_LOW = 100,UVM_MEDIUM = 200,UVM_HIGH = 300,UVM_FULL = 400,UVM_DEBUG = 500 } uvm_verbosity;UVM提供set_report_verbosity_level函數(shù)來設(shè)置某個特定component的默認冗余度閾值。在base_test中將driver的冗余度閾值設(shè)置為UVM_HIGH(UVM_LOW、UVM_MEDIUM、UVM_HIGH的信息都會被打印)代碼為:
virtual function void connect_phase(uvm_phase phase);env.i_agt.drv.set_report_verbosity_level(UVM_HIGH); endfunction把env.i_agt及其下所有的component的冗余度閾值設(shè)置為UVM_HIGH的代碼為:
env.i_agt.set_report_verbosity_level_hier(UVM_HIGH);set_report_verbosity_level會對某個component內(nèi)所有的uvm_info宏顯示的信息產(chǎn)生影響。如果這些宏在調(diào)用時使用了不同的ID:
`uvm_info("ID1", "ID1 INFO", UVM_HIGH) `uvm_info("ID2", "ID2 INFO", UVM_HIGH)那么可以使用set_report_id_verbosity函數(shù)來區(qū)分不同的ID的冗余度閾值:
env.i_agt.drv.set_report_id_verbosity("ID1", UVM_HIGH);除了在代碼中設(shè)置外,UVM支持在命令行中設(shè)置冗余度閾值:
<sim command> +UVM_VERBOSITY=UVM_HIGH 或者: <sim command> +UVM_VERBOSITY=HIGH上述的命令行參數(shù)會把整個驗證平臺的冗余度閾值設(shè)置為UVM_HIGH。它幾乎相當于是在base_test中調(diào)用 set_report_verbosity_level_hier函數(shù),把base_test及以下所有component的冗余度級別設(shè)置為UVM_HIGH:
set_report_verbosity_level_hier(UVM_HIGH)綜上,通過設(shè)置不同env的冗余度級別,可以更好地控制整個芯片驗證環(huán)境輸出信息的質(zhì)量。
2.
UVM默認有四種信息嚴重性:UVM_INFO、UVM_WARNING、UVM_ERROR、UVM_FATAL。這四種嚴重性可以互相重載。如果要把driver中所有的UVM_WARNING顯示為UVM_ERROR,可以使用如下的函數(shù):
virtual function void connect_phase(uvm_phase phase);env.i_agt.drv.set_report_severity_override(UVM_WARNING, UVM_ERROR);//env.i_agt.drv.set_report_severity_id_override(UVM_WARNING, "my_driver", UVM_ERROR); endfunction重載嚴重性可以只針對某個component內(nèi)的某個特定的ID起作用:
env.i_agt.drv.set_report_severity_id_override(UVM_WARNING, "my_driver", UVM_ERROR);3.
當uvm_fatal出現(xiàn)時,表示出現(xiàn)了致命錯誤,仿真會馬上停止。UVM同樣支持UVM_ERROR達到一定數(shù)量時結(jié)束仿真。實現(xiàn)這個功能的是set_report_max_quit_count函數(shù),其調(diào)用方式為:
function void base_test::build_phase(uvm_phase phase);super.build_phase(phase);env = my_env::type_id::create("env", this);set_report_max_quit_count(5); endfunction與set_max_quit_count相對應(yīng)的是get_max_quit_count,可以用于查詢當前的退出閾值。
除了在代碼中使用set_max_quit_count設(shè)置外,還可以在命令行中設(shè)置退出閾值:
<sim command> +UVM_MAX_QUIT_COUNT=6,NO在上一節(jié)中,當UVM_ERROR達到一定數(shù)量時,可以自動退出仿真。在計數(shù)當中,是不包含UVM_WARNING的。可以通過設(shè)置set_report_severity_action函數(shù)來把UVM_WARNING加入計數(shù)目標:
virtual function void connect_phase(uvm_phase phase);set_report_max_quit_count(5);env.i_agt.drv.set_report_severity_action(UVM_WARNING, UVM_DISPLAY|UVM_COUNT); endfunction類似的遞歸調(diào)用方式:
env.i_agt.set_report_severity_action_hier(UVM_WARNING, UVM_DISPLAY| UVM_COUNT);上述代碼把env.i_agt及其下所有結(jié)點的UVM_WARNING加入到計數(shù)目標中。
除了針對嚴重性進行計數(shù)外,還可以對某個特定的ID進行計數(shù):
env.i_agt.drv.set_report_id_action("my_drv", UVM_DISPLAY| UVM_COUNT);4.
在程序調(diào)試時,斷點功能是非常有用的一個功能。在程序運行時,預(yù)先在某語句處設(shè)置一斷點。當程序執(zhí)行到此處時,停止仿真,進入交互模式,從而進行調(diào)試。
當env.i_agt.drv中出現(xiàn)UVM_WARNING時,立即停止仿真,進入交互模式。
virtual function void connect_phase(uvm_phase phase);env.i_agt.drv.set_report_severity_action(UVM_WARNING, UVM_DISPLAY| UVM_STOP); endfunction5.
UVM提供將特定信息輸出到特定日志文件的功能:
將env.i_agt.drv的UVM_INFO輸出到info.log,UVM_WARNING輸出到warning.log,UVM_ERROR輸出到error.log,UVM_FATAL輸出到fatal.log。這里用到了set_report_severity_file函數(shù)。
UVM_FILE info_log; UVM_FILE warning_log; UVM_FILE error_log; UVM_FILE fatal_log; virtual function void connect_phase(uvm_phase phase);info_log = $fopen("info.log", "w");warning_log = $fopen("warning.log", "w");error_log = $fopen("error.log", "w");fatal_log = $fopen("fatal.log", "w");env.i_agt.drv.set_report_severity_file(UVM_INFO, info_log);env.i_agt.drv.set_report_severity_file(UVM_WARNING, warning_log);env.i_agt.drv.set_report_severity_file(UVM_ERROR, error_log);env.i_agt.drv.set_report_severity_file(UVM_FATAL, fatal_log);env.i_agt.drv.set_report_severity_action(UVM_INFO, UVM_DISPLAY| UVM_LOG);env.i_agt.drv.set_report_severity_action(UVM_WARNING, UVM_DISPLAY|UVM_LOG);env.i_agt.drv.set_report_severity_action(UVM_ERROR, UVM_DISPLAY| UVM_COUNT | UVM_LOG);env.i_agt.drv.set_report_severity_action(UVM_FATAL, UVM_DISPLAY|UVM_EXIT | UVM_LOG); endfunction這個函數(shù)同樣有遞歸調(diào)用方式:
env.i_agt.set_report_severity_file_hier(UVM_INFO, info_log); env.i_agt.set_report_severity_file_hier(UVM_WARNING, warning_log); env.i_agt.set_report_severity_file_hier(UVM_ERROR, error_log); env.i_agt.set_report_severity_file_hier(UVM_FATAL, fatal_log); env.i_agt.set_report_severity_action_hier(UVM_INFO, UVM_DISPLAY| UVM_LOG); env.i_agt.set_report_severity_action_hier(UVM_WARNING, UVM_DISPLAY| UVM_LOG); env.i_agt.set_report_severity_action_hier(UVM_ERROR, UVM_DISPLAY| UVM_COUNT |UVM_LOG); env.i_agt.set_report_severity_action_hier(UVM_FATAL, UVM_DISPLAY| UVM_EXIT | UVM_LOG);當然除了根據(jù)嚴重性設(shè)置不同的日志文件外,UVM中還可以根據(jù)不同的ID來設(shè)置不同的日志文件:
UVM_FILE driver_log; UVM_FILE drv_log; virtual function void connect_phase(uvm_phase phase);driver_log = $fopen("driver.log", "w");drv_log = $fopen("drv.log", "w");env.i_agt.drv.set_report_id_file("my_driver", driver_log);env.i_agt.drv.set_report_id_file("my_drv", drv_log);env.i_agt.drv.set_report_id_action("my_driver", UVM_DISPLAY| UVM_LOG);env.i_agt.drv.set_report_id_action("my_drv", UVM_DISPLAY| UVM_LOG); endfunction virtual function void final_phase(uvm_phase phase);$fclose(driver_log);$fclose(drv_log); endfunction當然也有遞歸調(diào)用方式:
env.i_agt.set_report_id_file_hier("my_driver", driver_log); env.i_agt.set_report_id_file_hier("my_drv", drv_log); env.i_agt.set_report_id_action_hier("my_driver", UVM_DISPLAY| UVM_LOG); env.i_agt.set_report_id_action_hier("my_drv", UVM_DISPLAY| UVM_LOG);UVM還可以根據(jù)嚴重性和ID的組合來設(shè)置不同的日志文件:
UVM_FILE driver_log; UVM_FILE drv_log; virtual function void connect_phase(uvm_phase phase);driver_log = $fopen("driver.log", "w");drv_log = $fopen("drv.log", "w");env.i_agt.drv.set_report_severity_id_file(UVM_WARNING, "my_driver",driver_log);env.i_agt.drv.set_report_severity_id_file(UVM_INFO, "my_drv", drv_log);env.i_agt.drv.set_report_id_action("my_driver", UVM_DISPLAY| UVM_LOG);env.i_agt.drv.set_report_id_action("my_drv", UVM_DISPLAY| UVM_LOG); endfunction6.
UVM共定義了如下幾種行為:
typedef enum {UVM_NO_ACTION = 'b000000,UVM_DISPLAY = 'b000001,UVM_LOG = 'b000010,UVM_COUNT = 'b000100,UVM_EXIT = 'b001000,UVM_CALL_HOOK = 'b010000,UVM_STOP = 'b100000 } uvm_action_type;與field automation機制中定義UVM_ALL_ON類似,這里也把UVM_DISPLAY等定義為一個整數(shù)。不同的行為有不同的位偏移,所以不同的行為可以使用“或”的方式組合在一起:
UVM_DISPLAY| UVM_COUNT | UVM_LOG其中
- UVM_NO_ACTION是不做任何操作;
- UVM_DISPLAY是輸出到標準輸出上;
- UVM_LOG是輸出到日志文件中,它能工作的前提是設(shè)置好了日志文件;
- UVM_COUNT是作為計數(shù)目標;
- UVM_EXIT是直接退出仿真;
- UVM_CALL_HOOK是調(diào)用一個回調(diào)函數(shù);
- UVM_STOP是停止仿真,進入命令行交互模式。
默認情況下,UVM作如下的設(shè)置:
set_severity_action(UVM_INFO, UVM_DISPLAY); set_severity_action(UVM_WARNING, UVM_DISPLAY); set_severity_action(UVM_ERROR, UVM_DISPLAY | UVM_COUNT); set_severity_action(UVM_FATAL, UVM_DISPLAY | UVM_EXIT);從UVM_INFO到UVM_FATAL,都會輸出到標準輸出中;UVM_ERROR會作為仿真退出計數(shù)器的計數(shù)目標;出現(xiàn) UVM_FATAL時會自動退出仿真。之前是通過設(shè)置信息的冗余級別來打開或關(guān)閉信息的現(xiàn)實,其實也可以通過設(shè)置為UVM_NO_ACTION來實現(xiàn)。
virtual function void connect_phase(uvm_phase phase);env.i_agt.drv.set_report_severity_action(UVM_INFO, UVM_NO_ACTION); endfunction無論原本的冗余度是什么,經(jīng)過上述設(shè)置后,env.i_agt.drv的所有的uvm_info信息都不會輸出。
config_db機制
1.
一個component(如my_driver)內(nèi)通過get_full_name()函數(shù)可以得到此component的路徑:
function void my_driver::build_phase();super.build_phase(phase);$display("%s", get_full_name()); endfunction上述代碼如果是在層次結(jié)構(gòu)中的my_driver中,那么打印出來的值是uvm_test_top.env.i_agt.drv。
為了方便,上圖使用了new函數(shù)而不是factory式的create方式來創(chuàng)建實例。在這幅圖中,uvm_test_top實例化時的名字是uvm_test_top,這個名字是由UVM在run_test時自動指定的。uvm_top的名字是__top__,但是在顯示路徑的時候,并不會顯示出這個名字,而只顯示從uvm_test_top開始的路徑。
2.
config_db機制用于在UVM驗證平臺間傳遞參數(shù)。它們通常都是成對出現(xiàn)的。set函數(shù)是寄信,而get函數(shù)是收信。如在某個測試用例的build_phase中可以使用如下方式寄信:
uvm_config_db#(int)::set(this, "env.i_agt.drv", "pre_num", 100);其中第一個和第二個參數(shù)聯(lián)合起來組成目標路徑,與此路徑符合的目標才能收信。第一個參數(shù)必須是一個uvm_component實例的指針,第二個參數(shù)是相對此實例的路徑。第三個參數(shù)表示一個記號,用以說明這個值是傳給目標中的哪個成員的,第四個參數(shù)是要設(shè)置的值。
在driver中的build_phase使用如下方式收信:
uvm_config_db#(int)::get(this, "", "pre_num", pre_num);get函數(shù)中的第一個參數(shù)和第二個參數(shù)聯(lián)合起來組成路徑。第一個參數(shù)也必須是一個uvm_component實例的指針,第二個參數(shù)是相對此實例的路徑。一般的,如果第一個參數(shù)被設(shè)置為this,那么第二個參數(shù)可以是一個空的字符串。第三個參數(shù)就是set函數(shù)中的第三個參數(shù),這兩個參數(shù)必須嚴格匹配,第四個參數(shù)則是要設(shè)置的變量。
在之前的例子中,在top_tb中通過config_db機制的set函數(shù)設(shè)置virtual interface時,set函數(shù)的第一個參數(shù)為null。在這種情況下,UVM會自動把第一個參數(shù)替換為uvm_root::get(),即uvm_top。換句話說,以下兩種寫法是完全等價的:
initial beginuvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.i_agt.drv", "vif", input_if); end initial beginuvm_config_db#(virtual my_if)::set(uvm_root::get(), "uvm_test_top.env.i_ag t. drv", "vif", input_if); end既然set函數(shù)的第一個和第二個參數(shù)聯(lián)合起來組成路徑,那么在某個測試用例的build_phase中可以通過如下的方式設(shè)置env.i_agt.drv中pre_num_max的值:
uvm_config_db#(int)::set(this.env, "i_agt.drv", "pre_num_max", 100);因為前兩個參數(shù)組合而成的路徑和之前的寫法一致。
set函數(shù)的參數(shù)可以使用這種靈活的方式設(shè)置,同樣的,get函數(shù)的參數(shù)也可以。在driver的build_phase中:
uvm_config_db#(int)::get(this.parent, "drv", "pre_num_max", pre_num_max); 或者: uvm_config_db#(int)::get(null, "uvm_test_top.env.i_agt.drv", "pre_num_max", p re_num_max);整個過程可以形象地這么去理解:
張三給李四寄了一封信,信上寫了李四的名字,這樣李四可以收到信。但是呢,由于保密的需要,張三只是在信上寫了“四”這一個字,只要張三跟李四事先約定好了,那么李四一看到上面寫著“四”的信就會收下來。
uvm_config_db#(int)::set(this, "env.i_agt.drv", "p_num", 100); uvm_config_db#(int)::get(this, "", "p_num", pre_num);3.
假設(shè)在my_driver中有成員變量pre_num,其使用uvm_field_int實現(xiàn)field automation機制:
int pre_num; `uvm_component_utils_begin(my_driver)`uvm_field_int(pre_num, UVM_ALL_ON) `uvm_component_utils_end function new(string name = "my_driver", uvm_component parent = null);super.new(name, parent);pre_num = 3; endfunction virtual function void build_phase(uvm_phase phase);`uvm_info("my_driver", $sformatf("before super.build_phase, the pre_num is %0d", pre_num),super.build_phase(phase);`uvm_info("my_driver", $sformatf("after super.build_phase, the pre_num is %0d", pre_num),if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))`uvm_fatal("my_driver", "virtual interface must be set for vif!!!") endfunction只要使用uvm_field_int注冊,并且在build_phase中調(diào)用super.build_phase(),就可以省略在build_phase中的如下get語句:
uvm_config_db#(int)::get(this, "", "pre_num", pre_num);這里的關(guān)鍵是build_phase中的super.build_phase語句,當執(zhí)行到driver的super.build_phase時,會自動執(zhí)行g(shù)et語句。這種做法的前提是:
- 第一,my_driver必須使用uvm_component_utils宏注冊;
- 第二,pre_num必須使用uvm_field_int宏注冊;
- 第三,在調(diào)用set函數(shù)的時候,set函數(shù)的第三個參數(shù)必須與要get函數(shù)中變量的名字相一致,即必須是pre_num。
所以上節(jié)中,雖然說這兩個參數(shù)可以不一致,但是最好的情況下還是一致。李四的信就是給李四的,不要打什么暗語,用一個“四”來代替李四。
4.
在前面的所有例子中,都是設(shè)置一次,獲取一次。但是假如設(shè)置多次,而只獲取一次,即跨層次的多重設(shè)置時,UVM采用如下機制決定最終收到信息的內(nèi)容,即:先看發(fā)信人,哪個發(fā)信人最權(quán)威就聽誰的,當同一個發(fā)信人先后發(fā)了兩封信時,那么最近收到的一封權(quán)威高,也就是發(fā)信人的優(yōu)先級最高,而時間的優(yōu)先級低。
假如uvm_test_top和env中都對driver的pre_num的值進行了設(shè)置,在uvm_test_top中的設(shè)置語句如下:
function void my_case0::build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db#(int)::set(this,"env.i_agt.drv","pre_num",999);`uvm_info("my_case0", "in my_case0, env.i_agt.drv.pre_num is set to 999",UVM_LOW)在env的設(shè)置語句如下:
virtual function void build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db#(int)::set(this,"i_agt.drv","pre_num",100);`uvm_info("my_env", "in my_env, env.i_agt.drv.pre_num is set to 100",UVM_LOW) endfunction那么driver中獲取到的值是100還是999呢?答案是999。UVM規(guī)定層次越高,那么它的優(yōu)先級越高。這里的層次指的是在UVM樹中的位置,越靠近根結(jié)點uvm_top,則認為其層次越高。uvm_test_top的層次是高于env的,所以uvm_test_top中的set函數(shù)的優(yōu)先級高。
上述結(jié)論在set函數(shù)的第一個參數(shù)為this時是成立的,但是假如set函數(shù)的第一個參數(shù)不是this會如何呢?假設(shè)uvm_test_top的set語句是:
function void my_case0::build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db#(int)::set(uvm_root::get(),"uvm_test_top.env.i_agt.drv","pre_num",999);`uvm_info("my_case0", "in my_case0, env.i_agt.drv.pre_num is set to 999", UVM_LOW)而env的set語句是:
virtual function void build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db#(int)::set(uvm_root::get(),"uvm_test_top.env.i_agt.drv","pre_num",100);`uvm_info("my_env", "in my_env, env.i_agt.drv.pre_num is set to 100",UVM_LOW) endfunction這種情況下,driver得到的pre_num的值是100。由于set函數(shù)的第一個參數(shù)是uvm_root::get(),所以寄信人變成了uvm_top。在這種情況下,只能比較寄信的時間。UVM的build_phase是自上而下執(zhí)行的,my_case0的build_phase先于my_env的build_phase執(zhí)行。所以my_env對pre_num的設(shè)置在后,其設(shè)置成為最終的設(shè)置。
假如uvm_test_top中set函數(shù)的第一個參數(shù)是this,而env中set函數(shù)的第一個參數(shù)是uvm_root::get(),那么driver得到的pre_num的值也是100。這是因為env中set函數(shù)的寄信人變成了uvm_top,在UVM樹中具有最高的優(yōu)先級。
因此,無論如何,在調(diào)用set函數(shù)時其第一個參數(shù)應(yīng)該盡量使用this。在無法得到this指針的情況下(如在top_tb中),使用null或者uvm_root::get()。
5.
關(guān)于同一層次的多重設(shè)置:
pre_num在99%的測試用例中的值都是7,只有在1%的測試用例中才會是其他值。
可以這么實現(xiàn):
classs base_test extends uvm_test;function void build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db#(int)::set(this, "env.i_agt.drv", pre_num_max, 7);endfunction endclass class case1 extends base_test;function void build_phase(uvm_phase phase);super.build_phase(phase);endfunction endclass … class case99 extends base_test;function void build_phase(uvm_phase phase);super.build_phase(phase);endfunction endclass但是對于第100個測試用例,則依然需要這么寫:
class case100 extends base_test;function void build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db#(int)::set(this, "env.i_agt.drv", pre_num_max, 100);endfunction endclasscase100的build_phase相當于如下所示連續(xù)設(shè)置了兩次:
uvm_config_db#(int)::set(this, "env.i_agt.drv", "pre_num", 7); uvm_config_db#(int)::set(this, "env.i_agt.drv", "pre_num", 100);按照時間優(yōu)先的原則,后面config_db::set的值將最終被driver得到。
6.
可以使用同配符設(shè)置config_db。
initial beginuvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.i_agt*", "vif", input_if); end盡可能不要使用通配符*,盡量寫清楚,避免給同事添麻煩。
7.
用于connect_phase的check_config_usage可以顯示出截止到此函數(shù)調(diào)用時有哪些參數(shù)是被設(shè)置過但是卻沒有被獲取過,這可以用來檢查使用config_db時的第二個參數(shù),即路徑字符串出錯的情況。
virtual function void connect_phase(uvm_phase phase);super.connect_phase(phase);check_config_usage(); endfunction使用例子:
function void my_case0::build_phase(uvm_phase phase);uvm_config_db#(uvm_object_wrapper)::set(this,"env.i_agt.sqr.main_phase","default_sequence",case0_sequence::type_id::get());uvm_config_db#(int)::set(this,"env.i_atg.drv","pre_num",999);uvm_config_db#(int)::set(this,"env.mdl","rm_value",10); endfunction仿真打印結(jié)果如下:
# UVM_INFO @ 0: uvm_test_top [CFGNRD] ::: The following resources have at least one write and no reads # default_sequence [/^uvm_test_top.env.i_agt.sqr.main_phase$/] : (class uvm_pkg::uvm_object_wrapper) # - # -------- # uvm_test_top reads: 0 @ 0 writes: 1 @ 0 ## pre_num [/^uvm_test_top.env.i_atg.drv$/] : (int) 999 # - # -------- # uvm_test_top reads: 0 @ 0 writes: 1 @ 0 #上述結(jié)果顯示有兩條設(shè)置信息分別被寫過(set)1次,但是一次也沒有被讀取(get)。其中pre_num未被讀取是因為錯把i_agt寫成了i_atg。default sequence的設(shè)置也沒有被讀取,是因為default sequence是設(shè)置main_phase的,它在main_phase的時候被獲取,而main_phase是在connect_phase之后執(zhí)行的。
8.
print_config函數(shù)用于config_db調(diào)試。其中參數(shù)1表示遞歸的查詢,若為0,則只顯示當前component的信息。它會遍歷整個驗證平臺的所有結(jié)點,找出哪些被設(shè)置過的信息對于它們是可見的。打印的信息大致如下:
# UVM_INFO @ 0: uvm_test_top [CFGPRT] visible resources: # <none> # UVM_INFO @ 0: uvm_test_top.env [CFGPRT] visible resources: # <none> # UVM_INFO @ 0: uvm_test_top.env.agt_mdl_fifo [CFGPRT] visible resources: # <none> … 參考自張強《UVM實戰(zhàn)》公眾號:程序員Marshall總結(jié)
以上是生活随笔為你收集整理的uvm 形式验证_UVM基础的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java-逻辑运算符、位运算符
- 下一篇: 编写C语言代码,实现以下功能:有N名学生