UVM入门进阶7:Item_Sequence_Sequencer_Driver
参考文档链接:https://verificationacademy.com/verification-methodology-reference/uvm/docs_1.2/html/
本节目录:
- Item, Sequence, Sequencer, Driver介绍
- Item, Sequence, Sequencer, Driver两两之间的关系与常见操作
- 如何构建一个测试序列(sequence)
背景:
小白第一年可能不会直接写验证环境,而是写一些seq和test
对于小白来讲第一年工作基本就两件事:
- (第一年)构建seq,使用这些宏
- (第一年)定义功能覆盖率,对他们进行收敛
第二年创建环境:
- (第二年)创建环境,你可以把实验代码照搬过工作中去修改
1 新手上路
1.1 前言
run_phase
到我们的run_phase了
driver,monitor,checker,reference_model的run_phase都是些forever,和sv里面的run任务相同,它们不会停下来,sv里面只有generator会停下来
这节课我们会讲,在run_phase我们的激励怎么一步一步把item通过sqr发送到dri一侧
1.2 四个核心词介绍
本章主要讲四个核心词的作用、分类以及之间的互动关系
- sequence item
- sequence
- sequencer
- driver
1.2.1 trans驱动作用
整个事物的驱动力就是transaction的传送
1.2.2 四者关系打比方
graph TB
sequence --> 道路
sequence_item --> 货车
sequencer --> 目的地关卡
driver --> 目的_进行扫描与分解
通路结构图:
- sqr到driver是通过req与rsp
1.2.3 sequencer item
sequence item是driver与DUT每一次互动的最小颗粒度
sequence item是一个类
- 内部可定义成员方法或变量,成员变量应考虑是否需要随机化
1.2.4 driver:一个例子
一个例子:假设DUT是slave端,driver扮演master访问DUT寄存器
- item定义的数据信息包括:地址、命令码、数据和状态值(访问是否成功,V1V2的MCDF不涉及状态码,UVM实战会完善)
- driver任务:
- driver取得后通过时序方式在initerface一侧发起激励至DUT
- 这种时序需要保持几个时钟(按照总线做寄存去访问的习惯)
- driver取得后通过时序方式在initerface一侧发起激励至DUT
1.2.5 sequence 的层次化
sequence内部包括item可以包括sequence,从而sequence一层一层实现层次化
1.2.6 sequencer
- sequencer是sequence与driver之间的桥梁,使用TLM进行通信,通信参数为sequence_item类
- sequencer的相关控制方法都在uvm_sequence里面
1.2.7 driver
driver特点:
- 永远喂不饱,forever驱动
- 不会修改item值
1.2.8 sequence的互动
1.3 继承关系
1.3.1 uvm_sequence是obj
uvm_sequence_item和uvm_sequence都继承于uvm_object,不同于comp只在build_phase里创建和配置,obj可以在任何时候穿几件
uvm_sequence与uvm_comp继承树
1.3.2 提示:sequence继承obj而带来的特点
(1)你无法预测run阶段什么时候会创建sequence并挂在到sqr上,也无法通过phase机制识别sequence 的运行阶段
(2)obj独立于build阶段外,所以可以动态挂在
(3)seq无法在顶层配置的时候按照层次关系直接进行配置
(4)seq必须挂载到sqr,并依赖于sqr的结构关系来间接获取顶层配置信息
1.3.3 提示
(1)新手:将时序控制的权力给seq,即seq产生item,seq交给driver
(2)明确职责的情况:seq应该只负责生成item
(3)item生成和传送并不代表最终的接口时序,决定接口时序的是sqr和dri。进而引申出一个问题,为什么要有sqr在seq和dri之间
- sqr既是关卡也是路由
- sqr是一个组件,可以通过TLM端口传送item
- sqr有仲裁机制,处理多个并行seq
1.4 总结:数据传送机制
(1)数据传送机制是get模式(关于put和ge模式参考TLM)
- sv里面是generator产生item,stimulator/driver去get
- uvm也是get模式,driver是initiator,是get模式
如图:
通信模式
数据传输方向:
(2)为什么是get模式:
- 效率较高:seq产生item可以直接到达sqr,穿过sqr到达dri即可结束传输;put还需要收到返回值才可以发起下一次的传输
- seq的仲裁特性:仲裁特性更符合设计思维:
2 Sequence和Item
2.1 概述(引入)
(3)
seq指的是:uvm_sequence类
item指的是:uvm_sequence_item类
(4)环境中至少有一个seq,不能仅仅一个item就可以想当然的发送到sqr
2.2 sequence item 介绍
item继承于uvm_object,具备obj核心方法:copy()
,clone()
,compare()
,record()
item内部数据成员可分为以下几类:
- 控制类:如总线协议上的读写类型、数据长度、传送模式
- 负载类:一般指数据总线上的数据包
- 配置类:用来控制driver的驱动行为,例如命令driver的发送间隔或者有无错误插入
- config_db只能配置一次driver
- item控制
- 调试类:用来标记一些额外信息方便调试,例如该对象的实例序号、创建时间、被driver解析的时间始末等
2.3 item的简单例子
sqr没有接dri与seq,仅仅看item是怎么回事
item定义
test
这里用new创建obj,复习一下create的好处:
- override
- 建立层次化关系
- comp不可以new?
输出结果
t1没有随机化,t2随机化了
2.4 item使用特点
(1)数据成员为rand形
(2)使用`uvm_field_xxx宏进行数据成员声明,便于域自动化
(3)seq的body()很重要,和run类似
(4)item生命周期:始于seq的body(),穿越sqr,最终到达dri并被笑话
- item要善用copy(),clone()等数据方法
2.5 item与seq的关系
(1)seq控制item实例的随机化,因此内部应预留可供外部随机化的变量,通过层级传递进seq进而控制item,控制item的内容有
- item内部随机变量
- item之间的组织和时序控制
2.6 三类常见的seq定义方式:
扁平类(flat sequence):最简单的,只组织最小颗粒度即item
层次类(hierarchical sequence):更高层seq包括底层seq
- 虚拟类(virtual sequence):工作后经常用的,控制整个测试场景中不同的seq
2.7 三类seq定义:Flat Sequence 介绍
2.7.1 介绍
2.7.2 代码示例一
seq
- body就像组件中的run一样,因为obj不参与phase,定义了body你不需要手动运行就和你不需要手动run一样
- 数据的随机化:
test
这里为什么手动执行body(),因为这不是个完整的代码,完整的应该是由挂载到的sqr自动运行
示例总结
(1)暂时没有用seq宏
(4)示例一颗粒度较小没有使用item
2.7.3 代码示例二:加大颗粒度(使用item)
trans
seq更多的去做一些控制的任务
seq更加简洁
多的去做一些控制的任务,粒度由trans控制
只需要例化一个trans实例
test没有什么大的变化
输出结果
2.7.4 如何去定义一个item,粒度多少合适?
简单来讲:seq里面包含多个item
2.8 三类seq定义:Hierarchical Sequence 介绍
2.8.1 介绍
- 嵌套seq好处是可以创建更丰富的激励场景
- hierarchical seq可以嵌套:hierarchical seq,flat seq,item
2.8.2 代码示例:trans与flatseq基于2.7.3
hier_seq
宏做了三件事:
- 创建了seq和item
- 完成了随机化
- 传送到sqr上
2.8.3 示例总结
(1)示例代码宏的三个作用
(3)示例中既有串行的激励关系,也有并行的激励关系
更复杂的场景中可以加入seq/item之间的时序关系:
- 事件同步
- 一定的延迟关系
3 Sequencer和Driver
3.1 概述
3.1.1 数据传输概述
seq与dri是组件,组件之间的通信依赖于TLM端口
数据传送机制是get模式(关于put和ge模式参考TLM)
sv里面是generator产生item,stimulator/driver去get
uvm也是get模式,driver是initiator,是get模式
seq与dri的端口结构图:双向的数据传输
3.1.2 端口
(1)端口:
UVM专门提供便于item传输的端口,供seq与driv使用:
1 |
|
(2)端口分类:
由于driver是请求发起端,所以在driver一侧例化了下面两种端口,一般我们只会使用其中一种:
1 |
|
而sequencer一侧则为请求的响应端,在sequencer一侧例化了 对应的两种端口:
1 |
|
- 为什么是export实际上是imp?:
- 因为sqr里面有fifo,对外显示的是export但已经是最后一个组件了,对内还需要连接
- 如图所示:
- 补充sqr只有rsp的fifo没有req的fifo
回忆三种port
3.1.3 端口的连接
dri与sqr的端口成对组合,一共两对,进行连接:
- 第一对:
uvm_seq_item_pull_port #(REP, RSP) seq_item_port
uvm_seq_item_pull_imp #(REQ, RSP, this_type) seq_item_export
- 第二对:
uvm_analysis_port #(RSP) rsp_port
uvm_analysis_export #(RSP) rsp_export
两组端口都可以实现rsp发送,但方式不一样
(1)通常使用第一对就够了,即:driver::seq_item_port.connect(sequencer::seq_item_export)
3.1.4 第一对端口的方法(需要反复听)
在driver一侧,通过seq_item_port调用这一种类型的TLM端口的方法:
- task get_next_item(output REQ req_arg):采取blocking的方式等待从sequence获 取下一个item
- 常用,blocking即阻塞的方式
- task try_next_item(output REQ req_arg):采取nonblocking的方式从sequencer获 取item,如果立即返回的结果req_arg为null,则表示sequence还没有准备好
- nonblocking即非阻塞
- function void item_done(input RSP rsp_arg=null):用来通知sequence当前的 sequence item已经消化完毕,可以选播性地传递RSP参数,返回状态值
- 不可缺少,完成一个完整的握手
- task wait_for_sequences():等待当前的sequence直到产生下一个有效的item
- 用的较少
- function bit has_do_available():如果当前的sequence准备好而且可以获取下一个有 效的item,则返向1,否则返回0
function void put_response(input RSP rsp_arg):采取nonblocking方式发送 response,如果丽]返回1,否则返回0
也可以用传统的:
task get(output REQ req_arg):采用get方式获取item
task peek(output REQ req_arg):采用peek方式获取item
task put(input RSP rsp_arg):采取blocking方式将response发送回sequence
为什么有这三个port?
- uvm seq_item pull_port,uvm seq_item pull_export,uvm seq_item pull_imp这三个port继承于之前的tlm_port,这些port保留下来
经常成对出现的方法:
- get_next_item与item_done
put rsp独立出现
3.1.5 sqr与dri参数类的REQ与RSP的类型参数
参数类声明原型:
参数类内部的类型一致,缺省值为uvm_sequence_item,即自定义的Item的父类类型
类型转换:
(1)如上所说缺省时拿到的是父类类型,需要动态进行类型转换
(2)类型参数为自定义类时不用在意
类型保持一致的作用:
把rsp反馈到sqr的四种方法:
- put(rsp)//put rsp
- put()//put方法
- rsp_port::write(RSP)//rsp_port进行write写入
- item_done
这四种方法最终都会写入到一个fifo里面
3.2 事务传递实例
(1)item(trans) 和 flat_seq
flat_seq:
- body
- 调用seq里面的函数create_item创建了一个item,创建时参数包括item的类型,被挂载到的sqr, (补充:用new也能)
- 父类句柄转换:
void`($cast(req, tmp))
,记住这种类型转换的方法(补充:如果只做randomize可以把子类也随机了,但这里要调用子类内的成员变量所以需要转换类型)
补充内容:
- 为什么item也被挂载到sqr上
- 实际上seq与Item都挂载到sqr上
- 而自己定义的sqr又是uvm_sequence的成员变量
- 一个完成的传输:
- seq收到item_done才算结束,这里使用get_response(tmp)
(2)sqr:最简单的一个
继承于uvm_sequencer
只要进行一个注册就ok,最简单的一个,更多的握手关系在seq和dri一侧,sqr做路由作用
(3)driver
关于REQ是哪来的,你忘了自己是个参数类了吗?这不是参数类的类型参数值吗?
- 这里没有进行参数指明,所以直接用默认了,就是没有下图红字部分
- 这里没有进行参数指明,所以直接用默认了,就是没有下图红字部分
run_phase
通过port拿到item:seq_item_port.get_next_item(tmp)
item父类转子类
子类克隆方法(返回父类句柄)
克隆的对象的父类句柄转子类
req的id交给rsp的id:rsp.set_sequence_id(req.get_sequence_id())
seq_id是什么时候产生的?item经过sqr时被打上
seq_id有什么用?用于识别不同seq
为什么要手动set?id没有进行域的自动化,克隆的时候不会复制id,克隆只关心数据部分而id不是数据部分
传输结构图:
item_done(rsp句柄),让seq结束
- 即便没有rsp句柄(即没有rsp消息)也是一个完整的数据传输过程
- 如果没有rsp句柄,seq能否等到?
- finish_item可以等到,因为等到的item_done
- get_response(tmp)会卡住,因为fifo为空没有数据
- 因此get_response一定要成对不然会卡住
(4)env
成员区域
- 对sqr和drv进行例化
build
- 对sqr和drv进行创建
connect
- 对第一个端口类进行连接,这就够了
(5)test
build:
- sqr的创建
run_phase控制各个run_phase:
挂起objection防止退出
例化flat_seq
- flat是Obj是在运行过程中动态产生的所以不在build里面例化
flat_sep挂载到sqr上
- 结束挂起结束运行
(6)输出结果
3.3 【重要】各个模块的动作总结
flat_seq:
1 |
|
driver以及之后的逻辑:
高层次端口的连接(env和test)
(1)env端口连接,使用一种端口进行成对连接就行
(2)
记得在test中挂起objection防止提前退出
挂载seq到sqr使用uvm_sequence::start(SEQUENCER)完成
3.4 【重要】通信时序
3.4.1 通信时序
3.4.2 seq与sqr的细节补充
(1)seq起点是create
(2)sqr的仲裁机制根据dri的get_next_item来的
(3)sqr在获取item之前item应该完成随机化
3.4.3 握手建议、类型问题
主要讲克隆和ID一部分的内容,可以参考3.2driver的run_phase
以及rep和rsp使用一个item所带来的危害
seq与driver类型完全保持一致
如何修改底层的sequence item类型?override
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 协议 ,转载请注明出处!