写在前面:
- 本系列为张强《UVM实战》白皮书的阅读笔记,书写得很好,很有意思,也适合入门。
- 本文目录序号均为书中章节编号,便于对应。
- 此外,本系列中的代码注释为本人补充,之后会上传到我的CSDN资源中。
2.4 UVM的终极大作:sequence
sequence机制用于产生激励。之前激励都是在driver中产生的,但在规范化的UVM验证平台上,driver只负责驱动transaction,而不负责产生transaction。
sequence机制有两大组成部分:sequence和sequencer。
2.4.1 在验证平台中加入sequencer
- sequencer的定义如下:
- sequencer负责产生transaction,driver负责接收,进而驱动transaction。实际上driver也是一个参数化的类(虽然之前没有用过它的参数)。这里还是在driver中产生激励,主要看参数化driver带来的区别:
- 由于sequencer与driver的关系非常密切,因此agent中也要加入sequencer:
- 目前为止,在UVM树中,my_sequencer位于i_agt的叶子结点
2.4.2 sequence机制
梳理一下目前的验证平台testbench框图: sequence不属于tb的任何一部分,但是与sequencer之间有着紧密的联系,也有显著的区别。
补充:sequencer与sequence的联系与区别:
- 联系:sequencer能够让sequence产生出的transaction传给driver。
- 区别:sequencer是uvm_component(始终存在),sequence是uvm_object(有生命周期)。sequence的生命周期比其产生的transaction长一些,其transaction全部发送完毕后,sequence的生命周期也随之结束。
- my_sequence.sv如下:
- 一个sequence向sequencer发送transaction前,要先发送一个请求,sequencer把这个请求放在一个仲裁队列中(UVM内部机制,不需要显式地处理)。sequencer要:检测仲裁队列里是否有某个sequence发送tr的请求;检测driver是否申请要tr。各种情况以及sequencer的处理如下:
- driver向sequencer申请transaction的方法:
步骤1 先建立driver到sequencer之间的通道:uvm_driver有成员变量seq_item_port,uvm_sequencer有成员变量seq_item_export,二者之间可以建立一个通道来传递transaction,即sequencer和driver指定的transaction类型(这里是my_transaction)。这是UVM内部机制,无需显式指定通道类型。不过agent中需要用connect来建立通道: 步骤2 在driver中通过get_next_item任务向sqr申请新的tr,注意其中的握手机制: - driver和sequencer都准备好了,下面是启动sequence,即让sequence把transaction送出(给sequencer):在某个component(sequencer, env均可)的main_phase中启动sequence即可。
如在env中启动sequence: 若在sequencer中启动sequence,start参数改为this:
补充:get_next_item与try_next_item(my_driver.sv调用)
- get_next_item是阻塞的,会一直等到有新的transaction才返回;
- try_next_item是非阻塞的,会尝试着询问sqr是否有新的tr,若有则拿到该tr,若无则直接返回。相比get_next_item,try更接近真实driver的行为:有数据时驱动数据,否则总线将一直处于空闲状态而不是一直等待。
- 使用get_next_item代码如下:
- 使用try_item_get代码如下:
2.4.3 default_sequence的使用
2.4.2中sequence是在env/sequencer的main_phase中手动启动的,但实际应用中,更多通过default_sequence来启动seq。 default_sequence的设置步骤如下:
- config_db的set:在某个component的build_phase中设置如下代码(这是目前除了在top_tb中用config_db设置virtual if后第一次用到config_db功能),这里用my_env举例:
除了在env的main_phase中设置default_sequence来启动sequence,还可以在top_tb里的initial begin-end对里设置,或其他component里,如my_agent的build_phase里。只需要正确地设置set的第1和第2个参数即可。 - config_db的get:这一步无需额外写,UVM已经默认在sequencer里做好了get。
- raise&drop_objection:手动启动sequence时需要在启动前后raise&drop_objection,设置default_sequence也需要,不过是在sequence中使用starting_phase进行:
2.5 建造测试用例
2.5.1 加入base_test
UVM使用树形结构,本书中最初树根是my_driver,后来变成了my_env,但实际应用中my_env并不是树根。通常树根是一个uvm_test派生的类,其中一个经典子类是base_test。真正的测试用例都是基于base_test派生的一个类。base_test.sv如下: 由于UVM树的结构变了,my_env不再是根节点,而是成为了base_test的叶结点,因此top_tb.sv也要作如下改变:
2.5.2 UVM中测试用例的启动
对DUT施加的激励被称为测试向量或pattern。一种激励作为一种测试用例,不同的激励就是不同的测试用例。测试用例的数量是衡量验证人员工作成果的最直接目标。 伴随着验证的进行,测试用例的数量一直在增加,要保证后加的测试用例不影响已经建好的测试用例。比如已经设置好了default_sequence的形式启动my_sequence,如果有新的my_sequence2,要在不影响my_sequence的前提下启动。最理想的办法是在命令行中指定参数来启动不同的测试用例。
- 先看两个不同的测试用例:
- 再看top_tb中需要的改动
整个启动及执行的流程:
- module top_tb
- 全局的run_test
- 启动验证平台
- 根据UVM_TESTNAME产生my_case0的实例
- 依次执行build_phase,形成完整的UVM树
- 顺序执行UVM树各结点的connect_phase、main_phase等
- 所有的phase执行完毕,结束仿真
|