目录
类比:sequence=道路,seuqence item=货车,sequencer=目的地收费站,driver=卸货地点。item从sequence一端出发经过sequencer最终到达driver。
- driver陆续拿到多个随机化的item,经过数据解析,按照与dut的物理接口协议将数据写入到接口上,对dut形成激励
- 有必要时,driver解析完一个item回返回最后的状态信息给item对象并回到出发点(互动)
继承关系
- item和sequence可以在任何phase创建
- 无法通过uvm环境结构或phase机制来识别sequence的运行阶段
- 顶层配置时无法按照层次关系直接配置到sequence中,因为item和sequence不在uvm结构中
- sequence活动起来必须挂在attach到一个sequencer上,间接通过sequencer来获取顶层的配置和信息
- sequence只负责生成item的内容,driver负责驱动激励时序,最终的接口驱动时序由driver和sequencer共同决定
- sequencer:作为一个组件可以通过tlm端口与driver传送tiem对象;面向多个并行sequence有仲裁机制来合理分配和传送item
- 数据传送机制采用get模式:driver从sequencer获取item
sequence和item
- 分别是uvm_sequence类和uvm_sequence_item类
- sequence编织激励生成和场景控制;对于激励所需要的的具体数据和控制要求,是从itme的成员数据得来的
uvm_sequence_item
- item基于uvm_object类,具备uvm核心基类的数据操作方法:copy,clone, compare,record
- item具备的数据成员:
- 控制类(总线协议上的读写模式、数据长度、传送模式),
- 负载类(数据总线上的数据包),
- 配置类(控制driver的驱动行为)
- 调试类(标记信息以方便调试,id、time)
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()
`uvm_field_int()
...
`uvm_object_utils_end
...endclass
class test1 extends uvm_test;
`uvm_component_utils()
...
task run_phase(u p);
bus_trans t1, t2;
phase.raise_objection(p);
#100ns;
t1=new("t1");
t1.print();
#200ns;
t2=new("t2");
void'(t2.randomize());
t2.print();
phase.drop_objection(p);
endtask
endclass
- 如果数据域属于需要用来做驱动,应该考虑定义为rand,按照驱动协议给出合适的约束constraint
- 域的自动化不要忘记,便于使用uvm_object的基本数据方法
- uvm要求item的创建和随机化都要发生在sequence的body()任务中
- item生命周期:产生于sequence的body(),经历随机化,穿越sequencer,到达driver,被消化
uvm_sequence
- 由于item在创建后需要被随机化,sequence在声明时也需要预留一些可供外部随机化的变量,部分用来通过层级传递约束最终控制item对象的随机变量,部分用来对item对象之间加以组织和时序控制
- 分类
- flat sequence扁平类:用来组织更小的粒度,即item实例构成的组织
- hierarchical sequence层次类:由更高层的sequence用来组织底层的sequence,按照一定顺序或并行,挂载到同一个sequencer
- virtual sequence虚拟类:最终控制整个测试场景的方式,针对多种类型的sequencer协调顶层的测试场景,该sequence不会固定挂载在某种sequencer上,而是将其内部不同类型sequence最终挂载到不同的目标sequencer上
flat sequence
往往由sequence item群落构成
- 包含的信息:
- itme以及相关的constraint,用来关联生成的item之间的关系;
- 给定各个item之间的时序信息,也就是pkg_idle
- 响应具体时间从而创建对应的item并发送出去,比如等待monitor的信号后与driver读操作
class bus_trans extends uvm_sequence_item;
rand bit write;
rand int data;
rand int addr;
rand int delay;
static int id_num;
//注册和域的自动化
...
endclass
class test1 extends uvm_test;
//注册
...//new
task run_phase(u p);
flat_seq seq;
phase.raise_objection(p);
seq=new();
seq.randomize() with {addr=='h200;
length== };
seq.body();
phase.drop_objection(p);
endtask
endclass
class flat_seq extends uvm_sequence;
rand int length;
rand int addr;
rand int data[];//1个trans只有1个data,1个sequence有多个data
rand bit write;
rand int delay;
constraint cstr {
data.size()==length;
foreach(data[i]) soft data[i]==i;
soft addr=='h100;
soft write==1;
delay inside{[1:5]};
};
//注册
...
task body();
bus_trans tmp;
foreach(data[i]) begin
tmp=new();
tmp.randomize() with {
data==local::data[i];
addr==local::addr+i<<2;
write==local::write;
delay==local::delay;
};
tmp.print();
end
endtask
endclass
class bus_trans extends uvm_sequence_item;
rand bit write;
rand int data;
rand int addr;
rand int delay;
static int id_num;
constraint cstr {
data.size()==length;
foreach(data[i]) soft data[i]==i;
soft addr=='h100;
soft write==1;
delay inside{[1:5]};
};
//注册和域的自动化
....endclass
class flat_seq extends uvm_sequence;
rand int length;//其他成员变量下放到trans
rand int addr;
`uvm_object_utils()
...
task body();
bus_trans tmp;
tmp=new();
tmp.randomize() with{ length==local::length;
addr==local::addr;
};
tmp.print();
endtask
endclass
class test1 extends uvm_test;
//注册utils
...
task run_phase(u p);
flat_seq seq;
phase.raise_objection(p);
seq=new();
seq.randomize() with {addr=='h200; length==3;};
seq.body();//body在挂载后会自动执行,这里只是刻意而为之
phase.drop_objection(p);
endtask
endclass
hierarchical sequence
- 可以使用其他sequence以及item,创建更丰富的激励场景
- 具体地,通过层次嵌套关系,hs可以使用其他hs、flat seq和item
- 如果底层的fs和item的粒度得当,则可以充分复用这些来构成形式更多样的hs
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;})//一个宏完成seq或者item的创建、随机化、传送
fork
`uvm_do_with(s1, {length==5;})
`uvm_do_with(s2, {length==8;})
join
`uvm_do_with(t2, {length==3;})
endtask
endclass
- sequence复用:通过高层的sequence来嵌套底层的sequence或者item,最后创建期望的场景
- 上述例子既有串行也有并行的激励关系,还可以考虑加入事件的同步,延迟,来构成seq或者item之间的时序关系
sequencer和driver
- uvm定义了匹配的tlm端口供这两位使用
- uvm_seq_item_pull_port#(type REQ=int, type RSP=REQ)
- uvm_seq_item_pull_export#(type REQ=int, type RSP=REQ)
- uvm_seq_item_pull_imp#(type REQ=int, type RSP=REQ, type imp=int)
- 作为initiator的driver,例化了两个端口
- uvm_seq_item_pull_port#(REQ,RSP) seq_item_port
- uvm_analysis_port#(RSP) rsp_port
- 作为responser的sequencer,例化了两个端口
- uvm_seq_item_pull_imp#(REQ, RSP, this_type) seq_item_port
- uvm_analysis_export #(RSP) rsp_export
- 一次完整的传送,driver::seq_itme_port和sequencer::seq_item_export,连接driver::seq_item_port.connect(sequencer::seq_item_export)
- 都是参数化的类
- uvm_sequencer #(type REQ= uvm_sequence_item, RSP=REQ)
- uvm_driver #(type REQ=uvm_sequence_item, RSP=REQ)
- driver得到req对象在进行下一步处理时,需要进行动态类型转换,将req转换成uvm_sequence_item的子类型
- 或者自定义时标明其传递的具体item类型
- req和rsp的类型尽量保持一致,便于统一处理方便item的操作
- driver消化完当前的req后可以通过item_done(input RSP rsp_arg = null)告知sequence此次传输已经结束,参数中的rsp可以填入,返回相应的状态值
- driver可以通过put_response()或者put()单独发送rsp,这可以通过成对的uvm_driver::rsp_port和uvm_driver::rsp_export端口来完成,方法为uvm_driver::rsp_port::write(RSP)
class bus_trans extends uvm_sequence_item;//item定义:就是个trans类
rand int data;
`uvm_object_utils_begin(bus_trans)//注册和域的自动化
`uvm_field_int(data, UVM_ALL_ON)
`uvm_object_utils_end
...endclass
class flat_seq extends uvm_sequence;//sequence定义
`uvm_object_utils()//注册和例化
...
task body();//sequence中不是run而是body
uvm_sequence_item tmp;
bus_trans req, rsp;
tmp=create_item(bus_trans::get_type(), m_sequencer, "req");
//创建req item对象(类型,被挂载目标m_sequencer,name)
//如果没有指定挂载目标,在start_item时会默认挂载当前所在sequence挂载的sequencer
void'($cast(req, tmp));//uvm_sequence_item类型的句柄转换成bus_trans型
start_item(req);//准备发送,发送前做随机化
req.randomize with {data==10;};//访问子类句柄的成员变量,需要句柄转换
//如果是req.randomize();就可以,父类句柄做随机化会调用到子类里面的随机化
`uvm_info("SEQ", "send a item")//sequence发出了item
finish_item(req);//完成发送
get_response(tmp);//必要的时候从driver处获取response item(可选)
void'($cast(rsp, tmp));
`uvm_info("SEQ", "got a item")//拿到item
endtask
endclass
class env extends uvm_env;
sequencer sqr;
driver drv;
`uvm_component_utils(env)
...
function void build_phase(u p);
sqr=sequencer::type_id::create("sqr", this);
drv=driver::type_id::create("drv", this);
endfunction
function void connect_phase(u p);//例化,连接driver到sequencer的tlm端口
drv.seq_item_port.connect(sqr.seq_item_export);
endfunction
endclass
class sequencer extends uvm_sequencer;
`uvm_component_utils()
...endclass
class driver extends uvm_driver;
`uvm_component_utils()
...
task run_phase(u p);
REQ tmp;
bus_trans req, rsp;
seq_item_port.get_next_item(tmp);//从sequencer获取有效的req item
void'($cast(req, tmp));//从req item获取数据,产生数据激励
`uvm_info("DRV", "got a item")
void'($cast(rsp, req.clone()));//克隆生成新对象rsp item
rsp.set_sequence_id(req.get_sequence_id());
rsp.data +=100;//?????修改数据成员
seq_item_port.item_done(rsp);//将rsp item对象返回给sequence(可选),成对操作
`uvm_info("DRV", "sent a item")
endtask
endclass
通信时序
- sequence和driver通话的对象都是sequencer(收费站,中间人)
- 多个sequence要挂载到同一个sequencer上时,涉及sequencer的仲裁功能
- 传递过程
- sequence中由create_item产生一个item,start_item准备发送item时询问sequencer能不能发出,并randomization做随机化,在获得批准后finish_item开始发送item
- item经过sequencer后到达driver,driver通过get_next_item获得这个item,将其解析后生成硬件dut的激励
- driver通过item_done告诉sequence完成数据传送,至此为一次握手传输
- 其中,driver可以选择将rsp作为状态返回值传回给sequence;sequence也可以调用get_response(RSP)主动等待从driver返回的数据对象
- 注意
- 每个item多应该有id信息表明从哪个sequence来的,在创建item时赋值
- sequencer可以根据id信息发送respinse item返回到对应的sequence
- 建议在driver中通过clone()单独创建rsp item,保证两个对象的独立性
- 为了统一,用户可以不再定义sequence或者driver时指定item的类型,用默认类型REQ=uvm_sequence_item;在driver做类型转换:对get_next_item(REQ)的返回值REQ句柄做动态转换
- 需要复用验证ip时,建议通过继承于原有sequence item的方式定义新的item子类,同时在顶层通过工厂覆盖的方式用新的item覆盖原有的item类型