简介
发送item的过程:
- sequence是产生sequence item对象的地方,也可以产生sequence对象来实现sequence层次化;
- 产生sequence item经过sequencer流向driver;
- driver得到sequence item后,将数据按照与DUT的物理接口协议写入到接口上,对DUT形成有效激励;如果DUT与driver要进行互动,需要driver返回一个sequence item,经过sequencer到达sequence;
基本概念:
- sequence item是driver与DUT互动的最小粒度内容;
- 通过SV的随机化和sequence item对随机化的支持,产生的每个sequence item对象中的数据内容都不同;
- driver和sequencer的通信时通过TLM端口实现;
- driver 和 sequencer 是component ;uvm_sequence_item 和 uvm_sequence 是object;
- uvm_sequence 和 uvm_sequence_item并不处于UVM树结构中,所以顶层无法直接做配置。sequence活动起来,会挂载在sequencer上,这样sequence就可以依赖于sequencer的结构关系,通过sequencer来获取顶层配置;
- 数据传送机制采取的get模式,而不是put模式;
sequencer存在的意义?
- sequencer是一个组件,可以通过TLM端口与driver传输item对象;
- 当有多个sequence挂载在sequencer上时,sequencer有充分的仲裁机制来合理分配和传送item,继而实现并行item数据传送至driver的测试场景;
- 由于sequence是一个object,而uvm_object是独立于build阶段的,这就使得用户可以有选择地、动态地在合适时间点挂载所需的sequence和item;
选取get()模式的原因?
- 如果是get模式,那么当item从sequence产生,穿过sequencer到达driver时,我们就可以结束该传输。而如果是put模式,则必须是sequencer将item传送至driver,同时必须收到返回值才可以发起下一次的传输。从效率上看,是由差别的。
- 如果需要让sequencer具有仲裁的特性,使得多个sequencer同时挂载sequencer上,那么get模式更符合“工学设计”。这是因为driver作为initiator,一旦发出get请求,会先通过sequencer,继而获得仲裁后的item。
1.sequence 和 item
item的使用:
- 如果数据域使用来做驱动,应该定义为rand,同时按照驱动协议给出合适的constraint
- 通过宏`uvm_field_xxx声明必要的数据成员,以便日后使用uvm_object的基本数据方法的自动实现,例如print( );
- UVM要求item的创建和随机化都发生在sequence的body()任务中;
- item对象的声明周期始于sequence中的body方法中的create_item(),直到被driver消化后,item生命周期结束;如果要对item做操作,可以合理利用copy()或clone()等数据方法。
sequence的分类:
- flat sequence只产生和发送item ;
- hierarchical sequence,区别于flat sequence的地方在于它可以使用其他sequence和item,具有层次;
- virtual sequence ,区别于hierarchical sequence,该序列本身不会固定挂载与某一个sequencer类型上,而是将内部不同类型的sequence最终挂载到不同目标的sequencer上。
sequence中的步骤:
- 通过create_item()创建 request item对象
- 调用start_item()方法,将item挂载到sequencer上,准备发送item
- 在完成发送item之前,对item进行随机化
- 调用finish_item()方法完成item发送
2.sequencer 和 driver
1. 通信方式——TLM端口
driver的TLM端口:
- uvm_seq_item_pull_port #(REP,RSQ) seq_item_port
sequencer的端口:
- uvm_seq_item_pull_imp #(REQ,RSP,this_type) seq_item_export
driver和sequencer的端口连接:
- driver::seq_item_port.connect(sequencer::seq_item_export)
端口中定义的常用方法:
-
task get_next_item (output REQ req_arg)
采取blocking方式等待从sequence中获取一个item;
注:这个方法属于uvm_sequencer中的方法,在layering sequencer中会提到
-
function void item_done(input RSP rep_arg = null)
通知sequence当前的item已经消化完毕
-
function void put_response( input RSP rep_arg )
采用nonblocking方式发送response,成功返回1
2.事务传输
sequence_item中:
- 创建item —— create_item(bus_trans::get_type(),m_sequencer,“rsp”);
- 发送item—— start_item() 等待获取sequencer的授权许可,立即返回结果;
- 在start_item()和finish_item()之间,对item做随机化;
- 完成发送item——finish_item(),阻塞,等待driver的item_done()返回;
- 如果driver返回rsp,可调用get_response对rsp处理,注意做句柄转换
driver中:
- 通过TLM端口seq_item_port.get_next_item(REQ),从sequencer获取item;
- clone() 获取的REQ,生成response item;
- 通过seq_item_port.item_done(RSP),告诉sequence item已经”消化“完毕;
高层环境中:
- connect_phase中对TLM端口做连接,driver.seq_item_port.connect(sequencer.seq_item_export)完成driver和sequencer的连接
在顶层test中:
- 利用uvm_sequnce类的方法,uvm_sequnce::start(“sequencer的句柄”)实现sequence挂载到sequencer上
3. 通信时序
- 无论是flat sequence 还是 hierarchical sequence,进一步切分的话,流向sequencer的都是sequence item;通过create_item创建item,继而通过start_item()尝试从sequencer那里获取通过的权限;
- driver一侧始终通过 get_next_item()尝试从sequencer那获取item;
- sequencer将通过权限交给底层的sequence之前,目标sequence中的item应该完成随机化;sequence在获得sequencer通过权限后,执行finish_item(),但这个是阻塞的方法,不会立刻执行结束。
- item将穿过sequencer到达driver一侧,由driver消化;
- driver在消化完item后,执行item_done()方法来告知sequence已经完成数据传输,完成一次握手;如果driver通过item_done(rsp)将RSP作为状态值返回,sequence可以选择调用get_response(rsp),此时finish_item()才算真正的结束。
建议:
- 多个sequence同时向sequencer发送item时,每个item都会在sequence创建时就自带一个id值。driver在返回response item时,可以使用rsp.set_sequence_id(req.get_sequence_id())给response item设置同样的id值,发送至对应的源sequence;
- 尽量不要直接对req直接做操作然后当做response item返回给sequence,这样会延长request item的寿命,也会导致request item原始数据记录丢失。通过clone()的方式创建response item,在进行操作;
- 修改原有sequence item时,通过继承原有sequence item的方式定义新的item;
- 注意get_response()传递的是uvm_sequence_item 类型的句柄,get_next_item() 传递的是一个REQ类型的句柄,任务执行完后,要做类型转换后得到正确的句柄在进行后续操作;
3.sequence和sequencer
1.常用方法和宏的定义
1.将sequence挂载到sequencer上
- uvm_sequence::start(“sequecer 句柄”,“上层sequence”,”优先级“,“指定pre_body()和post_body()执行次序”);
- 一般在顶层test中将顶层sequence挂载到sequencer上,挂载前需要将sequence例化;
2.将item挂载到sequencer上
- uvm_sequence::start_item(“item对象”,“优先级”,“指定item和其parent sequence挂载到的sequencer是否是一个,默认相同”)
- uvm_sequence::finish_item(“item对象”,”优先级“)
3.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发送至driver在完成与diver之间的握手。
- 最后执行parent sequence的post_do();
4.宏(红宝书P384)
- 只能在sequence中被调用 ,一般在sequence中的body()任务中调用;
- 如宏`uvm_do(item)就将create_item等方法封装了
- 避免使用fork…join_any或fork…join_none来控制sequence的发送顺序,避免产生sequence死锁的问题。
2. sequencer仲裁特性
- 通过uvm_sequencer::set_arbitration(“UVM预定枚举类型的仲裁模式”) 设置仲裁模式;(P385)
- uvm提供的两种锁定机制:lock()和grab()
lock()和grab()的区别:
- lock():当sequencer按照仲裁机制授权给该sequence,一旦该sequence拿到授权,就不会将授权返回。只有当sequence执行unlock()时,才会释放这一锁定的权限;
- grab():下一次授权周期就可以无条件地获取授权。也就是说,当已经有其他sequence获得授权时,grab()就无法获得授权。
4.sequence的层次化
1. hierarchical sequence
- hierarchical sequence本身直接挂载到sequencer上
2. virtual sequence
解决多个sequence挂载到多个sequencer上的问题
- virtual sequence中有各个子模块环境的sequence,
- virtual sequence中使用宏`uvm_declare_p_sequencer定义了一个virtual sequencer类型的变量p_sequencer,通过p_sequencer可以索引到virtua sequencer中的声明的各个sequencer句柄;
- virtual sequencer中有各个子模块环境的sequencer句柄,其本身并不传递item,承担一个路由的作用;
- 顶层环境中例化virtual sequencer,然后将virtual sequencer中各个sequencer句柄同各个子模块中的sequencer实例做连接,避免句柄悬空;
- 顶层test中将virtua sequence挂载在virtual sequencer上;
注意:
- 宏`uvm_declare_p_sequencer(virtual_sequencer)的作用:1.定义了一个virtual_sequencer p_sequencer;2.做了$(p_sequencer,m_sequencer)的类型转换;
- 在顶层test将virtua sequence挂载在virtual sequencer上之后,virtual sequence中的p_sequencer变量就可以索引到virtual sequencer中的声明的各个sequencer句柄;
- 在virtual sequence中,使用宏`uvm_do_on(“各个sequence”,p_sequencer,“virtual sequencer中的各个sequencer句柄”),将不同sequence的item指向不同的sequencer;
3. layering sequence
将高抽象级的item,通过层次化的sequence,转变为底层的item。这种层次化的sequence称为layering sequence。
- 转化层adapter sequence会不断forever接收高抽象级layer sequence传送过来的高抽象级item,将其转化为底层item,然后发送底层item;
- adapter sequence挂载的sequencer(phy_master_sequencer)中要声明layer sequencer句柄;
- 顶层环境中将phy_master_sequencer同driver做连接
- 顶层test中将layer sequence挂载在layer sequencer上,adapter sequence挂载phy_master_sequencer上;并且将phy_master_sequencer中声明的layer sequencer句柄同实例做连接;
adapter sequence中如何找到layer sequence传送过来的高抽象级item?
-
过程:adapter sequence — (p_sequencer) —> layer sequencer ——> 高抽象级item
-
因为adapter sequence挂载在phy_master_sequencer上,通过在adapter sequence中使用宏`uvm_declare_p_sequencer(phy_master_sequencer) 声明p_sequencer,;
-
phy_master_sequencer中声明layer sequencer句柄,而layer sequence是挂载在layer sequencer上的;
-
在顶层test中将phy_master_sequencer中声明layer sequencer句柄同layer sequencer的实例做连接;
-
这样在adapter sequence中就可以通过p_sequencer.layer_sequencer.get_next_item(req),获取layer sequence中发送的高抽象级item。