UVM库是类的集合,它通过提供如何使用SystemVerilog中的功能结构,使SystemVerilog语言使用起来更为通用顺畅。然而,在许多情况下,UVM提供多种机制来完成相同的工作。因此,Mentor提供了一些使用UVM的建议,意在帮忙大家更有效率的使用UVM。
1. 在package里定义class,其它package需要的话,采用import的方式导入类。不要使用include的方式将同一个class放到多个package里。
2. 除了name和parent之外,不要在构造函数里添加额外的参数。在最坏的情况下,构造函数的额外参数将导致 UVM Factory 无法创建所请求的对象。即使额外参数具有允许工厂运行的默认值,额外参数也将始终是默认值,因为factory仅传递name和parent参数。
3. 使用UVM Factory注册并创建所有类。使用 UVM Factory 注册所有类并创建所有对象可最大限度地提高 UVM 测试平台的灵活性。向 UVM Factory 注册类不会带来运行时损失,并且通过 UVM Factory 创建对象只会产生轻微的开销。UVM 定义的类在factory中注册,并且从这些类创建的对象应使用 UVM factory创建,这包括 uvm_sequencer 等类。
4. 使用 UVM Factory 创建对象时,将对象的句柄名称与传递到 create() 调用中的字符串名称相匹配。UVM 构建一个对象层次结构,用于许多不同的功能,包括 UVM Factory 覆盖和配置。此层次结构基于传递到每个构造函数的第一个参数的字符串名称。保持句柄名称和字符串层次结构名称相同将极大地有助于调试。
5. 使用 `uvm_object_utils()、`uvm_object_param_utils()、`uvm_component_utils() 和 `uvm_component_param_utils() 工厂注册宏。顾名思义,这些宏向 UVM 工厂注册 UVM object或component,这是必要且关键的。当这些宏展开时,它们提供了 type_id typedef (这是工厂注册)、静态 get_type() 函数(返回对象类型)、 get_object_type() 函数(不是静态的,也返回对象类型)和 create() 函数。
6. 使用UVM消息宏`uvm_info()、`uvm_warning()、`uvm_error()和`uvm_fatal()。UVM 消息宏在使用时可节省性能。这些宏带来的作用是检查在执行昂贵的字符串处理之前是否会过滤消息。它们还在输出消息时将文件和行号添加到消息中。
7. 编写自己的 do_copy()、do_compare()、do_print()、do_pack() 和 do_unpack() 函数。不要使用field automation宏。从表面上看,field automation宏看起来像是一种处理类中数据成员的非常快速且简单的方法。然而,field automation宏有一个非常大的隐藏成本。因此,Mentor不使用也不建议使用这些宏。这些宏包括`uvm_field_int()、`uvm_field_object()、`uvm_field_array_int()、`uvm_field_queue_string()等。当这些宏展开时,会导致数百行代码。生成的代码不是人类编写的代码,因此即使扩展也很难调试。
8. 尽量使用sequence.start(sequencer)来启动sequence。由于 start() 是一个task,它将阻塞直到sequence完成执行,因此您可以通过将sequence.start(sequencer)命令串在一起来控制TB中发生的事情的顺序。如果两个或多个sequences需要并行运行,则可以使用SystemVerilog的fork/join(_any, _none) 等。在父sequence中启动子sequence时,也可以使用sequence.start(sequencer)。
9. 使用factory创建sequence_item,且使用start_item() 和 finish_item()发送它们,不要使用 UVM sequence宏(`uvm_do 等)。要在sequence中使用sequence_item,Mentor 建议使用factory来创建sequence_item,使用 start_item() 任务与sequencer进行仲裁,使用 finish_item() 任务将随机化/准备好的sequence_item发送到连接到sequencer的driver上。
10. 不要使用sequence中定义的 pre_body() 和 post_body() 任务。根据sequence的调用方式,这些task可能会也可能不会被调用。相反,将本应进入 pre_body() 任务的功能放入 body() 任务的开头或pre_start()里。同样,将本来进入 post_body() 任务的功能放入 body() 任务的末尾或post_start()里。
11. sequence不应显式消耗时间语句,比如#10ns,具有显式延迟会减少重用。
12. uvm_driver和uvm_monitor是测试平台执行者,应该只实现 run_phase()它们不应该实施任何其他耗时的phase。Uvm_driver处理从sequence发送给它的任何事务,并且uvm_monitor捕获在任何时间上它在总线上观察到的事务。
13. 使用uvm_config_db API传递配置信息。不要使用 set/get_config_object()、set/get_config_string() 或 set/get_config_int(),因为它们已被弃用。也不要使用 uvm_resource_db API。
14. 创建配置类来保存配置值,为了给agent或env或test提供配置信息,应创建一个配置类,其中包含所需的位、字符串、整数、枚举、虚拟接口句柄等。每个在组建应该有自己的配置类,其中包含组件任何部分使用的每条配置信息。
15. 不要将配置空间(config_db)用于组件之间的频繁通信,使用资源数据库进行组件之间的频繁通信很浪费时间。不频繁的通信(例如提供寄存器模型的句柄)是可以接受的。
16. 使用 uvm_config_db API 将虚拟interface句柄从顶层测试测试module传递到测试平台对应的组件。
17. 将 covergroup 放置在从 uvm_object 扩展的包装类中。Covergroup 不是对象或类。它们不能被扩展,也不能以编程方式自行创建和销毁。但是,如果covergroup包装在类中,则测试平台可以在运行时决定是否构建covergroup的包装类。
18. 仅在uvm_test中raise和drop uvm_objection。在大多数情况下,仅应在test中耗时的阶段之一raise和drop uvm_objection。这是因为test是测试平台中将要发生的事情的主要控制器,因此它知道所有激励何时被创建和处理。如果components需要更多时间,可以使用 phase_ready_to_end() 函数以留出更多时间。因为raise和drop一个uvm_objection会产生开销,所Mentor建议不要在monitor或的driver中raise和drop uvm_objection,因为这会由于所涉及的开销而导致仿真速度减慢。
19. 不要使用callback。UVM 提供了一种为特定对象注册callback的机制。不应使用此机制,因为注册和启用callback需要许多复杂的步骤。此外,除了潜在的排序问题之外,callback还具有不可忽略的内存和性能占用。相反,在需要callback功能的地方,可以使用标准的面向对象编程 (OOP)来替换 。一种 OOP 选项是扩展要更改的类,然后使用 UVM factory覆盖创建的对象。另一个 OOP 选项是在父对象中创建一个子对象,该子对象将一些功能委托给它。为了控制使用哪个功能,可以使用配置设置或者可以使用factory覆盖。
20. 不要在用户定义的 plusargs 前添加“uvm_”或“UVM_”前缀,防止和UVM内置的参数重名。