在這篇博客中,我描述了在編寫序列時必須采取的必要步驟,以確保它可以重用。就我個人而言,我覺得編寫序列是驗證任何IP最具挑戰(zhàn)性的部分。編寫序列需要仔細規(guī)劃,否則我們最終會從頭開始為每個場景編寫一個序列。這使得序列難以維護和調試。
眾所周知,序列由幾個數(shù)據(jù)項組成,它們共同構成了一個有趣的場景。序列可以是分層的,從而創(chuàng)建更復雜的方案。在最簡單的形式中,序列應該是 uvm_sequence 基類的派生,方法是指定請求和響應項類型參數(shù),并使用要執(zhí)行的特定方案實現(xiàn) body 任務。
類 usb_simple_sequence 擴展uvm_sequence #(usb_transfer);
rand int unsigned sequence_length; constraint reasonable_seq_len { sequence_length < 10 }; //Constructor function new(string name=”usb_simple_bulk_sequence”); super.new(name); endfunction //Register with factory `uvm_object_utils(usb_simple_bulk_sequence) //the body() task is the actual logic of the sequence virtual task body(); repeat(sequence_length) `uvm_do_with(req, { //Setting the device_id to 2 req.device_id == 8’d2; //Setting transfer type to BULK req.type == usb_transfer::BULK_TRANSFER; }) endtask : body endclass
在上面的順序中,我們嘗試將 USB 批量傳輸發(fā)送到 id 為 2 的設備。測試編寫者可以通過將此序列分配給頂級測試中序列器的默認序列來調用此序列。
類usb_simple_bulk_test擴展uvm_test;
… virtual function void build_phase(uvm_phase phase ); … uvm_config_db#(uvm_object_wrapper)::set(this, "sequencer_obj. main_phase","default_sequence", usb_simple_sequence::type_id::get()); … endfunction : build_phase endclass
到目前為止,事情看起來簡單明了。為了確保序列可重用于更復雜的場景,我們必須遵循更多準則。
首先,通過在序列類中pre_start和post_start任務中提出和放棄異議來管理測試結束非常重要。這樣,我們只在最上面的序列中提出和放棄異議,而不是對所有子序列都這樣做。
t問 pre_start()
if(starting_phase != null) starting_phase.raise_objection(this); endtask : pre_start
t問 post_start()
if(starting_phase != null) starting_phase.drop_objection(this); endtask : post_start
請注意,starting_phase僅為作為特定階段的默認序列啟動的序列定義。如果已通過調用序列的 start 方法顯式啟動它,則用戶負責設置starting_phase。
類usb_simple_bulk_test擴展uvm_test;
usb_simple_sequence seq; … virtual function void main_phase(uvm_phase phase ); … //User need to set the starting_phase as sequence start method is explicitly called to invoke the sequence seq.starting_phase = phase; seq.start(); … endfunction : main_phase
結束類
使用 UVM 配置從頂級測試中獲取值。在上面的示例中,沒有為測試編寫者提供可控性,因為序列不使用配置從頂級測試或序列中獲取值(將使用此序列來構建復雜場景)。修改序列以更好地控制使用此簡單序列的頂級測試或序列。
類 usb_simple_sequence 擴展uvm_sequence #(usb_transfer);
rand int unsigned sequence_length; constraint reasonable_seq_len { sequence_length < 10 }; … virtual task body(); usb_transfer::type_enum local_type; bit[7:0] local_device_id; //Get the values for the variables in case toplevel //test/sequence sets it. uvm_config_db#(int unsigned)::get(null, get_full_name(), “sequence_length”, sequence_length); uvm_config_db#(usb_transfer::type_enum)::get(null, get_full_name(), “l(fā)ocal_type”, local_type); uvm_config_db#(bit[7:0])::get(null, get_full_name(),? “l(fā)ocal_device_id”, local_device_id); repeat(sequence_length) `uvm_do_with(req, { req.device_id == local_device_id; req.type == local_type; }) endtask : body
結束類
通過上述修改,我們已經(jīng)控制了頂級測試或序列來修改device_id、sequence_length和類型。這里需要注意的幾點:uvm_config_db#()::set 中使用的參數(shù)類型和字符串(第三個參數(shù))應該與 uvm_config_db#()::get 中使用的類型匹配。確保使用確切的數(shù)據(jù)類型“設置”和“獲取”。否則,值將無法正確設置,調試將成為一場噩夢。
上述序列的一個問題是:如果 usb_transfer 類中對device_id或類型有任何約束,那么這將限制頂級測試或序列以確保它在約束范圍內。
例如,如果usb_transfer類中的device_id存在約束,將其約束為低于 10,則頂級測試或序列應在此范圍內約束它。如果頂級測試或序列將其設置為類似 15 的值(超過 10),則在運行時將看到約束失敗。
有時,頂級測試或序列可能需要完全控制,并且可能不希望啟用在較低級別的序列或數(shù)據(jù)項中定義的約束。需要這樣做的一個示例是陰性測試:- 主機希望確保設備不會響應device_id大于 10 的傳輸,因此希望發(fā)送device_id 15 的傳輸。因此,為了完全控制頂級測試或序列,我們可以修改正文任務,如下所示:
虛擬任務正文();
usb_transfer::type_enum local_type; bit[7:0] local_device_id; int status_seq_len = 0; int status_type = 0; int status_device_id = 0; status_seq_len = uvm_config_db#(int unsigned)::get(null, get_full_name(), “sequence_length”, sequence_length); status_type = uvm_config_db#(usb_transfer::type_enum)::get(null, get_full_name(),“l(fā)ocal_type”,local_type); status_device_id = uvm_config_db#(bit[7:0])::get(null, get_full_name(), “l(fā)ocal_device_id”,local_device_id); //If status of uvm_config_db::get is true then try to use the values // set by toplevel test or sequence instead of the random value. if(status_device_id || status_type) begin `uvm_create(req) req.randomize(); if(status_type) begin //Using the value set by top level test or sequence //instead of the random value. req.type = local_type; end if(status_device_id) begin //Using the value set by top level test or sequence //instead of the random value. req.device_id = local_device_id; end end repeat(sequence_length) `uvm_send(req)
結束任務:正文
使用'uvm_do_with時總是要小心的,因為它會在較低級別的序列或序列項中的任何現(xiàn)有約束之上添加約束。
另請注意,如果您有更多變量要“設置”和“獲取”,那么我建議您創(chuàng)建對象并在創(chuàng)建的對象中設置值,然后使用頂級測試/序列中的uvm_config_db設置此對象(而不是顯式設置此對象中的每個變量)。這樣,我們可以通過不搜索每個變量(當我們執(zhí)行 uvm_config_db::get 時)來提高運行時性能,而是使用該對象一次性獲取所有變量。
虛擬任務正文();
usb_transfer::type_enum local_type; bit[7:0] local_device_id; int status_seq_len = 0; int status_type = 0; int status_device_id = 0; status_seq_len = uvm_config_db#(int unsigned)::get(null, get_full_name(), “sequence_length”, sequence_length); status_type = uvm_config_db#(usb_transfer::type_enum)::get(null, get_full_name(),“l(fā)ocal_type”,local_type); status_device_id = uvm_config_db#(bit[7:0])::get(null, get_full_name(), “l(fā)ocal_device_id”,local_device_id); //If status of uvm_config_db::get is true then try to use the values // set by toplevel test or sequence instead of the random value. if(status_device_id || status_type) begin `uvm_create(req) req.randomize(); if(status_type) begin //Using the value set by top level test or sequence //instead of the random value. req.type = local_type; end if(status_device_id) begin //Using the value set by top level test or sequence //instead of the random value. req.device_id = local_device_id; end end repeat(sequence_length) `uvm_send(req)
結束任務:正文
始終嘗試通過為復雜方案創(chuàng)建頂級序列來重用簡單序列。例如,在下面的順序中,我嘗試發(fā)送批量傳輸,然后向 2 個不同的設備發(fā)送中斷傳輸。對于此場景,我將使用我們的usb_simple_sequence如下所示:
類 usb_complex_sequence 擴展uvm_sequence #(usb_transfer);
//Object of simple sequence used for sending bulk transfer usb_simple_sequence simp_seq_bulk; //Object of simple sequence used for sending interrupt transfer usb_simple_sequence simp_seq_int; … virtual task body(); //Variable for getting device_id for bulk transfer bit[7:0] local_device_id_bulk; //Variable for getting device_id for interrupt transfer bit[7:0] local_device_id_int; //Variable for getting sequence length for bulk int unsigned local_seq_len_bulk; //Variable for getting sequence length for interrupt int unsigned local_seq_len_int; //Get the values for the variables in case top level //test/sequence sets it. uvm_config_db#(int unsigned)::get(null, get_full_name(), “l(fā)ocal_seq_len_bulk”,local_seq_len_bulk); uvm_config_db#(int unsigned)::get(null, get_full_name(), “l(fā)ocal_seq_len_int”,local_seq_len_int); uvm_config_db#(bit[7:0])::get(null, get_full_name(), “l(fā)ocal_device_id_bulk”,local_device_id_bulk); uvm_config_db#(bit[7:0])::get(null, get_full_name(), “l(fā)ocal_device_id_int”,local_device_id_int); //Set the values for the variables to the lowerlevel //sequence/sequence item, which we got from //above uvm_config_db::get. //Setting the values for bulk sequence uvm_config_db#(int unsigned)::set(null, {get_full_name(),”.”, ”simp_seq_bulk”}, “sequence_length”,local_seq_len_bulk); uvm_config_db#(usb_transfer::type_enum)::set(null, {get_full_name(), “.”,“simp_seq_bulk”} , “l(fā)ocal_type”,usb_transfer::BULK_TRANSFER); uvm_config_db#(bit[7:0])::set(null, {get_full_name(),“.”, ”simp_seq_bulk”}, “l(fā)ocal_device_id”,local_device_id_bulk); //Setting the values for interrupt sequence uvm_config_db#(int unsigned)::set(null, {get_full_name(),”.”, ”simp_seq_int”}, “sequence_length”,local_ seq_len_int); uvm_config_db#(usb_transfer::type_enum)::set(null, {get_full_name(), “.”,“simp_seq_int”} , “l(fā)ocal_type”,usb_transfer::INT_TRANSFER); uvm_config_db#(bit[7:0])::set(null,{get_full_name(),“.”, ”simp_seq_bulk”},“l(fā)ocal_device_id”,local_device_id_int); `uvm_do(simp_seq_bulk) simp_seq_bulk.get_response(); `uvm_send(simp_seq_int) simp_seq_int.get_response(); endtask : body
結束類
請注意,在上面的序列中,我們使用 uvm_config_db::get 從頂級測試或序列中獲取值,然后使用 uvm_config_db::set 再次將其設置為較低級別的序列。如果我們嘗試使用 'uvm_do_with 并將值傳遞到約束塊內,那么這將作為附加約束應用而不是設置這些值,這很重要。
審核編輯:郭婷
-
usb
+關注
關注
60文章
7966瀏覽量
265295 -
IP
+關注
關注
5文章
1715瀏覽量
149718 -
UVM
+關注
關注
0文章
182瀏覽量
19197
發(fā)布評論請先 登錄
相關推薦
評論