第六章 sequence
1.Seq的启动和执行——将seq和sqr联系起来 P165
有三种不同的方式,大致分第一种为直接启动start(),第二种使用default_sequence,使用default_sequence会自动调用start任务;
在不同的testcase中,将不同的seq设置成sqr.main_phase的default_sequence。当sqr执行到main.phase时,发现有default_sequence,那么它就启动seq
当seq启动后,会自动执行body任务,同时还有回调函数,pre_body和post_body
2.当有一个sqr上启动多个seq时,sqr会启动仲裁机制
首先,一个sqr上启动多个seq,常用的方法是start();
其次可以通过在seq的body()中使用`uvm_do_pri_with对tr进行配置优先级
然后可以通过在testcase的main_phase中通过start()对seq进行配置优先级
最后在testcase的main_phase配置sqr的仲裁方式即可;
注:若只对tr配置优先级,而不对seq配置优先级,sqr还是会交替开启seq,在当前的seq选取优先级最高的tr进行生成
3.当有一个sqr上启动多个seq时,并且一个seq中部分的tr一旦开始发送就要连续发送时,使用lock操作
不对seq配置优先级的话,seq会轮流发送自己的tr,当seq中对部分tr进行lock操作时,当开始执行lock时,sqr会连续生成同一个seq的tr,直至执行unlock后,sqr再交替执行不同seq;
当有多个seq中都进行了lock操作时,sqr的所有权采取先到先得模式
4.sqr的grab操作,类似lock,但是具体不明白
5.seq的有效性
Sqr在仲裁的时候,会查看seq的is_relevant函数的返回结果;如果是1,说明seq有效,否则无效;
因此可以通过重载(virtue function)is_relevant函数来控制seq的有效性;
同时还有一个task wait_for_relevant(),当seq失效之后,sqr会执行别的seq,当执行完了之后,会执行失效了的seq的task wait_for_relevant(),恢复当前seq的有效性,前提是该task中必须要将seq无效的条件清除;
6.uvm_do系列宏
Uvm_do_on有两个参数,第一个参数是tr的指针,第二个是sqr的指针;所以这个宏的作用是显式的指定使用那一个sqr发送这个tr;
而uvm_do会默认由启动时指定的seq发送tr,即seq将sqr的指针放在其自身的成员面量m_sequence中;
也就是说uvm_do 等价于 uvm_do_on(tr,m_sequence)
简单概括就是,pri控制tr的优先级,on指定sqr的指针,with用于加随机条件
7.uvm_create和uvm_send联合使用可以相当于uvm_do,但是过程可以更加灵活
Uvm_create的作用是例化tr,uvm_send是将tr交给sqr
8.uvm_rand_send系列宏
uvm_rand_send系列宏可以随机化tr
该系列的意义在于,如果tr需要的内存比较大,希望前后发送的tr都用同一块内存,可以节省内存
9.uvm__do宏实际上是将一系列的动作封装在了宏里面
具体如下:
tr=new(“tr”);
assert(tr.randomize()with{});
start_item(tr);
finish_item(tr);
可以在start_item和finish_item中加入优先级
10.使用pre_do、mid_do、post_do增加uvm_do的灵活性
11.嵌套的seq
在顶层的seq中嵌套其他的seq 操作如下:
Class top_seq extends uvm_sequence#(my_tr);
…
Virtual task body();
Seq1 seq_1;
Seq2 seq_2;
Repeat(10)begin
`uvm_do(seq_1)
`uvm_do(seq_2)
End
Endtask
Endclass
嵌套seq的前提是,在顶层seq下所有的seq产生的tr都是用一种类型,可以被同一个sqr所接受,不然将会报错;
若想让tr不是用一个类型,程序还能正常运行,可以参考白皮书P186,会稍微复杂一些
12.p_sequencer的使用
其实m_sequencer与p_sequencer本质上是一样的,都是指向同一个sequencer(即sequence start时挂载的那个sequencer)
区别在于它们的类型不同,m_sequencer是sequence的成员变量,其类型为uvm_sequencer_base。而p_sequencer类型为user_sequencer(用户定义的),在sequence中通过uvm_declare_p_sequencer(user_sequencer)可以声明一个为user_sequencer类型的成员变量,之后UVM会在pre_body()阶段之前将通过$cast自动完成m_sequencer转化为p_sequencer的过程(此时p_sequencer指向m_sequencer指向的sequencer)。而m_sequencer的赋值是在sequence启动时即将sequence挂载至某sequencer上时,UVM会自动将该sequencer的句柄赋值给m_sequencer。
为什么引入m_sequencer与p_sequencer?
m_sequencer的引入使得嵌套的sequence得以更加方便地实现,这样就可以在一个sequence中启动其它的sequence,内部这些sequence都可以挂载在m_sequencer中,因为m_sequencer实质上也是最外部sequence挂载sequencer的句柄,因此无论外部sequence今后挂载在哪一个sequencer上,嵌套的sequence都能挂载在同一个sequencer上,当有多个相同类型的sequence时,使用嵌套的sequence能简化了测试代码,提高了可靠性。
p_sequencer的引入是为了解决sequence访问其挂载的sequencer变量的问题,若直接使用m_sequencer会因句柄类型问题编译报错。因为想要访问挂载的sequencer中的变量,应该与该sequencer同一类型,而m_sequencer是uvm_sequencer_base,不同于该sequencer,因此引入了类型与该sequencer一样且值与m_sequencer相同的p_sequencer!
13.virtual sequence和virtual sequencer的实例应用
14. 什么时候需要virtual sequencer?
环境中有多个driver_agent,且这些driver之间需要同步,此时需要virtual sequencer。由于virtual sequence 的body是顺序执行的,所以只需在body调整seq的顺序即可;
virtual sequence:承载不同目标sequencer的sequence群落,实现sequence同步;virtual sequence一般只会挂载到virtual sequencer上,且没有自己的sequence_item,只用于控制其他的sequence执行顺序,起统一调度作用。
virtual sequencer:桥接其它sequencer,即连接所有底层sequencer的句柄(指针),是一个中心化的路由器。virtual sequencer本身并不传送item数据对象,因此不需要与driver进行TLM连接。所以用户需在顶层的connect阶段做好virtual sequencer中各个sequencer句柄与sequencer实体对象的一一连接,避免句柄悬空。
15.在seq中使用config_db去get参数
由testcase中向seq中set参数:
Uvm_config_db#(int)::set(this,”env.i_agt.sqr.*”,”count”,9);
Seq中get参数
Uvm_config_db#(int)::get(null,get_full_name(),”count”,count);
在get函数原型中,第一个参数必须是一个component,而sequence不是一个component,所以这里不能使用this指针,只能使用null或者uvm_root::get();
而使用null的话uvm会自动替换为uvm_root::get();
16.在seq中设置set参数
向sb中传递了一个cmp_en的参数
Uvm_config_db#(bit)::set(uvm_root::get(),”uvm_test_top.env0.scb”,”cmp_en”,0);
Seq向seq传递参数
Uvm_config_db#(bit)::set(uvm_root::get(),”uvm_test_top.v_sqr.*”,”cmp_en”,0);
17.wait_modified的使用
Component一般是在build_phase中调用get函数,并且调用的前提是参数已经被设置过。而seq设置set参数是在task_phase中运行的,当设置一个参数的时候,其时间往往是不固定的,故提供wait_modified检测参数是否已经更新
Uvm_config_db#(bit)::wait_modified(this,””,“cmp_en”);
Void’(Uvm_config_db#(bit)::get(this,””,“cmp_en”,cmp_en);
在seq中一样可以调用
18.put_response和get_response
Sequence机制提供反馈的支持,允许driver将一个response返回给sequence
在seq的task body 中完成uvm_do后,get_response(rsp);
在driver的main_phase中完成发送req后,
Rsp=new(“rsp”);
Rsp.set_id_info(req);
Seq_item_port.put_response(rsp);
Seq_item_port.item_done();
//当一个tr只对应一个response时,Seq_item_port.item_done(rsp);可以代替最后两句话
19.sequence library
可以满足sqr发送seq的时候,随机从sequence library中选取seq进行发送
第一步:定义sequence library
第二步:在需要加入library的seq中,使用`uvm_add_to_seq_lib
第三步:testcase的build_phase中,
Uvm_config_db#(uvm_object_wrapper)::set(this,”env.i_agt.sqr.main_phase”,”default_sequence”,simple_seq_library::type_id::get());