电子说
本篇介绍UVM中的sequence,这是UVM中最基础的部分。对于前面介绍的uvm_callback, uvm_visitor等,很少被使用到或者也只有搭建平台的人会使用。不能认为平台的搭建更富有“技术含量”,用例的构建有时候更重要。UVM提供了多种方式,常常让使用者混淆,本篇将会 “捋” 一下sequence_item,sequence,sequencer,driver的协作关系,结合设计模式中的命令模式,中介模式,桥接模式进行介绍。
启动与挂载
基本理解: UVM中各个组件的相互“交流”是基于实际业务提取出的transaction( uvm_sequence_item );uvm_sequence中的 body() 函数负责产生发送这些transaction,生命周期是body()函数的执行期;uvm_sequencer负责调度从uvm_sequence中拿到的transaction,然后发送给driver;uvm_sequcence和transaction一样,也是继承于uvm_sequence_item,uvm_sequcne是transaction的有机结合,代表一种具体的业务行为,描述一种scenario 这样更有利于在创建用例时的复用。同时在transaction,uvm_sequence中加入SV特有的语法特点,constrain约束,方便扩展使用到不同的场景。
启动: 这里的启动就是通过哪种方式调用uvm_sequence中的body()函数的意思。( body()函数被sequence的 start()函数调用,也可以理解成 start()函数的执行 )
sequence的挂载: uvm_sequence不属于component,没有phase概念,需要挂载在一个sequencer上,在这个sequencer的phase中被调用( default_sequence的情形 ),同时uvm_sequence可以操作所挂载的sequencer的成员变量( *比如在sequence中使用sequencer中的寄存器模型句柄;在virtual sequence中调用 virtual sequcner中的其他 sequcner 句柄 * )。
transaction的挂载: 对于transaction,挂载的意思就是将transaction发送给所挂载的sequencer,sequencer再发送给连接的driver。(无论sequence还是transaction,挂载的本质就是给uvm_sequence_item中的成员变量 m_sequencer赋值,sequence/transaction都继承于uvm_sequence_item)
( *启动,挂载在UVM英文文档中没有对应关键词,仅是根据实际使用总结出来的词汇 * )
继承关系:
三种sequence的启动方式:
1. start()函数显示调用
使用形式:
在sequence中显示调用strat()函数,第一个参数是需要挂载的sequencer;第二个是parent_sequence,一般传入this或者不传入;第三个是优先级;第四个call_pre_post默认为1,则自动执行pre_body/ post_body()函数
执行pre_start,body等函数。此时就完成了sequence的启动过程。
在start()函数中,首先调用了函数 set_item_context() 函数,这个函数位于sequence的父类uvm_sequence_item中,负责给成员变量m_sequencer, m_parent_sequence赋值。
如果没有指定挂载的sequencer,则挂载到parent_sequence的sequencer上。
此处seq0显示传入了需要挂载的p_sqr0,则调用 set_sequencer() 函数完成挂载,给m_sequencer赋值。
set_sequencer()函数会调用m_set_p_sequencer(),这个函数是一个空的虚函数。如果在sequence中调用了宏 uvm_declare_p_sequencer则会重写这个函数,将成员变量p_sequencer指向sequence所挂载的sequencer上。所以使用者要保证start()函数传入的sequencer应该和宏 uvm_declare_p_sequencer声明的类型一致。否则$cast转换的时候会报错。
使用uvm_declare_p_sequencer后,就可以在sequence中调用挂载sequencer的成员函数和成员变量了。
2. `uvm_do()宏
**使用形式: **
UVM中提供了多个宏,uvm_do,uvm_do_with,`uvm_do_on_with等,但最终都是调用了uvm_do_on_pri_with宏。uvm_do_on_pri_with宏第一个参数可以传入sequence,也可以传入transaction。
uvm_do_on宏第一参数是sequence。uvm_do_on_pri_with宏中调用宏uvm_create_on。
create_item通过工厂模式创建sequence实例
调用 set_item_context() 函数,给成员变量m_sequencer, m_parent_sequence赋值,参考上节分析。此处parent_sequence默认是this。
调用sequence的start函数,流程和上节一样,最后调用body()函数。
通过宏的形式相比于直接调用start函数,节省了sequence的实例化和随机化的步骤,但是对于不熟悉宏的人来说,宏封装的内容可能与其使用意图偏差。
3. default_sequence方式
使用形式:
第一种:
第二种:
使用default_sequence的方式也是通过工厂模式创建sequence,再隐式的调用seq.start(this) 函数
【拓展】
对于sequence中的资源访问,可以参考 UVM设计模式 (三) 静态类、资源管理、uvm_event、uvm_*_pool、uvm_config_db、UVM_REGEX_NO_DPI 中的 "sequence中的资源访问" 小节,总结了5种使用方式。
命令模式
Comand Pattern: 将一个请求(命令)封装成一个对象,从而可以用不同的请求对接收者进行参数化,实现请求的发送者和接收者解耦;还可以对请求排队,或者记录请求日志,以及支持撤销操作。
命令模式和前面提到的策略模式很相似,也是通过“ 组合 + 多态 ”实现的。设计模式更关注于设计意图,也就是应用场景,单纯地从代码实现上看,有些模式确实很相似,比如命令模式和策略模式。但策略模式是不同策略具有相同的目的,不同的实现,相互之间可以替换,在命令模式中,是不同的命令具有不同的目的,对应不同的逻辑处理,相互之间不可替换。
一个简单的示例:captain是命令的发起者,soldier是命令的接收者。
start_item finish_item
上节提到使用uvm_do宏启动sequence,如果宏传入的第一个参数不是uvm_sequence_base类型,就是我们的transaction, 则调用start_item, finish_item函数。
start_item()三个参数,第一个是传入的transaction, 第二个是优先级,第三个是指定该transaction发送给哪一个sequencer, transaction挂载在哪一个sequencer上。
如果之前没有给transaction的m_sequencer赋值,此处sequcner仍未null
调用get_sequencer()函数,将transaction挂载到sequence启动的sequencer上。
transaction必须指定挂载的sequencer, 否者transaction无法通过sequencer发送给driver。而sequence却不一定需要挂载到sequencer上,因为sequence的主要目的是执行body函数,直接在tc中调用seq.start()不指定sequencer,也可以。但是default_sequence的形式必须挂载到sequencer上。
set_item_context()函数上节已提到。wait_for_grant()等待sequencer仲裁。pre_do() hook函数。
在finish_item中,调用transaction挂载sequencer的函数send_request(), 这个函数定义在uvm_sequencer_param_base中。将transaction放入m_req_fifo容器中。
当driver中调用**seq_item_port.get_next_item(req)**时,实际调用的是uvm_sequencer中的get_next_item函数。从m_req_fifo容器中拿到之前sequence放入的transaction。
driver中的seq_item_port.item_done(),实际调用的是uvm_sequencer中的item_done函数。sequence通过wait_for_item_done和sequencer的item_done握手,通过成员变量m_wait_for_item_sequence/transaction_id判断。每个sequence, sequence中的每个transaction其ID都是唯一的。
如果item_done()传入rsp,调用put_response函数,与sequence中的get_response配合使用。
start_item/finish_item封装函数以及sequence,sequencer,driver的握手关系如下图:
结合命令模式,sequence作为命令的发起者,sequencer作为接收者。sequence负责发送各种transaction, 至于是哪一个sequencer接收,由transaction中的成员变量m_sequencer决定。
实现发送者与接收者的解耦 。设计模式侧重应用场景,transaction仅仅是事务,不具备命令的实现行为,所以UVM的实现并不完全符合命令模式。
UVM中的sequencer更像一个仲裁者,一边是driver不断请求transaction,一边根据priority给sequcence授权,接收sequence发送的transaction,支持lock,grab操作。同时driver,sequence之间也有联系,driver可以response transaction给sequence。
在实际使用中,建议不要使用宏,而是通过start()函数显示启动sequence, 通过start_item finish_item发送transaction。
中介者模式
Mediator Pattern: 定义一个单独的(中介)对象,来封装一组对象之间的交互。将这组对象之间的交互委派给与中介对象操作,来避免对象之间的直接交互,使耦合松散。
如果不使用中介者模式,各个系统模块,或者说各个类之间,互相依赖,就会形成一个复杂的网装结构;使用了中介者模式,系统就变成了结构清晰的星形结构。
UVM中virtual sequence就是一个典型的中介者模式的使用案列。在virtual sequence中可以统一调度各个sequence,负责每个sequence的同步,成员变量赋值,随机约束等。中介者模式实现简单,具体示例不在演示。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !