一、概述
1.1 类的继承
- uvm_object->uvm_transaction->**uvm_sequence_item**->uvm_sequence_base->**uvm_sequence**
1.2 数据传送机制
- get模式:driver从sequencer获取item
- 优势:1. driver获取到item即可结束传输,put需要收到返回值才发起下一次传输,效率会低一些;2. driver作为initiator能更好的实现sequence的仲裁特性
二、uvm_sequence_item
2.1 功能与数据成员
- 功能:生成item
- 数据成员类型
- 控制类:总线协议上的读写类型,数据长度,传送模式
- 负载类:数据总线上的数据包
- 配置类:控制driver的驱动行为,命令driver的发送间隔等
- 调试类:标记格外信息
2.2 示例
1.声明
2.注册与域的自动化
3.test中构建,用create具有可以覆盖,建立层次化关系的优点
4.随机化t.randomize();
class bus_trans extends uvm_sequence_item;
rand bit write ;
rand int data ;
rand int addr ;
rand int delay;
static int id_num;
`uvm_object_utils_begin(bus_trans)
`uvm_field_int...
`uvm_object_utils_end
endclass
class test1 extends uvm_test ;
`uvm_component_utils(test1)
task run_phase(uvm_phase phase) ;
bus_trans t1,t2 ;
phase.raise_objection(phase) ;
#100ns;
t1 = new("t1") ;
t1.print() ;
#200ns;
t2 = new("t2") ;
void'(t2.randomize()) ;
t2.print() ;
phase.drop_objection(phase) ;
endtask
endclass
三、uvm_sequence
3.1 功能与分类
- flat sequence:item实例构成
- hierarchical sequence:高层的sequence组织底层的sequence,让这些sequence按照顺序或并行挂载到同一个sequencer上
- virtual sequence:自身不挂载,将内部不同类型sequence挂载到不同的目标sequencer上
3.2 flat sequence
sequence被挂载后,自动执行**body**内容
class bus_trans extends uvm_sequence_item;
rand bit write;
rand int data[];
rand int length;
rand int addr;
rand int delay;
static int id_num;
constrint cstr{
data.size() == length;
soft addr == 'h10;
soft write == 1;
delay inside{[1:5]};
}//1.尽量将一段完整发生的数据传输中的、更长的的数据囊括在trans类中,可以使sequence不考虑数据内容,只考虑数据长度与地址等
`uvm_object_utils_begin(bus_trans)
uvm_field_int...
`uvm_object_utils_end
endclass
class flat_seq extends uvm_sequence;
rand int length;
rand int addr;
`uvm_object_utils(flat_seq)
function new(string name = "flat_seq");
super.new(name);
endfunction
task body();
bus_trans tmp;
tmp = new();
tmp.randomize() with {length == local::length;
addr == local::addr;};
tmp.print();
endclass
class test extends uvm_test;
`uvm_component_utils(test)
task run_phase(uvm_phase phase)
flat_seq seq;
phase.raise_objection(phase);
seq = flat_seq::type_id::create("seq");
seq.randomize() with{addr == 'h20; length = 3;};
seq.body();
phase.drop_objection(phase);
endtask
3.2 hierarchical sequence
1.创建sequence和trans
2.完成随机化
3.发送到sequencer
挂载到同一个sequencer
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 flat_seq extends uvm_sequence;
rand int length;
rand int addr;
`uvm_object_utils(flat_seq)
function new(string name = "flat_seq");
super.new(name);
endfunction
task body();
bus_trans tmp;
tmp = new();
tmp.randomize() with {length == local::length;
addr == local::addr;};
tmp.print();
endclass
class hier_seq extends uvm_sequence;
`uvm_object_utils(hier_seq)
function new(string name = "hier_seq");
super.new(name);
endfunction
task body();
bus_trans t1,t2;
flat_seq s1,s2;
`uvm_do_with(t1,{length == 2;})//item创建,随机化与发送到sequencer
fork
`uvm_do_with(s1,{length == 5})
`uvm_do_with(s2,{length == 8})
join
`uvm_do_with(t2,{length == 3})
endtask
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;
//读写
`uvm_do_with(req, {cmd == WRREG, addr == chnl*'h4;})
`uvm_do_with(req, {cmd == RDREG, addr == chnl*'h4;})
//读
`uvm_do_with(req, {cmd == RDREG, addr == chnl*'h4 + 'h10;})
endtask
endclass
class top_seq extends uvm_sequence;
task body();
reg_test_seq regseq0, regseq1, regseq2;
clk_rst_seq clkseq;
grab_seq grabs;
m_sequencer.set_arbitration(UVM_SEQ_ARB_STRICT_FIFO);
fork
`uvm_do_with(clkseq, {freq== 150;})//打开时钟150Mhz,reset
`uvm_do_with(regseq0, {chnl == 0;})
`uvm_do_with(regseq1, {chnl == 1;})
`uvm_do_with(regseq2, {chnl == 2;})//优先级比seq1和seq2低
join
endtask
endclass
class reg_master_driver extends uvm_driver;
task run_phase(uvm_phase phase);
REQ tmp;
bus_tranas 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
3.3 virtual sequence
- virtual sequencer定义所有要挂载的sequencer的句柄
- env实例化virtual sequencer,将所有sequencer句柄赋值给virtual sequencer
- virtual sequence通过uvm_declare_p_sequencer(virtual sequencer)获取所有句柄,通过uvm_do_on将不同的sequence的数据挂载到不同的sequencer上且发送
- test中实例化virtual sequence,将virtual sequence挂载到virtual sequencer上
//1.virtual sequencer
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;
mcdf_virtual_sequencer virt_sqr;
`uvm_component_utils(mcdf_virtual_sequencer)
function new(string name, uvm_coponent parent)
super.new(name,parent);
endfunction
endclass
//2.env
class mcdf_env extends uvm_env;
cr_master_agent cr_agt;
reg_master_agent reg_agt;
chnl_master_agent chnl_agt0;
chnl_master_agent chnl_agt1;
chnl_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(this);
endfunction
//创建各个有sequencer的容器
function 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_agt0",this);
chnl_agt1 = chnl_master_agent::type_id::create("chnl_agt1",this);
chnl_agt2 = chnl_master_agent::type_id::create("chnl_agt2",this);
fmt_agt = fmt_slave_agent::type_id::create("fmt_agt",this);
virt_sqr = mcdf_virtual_sequencer::type_id::create("virt_sqr",this);
endfunction
//避免悬空,非常重要
function void connect_phase(uvm_phase phase);
virt_sqr.cr_sqr = cr_agt.sqr;
virt_sqr.reg_sqr = reg_agr.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;
endfunciton
endclass:mcdf_env
//3.virtual sequence,分属不同的sequencer,clk_rst_seq....分别配置sequencer且发送数据
class mcdf_normal_seq extends uvm_sequence;
`uvm_object_utils(mcdf_normal_seq)
`uvm_declare_p_sequencer(mcdf_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;
//配置formatter 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
//4.test,将virtual sequence挂载到virtual sequencer上
class test1 extends uvm_test;
mcdf_env e;
task run_phase(uvm_phase phase);
mcdf_normal_seq seq;
phase.raise_objection(phase);
seq = mcdf_normal_seq::type_id::create("seq",this);
seq.start(e.virt_sqr);
phase.drop_objection(phase);
endtask
endclass:test1
3.4 layering sequence
高抽象的item、中间转换的sequence、低抽象的item
typedef enum {CLKON, CLKOFF, RESET, WRREG, RDREG} phy_cmd_t;
typedef enum {FREQ_LOW_TRANS, FREQ_MID_TRANS, FREQ_HIGH_TRANS} layer_cmd_t;
//1.layer_trans高抽象级item
class layer_trans extends uvm_sequence_item;
rand layer_cmd_t 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_MID_TRANS -> pkt_idle inside {[100:200]};
layer_cmd == FREQ_HIGH_TRANS -> pkt_idle inside {[20:40]};
}
endclass
//2.bus_trans低抽象的item
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
//3.packet_seq 低抽象级的item,bus_trans
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
//4.adapter_seq 转化层,从layer获取数据;挂载到phy_sequencer中,产生的为phy_sequence
class adapter_seq extends uvm_sequence;
`uvm_objecet_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);//upsqr为更上层的sequencer句柄,从layersequencer中获取layer item;经常在driver中调用,但是这个是sequencer的方法,拿到句柄后可以调用,得到layer_trans;
void'($cast(trans, req));
repeat(trans.pkt_len) begin
`uvm_do(pkt)
delay(trans.pkt_idle);
end
p_sequencer.up_sqr.item_done();//消化掉高抽象级的item
end
endtask
virtual task delay(int delay);
endtask
endclass
//5.top_seq 更顶层的sequence,连续发送两个layer_trans
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
//6.sequencer phy中有up_sqr
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_maaster_driver::type_id::create("drv",this);
endfunction
function void connect_phase(uvm_phasae 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;
adaper_seq adapter;
phase.raise_objection(phase):
seq = new();
adapter = new();
fork
adapter.start(phy_agt.sqr);//adaper_seq 挂载到phy上面
join_none
seq.start(layer_sqr);//top_seq 挂载到layer上面
phase.drop_objection(phase);
endtask
endclass
四、uvm_sequencer与uvm_driver
4.1 功能
driver从sequencer通过get模式获取item
uvm_sequencer
1. component通过TLM端口与driver传送item对象
2. 仲裁多个并行sequence,路由,sequencer有选择的动态挂载所需的sequence
uvm_driver
驱动激励时序
4.2 端口与方法
uvm_sequencer
· uvm_seq_item_pull_imp #(REQ,RSP,this_type) seq_item_export
· uvm_analysis_export#(RSP) rsp_export //analysis一对多端口,与fifo组件连接,fifo内部有imp,所以接收用的export端口;
uvm_driver
· uvm_seq_item_pull_port#(REQ,RSP) seq_item_port
· uvm_analysis_port#(RSP) rsp_port
driver常用方法
· seq_item_port.get_next_item(tmp)
· seq_item_port.item_done(rsp)
· seq_item_port.put_rsponse(rsp)//在sequence中get_response(rsp)对应
· rsp_port.write(RSP)
4.3 传输实例
//1.传输类
class bus_trans extends uvm_sequence_item;
rand int data;
`uvm_object_utils_begin(bus_trans)
`uvm_field_int(data,UVM_ALL_ON)
`uvm_object_utils_end
endclass
/*2.sequence类
挂载后通过seq.start自动执行body()内容
req=create_item(bus_trans::get_type(),m_sequencer,"req");创建request item对象
start_item(req);准备发送item
完成发送前对item进行随机处理
finish_item(req);完成item发送
get_response(tmp);从driver获取响应
*/
class flat_seq extends uvm_sequence;
`uvm_object_utils(flat_seq)
task body();
bus_trans req,rsp;
uvm_sequence_item tmp;//过渡与转换的
//trans的type,name和要被挂载的sequencer
tmp = create_item(bus_trans::get_type(),m_sequencer,"req");//类型强制转换了
void'($cast(req,tmp));
//可改为req=create_item(bus_trans::get_type(),m_sequencer,"req");
start_item(req);//立即返回,表示开始等待
req.randomize with {data == 10;};
finish_item(req);//阻塞,数据准备好,真实的在等待
get_response(tmp);//driver::put_response与item_done(rsp)一一对应
void'($cast(rsp,tmp));
endtask
endclass
//3.sequencer类
class sequencer extends uvm_sequencer;
`uvm_component_utils(sequencer)
endclass
//typedef uvm_sequencer#(bus_trans) sequencer;
//4.uvm_driver类
class driver extends uvm_driver;
`uvm_component_utils(driver)
task run_phase(uvm_phase phase);
REQ tmp;//源码的REQ
bus_trans req, rsp;
seq_item_port.get_next_item(tmp);//driver开始从sequencer获取数据
void'($cast(req,tmp));//强制转换,这个类型可以在定义类时将参数传入,默认为uvm_sequence_item
`uvm_info("DRV",$sformatf("got a item \n %s",req.sprint()),UVM_LOW)
void'($cast(rsp, req.clone()));//clone返回都是object
rsp.set_sequence_id(req.get_sequence_id());//id可以在response才知道发送给谁
rsp.data += 100;
seq_item_port.item_done(rsp);//done发送了respone,sequence需要获取get_response
`uvm_info("DRV",$sformatf("got a item \n %s",req.sprint()),UVM_LOW)
endtask
endclass
//5.env
class env extends uvm_env;
sequencer sqr;
driver drv;
`uvm_component_utils(env)
function void build_phase(uvm_phase phase);
sqr = sequencer::type_id::create("sqr",this);
drv = 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
//6.test
class test extends uvm_test;
env e;
`uvm_component_utils(test)
function void build_phase(uvm_phase phase);
e = env::type_id::create("e",this);
endfunction
task run_phase(uvm_phase phase);
flat_seq seq;
phase.raise_objection(phase);
seq = flat_seq::type_id::create("seq");
seq.start(e.sqr);//挂载,body自动执行,这里sequence还没有随机化
phase.drop_objection(phase);
endtask
endclass
五、uvm_sequencer与uvm_sequence
5.1 sequence挂载到sequencer
seq.start(要挂载的sequencer, 上层的sequence可以保留优先级,优先级,是否执行pre_body()和post_body())
5.2 item挂载到sequencer
create_item//创建item
start_item(req);
finish_item(req);
只有item才有优先级
5.3 发送序列的相关宏
只有sequence可以调用这些宏
class child_seq extends uvm_sequence;
task body();
bus_trans req;
`uvm_create(req)
`uvm_rand_send_with(req, {data == 10;})
endtask
endclass
class top_seq extends uvm_sequence;
task body();
child_seq cseq;
bus_trans req;
`uvm_do(cseq)
`uvm_do_with(req,{data == 20})
endtask
endclass
5.4 仲裁模式
uvm_sequencer::set_arbitration(UVM_SEQ_ARB_TYPE val)设置仲裁模式
UVM_SEQ_ARB_FIFO:默认,fifo先入先出一次授权
UVM_SEQ_ARB_WEIGHTED:按优先级权重随机授权
UVM_SEQ_ARB_RANDOM:随机授权
UVM_SEQ_ARB_STRICT_FIFO:按优先级和抵达顺序依次授权
class child_seq extends uvm_sequence;
rand int base;
task body();
bus_trans req;
repeat(2) `uvm_do_with(req, {data inside {[base: base + 9]};//wait_for_grant才会等待授权
endtask
endclass
class top_seq extends uvm_sequence;
task body();
child_seq seq1, seq2, seq3;
m_sequencer.set_arbitration(UVM_SEQ_ARB_STRICT_FIFO);
fork
`uvm_do_pri_with(seq1, 500, {base == 10;})//同一时间申请发起传送请求,uvm_do_pri_with发送sequence时可以传递优先级参数
`uvm_do_pri_with(seq2, 500, {base == 20;})
`uvm_do_pri_with(seq3, 300, {base == 30;})
join
endtask
endclass
class 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 %0d from parent sequence %s", req.data, req.get_parent_sequence().get_name()),UVM_LOW)
seq_item_port.item_done();
end
endtask
endclass
class test extends uvm_test;
env e;
task run_phase(uvm_phase phase);
top_seq seq;
phase.raise_objection(phase);
seq = top_seq::type_id::create("seq");
seq.start(e.sqr);//只有在sequence里面才可以用宏
phase.drop_objection(phase);
endtask
endclass
5.5 sequencer的锁定机制
lock() unclock()按优先级获取
grab() ungrab()不按优先级
class lock_seq extends uvm_sequence;
task body();
bus_trans req;
#10ns;
m_sequencer.lock(this);//等待权限时,lock住
`uvm_info("LOCK","get exclusive access by lock()"),UVM_LOW)
repeat(3) #10ns
`uvm_do_with(req, {data inside {[100:110]};})
m_sequencer.unclock(this);
endtask
endclass
class grab_seq extends uvm_sequence;
task body();
bus_trans req;
#20ns;
m_sequencer.grab(this);//
`uvm_info("GRAB","get exclusive access by grab()"),UVM_LOW)
repeat(3) #10ns
`uvm_do_with(req, {data inside {[200:210]};})
m_sequencer.ungrab(this);
endtask
endclass
class top_seq extends uvm_sequence;
task body();
child_seq seq1, seq2, seq3;
lock_seq locks;
grab_seq grabs;
m_sequencer.set_arbitration(UVM_SEQ_ARB_STRICT_FIFO);
fork
`uvm_do_pri_with(seq1, 500, {base == 10;})//同一时间申请发起传送请求,uvm_do_pri_with发送sequence时可以传递优先级参数
`uvm_do_pri_with(seq2, 500, {base == 20;})
`uvm_do_pri_with(seq3, 300, {base == 30;})
`uvm_do_pri(locks,300)//优先级比seq1和seq2低
`uvm_do(grabs)//没有优先级,不管优先级,20ns才开始获取
join
endtask
endclass