就我個(gè)人而言,我覺得編寫sequence是在驗(yàn)證任何IP時(shí)最具挑戰(zhàn)性的部分。 首先需要仔細(xì)構(gòu)想場景,然后coding。如果沒有任何程度的復(fù)用,我們需要從頭為每個(gè)場景編寫一個(gè)sequence,這使得sequence難以維護(hù)和調(diào)試。
sequence的編寫和調(diào)試是非常體現(xiàn)驗(yàn)證工程師編碼能力的地方之一,如果每一個(gè)sequnce都有著完全不同的工作模式,那么維護(hù)起來非常痛苦。
網(wǎng)絡(luò)上有一個(gè)段子,程序員最討厭4件事情:
1、寫文檔
2、別人不寫文檔
3、寫注釋
4、別人不寫注釋
想象一下,如果你驗(yàn)證同事離職,交接給你上百個(gè)定向且詭異的測試用例或者sequence?你會(huì)不會(huì)立馬想去重構(gòu)。
sequences 由多個(gè)事務(wù)激勵(lì)組成,在UVM中其繼承自 參數(shù)化類uvm_sequence 。通過這些事務(wù)觸發(fā)一些驗(yàn)證工程師希望觸及的場景,而sequence的分層會(huì)創(chuàng)建一些更加復(fù)雜的場景激勵(lì)。驗(yàn)證空間隨著設(shè)計(jì)規(guī)模指數(shù)級(jí)上升,驗(yàn)證激勵(lì)自然也會(huì)越來越復(fù)雜。
|
class usb_simple_sequence extends 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
在上面的sequence 中,我們試圖將發(fā)送多次id為2的事務(wù),在uvm_test中將該sequence指定為default sequence即可。
|
class usb_simple_bulk_test extends 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
到目前為止,sequence看起來既簡單又直接。但是 直接的代碼往往意味著麻煩的堆疊。 為了確保sequence在更復(fù)雜的場景中重用,我們必須遵循一些準(zhǔn)則或者說代碼規(guī)范。
1、只在base sequence 類中的pre_start和post_start任務(wù)中raising objections和 dropping objections來管理測試用例的開始和結(jié)束。 通過這種方式,能夠減少每一個(gè)sequence子類中的相關(guān)phase控制代碼。
|
task pre_start()
if(starting_phase != null)
starting_phase.raise_objection(this);
endtask : pre_start
task post_start()
if(starting_phase != null)
starting_phase.drop_objection(this);
endtask : post_start
需要注意的是,只有被定義為default sequence才會(huì)自動(dòng)執(zhí)行starting_phase,否則就需要手動(dòng)調(diào)用了。
|
class usb_simple_bulk_test extends 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
endclass
2、使用UVM configurations 機(jī)制從測試用例中獲取值。 在上面的示例中,沒有給出控制sequence的按鈕,一些都靠sequence自身的隨機(jī),這對(duì)于擴(kuò)展用例非常不友好。我們可以對(duì)sequence做如下的修改,以 提供更加精確的激勵(lì)控制 。
|
class usb_simple_sequence extends 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
endclass
通過上述修改,我們對(duì)測試用例進(jìn)行了控制,以配置device_id、sequence_length和type。
這里需要注意的是:
uvm_config_db#()::set
中使用的參數(shù)類型和字符串(第三個(gè)參數(shù))應(yīng)該與
uvm_config_db#()::get
中使用的類型相匹配,否則將無法正確配置。**這個(gè)地方如果出錯(cuò)會(huì)非常痛苦,但好在不需要經(jīng)常修改,痛苦一次就好。**另外,這幾個(gè)配置項(xiàng)是隨機(jī)類型,相應(yīng)的配置值也需要滿足約束范圍。
3、在創(chuàng)建復(fù)雜sequence的時(shí)候盡量去復(fù)用簡單的sequence。 例如,在下面的sequence 中順序發(fā)送不同的sequnce (層次化和模塊化,永遠(yuǎn)是編碼規(guī)范之一) :
|
class usb_complex_sequence extends 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
endclass
-
UVM
+關(guān)注
關(guān)注
0文章
182瀏覽量
19184 -
代碼
+關(guān)注
關(guān)注
30文章
4791瀏覽量
68686 -
Coding
+關(guān)注
關(guān)注
0文章
6瀏覽量
6447
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論