UVM源碼探索之Factory(下篇)
github.io鏈接
到下篇的時候, 我們來探索一下UVM的Factory的另外一個主要功能, 那就是重載. 說起來, 重載功能跟Factory這個設計模式已經沒有關係了. 而且, 這裡的」重載」跟常見的重載的意義也不一樣, 這裡的重載準確的描述的話是類」替換」.
在UVM裡面使用重載的大致流程如下:
- 對需要重載的類使用uvm_*_util進行類型註冊, 原始類和重載類都需要註冊;
- 設置重載, 設置原始類」替換」為重載類;
- 使用Factory的create創建原始類對象, 返回的是重載類對象.
步驟1我們在上篇已經講過了, 步驟2跟3的在上篇其實也有相關的代碼展示, 不過我們略過了. 現在到了下篇, 我們的主要任務就是把上面流程中的2跟3的源碼實現給搞清楚.
重載的設置
在上篇, uvm_*_registry的類定義裡面有這樣的代碼:
class uvm_object_registry #(type T=uvm_object, string Tname="<unknown>") extends uvm_object_wrapper; ... static function void set_type_override (uvm_object_wrapper override_type, bit replace=1); uvm_coreservice_t cs = uvm_coreservice_t::get(); uvm_factory factory=cs.get_factory(); factory.set_type_override_by_type(get(),override_type,replace); endfunction static function void set_inst_override(uvm_object_wrapper override_type, string inst_path, uvm_component parent=null); string full_inst_path; uvm_coreservice_t cs = uvm_coreservice_t::get(); uvm_factory factory=cs.get_factory(); if (parent != null) begin if (inst_path == "") inst_path = parent.get_full_name(); else inst_path = {parent.get_full_name(),".",inst_path}; end factory.set_inst_override_by_type(get(),override_type,inst_path); endfunctionendclass
set_type_override和set_inst_override就是設置重載的兩個函數了, 這兩個函數都是基於類型的重載函數, 第一個參數是需要重載的類型. 後面的函數, 從內容上來看, 是為了支持UVM的component樹的特定節點的重載的, 這個就要跟component扯上關係了, 所以先不細究了(UVM的component代碼很多的, 光是uvm_component.svh這個文件裡面, 只有一個類定義, 就有3000多行, 而且內容很雜, 我也很無奈~).
所以, 我們就把重點放在set_type_override上面來吧. 這個函數裡面, 調用的其實是Factory里定義的set_type_override_by_type()這個方法, 第二個參數就是uvm_object_registry裡面set_type_override方法的第一個參數, 就是重載的類型, 而第一個參數是uvm_object_registry裡面get方法的返回值, 也就是本類型的單例對象. 第三個參數replace的默認值是1, 這個參數是什麼用的, 這裡還體現不出來. set_type_override這個函數要做的就是就是通過Factory的set_type_override_by_type方法把本類型替換為指定的重載類型, 具體怎麼重載的呢就要看Factory的代碼了.
這個部分代碼有點長, 我們要慢慢看:
function void uvm_default_factory::set_type_override_by_type (uvm_object_wrapper original_type, uvm_object_wrapper override_type, bit replace=1); bit replaced; // check that old and new are not the same if (original_type == override_type) begin if (original_type.get_type_name() == "" || original_type.get_type_name() == "<unknown>") uvm_report_warning("TYPDUP", {"Original and override type ", "arguments are identical"}, UVM_NONE); else uvm_report_warning("TYPDUP", {"Original and override type ", "arguments are identical: ", original_type.get_type_name()}, UVM_NONE); end ...
第一部分, 是輸入檢查, 如果類型相同的話輸出一個告警.
function void uvm_default_factory::set_type_override_by_type (uvm_object_wrapper original_type, uvm_object_wrapper override_type, bit replace=1); bit replaced; ... // register the types if not already done so, for the benefit of string-based lookup if (!m_types.exists(original_type)) register(original_type); if (!m_types.exists(override_type)) register(override_type); ...endfunction
第二部分, 是類型註冊檢查. 還記得m_types[$]這個bit類型的哈希表嗎, 上篇中說過, 這個表記錄了類型是否已註冊. 在此之前, 我們知道類型的註冊發生在第一次使用Factory的create的時候. 在這裡我們要思考一下了, 重載的設置, 其實是應該發生在create之前的是不是, 所以這裡也是需要保證類型註冊的.
function void uvm_default_factory::set_type_override_by_type (uvm_object_wrapper original_type, uvm_object_wrapper override_type, bit replace=1); bit replaced; ... // check for existing type override foreach (m_type_overrides[index]) begin if (m_type_overrides[index].orig_type == original_type || (m_type_overrides[index].orig_type_name != "<unknown>" && m_type_overrides[index].orig_type_name != "" && m_type_overrides[index].orig_type_name == original_type.get_type_name())) begin string msg; msg = {"Original object type ",original_type.get_type_name(), " already registered to produce ", m_type_overrides[index].ovrd_type_name,""}; if (!replace) begin msg = {msg, ". Set replace argument to replace the existing entry."}; uvm_report_info("TPREGD", msg, UVM_MEDIUM); return; end msg = {msg, ". Replacing with override to produce type ", override_type.get_type_name(),"."}; uvm_report_info("TPREGR", msg, UVM_MEDIUM); replaced = 1; m_type_overrides[index].orig_type = original_type; m_type_overrides[index].orig_type_name = original_type.get_type_name(); m_type_overrides[index].ovrd_type = override_type; m_type_overrides[index].ovrd_type_name = override_type.get_type_name(); end end ...endfunction
接著往下看, 這裡是一個表的搜索操作. m_type_overrides顯然是一個重載的記錄表了, 這段代碼字面上看是要先查找記錄表中是不是已經有輸入參數original_type的重載記錄了, 如果有的話需要把記錄表中的ovrd_type的信息賦值為輸入參數override_type的相關信息. 中間的if表達式需要對第三個輸入參數replace做檢查, 不為0的時候就直接返回, 不進行後面的override_type的記錄表賦值操作了. 這樣看來, UVM的Factory是支持重載的改寫操作的, replace參數設為1的話就可以實現先把A類型重載為B類型, 之後再把A類型重新重載為C類型.
這個部分其實挺複雜的, 而且我們對m_type_overrides這個數據結構其實一無所知. 現在先假定我們是第一次進入這個方法, 所以這個m_type_overrides表是空的, 這樣, 我們就可以先看看這個方法的最後一部分是什麼:
function void uvm_default_factory::set_type_override_by_type (uvm_object_wrapper original_type, uvm_object_wrapper override_type, bit replace=1); bit replaced; ... // make a new entry if (!replaced) begin uvm_factory_override override; override = new(.orig_type(original_type), .orig_type_name(original_type.get_type_name()), .full_inst_path("*"), .ovrd_type(override_type)); m_type_overrides.push_back(override); endendfunction
這裡是一個創建重載對象的操作, 使用uvm_factory_override這個類型的數據來保存重載信息: 原始類型和重載類型. 然後把這個記錄插入到m_type_overrides這個表裡. repalced這個變數在上面的重載改寫的時候會置1, 有重複記錄的表項不設置改寫的時候會直接返回, 所以, 插入的表項都是之前沒有重載記錄的表項.
重載的支持數據結構
到這裡來講, 我們的重載設置這塊任務已經算完成的差不多了. 重載設置函數內容上看還是挺簡單的, 不過出現了之前沒有見過的數據結構. 在重載應用的時候, 這些數據結構也是要發揮作用的, 這裡我們還是先了解一下這些數據結構為好.
uvm_default_factory類裡面的主要數據成員如下:
protected bit m_types[uvm_object_wrapper];protected bit m_lookup_strs[string];protected uvm_object_wrapper m_type_names[string];protected uvm_factory_override m_type_overrides[$];protected uvm_factory_queue_class m_inst_override_queues[uvm_object_wrapper];protected uvm_factory_queue_class m_inst_override_name_queues[string];protected uvm_factory_override m_wildcard_inst_overrides[$];local uvm_factory_override m_override_info[$];
裡面有老相識m_types和m_type_names, 還有之前見過的列表m_type_overrides和他的類型uvm_factory_override, 這裡可以看到還有新的類型uvm_factory_queue_class和這個類型的3個成員m_inst_override_queues, m_inst_override_name_queues和m_wildcard_inst_overrides, 這三個成員分別是key為uvm_object_wrapper的聯合數組, key為字元串的聯合數組和隊列.
uvm_factory_queue_class的定義在uvm_factory.svh文件的開頭部分, 可以先瞄一眼.
class uvm_factory_queue_class; uvm_factory_override queue[$];endclass
這個uvm_factory_queue_class這個數據竟然還是uvm_factory_override的隊列, 這三個m_inst_*成員還真是複雜的很啊.
這裡沒有注釋, 不過從變數的命名和空行分隔排列來看, uvm_factory_queue_class的3個成員是用來管理component樹節點重載的數據. 之前說過先不管基於component節點的重載真是先見之明啊.
然後我們來看看uvm_factory_override的類定義:
class uvm_factory_override; string full_inst_path; string orig_type_name; string ovrd_type_name; bit selected; int unsigned used; uvm_object_wrapper orig_type; uvm_object_wrapper ovrd_type; function new (string full_inst_path="", string orig_type_name="", uvm_object_wrapper orig_type=null, uvm_object_wrapper ovrd_type); if (ovrd_type == null) begin uvm_report_fatal ("NULLWR", "Attempting to register a null override object with the factory", UVM_NONE); end this.full_inst_path= full_inst_path; this.orig_type_name = orig_type == null ? orig_type_name : orig_type.get_type_name(); this.orig_type = orig_type; this.ovrd_type_name = ovrd_type.get_type_name(); this.ovrd_type = ovrd_type; endfunctionendclass
就是一些基本的數據, 兩個類型數據orig_type和ovrd_type和這兩個類型對應的類型名字元串orig_type_name和ovrd_type_name. 雖然我們是不管component樹節點重載了, 但是uvm_factory_override還是要給它提供支持的, full_inst_path看起來就是支持這個的. 然後selected和used是之前沒有出現過的, 這個是怎麼用的要看後面了.
這個類的數據成員都是公用的, 也只提供了new這一個方法, new方法也只是用來給成員賦值用的, 只是做了一些小小的空指針檢查. 這個類看起來是一副要做大事的樣子啊…
重載的實現
好了, 現在我們開始研究重載的最後一步吧. 在上篇中我們講到:
用Factory創建對象的調用棧為: user_object::type_id::create("name")—>uvm_object_registry#(user_object, "uvm_object")::create("name")—>factory.create_object_by_type(), 最後返回了對象.
重載的實現就隱藏在factory.create_object_by_type()里了, 我們再來恢復一下現場看看:
function uvm_object uvm_default_factory::create_object_by_type (uvm_object_wrapper requested_type, string parent_inst_path="", string name=""); string full_inst_path; if (parent_inst_path == "") full_inst_path = name; else if (name != "") full_inst_path = {parent_inst_path,".",name}; else full_inst_path = parent_inst_path; m_override_info.delete(); requested_type = find_override_by_type(requested_type, full_inst_path); return requested_type.create_object(name);endfunction
這裡的requested_type = find_override_by_type(requested_type, full_inst_path);這一句就是重載函數了, 字面上看就是在重載表裡對requested_type進行查找, 然後返回重載記錄表的結果, 然後用返回的結果來創建對象. 按照之前的參數傳遞, 這裡傳入的full_inst_path是傳入的字元串名name, 而傳入的requested_type是uvm_object_registry#(user_object, "user_object")的單例對象. 剩下的秘密, 盡在find_override_by_type這個方法了.
這個方法的代碼:
function uvm_object_wrapper uvm_default_factory::find_override_by_type(uvm_object_wrapper requested_type, string full_inst_path); uvm_object_wrapper override; uvm_factory_override lindex; uvm_factory_queue_class qc; qc = m_inst_override_queues.exists(requested_type) ? m_inst_override_queues[requested_type] : null; foreach (m_override_info[index]) begin if ( //index != m_override_info.size()-1 && m_override_info[index].orig_type == requested_type) begin uvm_report_error("OVRDLOOP", "Recursive loop detected while finding override.", UVM_NONE); if (!m_debug_pass) debug_create_by_type (requested_type, full_inst_path); m_override_info[index].used++; return requested_type; end end // inst override; return first match; takes precedence over type overrides if (full_inst_path != "" && qc != null) for (int index = 0; index < qc.queue.size(); ++index) begin if ((qc.queue[index].orig_type == requested_type || (qc.queue[index].orig_type_name != "<unknown>" && qc.queue[index].orig_type_name != "" && qc.queue[index].orig_type_name == requested_type.get_type_name())) && uvm_is_match(qc.queue[index].full_inst_path, full_inst_path)) begin m_override_info.push_back(qc.queue[index]); if (m_debug_pass) begin if (override == null) begin override = qc.queue[index].ovrd_type; qc.queue[index].selected = 1; lindex=qc.queue[index]; end end else begin qc.queue[index].used++; if (qc.queue[index].ovrd_type == requested_type) return requested_type; else return find_override_by_type(qc.queue[index].ovrd_type,full_inst_path); end end end // type override - exact match foreach (m_type_overrides[index]) begin if (m_type_overrides[index].orig_type == requested_type || (m_type_overrides[index].orig_type_name != "<unknown>" && m_type_overrides[index].orig_type_name != "" && requested_type != null && m_type_overrides[index].orig_type_name == requested_type.get_type_name())) begin m_override_info.push_back(m_type_overrides[index]); if (m_debug_pass) begin if (override == null) begin override = m_type_overrides[index].ovrd_type; m_type_overrides[index].selected = 1; lindex=m_type_overrides[index]; end end else begin m_type_overrides[index].used++; if (m_type_overrides[index].ovrd_type == requested_type) return requested_type; else return find_override_by_type(m_type_overrides[index].ovrd_type,full_inst_path); end end end // type override with wildcard match //foreach (m_type_overrides[index]) // if (uvm_is_match(index,requested_type.get_type_name())) begin // m_override_info.push_back(m_inst_overrides[index]); // return find_override_by_type(m_type_overrides[index],full_inst_path); // end if (m_debug_pass && override != null) begin lindex.used++; if (override == requested_type) begin return requested_type; end else return find_override_by_type(override,full_inst_path); end return requested_type;endfunction
這個代碼量也是相當的恐怖, 我們還是來分段看看:
function uvm_object_wrapper uvm_default_factory::find_override_by_type(uvm_object_wrapper requested_type, string full_inst_path); uvm_object_wrapper override; uvm_factory_override lindex; uvm_factory_queue_class qc; qc = m_inst_override_queues.exists(requested_type) ? m_inst_override_queues[requested_type] : null; foreach (m_override_info[index]) begin if ( //index != m_override_info.size()-1 && m_override_info[index].orig_type == requested_type) begin uvm_report_error("OVRDLOOP", "Recursive loop detected while finding override.", UVM_NONE); if (!m_debug_pass) debug_create_by_type (requested_type, full_inst_path); m_override_info[index].used++; return requested_type; end end ...endfunction
最前面的部分, 就出現了uvm_factory_queue_class類型的qc和之前只提到過的m_override_info的操作. 這真是尷尬, 現在我們只好先假定我們用的很克制, m_inst_override_queues裡面什麼都沒有, 然後qc就是個空指針啦. 上篇出現的m_override_info的時候, 都是只有delete操作, 然後這裡也就假定這個列表是空吧. 蛤蛤, 先硬著頭皮繼續往下看.
function uvm_object_wrapper uvm_default_factory::find_override_by_type(uvm_object_wrapper requested_type, string full_inst_path); uvm_object_wrapper override; uvm_factory_override lindex; uvm_factory_queue_class qc; ... // inst override; return first match; takes precedence over type overrides if (full_inst_path != "" && qc != null) for (int index = 0; index < qc.queue.size(); ++index) begin if ((qc.queue[index].orig_type == requested_type || (qc.queue[index].orig_type_name != "<unknown>" && qc.queue[index].orig_type_name != "" && qc.queue[index].orig_type_name == requested_type.get_type_name())) && uvm_is_match(qc.queue[index].full_inst_path, full_inst_path)) begin m_override_info.push_back(qc.queue[index]); if (m_debug_pass) begin if (override == null) begin override = qc.queue[index].ovrd_type; qc.queue[index].selected = 1; lindex=qc.queue[index]; end end else begin qc.queue[index].used++; if (qc.queue[index].ovrd_type == requested_type) return requested_type; else return find_override_by_type(qc.queue[index].ovrd_type,full_inst_path); end end end ...endfunction
第二部分看起來也是來者不善啊, 不過好在這一段全是基於qc非空的行為, 所以, 還是可以再跳一段…
function uvm_object_wrapper uvm_default_factory::find_override_by_type(uvm_object_wrapper requested_type, string full_inst_path); uvm_object_wrapper override; uvm_factory_override lindex; uvm_factory_queue_class qc; ... // type override - exact match foreach (m_type_overrides[index]) begin if (m_type_overrides[index].orig_type == requested_type || (m_type_overrides[index].orig_type_name != "<unknown>" && m_type_overrides[index].orig_type_name != "" && requested_type != null && m_type_overrides[index].orig_type_name == requested_type.get_type_name())) begin m_override_info.push_back(m_type_overrides[index]); if (m_debug_pass) begin if (override == null) begin override = m_type_overrides[index].ovrd_type; m_type_overrides[index].selected = 1; lindex=m_type_overrides[index]; end end else begin m_type_overrides[index].used++; if (m_type_overrides[index].ovrd_type == requested_type) return requested_type; else return find_override_by_type(m_type_overrides[index].ovrd_type,full_inst_path); end end end ...endfunction
終於好像到了我們最關注的部分了. 這裡是對m_type_overrides的搜索操作, 成功條件是記錄表中一個成員的orig_type為requested_type, 或者記錄表中成員的orig_type_name跟requested_type的type_name相等. 咦, 會出現這種情況嗎?
這裡面的第一步就是把這條記錄插入到m_override_info這個表裡面了, 這~~~看來還是不能不管m_override_info這個東西啊. 不過還是先把這部分看完先, 下面出現了m_debug_pass, 這個變數也先假定為0好了, 然後看後面的else分支. 這裡先是把記錄表裡面的used變數加1, 然後判斷requested_type是不是跟記錄表裡的ovrd_type一致, 一致的話就返回requested_type.
我們的輸入就是requested_type啊, 這裡的意思似乎是說設置的重載類型就是原始類型? 會這麼傻嗎? 不過再看到後面的分支, 這還是個遞歸函數呢, 牛逼. 現在假定我們是一個正常的調用, 也就是說requested_type跟ovrd_type是不一樣的, 這裡就會使用m_type_overrides表裡的ovrd_type作為requested_type參數進行遞歸, 再到開頭的那裡, 就不能跳過m_override_info的處理啦:
function uvm_object_wrapper uvm_default_factory::find_override_by_type(uvm_object_wrapper requested_type, string full_inst_path); uvm_object_wrapper override; uvm_factory_override lindex; ... foreach (m_override_info[index]) begin if ( //index != m_override_info.size()-1 && m_override_info[index].orig_type == requested_type) begin uvm_report_error("OVRDLOOP", "Recursive loop detected while finding override.", UVM_NONE); if (!m_debug_pass) debug_create_by_type (requested_type, full_inst_path); m_override_info[index].used++; return requested_type; end end ...
這裡要的搜索操作要檢查的是記錄表m_override_info的成員的orig_type是不是requested_type, 這裡的requested_type就是之前ovrd_type, 這個檢查為真的話說明真的是有重載類和原始類相同的重載設置操作啦, 然後這裡就先輸出一個Error. 後面的m_debug_pass值是0, 所有要先做一個debug_create_by_type的操作, 不過我過代碼, 這裡沒有對requested_type做改動. 之後就是對used計數加1, 然後返回啦.
因為正常調用的話這裡是不會執行到了(蛤蛤, 尷尬), 所以還是要繼續往下執行:
function uvm_object_wrapper uvm_default_factory::find_override_by_type(uvm_object_wrapper requested_type, string full_inst_path); uvm_object_wrapper override; uvm_factory_override lindex; ... // type override - exact match foreach (m_type_overrides[index]) begin if (m_type_overrides[index].orig_type == requested_type || (m_type_overrides[index].orig_type_name != "<unknown>" && m_type_overrides[index].orig_type_name != "" && requested_type != null && m_type_overrides[index].orig_type_name == requested_type.get_type_name())) begin ... else begin m_type_overrides[index].used++; if (m_type_overrides[index].ovrd_type == requested_type) return requested_type; else return find_override_by_type(m_type_overrides[index].ovrd_type,full_inst_path); end end end ...endfunction
還是在這裡, 這裡的requested_type其實是之前的記錄表中的ovrd_type, 所以跟所有的orig_type是不相等的, 而且orig_type_name跟requested_type的type_name也是不一樣的, 所以這裡就要跳過了…
function uvm_object_wrapper uvm_default_factory::find_override_by_type(uvm_object_wrapper requested_type, string full_inst_path); uvm_object_wrapper override; uvm_factory_override lindex; ... // type override with wildcard match //foreach (m_type_overrides[index]) // if (uvm_is_match(index,requested_type.get_type_name())) begin // m_override_info.push_back(m_inst_overrides[index]); // return find_override_by_type(m_type_overrides[index],full_inst_path); // end if (m_debug_pass && override != null) begin lindex.used++; if (override == requested_type) begin return requested_type; end else return find_override_by_type(override,full_inst_path); end return requested_type;endfunction
然後就到了最後了, 中間的一段注釋不管了, 然後m_debug_pass的分支也不管了, 直接就返回了requested_type, 也就是之前傳入的ovrd_type. 看起來一次重載查找操作至少要調用兩次這個方法呢…
插播一點點, 關於鏈式重載
回到第二次調用, 這裡的requested_type其實是之前的記錄表中的ovrd_type, 我們之前是假定這個requested_type是不會跟任何的orig_type相等的, 然後就往下執行了. 正常使用的時候會不會出現requested_type跟orig_type相等呢?
仔細考慮一下其實是會出現的. 比如過, 我們先設置A重載為B, 再設置B重載為C, 然後我們調用Factory的create創建A的句柄, 這個時候, 在調用到find_override_by_type的時候, 就會出現orig_type為B類型的重載記錄, 這個時候需要繼續執行, 這一段代碼:
function uvm_object_wrapper uvm_default_factory::find_override_by_type(uvm_object_wrapper requested_type, string full_inst_path); uvm_object_wrapper override; uvm_factory_override lindex; ... // type override with wildcard match //foreach (m_type_overrides[index]) // if (uvm_is_match(index,requested_type.get_type_name())) begin // m_override_info.push_back(m_inst_overrides[index]); // return find_override_by_type(m_type_overrides[index],full_inst_path); // end if (m_debug_pass && override != null) begin lindex.used++; if (override == requested_type) begin return requested_type; end else return find_override_by_type(override,full_inst_path); end return requested_type;endfunction
也就是說, 需要傳入B類型的requested_type再次調用find_override_by_type.
總共經過3次調用之後, Factory實際上生成的是C類型的對象, 這樣就實現了類型的鏈式重載.
總結
到這裡為止, Factory的重載功能也介紹完了, 我們來總結一下吧:
- UVM裡面使用Factory重載的流程為: 使用uvm_*_util進行註冊, 設置重載, 使用create創建對象;
- 為重載提供支持的數據結構是uvm_factory_override類型;
- UVM的Factory支持多級鏈式重載.
然後, 我們的Factory源碼探索也就結束了. Factory源碼的內容不止這麼多, 比如我們在上篇只講了uvm_object的Factory實現沒講uvm_component的, 在下篇只講了type_override沒有將基於component的inst_override. 另外, 從介面上調用不到的很多代碼也沒有講到. 不過, 這兩篇讀下來, 然後再看剩下的代碼相信就沒什麼難度了.
推薦閱讀: