学习目标
UVM入门和进阶部分8
学习内容
1.将sequence挂载到sequencer上:
uvm_sequence::start(uvm_sequencer_base sequencer,uvm_sequence_base parent_sequence=null,int this_priority=-1,bit call_pre_post=1)
用户首先应该指明sequencer的句柄,如果sequence是顶层sequence,则可以省略第二个参数parent_sequence的指定;
第三个参数的默认值为-1,会使得该sequence继承parent_sequence的优先级,若为顶层sequence,默认为100,用户也可以自定义优先级数值;
第四个参数建议使用默认值,为1时,uvm_sequence::pre_doby()和uvm_sequence::post_body()两个方法会在uvm_sequence::body()的前后执行
2.将item挂载到sequencer上:
uvm_sequence::start_item(uvm_sequence_item item,int set_priority=-1,uvm_sequencer_base sequencer=null);
uvm_sequence::finish_item(uvm_sequence_item item,int set_priority=-1);
3.对于一个item的完整传送,sequence要在sequencer一侧获得通过权限,才可以顺利将item发送至driver,具体步骤:
创建item
通过start_item()方法等待获取sequencer的授权许可,其后执行parent sequence的方法pre_do()
对item进行随机化处理
通过finish_item()方法在对item进行了随机化处理之后,执行parent sequence的mid_do(),以及调用uvm_sequencer::send_request()和uvm_sequencer::wait_for_item_done()来将item发送至sequencer再完成与driver之间的握手,最后执行了parent_sequence的post_do()
4.注意点:
sequence和item自身的优先级,可以决定什么时刻可以获得sequencer的授权
parent sequence的虚方法pre_do()、mid_do()和post_do()会发生在发送item的过程中间
5.start()的自然代码执行顺序和条件
6.start_item()/finish_item()的自然代码执行顺序和条件
7.发送序列的相关宏
8.uvm_sequencer类自建了仲裁机制,用来保证多个sequence在同时挂载到sequencer时,可以按照仲裁规则允许特定sequence中的item优先通过
9.通过uvm_sequencer::set_arbitration(UVM_SEQ_ARB_TYPE val)函数来设置仲裁模式
10.lock()和grab()锁定机制,grab()更为强势,只需要在sequencer下一次授权周期时就可以无条件得获得授权,可以无视同一时刻内发起传送请求的其他sequence,但是不能无视已经预先获得授权的其他lock或者grab的sequence
11.层次化sequence:hierarchical sequence(hierarchical等级制)、virtual sequence、layering sequence
12.hierarchical sequence例子
typedef enum {CLKON,CLKOFF,RESET,WRREG,RDREG} cmd_t;
class bus_trans extends uvm_sequence_item;
rand cmd_t cmd;
rand int addr;
rand int data;
constraint cstr{
soft addr=='h0;
soft data=='h0;
}
...
endclass
class clk_rst_seq extends uvm_sequence;
rand int freq;
...
task body();
bus_trans req;
'uvm_do_with(req,{cmd==CLKON;data==freq;})
'uvm_do_with(req,{cmd==RESET;})
endtask
endclass
class reg_test_seq extends uvm_sequence;
rand int chnl;
...
task body();
bus_trans req;
//对wr寄存器读写测试
'uvm_do_with(req,{cmd==WRREG;addr==chnl*'h4;})
'uvm_do_with(req,{cmd==RDREG;addr==chnl*'h4;})
//对rd寄存器读测试
'uvm_do_with(req,{cmd==RDREG;addr==chnl*'h4+'h10;})
endtask
endclass
class top_seq extends uvm_sequence; //top_seq为hierarchical sequence
... //clk_rst_seq、reg_test_seq为element sequence
task body();
clk_rst_seq clkseq;
reg_test_seq regseq0,regseq1,regseq2;
//打开150mhz时钟,重置断言
'uvm_do_with(clkreq,{freq==150;})
'uvm_do_with(regreq0,{chnl==0;})
'uvm_do_with(regreq1,{chnl==1;})
'uvm_do_with(regreq2,{chnl==2;})
endtask
endclass
class reg_master_sequencer extends uvm_sequencer;
...
endclass
class reg_master_driver extends uvm_driver;
...
task run_hase(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
enclass
class reg_master_agent extends uvm_agent;
reg_master_sequencer sqr;
reg_master_driver drv;
...
function void build_phase(uvm_phase phase);
sqr=reg_master_sequencer::type_id::create("seq",this);
drv=reg_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
enclass
13.hierarchical sequence面向的对象是同一个sequencer,即hierarchical sequence本身也会挂载到sequencer上面,而对于virtual sequence而言,它内部不同的sequence可以允许面向不同的sequencer种类
14.virtual sequencer与普通的sequencer相比有着很大的不同,它们起到了桥接其他sequencer的作用,即virtual sequencer是一个链接所有底层sequencer句柄的地方,它是一个中心化的路由器;
virtual sequencer本身并不会传送item数据对象,因此virtual sequencer不需要与任何driver进行TLM连接,所以UVM用户需要在顶层的connect阶段,做好virtual sequencer中各个sequencer句柄与底层sequencer实体对象的一一对接,避免句柄悬空
15.virtual sequencer示例
class mcdf_normal_seq extends uvm_sequence;
'uvm_object_utils(mcdf_normal_seq)
'uvm_declare_p_sequencer(mdcf_virtual_sequencer) //宏
...
task body();
clk_rst_seq clk_seq;
reg_cfg_seq cfg_seq;
data_trans_seq data_seq;
fmt_slv_cfg_seq fmt_seq;
//配置fmt slave agent
‘uvm_do_on(fmt_seq,p_sequencer.fmt_sqr)
//打开时钟并完成复位
‘uvm_do_on(clk_seq,p_sequencer.cr_sqr)
//配置MCDF寄存器
‘uvm_do_on(cfg_seq,p_sequencer.reg_sqr)
//传送channel数据包
fork
'uvm_do_on(data_seq,p_sequencer.chnl_sqr0)
'uvm_do_on(data_seq,p_sequencer.chnl_sqr1)
'uvm_do_on(data_seq,p_sequencer.chnl_sqr2)
join
endtask
endclass
//省略子一级的sequencer和agent定义
class mcdf_virtual_sequencer extends uvm_sequencer;
cr_master_sequencer cr_sqr;
reg_master_sequencer reg_sqr;
chnl_master_sequencer chnl_sqr0;
chnl_master_sequencer chnl_sqr1;
chnl_master_sequencer chnl_sqr2;
fmt_slave_sequencer fmt_sqr;
'uvm_component_utils(mcdf_virtual_sequencer)
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
endclass
class mcdf_env extends uvm_env;
cr_master_agent cr_agt;
reg_master_agent reg_agt;
cnl_master_agent chnl_agt0;
cnl_master_agent chnl_agt1;
cnl_master_agent chnl_agt2;
fmt_slave_agent fmt_agt;
mcdf_virtual_sequencer virt_sqr;
'uvm_component_utils(mcdf_env)
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
fucntion void build_phase(uvm_phase phase);
cr_agt=cr_master_agent::type_id::create("cr_agt",this);
reg_agt=reg_master_agent::type_id::create("reg_agt",this);
chnl_agt0=chnl_master_agent::type_id::create("chnl_agt",this);
chnl_agt1=chnl_master_agent::type_id::create("chnl_agt",this);
chnl_agt2=chnl_master_agent::type_id::create("chnl_agt",this);
fmt_agt=fmt_slave_agent::type_id::create("fmt_agt",this);
virt_sqr=mcdf_virtual_sequencer::type_id::create("virt_sqr",this);
enfunction
funciton void connect_phase(uvm_phase phase);
//virtual sequencer连接,但是不和sequencers进行任何TLM连接
//virtual sequencer中各个sequencer句柄与底层sequencer的跨层次链接
virt_sqr.cr_sqr=cr_agt.sqr;
virt_sqr.reg_sqr=reg_agt.sqr;
virt_sqr.chnl_sqr0=chnl_agt0.sqr;
virt_sqr.chnl_sqr1=chnl_agt1.sqr;
virt_sqr.chnl_sqr2=chnl_agt2.sqr;
virt_sqr.fmt_sqr=fmt_agt.sqr;
endfunction
endclass:mcdf_env
class test1 extends uvm_test;
mcdf_env e;
...
task run_phase(uvm_phase phase);
mcdf_normal_seq seq;
phase.raise_objection(phase);
seq=new();
seq.start(e.virt_sqr);
phase.drop_objection(phase);
endtask
endclass:test1
在最后test1中,将virtual sequence挂载到virtual sequencer上,这种挂载的根本目的是为了提供给virtual sequence一个中心化的sequencer路由,而借助virtual sequence mcdf_normal_seq中使用了宏’uvm_declare_p_sequencer,使得virtual sequence可以使用声明后的成员变量p_sequencer,来进一步回溯到virtual sequencer内部的各个sequencer句柄
virtual sequencer的路由作用,以及在顶层中需要完成virtual sequencer同底层sequencer的连接,并最终在test层实现virtual sequence挂载到virtual sequencer上
16.通过层次化的sequence可以分别构建transaction layer、transport layer和physical layer等从高抽象级到低抽象级的transaction转化,这种层次化的sequence构建方式,称之为layering sequence
17.layering sequence例子
typedef enum{CLKON,CLKOFF,RESET,WRREG,REREG} 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;
}
...
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;
}
...
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 interestingpkt_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
class adapter_seq extends uvm_sequence;
'uvm_object_utils(adapter_seq)
'uvm_declare_p_sequencer(phy_master_sequencer)
...
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;
...
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;
...
endclass
class phy_master_sequencer extends uvm_sequencer;
layering_sequencer up_sqr;
...
endclass
class phy_master_driver extends uvm_driver;
...
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;
...
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 test1 extends uvm_test;
layering_sequencer layer_sqr;
phy_master_agent phy_agt;
...
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