*2.3.4_封裝成agent
上一節在驗證平臺中加入monitor時,讀者看到了driver和monitor之間的聯系:兩者之間的代碼高度相似。其本質是因為二者處理的是同一種協議,在同樣一套既定的規則下做著不同的事情。由於二者的這種相似性,UVM中通常將二者封裝在一起,成為一個agent。因此,不同的agent就代表了不同的協議。
代碼清單 2-33
文件:src/ch2/section2.3/2.3.4/my_agent.sv
4 class my_agent extends uvm_agent ;
5 my_driver drv;
6 my_monitor mon;
7
8 function new(string name, uvm_component parent);
9 super.new(name, parent);
10 endfunction
11
12 extern virtual function void build_phase(uvm_phase phase);
13 extern virtual function void connect_phase(uvm_phase phase);
14
15 `uvm_component_utils(my_agent) //註意此處不需要分號
16 endclass
17
18
19 function void my_agent::build_phase(uvm_phase phase);
20 super.build_phase(phase);
21 if (is_active == UVM_ACTIVE) begin
22 drv = my_driver::type_id::create("drv", this);
23 end
24 mon = my_monitor::type_id::create("mon", this);
25 endfunction
26
27 function void my_agent::connect_phase(uvm_phase phase);
28 super.connect_phase(phase);
29 endfunction
所有的agent都要派生自uvm_agent類,且其本身是一個component,應該使用uvm_component_utils宏來實現factory註冊。
這裏最令人困惑的可能是build_phase中為何根據is_active這個變量的值來決定是否創建driver的實例。is_active是uvm_agent的一個成員變量,從UVM的源代碼中可以找到它的原型如下:
代碼清單 2-34
來源:UVM源代碼
uvm_active_passive_enum is_active = UVM_ACTIVE;
而uvm_active_passive_enum是一個枚舉類型變量,其定義為:
代碼清單 2-35
來源:UVM源代碼
typedef enum bit { UVM_PASSIVE=0, UVM_ACTIVE=1 } uvm_active_passive_enum;
這個枚舉變量僅有兩個值:UVM_PASSIVE和UVM_ACTIVE。在uvm_agent中,is_active的值默認為UVM_ACTIVE,在這種模式下,是需要實例化driver的。那麽什麽是UVM_PASSIVE模式呢?以本章的DUT為例,如圖2-5所示,在輸出端口上不需要驅動任何信號,只需要監測信號。在這種情況下,端口上是只需要monitor的,所以driver可以不用實例化。
在把driver和monitor封裝成agent後,在env中需要實例化agent,而不需要直接實例化driver和monitor了:
代碼清單 2-36
文件:src/ch2/section2.3/2.3.4/my_env.sv
4 class my_env extends uvm_env;
5
6 my_agent i_agt;
7 my_agent o_agt;
…
13 virtual function void build_phase(uvm_phase phase);
14 super.build_phase(phase);
15 i_agt = my_agent::type_id::create("i_agt", this);
16 o_agt = my_agent::type_id::create("o_agt", this);
17 i_agt.is_active = UVM_ACTIVE;
18 o_agt.is_active = UVM_PASSIVE;
19 endfunction
…
22 endclass
完成i_agt和o_agt的聲明後,在my_env的build_phase中對它們進行實例化後,需要指定各自的工作模式是active模式還是passive模式。現在,整棵UVM樹變為了如圖2-6所示形式。
由於agent的加入,driver和monitor的層次結構改變了,在top_tb中使用config_db設置virtual my_if時要註意改變路徑:
代碼清單 2-37
文件:src/ch2/section2.3/2.3.4/top_tb.sv
48 initial begin
49 uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.i_agt.drv", "vif", input_if);
50 uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.i_agt.mon", "vif", input_if);
51 uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.o_agt.mon", "vif", output_if);
52 end
在加入了my_agent後,UVM的樹形結構越來越清晰。
首先,只有uvm_component才能作為樹的結點,像my_transaction這種使用uvm_object_utils宏實現的類是不能作為UVM樹的結點的。
其次,在my_env的build_phase中,創建i_agt和o_agt的實例是在build_phase中;在agent中,創建driver和monitor的實例也是在build_phase中。
按照前文所述的build_phase的從樹根到樹葉的執行順序,可以建立一棵完整的UVM樹。
UVM要求UVM樹最晚在build_phase時段完成,如果在build_phase後的某個phase實例化一個component:
class my_env extends uvm_env;
…
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
virtual task main_phase(uvm_phase phase);
i_agt = my_agent::type_id::create("i_agt", this);
o_agt = my_agent::type_id::create("o_agt", this);
i_agt.is_active = UVM_ACTIVE;
o_agt.is_active = UVM_PASSIVE;
endtask
endclass
如上所示,將在my_env的build_phase中的實例化工作移動到main_phase中,UVM會給出如下錯誤提示:
UVM_FATAL @ 0: i_agt [ILLCRT] It is illegal to create a component (‘i_agt‘ under ‘uvm_test_top‘) after the build phase has ended.
那麽是不是只能在build_phase中執行實例化的動作呢?答案是否定的。其實還可以在new函數中執行實例化的動作。如可以在my_agent的new函數中實例化driver和monitor:
代碼清單 2-39
function new(string name, uvm_component parent);
super.new(name, parent);
if (is_active == UVM_ACTIVE) begin
drv = my_driver::type_id::create("drv", this);
end
mon = my_monitor::type_id::create("mon", this);
endfunction
這樣引起的一個問題是無法通過直接賦值的方式向uvm_agent傳遞is_active的值。在my_env的build_phase(或者new函數)中,向i_agt和o_agt的is_active賦值,根本不會產生效果。因此i_agt和o_agt都工作在active模式(is_active的默認值是UVM_ACTIVE),這與預想差距甚遠。要解決這個問題,可以在my_agent實例化之前使用config_db語句傳遞is_active的值:
代碼清單 2-40
class my_env extends uvm_env;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_active_passive_enum)::set(this, "i_agt", "is_active", UVM_ACTIVE);
uvm_config_db#(uvm_active_passive_enum)::set(this, "o_agt", "is_active", UVM_PASSIVE);
i_agt = my_agent::type_id::create("i_agt", this);
o_agt = my_agent::type_id::create("o_agt", this);
endfunction
endclass
class my_agent extends uvm_agent ;
function new(string name, uvm_component parent);
super.new(name, parent);
uvm_config_db#(uvm_active_passive_enum)::get(this, "", "is_active", is_active);
if (is_active == UVM_ACTIVE) begin
drv = my_driver::type_id::create("drv", this);
end
mon = my_monitor::type_id::create("mon", this);
endfunction
endclass
只是UVM中約定俗成的還是在build_phase中完成實例化工作。因此,強烈建議僅在build_phase中完成實例化。
*2.3.4_封裝成agent