在這篇博客中,我描述了在編寫序列時必須采取的必要步驟,以確保它可以重用。就我個人而言,我覺得編寫序列是驗(yàn)證任何IP最具挑戰(zhàn)性的部分。編寫序列需要仔細(xì)規(guī)劃,否則我們最終會從頭開始為每個場景編寫一個序列。這使得序列難以維護(hù)和調(diào)試。
眾所周知,序列由幾個數(shù)據(jù)項(xiàng)組成,它們共同構(gòu)成了一個有趣的場景。序列可以是分層的,從而創(chuàng)建更復(fù)雜的方案。在最簡單的形式中,序列應(yīng)該是 uvm_sequence 基類的派生,方法是指定請求和響應(yīng)項(xiàng)類型參數(shù),并使用要執(zhí)行的特定方案實(shí)現(xiàn) body 任務(wù)。
類 usb_simple_sequence 擴(kuò)展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 的設(shè)備。測試編寫者可以通過將此序列分配給頂級測試中序列器的默認(rèn)序列來調(diào)用此序列。
類usb_simple_bulk_test擴(kuò)展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
到目前為止,事情看起來簡單明了。為了確保序列可重用于更復(fù)雜的場景,我們必須遵循更多準(zhǔn)則。
首先,通過在序列類中pre_start和post_start任務(wù)中提出和放棄異議來管理測試結(jié)束非常重要。這樣,我們只在最上面的序列中提出和放棄異議,而不是對所有子序列都這樣做。
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僅為作為特定階段的默認(rèn)序列啟動的序列定義。如果已通過調(diào)用序列的 start 方法顯式啟動它,則用戶負(fù)責(zé)設(shè)置starting_phase。
類usb_simple_bulk_test擴(kuò)展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
結(jié)束類
使用 UVM 配置從頂級測試中獲取值。在上面的示例中,沒有為測試編寫者提供可控性,因?yàn)樾蛄胁皇褂门渲脧捻敿墱y試或序列中獲取值(將使用此序列來構(gòu)建復(fù)雜場景)。修改序列以更好地控制使用此簡單序列的頂級測試或序列。
類 usb_simple_sequence 擴(kuò)展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
結(jié)束類
通過上述修改,我們已經(jīng)控制了頂級測試或序列來修改device_id、sequence_length和類型。這里需要注意的幾點(diǎn):uvm_config_db#()::set 中使用的參數(shù)類型和字符串(第三個參數(shù))應(yīng)該與 uvm_config_db#()::get 中使用的類型匹配。確保使用確切的數(shù)據(jù)類型“設(shè)置”和“獲取”。否則,值將無法正確設(shè)置,調(diào)試將成為一場噩夢。
上述序列的一個問題是:如果 usb_transfer 類中對device_id或類型有任何約束,那么這將限制頂級測試或序列以確保它在約束范圍內(nèi)。
例如,如果usb_transfer類中的device_id存在約束,將其約束為低于 10,則頂級測試或序列應(yīng)在此范圍內(nèi)約束它。如果頂級測試或序列將其設(shè)置為類似 15 的值(超過 10),則在運(yùn)行時將看到約束失敗。
有時,頂級測試或序列可能需要完全控制,并且可能不希望啟用在較低級別的序列或數(shù)據(jù)項(xiàng)中定義的約束。需要這樣做的一個示例是陰性測試:- 主機(jī)希望確保設(shè)備不會響應(yīng)device_id大于 10 的傳輸,因此希望發(fā)送device_id 15 的傳輸。因此,為了完全控制頂級測試或序列,我們可以修改正文任務(wù),如下所示:
虛擬任務(wù)正文();
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)
結(jié)束任務(wù):正文
使用'uvm_do_with時總是要小心的,因?yàn)樗鼤谳^低級別的序列或序列項(xiàng)中的任何現(xiàn)有約束之上添加約束。
另請注意,如果您有更多變量要“設(shè)置”和“獲取”,那么我建議您創(chuàng)建對象并在創(chuàng)建的對象中設(shè)置值,然后使用頂級測試/序列中的uvm_config_db設(shè)置此對象(而不是顯式設(shè)置此對象中的每個變量)。這樣,我們可以通過不搜索每個變量(當(dāng)我們執(zhí)行 uvm_config_db::get 時)來提高運(yùn)行時性能,而是使用該對象一次性獲取所有變量。
虛擬任務(wù)正文();
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)
結(jié)束任務(wù):正文
始終嘗試通過為復(fù)雜方案創(chuàng)建頂級序列來重用簡單序列。例如,在下面的順序中,我嘗試發(fā)送批量傳輸,然后向 2 個不同的設(shè)備發(fā)送中斷傳輸。對于此場景,我將使用我們的usb_simple_sequence如下所示:
類 usb_complex_sequence 擴(kuò)展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
結(jié)束類
請注意,在上面的序列中,我們使用 uvm_config_db::get 從頂級測試或序列中獲取值,然后使用 uvm_config_db::set 再次將其設(shè)置為較低級別的序列。如果我們嘗試使用 'uvm_do_with 并將值傳遞到約束塊內(nèi),那么這將作為附加約束應(yīng)用而不是設(shè)置這些值,這很重要。
審核編輯:郭婷
-
usb
+關(guān)注
關(guān)注
60文章
8441瀏覽量
284603 -
IP
+關(guān)注
關(guān)注
5文章
1863瀏覽量
155877 -
UVM
+關(guān)注
關(guān)注
0文章
183瀏覽量
20018
發(fā)布評論請先 登錄
UVM序列的創(chuàng)建和運(yùn)行及中斷服務(wù)程序?qū)崿F(xiàn)方案
可重用機(jī)床編碼技術(shù)及重構(gòu)算法研究
IC驗(yàn)證"為什么要學(xué)習(xí)UVM呢"
數(shù)字IC驗(yàn)證之“什么是UVM”“UVM的特點(diǎn)”“UVM提供哪些資源”(2)連載中...
數(shù)字IC驗(yàn)證之“典型的UVM平臺結(jié)構(gòu)”(3)連載中...
談?wù)?b class='flag-5'>UVM中的uvm_info打印
可重用機(jī)床編碼技術(shù)及重構(gòu)算法研究
UVM驗(yàn)證平臺執(zhí)行硬件加速
ASIC芯片設(shè)計(jì)之UVM驗(yàn)證
什么是UVM environment?
UVM中的虛擬序列:為什么,如何?
介紹從一組可重用的驗(yàn)證組件中構(gòu)建測試平臺所需的步驟
可重用的驗(yàn)證組件中構(gòu)建測試平臺的步驟
UVM中的可重用序列
評論