欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

UVM设计模式详解(七):命令行为、三种序列启动策略、起止事件元素、中介机制以及虚拟序列

最编程 2024-02-19 11:26:24
...

本篇介绍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的有机结合,代表一种具体的业务行为, 这样更有利于在创建用例时的复用。同时在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()函数显示调用

使用形式:

1. 在sequence中显示调用strat()函数,第一个参数是需要挂载的sequencer;第二个是parent_sequence,一般传入this或者不传入;第三个是优先级;第四个call_pre_post默认为1,则自动执行pre_body/ post_body()函数

2. 执行pre_start,body等函数。此时就完成了sequence的启动过程。

3. 在start()函数中,首先调用了函数set_item_context() 函数,这个函数位于sequence的父类uvm_sequence_item中,负责给成员变量m_sequencer, m_parent_sequence赋值。

4. 如果没有指定挂载的sequencer,则挂载到parent_sequence的sequencer上。

5. 此处seq0显示传入了需要挂载的p_sqr0,则调用set_sequencer() 函数完成挂载,给m_sequencer赋值。

6. 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。

1. uvm_do_on宏第一参数是sequence。uvm_do_on_pri_with宏中调用宏uvm_create_on。

2. create_item通过工厂模式创建sequence实例

3. 调用set_item_context() 函数,给成员变量m_sequencer, m_parent_sequence赋值,参考上节分析。此处parent_sequence默认是this。

4. 调用sequence的start函数,流程和上节一样,最后调用body()函数。

通过宏的形式相比于直接调用start函数,节省了sequence的实例化和随机化的步骤,但是对于不熟悉宏的人来说,宏封装的内容可能与其使用意图偏差。

3. default_sequence方式

使用形式:

第一种:

第二种:

使用default_sequence的方式也是通过工厂模式创建sequence,再隐式的调用seq.start(this) 函数,可以参考:???? UVM设计模式 ( 四 ) 模板模式、策略模式、default_sequence、uvm_callback

 【拓展】对于sequence中的资源访问,可以参考 UVM设计模式 (三) 静态类、资源管理、uvm_event、uvm_*_pool、uvm_config_db、UVM_REGEX_NO_DPI 中的 "sequence中的资源访问" 小节。

命令模式

Comand Pattern: 将一个请求(命令)封装成一个对象,从而可以用不同的请求对接收者进行参数化,实现请求的发送者和接收者解耦;还可以对请求排队,或者记录请求日志,以及支持撤销操作。

命令模式和前面提到的策略模式很相似,也是通过“组合 + 多态”实现的。设计模式更关注于设计意图,也就是应用场景,单纯地从代码实现上看,有些模式确实很相似,比如命令模式和策略模式。但策略模式是不同策略具有相同的目的,不同的实现,相互之间可以替换,在命令模式中,是不同的命令具有不同的目的,对应不同的逻辑处理,相互之间不可替换。

一个简单的示例:captain是命令的发起者,soldier是命令的接收者。

start_item finish_item

上节提到使用uvm_do宏启动sequence,如果宏传入的第一个参数不是uvm_sequence_base类型,就是我们的transaction, 则调用start_item, finish_item函数。

1. start_item()三个参数,第一个是传入的transaction, 第二个是优先级,第三个是指定该transaction发送给哪一个sequencer, transaction挂载在哪一个sequencer上。

2. 如果之前没有给transaction的m_sequencer赋值,此处sequcner仍未null

3. 调用get_sequencer()函数,将transaction挂载到sequence启动的sequencer上。

4. transaction必须指定挂载的sequencer, 否者transaction无法通过sequencer发送给driver。而sequence却不一定需要挂载到sequencer上,因为sequence的主要目的是执行body函数,直接在tc中调用seq.start()不指定sequencer,也可以。但是default_sequence的形式必须挂载到sequencer上。

5. set_item_context()函数上节已提到。wait_for_grant()等待sequencer仲裁。pre_do() hook函数。

6.  在finish_item中,调用transaction挂载sequencer的函数send_request() , 这个函数定义在uvm_sequencer_param_base中。将transaction放入m_req_fifo容器中。

7.  当driver中调用seq_item_port.get_next_item(req) 时,实际调用的是uvm_sequencer中的get_next_item函数。从m_req_fifo容器中拿到之前sequence放入的transaction。

8.  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都是唯一的。

9. 如果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的同步,成员变量赋值,随机约束等。中介者模式实现简单,具体示例不在演示。