歡迎關注個人公眾號摸魚范式
本期將講解UVM環境運行以及他的樹狀結構。主要參考資料為
白皮書: http://bbs.eetop.cn/thread-320165-1-1.html
紅寶書: http://rockeric.com/
上期推送中,我們講解了整體環境的構成,以及他們之間的關系。那么當仿真開始時,整個環境又將如何建立起來呢,組件按照什么順序進行組件實例化,如何將組件之間的通訊構建起來,以及在運行時我們需要做什么呢?
樹狀結構
整體的驗證環境由許多的組件實現,作為環境組件,UVM提供了uvm_component,所有下圖中的測試組件都繼承於uvm_component,只有uvm_component能夠作為組件構建起整個環境。
上圖中可以看到框圖一層套一層,實際上就是UVM環境的樹狀結構的體現。下圖是一個典型的測試環境的樹狀結構圖。結合上下兩圖,uvm_top是一個全局變量,它是uvm_root的一個實例(而且也是唯一的一個實例 ,它的實現方式非常巧妙),而uvm_root 派生自uvm_component,所以uvm_top本質上是一個uvm_component,它是樹的根。uvm_test_top的parent是uvm_top,而uvm_top的 parent則是null。
這里我們要注意區分,uvm平台中的parent和我們在oop中所說的父類是不同的概念,這里指的是節點,而非類的繼承關系,上圖的箭頭就像是鈎子,從上至下一個鈎住一個。通常我們不會接觸到uvm_root這個類,他是一個全局變量,構建環境我們需要構建uvm_test_top及其以下的組件。
而具體每個組件的作用,可以參照上一期的推送。
運行機制
仿真的進程分為幾個基本階段,例化組件,連接組件,仿真運行和報告階段。但實際上UVM在運行時提供了更加細致的階段划分,將不同的任務划分到對應的階段中。UVM稱這種機制為phase機制,一共包括九個phase。
仿真開始后,phase將從上至下,依次自動運行。phase 的引入在很大程度上解決了代碼順序雜亂可能會引發的問題。它本質上是通過把代碼順序強制固定來實現這個目的的,如 build_phase 的代碼一定在connect_phase之前執行 ,而 connect_phase的代碼一定在 end_of_elaboration_phase之前執行等等。遵循 UVM 的這種代碼順序划分原則,可以在很大程度上減少驗證平台開發者的工作量,讓其從一部分雜亂的工作中解脫出來。
- function phase:不消耗仿真時間,而其也可分成兩大類:
- 繼承自uvm_bottomup_phase, 在UVM component樹中,自下而上的執行, 如connect_phase
- 繼承自uvm_topdown_phase, 在UVM component樹中,自上而下執行, 如build_phase
- task phase:消耗仿真時間的,也稱動態運行(run-time)phase.
在初學UVM進行驗證過程中,只需要關注其中幾個phase即可。將組件的例化在build_phase實現,組件連接在connect_phase實現,測試的過程在run_phase中實現。就像下面cfg_monitor的代碼一樣,分步驟完成環境的構建。
class cfg_monitor extends uvm_monitor;
local virtual cfg_intf intf;
uvm_blocking_put_port #(cfg_item) mon_bp_port;
`uvm_component_utils(cfg_monitor)
function new(string name="cfg_monitor", uvm_component parent);
endfunction
function void set_interface(virtual cfg_intf intf);
endfunction
task run_phase(uvm_phase phase);
endtask
task mon_trans();
endtask
endclass: cfg_monitor
從前面的圖中可以看到,run_phase和旁邊十二個小的phase是並行的,UVM為仿真提供了非常細致的階段划分,即十二個小phase,但是只使用run_phase也可以完成仿真。
工廠機制
在講解我們在build_phase所需,要完成的任務前,我們需要了解UVM中一個非常重要的機制-工廠機制。這並非是UVM獨創的機制,而時在設計模式中一種常用的機制。簡而言之,就是我們在編寫每個類時,都是在繪制圖紙,當我們完成圖紙以后,將圖紙遞交到工廠。仿真運行時,我們就能通過工廠,使用圖紙的名字,創建出不同的類。
繪制圖紙的過程就是在編寫類的代碼,而提交圖紙這個過程,體現在代碼中就是通過UVM的宏進行注冊,告訴工廠圖紙的名字。宏uvm_component_utils將cfg_agent注冊到工廠,而在構造函數中,可以同時指定對象的默認名字。
注意new方法的參數中,string是我們在創建組件時所需要傳遞的內容,一定要與句柄的名字一直,否則在后續其他步驟中會出現意想不到的錯誤。而第二個參數parent就是前面所提到的樹狀結構的鈎子,父節點。
class cfg_agent extends uvm_agent;
`uvm_component_utils(cfg_agent)
function new(string name = "cfg_agent", uvm_component parent);
super.new(name, parent);
endfunction
endclass:cfg_agent
build_phase的內容
通過工廠機制我們可以很輕松地完成組件對象的實例化,下面就是一個示例。
class cfg_agent extends uvm_agent;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
driver = cfg_driver::type_id::create("driver", this);
monitor = cfg_monitor::type_id::create("monitor", this);
sequencer = cfg_sequencer::type_id::create("sequencer", this);
endfunction
endclass:cfg_agent
使用component_name::type_id::create()就能通過工廠實例化對象,create是uvm_component的一個靜態函數,會返回組件的句柄,第一個參數就是我們new中的string,建議與句柄名保持一致,第二個參數通常使用this,表示創建的組件是在當前的組件的子節點上,從而構建樹狀結構。
有了創建組件的方法,我們就能通過build_phase實現組件的構建。我們知道,整個環境是一個樹狀結構,那么檢索樹狀結構就有兩個方向,自頂向下和自底向上。我們觀察create的參數,發現需要傳遞父節點的句柄,那么很顯然,build_phase是自頂向下的。從下面的代碼就能看到,在build_phase中,我們需要完成與本節點相連的子節點的創建,這樣一層一層的勾起組件,從根到最末端,所有的build_phase完成以后再進入下一個phase。
class conv_base_test extends uvm_test;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
this.env = conv_env::type_id::create("env", this);
endfunction
endclass: conv_base_test
class conv_env extends uvm_env;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
this.chker = conv_checker::type_id::create("chker", this);
this.cfg_agt = cfg_agent::type_id::create("cfg_agt", this);
this.fmi_agt = mem_in_agent::type_id::create("fmi_agt", this);
this.wt_agt = mem_in_agent::type_id::create("wt_agt", this);
this.bias_agt = mem_in_agent::type_id::create("bias_agt", this);
this.fmo_agt = mem_out_agent::type_id::create("fmo_agt", this);
this.cvrg = conv_coverage::type_id::create("cvrg", this);
this.virt_sqr = conv_virtual_sequencer::type_id::create("virt_sqr", this);
endfunction
endclass: conv_env
class cfg_agent extends uvm_agent;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
driver = cfg_driver::type_id::create("driver", this);
monitor = cfg_monitor::type_id::create("monitor", this);
sequencer = cfg_sequencer::type_id::create("sequencer", this);
endfunction
endclass:cfg_agent
本期總結
- UVM的組件是樹狀連接的,環境中的parent和oop的父類是不一樣的
- UVM的運行划分成多個phase,每個phase負責不同的任務,run_phase與旁邊的十二個phase是並行的。整體運行從上至下順序運行。只有run_phase和並行的phase是task,其他都是funtion。
- 使用宏在工廠中注冊,然后就能夠通過create靜態方法實現組件實例化
- 在build_phase中順序創建字節點的組件,從而構建整個環境