UVM入门进阶8:Sequencer仲裁_Sequence层次化
参考文档链接:https://verificationacademy.com/verification-methodology-reference/uvm/docs_1.2/html/
本节内容为上一节《UVM入门进阶7:Item_Sequence_Sequencer_Driver》的后半部节选,因为UVM7内容过多因此创建本小节。本节主要学习Sequencer和Sequence的关系
4 Sequencer和Sequence:初步
4.1 概述
4.1.1 主要学习内容
主要讲几种常见的仲裁方式,一般可能用不太到
4.1.2 sequence宏概述:区分两种挂载seq到sqr方式
- start()
- 在test对top_seq挂载(参考第三章),在top_sep对child_sep和item挂载,在child_sep对item挂载
- `uvm_do
- 一种宏:参考第五章
4.2 sequence和item发送示例
bus_trans
child_seq
top_seq
包含child_seq与item
sqr
driver
driver里面get的全是最小颗粒度item,seq在sqr处被拆解
env
test与输出结果
4.3 示例总结:两种挂载方式
两种挂载方式,分别把seq和item挂载到sqr上
4.3.1 把seq挂载到sqr上:这种挂载发生在top_seq上
top_seq中的uvm_sequence::start()
- start内部参数解释
- uvm_sequencer_base sequencer:
- 挂载到的sqr实例句柄
- uvm_sequence_base parent_sequence:
- 上层对象句柄,示例中top_seq调用该方法时代入”this”代表child_seq的上层对象为自己
- 这样做的好处是保持子对象优先级一致,进而对仲裁有帮助仲裁表现一致
- int this_priority:
- 优先级,默认值-1,此时实际优先级为100
- call_pre_post:
- 建议使用默认值
- 补充:body的pre和post回调函数不建议实现
- 建议使用默认值
- uvm_sequencer_base sequencer:
4.3.2 把item挂载到sqr上
第二个参数不需要特别关心,往往知道挂载哪个item就够了
4.3.3 item创建发送过程
pre_do(),mid_do(),post_do():不建议实现,会变复杂
4.3.4 seq/item的start()挂载内部逻辑
sequence挂载到sequencer
为什么可以不关心:有时候call_pre_post=1时这些post和pre都不会执行
item挂载到sequencer
start_item立即返回,因为只有一个sqr进行仲裁可以立刻返回结果
5 Sequencer和Sequence:更简单的创建挂载传输方式:宏
图中宏省略了`
这些宏封装了好多方法函数,x表示包括,空表示不包括
Item:
宏解释:
- `uvm_do:是一个完整的过程,但直接randomiztion
- `uvm_do_with:做一个带constraints的randomiztion
- `uvm_create:就是一个创建
- `uvm_send():就是发送
执行顺序解释:
- body为空,因为item没有
Sequence:
宏解释:
- `uvm_do:是一个完整的过程
+ - `uvm_do_with:做一个constraints的randomiztion
- `uvm_create:就是一个创建
- `uvm_send():就是发送
- `uvm_do:是一个完整的过程
执行顺序解释:
- 没有做sync和post-sync
- 因为sync和post-sync需要在body里面拿到优先级,`uvm_do本身不会拿到优先级,只有在seq_body里面,只有在发送item时候才会拿到(即每次发送一个item的时候都会申请一次优先级)
- 没有做sync和post-sync
宏的调用位置
- 这些宏调用的都是 sequence方法,因此只有sequence才可以调用这些宏
宏带来的怠惰
5.2 其他的宏
- 将优先级作为参数传递的
`uvm_do_pri/`uvm_do_on_prio
等宏- 并行发送多个seq时,伴随优先级发送
- 形参优先级,默认为100
- `uvm_do_on:这个on是什么意思,表示把这个特定的seq挂载到某一个sqr上面
- 对于item来说,使用`uvm_do则会被同样挂载到seq挂载到的sqr上
- `uvm_do_on则可以进行指定
- 专门针对seq的
`uvm_create_seq/`uvm_do_seq/`uvm_do_seq_with
等宏
5.3 序列宏的示例
child_seq
怎样利用宏发送的:
- `uvm_create(req):创建item
- `uvm_rand_send_with:添加constraint
上述宏可以合并为:
- `uvm_do_with
top_seq
- `uvm_do:这里发送了child_seq
- `uvm_do_with:这里发送了item
5.4 序列宏的建议
(1)不管seq在哪个层次,都应在test结束前执行完毕
(2)可以用fork_join完成seq发送。而不建议用fork_join_any和fork_join_none,可能会导致seq没结束就立即退出,使得后台仍然有一些seq像挂载到sqr上面;进一步如果你针对fork调用disable可能会所锁sqr,因为你来不及释放seq线程权限。
- 补充:你在代码中使用fork_join_any和fork_join_none一定要使用同步,如此处假如你使用了fork_join_any和fork_join_none你应该完成即便fork退出seq运行在后台时,仍然能与后台运行的seq进行同步,使得所有seq结束以后才进行后续的操作
(3)fork_join其中一个seq线程无法结束时,考虑在合适时间点使用disbale
5 Sequencer和Sequence:仲裁特性
5.1 Sequencer和Sequence的仲裁特性
5.1.1 仲裁特性介绍
仲裁场景结构图:
(1)sqr已经内建了仲裁机制,只需要在top_seq设置仲裁的模式就足够
(2)
如何设置:uvm_sequencer::set_arbitration(UVM_SEQ_ARB_TYPE val)
模式介绍:
- UVM_SEQ_ARB_FIFO:默认模式,竞争优先
- UVM_SEQ_ARB_WEIGHTED:优先级模式
- UVM_SEQ_ARB_RANDOM:随机模式
- UVM_SEQ_ARB_STRICT_FIFO:按照优先和抵达顺序授权
- UVM_SEQ_ARB_STRICT_RANDOM:最高优先级并随机授权
- UVM_SEQ_ARB_USER:自定义仲裁,很少用
5.1.2 Sequencer的仲裁示例
item和child_seq
top_seq和sqr
top_seq:
- body:
- 设置优先级模式
- fork_join进行调度:
- 三个同一时间申请的,seq1和seq2优先级更高
- base是constraint
- 此时赋予了优先级之后child_seq中的`uvm_do_with就可以进行挂在了,因为它们等待的就是这个顶层的优先级,使用wait_for_guant等待授权
driver
没有太多新意
env
test和输出结果
test:
- 为什么test里面不用宏:因为用不了只能在seq里面用
为什么是seq1seq2seq1seq2seq3seq3,而且还是0时刻?:
- 发送item没有耗时
示例分析
5.2 Sequencer的锁定机制介绍
(1)锁定机制
什么是锁定机制:seq拿到权限后进行锁定,
什么是锁定:seq拿到一个item后锁定下来,后续item都可以拿到
(2)两种锁定方式:两个函数都可以
- lock()与unlock
- 一定记得unlock
- grab()与ungrab()
- 优先级比lock高,下一次授权周期可以无条件获取授权
5.2.2 Sequencer的锁定示例
item、child_seq、lock_seq
child_seq:内部重复发送两次,间隔10ns,参考之前一节的示例
lock_seq:
先等了10ns,试图达到等待授权把sqr锁住
m_sequencer.lock(this);
- 什么时候才能lock住:下一次等待授权的时候,即大家都在等待的时候
- 重复发送三次
grab_seq
grab_seq:
等了20ns
m_sequencer.grab(this);
- 什么时候才能锁住:只要重新做仲裁就能锁住
- 重复发送三次
top_seq
seq1,seq2,seq3
lock_seq:优先级为300
grab_seq:没有优先级
输出结果
注释版:
结果解释
10ns:
- seq1,seq2优先级500先发送sep3优先级300再发送(发送完等待10纳秒,重新排队伍)
- lock_seq开始要权限
10ns-20ns:
- seq发送
20ns:
- seq1,2,3,lock都开始等待(lock因为优先级低所以也要等待)
20ns-40ns:
- 被lock锁住
40ns:
- grabs拿到权限(实际上20nsgrab就开始试图拿权限,但被lock控制,只能等到重新仲裁,届时即可直接锁住无需)
40ns-70ns:
- 被grabs锁住
70ns:
- seq1,seq2优先级500先发送sep3优先级300再发送
结果补充
seq优先级如果使用默认,不给定,则默认100
即直接:
`uvm_do_pri_with(seq)
`uvm_do_pri_with(lock)
`uvm_do_pri_with(grab)
5.2.3 结论
无论是lock或者grab都是在重新仲裁时才能锁住
6 Sequence的层次化
对于小白我们可能更关心如何让你的sequence层次化,如何理解别人写的层次化的sequence,以及把它们作为更顶层的sequence如何一层一层的调用
6.1 概述:水平复用和垂直复用
- hierarchical sequence:层次序列,seq都挂载在一个sqr上
- virtual sequence:seq可挂载在多个sqr上
- layering sequence
6.2 Hierarchical Sequence层次序列
6.2.1 Hierarchical Sequence介绍
6.2.2 Hierarchical Sequence代码示例
cmd 和 item
clk_rst_seq
reg_test_seq
top_seq
reg_master_sequencer
driver
agent
- 例化与连接
6.2.3 示例解析
6.2.4 总结
6.3 Virtual Sequence
6.3.1 Virtual Sequence介绍
把所有子系统的sqr都放在virtual_sequencer里面
- virtual_sequencer包含着很多实例句柄
- 它就像一个路由一样,找到他就能找到各个子系统的sqr
- 起到一个统筹的作用
- virtual sequencer往往与virtual sequence有联系的
virtual sequencer就是个简单的路由器,包含句柄而已,没有任何item经过,也不需要与driver连接
6.3.2 Virtual Sequence示例
vritual sequencer结构图:
【重要】有关挂载
mcdf_normal_seq(virtual sequence)要挂载到virutal sequencer
其他的seq间接的通过virutal sequencer挂载到不同的sqr上面,挂载的目的地不一样所以用uvm_do_on
virtual sequence
定义了一个sequence:实际上就是我们的virtual sequence
包括了好多seq句柄
sqr与vsqr区别:
- 用了很多`uvmdoon
- 有别于Hierarchical Sequence使用的`uvm_do
子
p_sequencer和m_sequencer的区别:
- m_sequencer:是父类句柄,类型是uvm_sequencer
- p_sequencer:是子类句柄,类型是`uvm_declare_p_sequencer(mcdf_virtual_sequencer)
- 自定义出来的,不是与定义好的,定义语句就来源于`uvm_declare_p_sequencer(mcdf_virtual_sequencer)
- 定义语句完成了两个步骤:
- mcdf_virtual_sequencer p_sequencer;
- $cast(p_sequencer, m_sequencer);//把父类句柄转为子类句柄
- 为什么一定要子类句柄:
- 因为父类的访问不到这些子系统sqr句柄
- 通过这个子类句柄p_sequencer我们可以访问到vsqr里面所有成员变量
子一级的sqr,agent,和virutal sequencer
virutal sequencer 包含各个句柄就完事了
mcdf_env
千万不要忘了再connect里进行句柄传递,避免悬空
test
6.3.3 示例解析
6.3.4 总结
中心化协调
6.3.5 Virutal Sequencer建议
- vseq与普通seq区分
- vsqr同底层负责传输对象的sqr区分
- vseq中使用`uvm_declare_p_sqr来创建p_sequencer变量方便之后索引
- 顶层环境中创建vsqr并完成内部各个sqr句柄与底层sqr实例连接
6.4 Layering Sequence:层次化
6.4.1 Layering Sequence介绍
普通协议,构建协议总线时通过:sequencer item包含与约束关系
复杂协议,网络传输协议,需要更深层次一层一层解析,通过:若干个抽象层次去做
由高抽象级到低抽象级(传输层>>链路层>>物理层)的构建,我们称为Layering Sequence
接下来学习,一定考虑三个层次:高抽象级的item,低抽象级的item,中间做转化的sequence
Layering Sequence层次结构图:
寄存器模型下的item传递结构图与layering sequence具体作用内容:
- 对寄存器的reg.write()和reg.read()指令,通过寄存器模型的Adaption Layer转化层次,进而转化为总线的一个item。即把reg_item转为bug_item
- 得出layering sequence包括三者:高抽象级的item,低抽象级的item,中间做转化的sequence
- 高抽象级的item与低抽象级的item没有直接的继承关系所以需要做一个映射
- 高抽象级不包括具体的数据,只有类似命令、长度、idle等信息
6.4.2 Layering Sequence代码示例
cmd和bus_trans(item)
比较底层
packet_seq
layer_trans(item)
比较抽象:
- 没有任何与数据有关的传输内用,只有cmd,len,idle
- 与底层的bus_trans没有任何继承关系所以要做一个映射
adapter_seq:转化层
- p_sequencer通过`ucm_declare获得,通过p_sqr拿到up_sqr更上层的layersqr句柄
拿到子类句柄void($cast(trans,req))
转化为一个底层个seq,把高抽象级的长度转化为低抽象级的pkt_seq,`uvm_do挂载到了phy_driver上
- 做一个握手,告诉高层,我已经把高抽象层的trans消化掉了
top_seq , layering_sqr , phy_sqr
layering_sqr , phy_sqr定义不讲了
phy_sqr里面有个句柄up_sqr,需要顶层把相关实例传递进来
top_seq:连续发送了两个layer_trans
phy_driver
phy_agent
包含phy_drv和phy_sqr的例化与连接
test,重点在这里,查看连接关系
- 例化了layer_sqr与phy_agt
- sqr句柄的传递,layer_seq/top_seq,adapter_seq,phy_seq的挂载
- up_sqr句柄的传递
- adapter_seq的挂载
- top_seq的挂载
- phy_seq的挂载实际上不是个挂载而是产生的过程,在adapter_seq用uvm_do出来好多pkg_seq,这些pkg_seq时加上就属于phy到drv的seq
- up_sqr句柄的传递
- 为什么用fork_join_none
- 用fork_join_none直接挂载上去就可以了
- 用fork_join不行,这个start会自动帮我们执行body(),adapter.body()是个forever会卡在这里
6.4.3 如何实现sequencer layer协议转换的方法:
(1)layer_trans,bus_trans完全两个不同的抽象级
(2)需要adapter_seq完成从获取layer_trans再去生成phy_trans和seq,再从physqr发送出去
(2)fork_join_none
(4)至于多少个层次取决于
6.4.4 总结
(2)只有高抽象级到低抽象级,低到高需要有回路,通过response_item实现,两者的思想实际上一样
(3)也可以不通过高到低的回路,而是外部通过monitor采集response trans最终实现返回,避开adapter
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!