本文是uvm实战这本书的笔记
P45
一个sequence在向sequencer发送transaction前,要先向sequencer发送一个请求。sequencer把这个请求放在一个仲裁队列中。作为sequencer,它需要做两件事,第一,检测仲裁队列里面是否有某个sequence发送transaction的请求;第二,检测driver是否申请transaction。
如果仲裁队列里有发送请求,但是driver没有申请transaction,那么sequencer将会一直处于等待driver的状态,直到driver申请。此时,sequencer同意sequence的发送请求,sequence在得到sequencer的批准后,产生出一个transaction并交给sequencer,后者把这个transaction交给driver。
如果仲裁队列中没有发送请求,但是driver向sequencer申请新的transaction,那么sequencer会处于等待sequence的状态,一直到有sequence递交发送请求,sequencer马上同意这个请求,sequence产生transaction并交给sequencer,最终driver获得到这个transaction。
如果仲裁队列中有发送请求,同时driver也在向sequencer申请新的transaction,那么将会同意发送请求,sequence产生transaction并交给sequencer,最终driver获得。
P46
为什么有item_done?
1
2
3seq_item_port.get_next_item(req);
driver_one_pkt(req);
seq_item_port.item_done();当driver使用get_next_item得到一个transaction时,sequencer自己也保留了一份刚发送出的transaction,当出现sequencer发出了而driver没有得到的情况,sequencer会把保留的这份再发送出去,那么sequencer如何知道driver是否成功得到?如果在下次调用get_next_item之前,item_done被调用,那么sequencer就认为driver已经得到,将会把这个保留的transaction删除,换言之,其实是一种为了增加可靠性而使用的握手机制。
在sequence中,向sequencer发送transaction使用的是uvm_do宏。这个宏什么时候返回呢?uvm_do宏产生了一个transaction并交给sequencer,driver取走这个transaction后,uvm_do并不会立刻返回执行下一次uvm_do,而是等待在那里,知道driver返回Item_done信号,uvm_do宏才算执行完毕,返回后开始执行下一个uvm_do。
P57
uvm hierarchy(摘录自uvm_users_guide_1.1.pdf)
P89
多次config_db,取哪个?
case中:
uvm_config_db#(int)::set(this, "env.i_agt.drv","num",999)
env中:
uvm_config_db#(int)::set(this, "env.i_agt.drv","num",1000)
都在build_phase中。
答案是999。因为case的优先级高,uvm树中越靠近根节点,优先级越高。
这样设置的原因是,希望越外层的优先级高,可以替换掉低层次中配的值,可以不用修改内部模块。
case中:
uvm_config_db#(int)::set(uvm_root::get(),"uvm_test_top.env.i_agt.drv","num",999)
env中:
uvm_config_db#(int)::set(uvm_root::get(),"uvm_test_top.env.i_agt.drv","num",1000)
答案是1000。因为第一个参数相同,则优先级相同,只能比较时间。由于build_phase是自上而下执行的,case先执行,env后执行,所以后设置的替换了先设置的。
如果是同一层次设置两次呢?
case中:
1
2`uvm_config_db#(int)::set(this, "env.i_agt.drv","num",999)`
`uvm_config_db#(int)::set(this, "env.i_agt.drv","num",1000)`答案是1000。同样的,按时间,后设置的有效。
P93
非直线设置与获取
非直线设置,避免使用
scoreboard中:
uvm_config_db#(int)::set(this.parent,"i_agt.drv","uvm",200)
因为不能保证同级build_phase的执行顺序,可能先get再set,就没有生效。
非直线获取,可以使用
model中:
void'(uvm_config_db#(int)::get(this.parent, "i_agt.drv","num",200));
某些情况可以减少set的冗余,只需要set一次就可以get两次。
P96
check_config_usage()
可以检查有哪些set过,没有get过,用来检查是否有字符串写错了。
P97
print_config(0/1)
1:递归查询
0:只显示当前component
P119
put和get系列port与analysis_port系列区别
- analysis_port可以连接多个imp,一对多通信,而put/get系列一对一通信
- put/get系列有阻塞和非阻塞之分,而analysis系列没有
P132
uvm phase
P135
build_phase 自上而下,因为要先例化子模块
其他function phase,自下而上
同层次component,按字典顺序
task phase,自下而上启动,同时执行
P139
树遍历,深度优先
i_agt,scb,那么i_agt的build_phase先执行,然后是driver,monitor,再试scb的build_phase。
P140
super.xx_phase(phase);
build_phase,component对其最重要的事是自动获得通过config_db::set设置的参数。
其他phase几乎没有做任何相关事情。
P145
phase跳转
往前跳转到从build到start_of_simulation的function phase是不可行的,run phase也不可行。
往后跳转除了动态运行的phase,还可以是函数phase,如main跳转到final。
P170
sequencer的lock操作
所谓lock,就是sequence向sequencer发送一个请求,这个请求与其他sequence发送transaction的请求一同被放入sequencer的仲裁队列中,当前面请求处理完毕后,sequencer会开始响应该lock,此后sequencer会一直连续发送此sequence的transaction直到unlock被调用。
P172
grab
与lock类似,不同的是,lock是插入sequencer仲裁队列后,而grab是之前。比lock优先级更高,一旦发出就拥有了sequencer的所有权。
P175
sequence相关宏
1
2
3
4`uvm_do(SEQ_OR_ITEM)
`uvm_do_with(SEQ_OR_ITEM,CONSTRAINTS)
`uvm_do_on(SEQ_OR_ITEM,SEQR)
`uvm_doon_with(SEQ_OR_ITEM,SEQR,CONSTRAINTS)1
2
3`uvm_create(m_trans)
m_trans.randomize();
`uvm_send(m_trans);1
`uvm_rand_send(SEQ_OR_ITEM);//与前者不同的是,会对trans随机化,前提是trans需要被分配了空间
1
2
3
4tr = new("tr");
start_item(tr);
tr.randomize();
finish_item(tr);P183
嵌套的sequence
1
2
3
4
5
6
7
8
9
10class seq_all extends uvm_sequence#(my_trans);
virtual task body();
seq0 seq0_i;
seq1 seq1_i;
repeat(10)begin
`uvm_do(seq0_i)
`uvm_do(seq1_i)
end
endtask
endclassP185
有没有办法将两个不同的transaction类型给同一个sequencer呢?
可以。将数据类型设为uvm_sequence_item,接收时用cast转回来,多态。
P186
p_sequencer
若在sequence中想获得sequencer,在sequence中使用宏
uvm_declare_p_sequencer(my_sequencer)`
则相当于将m_sequencer给cast为my_sequencer类型的p_sequencer变量。
virtual sequence
是为了解决一个验证环境中,存在多个agent,每个agent中的sequence需要按照不同场景调度问题
可以用进程语句来控制各个sequence的并行,串行,甚至可以延迟执行
sequence library
在单个agent对应多个不同sequence执行的场景,定向对应每个场景执行每个不同sequence显然不是好办法,我们希望可以执行类似sequence时,一个用例即可随机执行完,可用sequence library。
在写sequence时,注册sequence library中,然后用例中设置library的执行方式。
各phase任务
build:用于创建和配置组件,可以动态的更改平台需要运行的组件,通过对构建不同子组件组合,达到一个平台实现多个功能的目的。
connect:完成内部各个子组件的连接,以及虚拟接口的连接。
end_of_elaboration:对上述结果做最后的调整。
start_of_simulation:组件在此阶段初始化完成。
run:消耗时间,定义组件运行的主要功能。
extract:收集断言错误,覆盖率,以及组件的统计结果。
check:对收集到的重要结果进行分析判断,与预期结果比较。
report:将运行结果输出到屏幕或成文件
analysis port/export和port/export的区别
- analysis可以连接多个imp,它和imp是一对多通信。而port是一对一的。analysis更像广播。
- 作为port/export,有put,get,transport操作。对于普通port来说,一个port要么是put port,要么是get port,要么是transport port,不可能三者兼得。但对于analysis port来说,它只有一个操作,write,意思是广播一下,剩下的事情与它无关了。
- port/export有阻塞和非阻塞之分,相应的put/get/transport操作也有阻塞和非阻塞。但analysis由于是广播,没有阻塞和非阻塞之分。