一、为什么需要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