初識Layering Sequence
一、為什么需要Layering Sequence
隨著集成電路技術的發展,芯片的復雜度日益提升。對驗證而言,為了更好地應對這種復雜性,一方面是提高各個級別的可移植性和復用性,另一方面是提高抽象級別,減小復雜度。
Layering Sequence這正是從第二個方面出發衍生的,它能夠將高抽象級的uvm_sequence_item和低抽象級的uvm_sequence_item相互轉換。
二、Layering Sequence機制
下面以《芯片驗證漫游指南》13.5.3節為例,簡單分析Layering Sequence的工作機制
1、高抽象級的layer_trans和低抽象級的bus_trans
首先定義一個低抽象級的bus_trans,并且將其打包成為packet_seq
點擊查看代碼
//低抽象級的bus_trans
class bus_trans extends uvm_sequence_item;
rand phy_cmd_t cmd;
rand int addr;
rand int data;
constraint cstr{
soft addr == 'h0;
soft data == 'h0;
}
...
endclass
//打包后的packet_seq
class packet_seq extends uvm_sequence;
rand int len;
rand int addr;
rand int data[];
rand phy_cmd_t cmd;
constraint cstr{
soft len inside {[30:50]};
soft addr[31:16] == 'hFF00;
data.size() == len;
}
...
task body();
bus_trans req;
foreach(data[i])
`uvm_do_with(req, {cmd == local::cmd;
addr == local::addr;
data == local::data[i];})
endtask
endclass
其對應的sequencer是:
點擊查看代碼
class phy_master_sequencer extends uvm_sequencer;
layering_sequencer up_sqr;
...
endclass
接下來定義一個高抽象級layer_trans
點擊查看代碼
class layer_trans extends uvm_sequence_item;
rand layer_cmd_t layer_cmd;
rand int pkt_len;
rand int pkt_idle;
constraint cstr {
soft pkt_len inside {[10: 20]};
layer_cmd == FREQ_LOW_TRANS -> pkt_idle inside {[300:400]};
layer_cmd == FREQ_MED_TRANS -> pkt_idle inside {[100:200]};
layer_cmd == FREQ_HIGH_TRANS -> pkt_idle inside {[20:40]};
}
...
endclass
其對應的sequencer是:
點擊查看代碼
class layering_sequencer extends uvm_sequencer;
...
endclass
可以看到高抽象級的layer_trans和低抽象級的bus_trans差別很大,也沒有任何層次上的關系,因此需要有中間的轉化層將兩者進行轉化后才能夠正常發送給DUT。
2、轉化層的sequece
用adapter_seq作為轉化層
點擊查看代碼
class adapter_seq extends uvm_sequence;
`uvm_object_utils(adapter_seq)
`uvm_declare_p_sequencer(phy_master_sequencer)//句1
function new(string name = "adapter_seq");
super.new(name);
endfunction
task body();
layer_trans trans;
packet_seq pkt;
forever begin
p_sequencer.up_sqr.get_next_item(req);//句2-1
void'($cast(trans, req));
repeat(trans.pkt_len) begin
`uvm_do(pkt)//句3
delay(trans.pkt_idle);
end
p_sequencer.up_sqr.item_done();//句2-2
end
endtask
virtual task delay(int delay);
endtask
endclass
3、layering sequence的工作機制
下面結合結構圖分析一下layering sequence的工作機制
-
句1:通過uvm_declare_p_sequencer宏指定adapter_seq的item由phy_master_sequencer來發送,也構建了adapter_seq訪問phy_master_sequencer成員變量和成員方法的橋梁(詳見初識m_sequencer、p_sequencer和uvm_declare_p_sequencer宏)
-
句2-1:如圖綠色曲線所示,通過p_sequencer訪問phy_master_sequencer中的up_sqr,而up_sqr在test中通過句4將up_sqr指向layer_sqr(test代碼第13行)。此時adapter_seq能夠調用layer_sqr的get_next_item任務取得一個高抽象級的layer_trans。
-
句3:如圖藍色曲線所示,按照已取得的layer_trans中的屬性(pkt_len和pkt_idle)約束低抽象級的packet_seq類型的pkt的屬性,并交給phy_master_sequencer通過driver發送給DUT。
-
句2-2:依舊通過綠色曲線所示通路,使用item_done通知layer_sqr。
4、在test中的連接關系
test代碼:
點擊查看代碼
class test extends uvm_test;
layering_sequencer layer_sqr;
phy_master_agent phy_agt;
`uvm_component_utils(test1)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
layer_sqr = layering_sequencer::type_id::create("layer_sqr", this);
phy_agt = phy_master_agent::type_id::create("phy_agt", this);
endfunction
function void connect_phase(uvm_phase phase);
phy_agt.sqr.up_sqr = layer_sqr;//句4
endfunction
task run_phase(uvm_phase phase);
top_seq seq;
adapter_seq adapter;
phase.raise_objection(phase);
seq = new();
adapter = new();
fork
adapter.start(phy_agt.sqr);//句5
join_none
seq.start(layer_sqr);//句6
phase.drop_objection(phase);
endtask
endclass
- 句4:將phy_master_sequencer中的up_sqr指向layer_sqr,構建與layer_sqr的通路,使得adapter能夠取得layer_trans。
- 句5:將adapter_seq掛載到phy_master_sequencer。
- 句6:將top_seq掛載到layer_sqr。
一些其他問題:
- 為什么使用fork-join_none:在adapter_seq的body()中使用的forever語句,使得adapter_seq永不停歇的取得高抽象級item,轉化成低抽象級的item去執行。因此使用fork-join_none使adapter_seq不阻擋其他功能正常運行。
- 為什么采用句4和get_next_item()而不是TLM的方式:這是為了最大程度的提高復用性和減小復雜度。
參考資料
- 《芯片驗證漫游指南——從系統理論到UVM的驗證全視界》劉斌著
- Universal Verification Methodology (UVM) 1.2 User’s Guide
完整代碼如下:
點擊查看代碼
module layer_seq;
import uvm_pkg::*;
`include "uvm_macros.svh"
typedef class phy_master_sequencer;
typedef enum {CLKON, CLKOFF, RESET, WRREG, RDREG} phy_cmd_t;
typedef enum {FREQ_LOW_TRANS, FREQ_MED_TRANS, FREQ_HIGH_TRANS} layer_cmd_t;
class bus_trans extends uvm_sequence_item;
rand phy_cmd_t cmd;
rand int addr;
rand int data;
constraint cstr{
soft addr == 'h0;
soft data == 'h0;
}
`uvm_object_utils_begin(bus_trans)
`uvm_field_enum(phy_cmd_t, cmd, UVM_ALL_ON)
`uvm_field_int(addr, UVM_ALL_ON)
`uvm_field_int(data, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "bus_trans");
super.new(name);
endfunction
endclass
class packet_seq extends uvm_sequence;
rand int len;
rand int addr;
rand int data[];
rand phy_cmd_t cmd;
constraint cstr{
soft len inside {[30:50]};
soft addr[31:16] == 'hFF00;
data.size() == len;
}
`uvm_object_utils(packet_seq)
function new(string name = "reg_test_seq");
super.new(name);
endfunction
task body();
bus_trans req;
foreach(data[i])
`uvm_do_with(req, {cmd == local::cmd;
addr == local::addr;
data == local::data[i];})
endtask
endclass
class layer_trans extends uvm_sequence_item;
rand layer_cmd_t layer_cmd;
rand int pkt_len;
rand int pkt_idle;
constraint cstr {
soft pkt_len inside {[10: 20]};
layer_cmd == FREQ_LOW_TRANS -> pkt_idle inside {[300:400]};
layer_cmd == FREQ_MED_TRANS -> pkt_idle inside {[100:200]};
layer_cmd == FREQ_HIGH_TRANS -> pkt_idle inside {[20:40]};
}
`uvm_object_utils(layer_trans)
function new(string name = "layer_trans");
super.new(name);
endfunction
endclass
class adapter_seq extends uvm_sequence;
`uvm_object_utils(adapter_seq)
`uvm_declare_p_sequencer(phy_master_sequencer)
function new(string name = "adapter_seq");
super.new(name);
endfunction
task body();
layer_trans trans;
packet_seq pkt;
forever begin
p_sequencer.up_sqr.get_next_item(req);
void'($cast(trans, req));
repeat(trans.pkt_len) begin
`uvm_do(pkt)
delay(trans.pkt_idle);
end
p_sequencer.up_sqr.item_done();
end
endtask
virtual task delay(int delay);
endtask
endclass
class top_seq extends uvm_sequence;
`uvm_object_utils(top_seq)
function new(string name = "top_seq");
super.new(name);
endfunction
task body();
layer_trans trans;
`uvm_do_with(trans, {layer_cmd == FREQ_LOW_TRANS;})
`uvm_do_with(trans, {layer_cmd == FREQ_HIGH_TRANS;})
endtask
endclass
class layering_sequencer extends uvm_sequencer;
`uvm_component_utils(layering_sequencer)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
endclass
class phy_master_sequencer extends uvm_sequencer;
layering_sequencer up_sqr;
`uvm_component_utils(phy_master_sequencer)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
endclass
class phy_master_driver extends uvm_driver;
`uvm_component_utils(phy_master_driver)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
REQ tmp;
bus_trans req;
forever begin
seq_item_port.get_next_item(tmp);
void'($cast(req, tmp));
`uvm_info("DRV", $sformatf("got a item \n %s", req.sprint()), UVM_LOW)
seq_item_port.item_done();
end
endtask
endclass
class phy_master_agent extends uvm_agent;
phy_master_sequencer sqr;
phy_master_driver drv;
`uvm_component_utils(phy_master_agent)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
sqr = phy_master_sequencer::type_id::create("sqr", this);
drv = phy_master_driver::type_id::create("drv", this);
endfunction
function void connect_phase(uvm_phase phase);
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
endclass
class test extends uvm_test;
layering_sequencer layer_sqr;
phy_master_agent phy_agt;
`uvm_component_utils(test1)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
layer_sqr = layering_sequencer::type_id::create("layer_sqr", this);
phy_agt = phy_master_agent::type_id::create("phy_agt", this);
endfunction
function void connect_phase(uvm_phase phase);
phy_agt.sqr.up_sqr = layer_sqr;
endfunction
task run_phase(uvm_phase phase);
top_seq seq;
adapter_seq adapter;
phase.raise_objection(phase);
seq = new();
adapter = new();
fork
adapter.start(phy_agt.sqr);
join_none
seq.start(layer_sqr);
phase.drop_objection(phase);
endtask
endclass
initial begin
run_test("test");
end
endmodule
本文作者:程默白
轉載請注明原文鏈接:http://www.htjh666.com/ChengMobai/p/LayeringSequence.html