(1)加入base_test
????????在一個實際應用的UVM驗證平臺中,my_env并不是樹根,通常來說,樹根是一個基于uvm_test派生的類。真正的測試用例都是基于base_test派生的一個類。
class base_test extends uvm_test;my_env env;function new(string name = "base_test", uvm_component parent = null);super.new(name,parent);endfunctionextern virtual function void build_phase(uvm_phase phase);extern virtual function void report_phase(uvm_phase phase);`uvm_component_utils(base_test)
endclassfunction void base_test::build_phase(uvm_phase phase);super.build_phase(phase);env = my_env::type_id::create("env", this); uvm_config_db#(uvm_object_wrapper)::set(this,"env.i_agt.sqr.main_phase","default_sequence",my_sequence::type_id::get());
endfunctionfunction void base_test::report_phase(uvm_phase phase);uvm_report_server server;int err_num;super.report_phase(phase);server = get_report_server();err_num = server.get_severity_count(UVM_ERROR);if (err_num != 0) begin$display("TEST CASE FAILED");endelse begin$display("TEST CASE PASSED");end
endfunction
????????base_test派生自uvm_test,使用uvm_component_utils宏來注冊到factory中。在build_phase中實例化my_env,并設置sequencer的 default_sequence。需要注意的是,這里設置了default_sequence,其他地方就不需要再設置了。
????????除了實例化env外,base_test中做的事情在不同的公司各不相同。上面的代碼中出現了report_phase,在report_phase中根據 UVM_ERROR的數量來打印不同的信息。一些日志分析工具可以根據打印的信息來判斷DUT是否通過了某個測試用例的檢查。report_phase也是UVM內建的一個phase,它在main_phase結束之后執行。
????????除了上述操作外,還通常在base_test中做如下事情:第一,設置整個驗證平臺的超時退出時間;第二,通過config_db設置驗 證平臺中某些參數的值。這些根據不同的驗證平臺及不同的公司而不同,沒有統一的答案。
????????在把my_env放入base_test中之后,UVM樹的層次結構變為如圖2-11所示的形式。
????????top_tb中run_test的參數從my_env變成了base_test,并且config_db中設置virtual interface的路徑參數要做如下改變:
?
initial beginrun_test("base_test");
endinitial beginuvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.i_agt.drv", "vif", input_if);uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.i_agt.mon", "vif", input_if);uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.env.o_agt.mon", "vif", output_if);
end
(2)UVM中測試用例的啟動
????????要測試一個DUT是否按照預期工作,需要對其施加不同的激勵,這些激勵被稱為測試向量或pattern。一種激勵作為一個測試 用例,不同的激勵就是不同的測試用例。測試用例的數量是衡量驗證人員工作成果的最直接目標。
????????伴隨著驗證的進行,測試用例的數量一直在增加,在增加的過程中,很重要的一點是保證后加的測試用例不影響已經建好的 測試用例。在前面所有的例子中,通過設置default_sequence的形式啟動my_sequence。假如現在有另外一個my_sequence2,如何在 不影響my_sequence的前提下將其啟動呢?最理想的辦法是在命令行中指定參數來啟動不同的測試用例。
????????無論是在my_env中設置default_sequence,還是在base_test中或者top_tb中設置,都必須修改相關的設置代碼才能啟動 my_sequence2,這與預期相去甚遠。為了解決這個問題,先來看兩個不同的測試用例。my_case0的定義如下:
`ifndef MY_CASE0__SV
`define MY_CASE0__SV
class case0_sequence extends uvm_sequence #(my_transaction);my_transaction m_trans;function new(string name= "case0_sequence");super.new(name);endfunction virtual task body();if(starting_phase != null) starting_phase.raise_objection(this);repeat (10) begin`uvm_do(m_trans)end#100;if(starting_phase != null) starting_phase.drop_objection(this);endtask`uvm_object_utils(case0_sequence)
endclassclass my_case0 extends base_test;function new(string name = "my_case0", uvm_component parent = null);super.new(name,parent);endfunction extern virtual function void build_phase(uvm_phase phase); `uvm_component_utils(my_case0)
endclassfunction void my_case0::build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db#(uvm_object_wrapper)::set(this, "env.i_agt.sqr.main_phase", "default_sequence", case0_sequence::type_id::get());
endfunction`endif
my_case1的定義如下:
`ifndef MY_CASE1__SV
`define MY_CASE1__SV
class case1_sequence extends uvm_sequence #(my_transaction);my_transaction m_trans;function new(string name= "case1_sequence");super.new(name);endfunction virtual task body();if(starting_phase != null) starting_phase.raise_objection(this);repeat (10) begin`uvm_do_with(m_trans, { m_trans.pload.size() == 60;})end#100;if(starting_phase != null) starting_phase.drop_objection(this);endtask`uvm_object_utils(case1_sequence)
endclassclass my_case1 extends base_test;function new(string name = "my_case1", uvm_component parent = null);super.new(name,parent);endfunction extern virtual function void build_phase(uvm_phase phase); `uvm_component_utils(my_case1)
endclassfunction void my_case1::build_phase(uvm_phase phase);super.build_phase(phase);uvm_config_db#(uvm_object_wrapper)::set(this, "env.i_agt.sqr.main_phase", "default_sequence", case1_sequence::type_id::get());
endfunction`endif
????????在case1_sequence中出現了uvm_do_with宏,它是uvm_do系列宏中的一個,用于在隨機化時提供對某些字段的約束。
????????要啟動my_case0,需要在top_tb中更改run_test的參數:
initial begin
run_test("my_case0");
end
????????而要啟動my_case1,也需要更改:
initial begin
run_test("my_case1");
end
????????當my_case0運行的時候需要修改代碼,重新編譯后才能運行;當my_case1運行時也需如此,這相當不方便。事實上,UVM提 供對不加參數的run_test的支持:
initial beginrun_test();
end
????????在這種情況下,UVM會利用UVM_TEST_NAME從命令行中尋找測試用例的名字,創建它的實例并運行。如下所示的代碼可 以啟動my_case0:
<sim command>
… +UVM_TEST_NAME=my_case0
????????而如下所示的代碼可以啟動my_case1:
<sim command>
… +UVM_TEST_NAME=my_case1
????????整個啟動及執行的流程如下圖所示
????????????????????????????????????????????????圖1 測試用例的啟動及執行流程
啟動后,整棵UVM樹的結構如下圖所示。
????????????????????????????????????????圖2 每個測試用例建立的UVM樹
????????圖2-13與圖2-11的唯一區別在于樹根的類型從base_test變成了my_casen。