UVM交互式调试库:缩短调试周转时间

摘要:

与其他硬件验证语言(Hadware Verfication Languages,HVL)(例如Specman e)不同,在System Verilog(SV)模拟中是不原生支持交互式调试功能的。在本文中,我们将介绍利用SystemVerilog直接编程接口(SVDPI)[2]实现针对UVM [1]的交互式调试库。从根本上讲,这实现了模拟运行时的高级交互式调试。该库的功能包括:1)通过UVM寄存器抽象层写入或读取寄存器,2)在任何序列发生器上创建,随机化,初始化和启动UVM序列,以及3)调用用户定义的SV函数或任务从交互式调试提示符。我们还将介绍一种调试和回归方法,概述了如何加快调试速度并最大程度地减少回归运行时间的最佳实践方式。初步结果表明,使用交互式调试库可显着减少调试周转时间和回归运行时间。用户可以在几秒钟内重新测试不同的场景,而无需等待几分钟或几小时让测试台从零时开始重新编译,制作和重新运行仿真。 uvm_debug库作为开源项目可在github [3]中获取。

 

关键词:SystemVerilog, Verification, UVM, SV-DPI, 交互(interactive), 调试(debug),开源 (open source) 

 

引言

使用其他编程语言(例如Specman e [4]和Cocotb / Python [5])实现验证框架的用户已经习惯了运行时灵活的交互式调试。用户可以将模拟运行到某个时间点,暂停模拟,激活调试提示符,然后调用任何testbench中的函数来更改测试方案的行为,然后再继续运行。在SystemVerilog中,由于语言的限制,一旦编译了一个测试平台,用户在仿真过程中几乎无法控制测试场景。当前,在仿真运行期间,用户仅能在仿真环境中使用差异种子或poke/force信号。

 

设想以下情形:用户需要运行长达几小时的长时间模拟。在模拟过程中,测试平台检查器会报告错误。如果用户需要更改激励以调试问题,则他们必须更新SV代码,重新编译测试平台,并等待很长时间才能再次发现错误。借助交互式调试库,用户可以尝试不同的调试方案而无需重新启动模拟。他们可以通过寄存器读取来查询设备状态,可以通过寄存器写入来尝试新的设备配置,通过执行更高优先级的新UVM序列来更改由测试平台产生的激励,或者可以调用任何测试平台的函数来帮助诊断问题。他们可以尝试数十种不同的情况,并在几分钟而不是几小时内找出错误。调试周转时间的大幅度减少可提高生产率,并且将时间用于隔离错误,而不是等待模拟编译/运行。

 

M. Peryer编写了cli_seq_pkg [6]进行了较早的尝试,以通过引入命令行调试接口来支持UVM中的交互式调试。 cli_seq_pkg的最大缺点是,每个序列都需要一个包装器类。每个包装器类都是手动创建的,或者由预处理脚本在两个阶段中生成的-解决方案既繁琐又容易出错。此外,作者没有提供cli_seq_pkg的源代码,并且本文中的描述没有包含足够的详细信息,以使用户可以轻松地重新实现该软件包。

 

J. McNeal和B. Morris编写了RESSL [7]进行另一种尝试,以实现读取,评估,启动序列循环。它的实现对现有的测试平台具有很高的侵入性。 RESSL需要UVM的被黑版本。 读取,评估循环中支持的所有序列必须派生自公共基类并手动注册到测试台中的序列注册表中。这些要求使得在大规模生产项目中采用非常困难,除非管理层强烈要求在整个项目中推动这些变化。

 

本文介绍的UVM交互式调试库(uvm_debug)是独立开发的,没有[6] [7]的先验知识。 uvm_debug库支持cli_seq_pkg的所有函数,并间接支持RESSL的所有函数,但功能更多。作者希望将Specman的强大调试功能和灵活性引入SV-UVM,以便多个SV测试平台环境可以从中受益。 uvm_debug库可从github [3]作为开源项目获得。设置uvm_debug库很容易且无干扰:只需下载代码,然后将.sv和.c文件与testbench一起编译即可。用户不必经历复杂的测试台连接过程或UVM源代码中的任何补丁。只需调用一个简单的函数即可初始化uvm_debug对象。

 

UVM交互式调试库综述

核心逻辑

交互式调试库的核心非常简单。模拟器运行时,SV缺少任何语言语法来读取用户的输入。幸运的是,SV可以通过SV-DPI接口调用C代码,因此可以在C代码的域中读取用户输入,然后将其传递回SV域。

 

在SV代码中,uvm_debug_pkg中的dpi_read_line()函数将提示打印到屏幕上,等待用户输入行,然后将输入的行作为字符串返回。在C代码中,最简单的实现是从<stdio.h>调用C内置的getline()函数,以从标准输入中读取一行。在模拟器以GUI模式运行的情况下,在Shell和GUI之间来回切换以输入调试命令非常不方便。或者,可以编译C代码以通过模拟器的Tcl提示符读取用户输入。

 

SV代码

C代码

 

一旦测试平台可以读取模拟中的用户输入,其余的操作就非常简单了。在UVM中创建的所有对象和组件都可以通过其名称(即文本字符串)来访问。调试库实现了命令行解析器,以处理用户输入,使用全名查询对象指针,然后使用用户提供的参数调用指定的任务或函数.

 

b. 启动调试提示符

有两种方法可以激活交互式调试提示符。第一种是在SV代码中嵌入函数调用uvm_debug对象。 uvm_debug对象是单例类,因此可以在测试平台或测试用例中的任何位置使用它,而不必担心需要跟踪不同的实例。用户可以将prompt()函数包装在if-else语句中,以指定任何触发条件,例如在测试平台检测到DUT错误或接收到某些事件标志时触发调试提示符。 prompt()函数带有一个调试级别的参数,该参数允许用户通过UVM命令行参数+ debug_level = <debug level>来控制何时激活调试提示符。仅当仿真中的调试级别高于prompt()函数中指定的调试级别时,调试提示符才会激活,这与UVM报告的详细程度类似。触发调试提示符的第二种方法是暂停模拟,然后从Tcl提示中调用debug_prompt的VPI系统函数。

 

SV代码

Tcl命令

 

c. 内务调试命令

调试提示符支持以下内置调试命令,以提供基本的内务管理功能。

表1 内务调试命令

 

命令 描述
help [command] 显示帮助信息。不带参数,则列出所有可用指令;否则,显示指定指令的帮助信息。
continue 离开调试指示并继续模拟。
pause 暂停模拟并切换到模拟器的Tcl脚本。
run <runtime> 模拟器运行指定的时间,随后返回调试指示。
.history [list]|clear|save <file> 列出用户之前输入的全部指令;清除历史记录;保存历史记录到指定文件。
repeat # 重复指令历史记录里的指定行。
read <file> 读取一个指令文件。
save_checkpoint [-path<path>] <snapshot name> 保存检查点的快照。

d. 寄存器调试命令

在执行寄存器操作之前,用户必须通过传入uvm_reg_block的根来初始化uvm_debug库,以设置调试命令使用的相对uvm_reg分层全名。

 

SV代码

 

调试提示符支持以下内置的uvm_reg相关调试命令。

表2 寄存器调试命令

 

命令 描述
wr_add <addr> <value> 通过地址写寄存器。
rd_addr <addr> 通过地址读寄存器。
wr_reg <reg> <value> 通过uvm_reg的全名写寄存器。
rd_reg <reg> 通过uvm_reg的全名读寄存器。
wr_regfld <field> <value> 通过uvm_reg的全名写寄存器的一个域。
rd_regfld <field> 通过uvm_reg的全名读寄存器的一个域。

e. 序列调试命令

调试提示符支持下表中列出的与内置uvm_sequence相关的调试命令。调试提示符会跟踪用户在关联数组中创建的所有序列和序列项。用户始终可以使用在创建过程中指定的名称来引用创建的序列或序列项。序列调试命令在UVM序列基类上运行-没有动态类型检查。如果序列或序列项与序列发生器的所需数据类型不匹配,则模拟器可能会出现致命错误。调试提示符使用的序列发生器路径是序列发生器相对于uvm_top_test的uvm_component完整层次结构名称。序列或序列项可以在阻塞模式下或在新线程中运行。在阻塞模式下,模拟时间会提前直到序列或序列项完成,然后返回调试提示符。在新线程中运行时,序列或序列项将在fork..join_none块内的后台运行。分派命令后,它将返回到调试提示符。

表3 序列调试命令

 

命令 描述
seq_list 列出在调试提示符中创建的序列。
seq_create <seq_type> <seq_name> 创建新的序列。
seq_rand <seq_name> 随机化调试指示符中创建的序列。
seq_set_fields <seq_name> [<field>=<value>…] 设置调试指示符中创建序列中的域值。
seq_start [-priority <priority>] [-new_thread <0|1>] <seq_name> <seqr_path> 在序列发生器中启动序列。
seq_kill <seq_name> 终止一个运行的序列。
seq_item_list 列出调试指示器中出创建的所有序列项。
seq_item_create <seq_item_type> <seq_item_name> 创建一个新的序列项。
seq_item_rand <seq_item_name> 随机化调试指示符中创建的序列项。
seq_item_set_fields <seq_item_name> [<field>=<value>…] 设置调试指示符中创建序列项的值
seqr_stop_sequences <seqr_path> 停止序列发生器中的全序列。
seqr_execute_item [-new_thread <0|1>] <seqr_path> <seq_item_name> 在序列发生器中执行一个序列项。

f. 用户自定义调试命令

除了uvm_debug库附带的内置调试命令之外,用户还可以通过创建调试命令回调函数来创建自定义调试命令,用户可以在其中调用测试平台中的任何函数或任务。用户不必显式注册自定义调试命令。唯一需要的设置是创建调试命令回调对象。 debug命令回调类的内置构造函数会自动将调试命令注册到uvm_debug单例对象。在调试命令回调基类的构造函数中处理到调试提示的命令注册。以下代码片段显示了如何定义自定义调试命令的示例。

 

SV代码

 

调试提示符中的输入参数作为字符串列表传递到回调对象中。为了帮助用户在调用testbench函数/任务之前解析参数列表,uvm_debug库带有内置的参数解析器。参数解析器支持两种参数格式,tcl中用破折号选项格式(-option值)和SV命令行中用加参数格式(+ option = value)。在SV中,它还支持用’b /’o /’h表示指定的bin,oct或hex值从字符串转换为整数值。下表列出了参数解析器文本处理功能。

 

表4 序列调试命令

 

命令 描述
extract_options(ref string args[$], ref string options[string]) 从参数列表中提取破折号选项和加号选项,并将它们存储在关联数组中。
has_option_flag(string args[$], string flag) 检查参数列表中是否存在选项标志或没有值的选项。
extract_keyvals(string args[$], ref string vals[string] 在参数列表中提取键-值对,<键> = <值>。
int str_to_int(string s) 将字符串转换为整数(支持SV bin / dec / oct / hec表示法)。
qint str_to_qint(string s) 将字符串转换为整数列表。整数列表由逗号分隔。它还支持<min> .. <max>格式的整数范围。
string get_option_string(string options[string], string key, string default_value=””) 获取指定选项的值,并以字符串形式返回。如果找不到该选项,则返回默认值。
int get_option_int(string options[string], string key, int default_value=0) 获取指定选项的值,并以整数形式返回。如果找不到该选项,则返回默认值。
qint get_option_int_list(string options[string], string key, qint default_value={}); 获取指定选项的值,并以整数列表形式返回。如果找不到该选项,则返回默认值

g. Tcl脚本整合

调试提示符非常适合以交互方式尝试不同的测试方案。如果多次使用同一组调试命令,则加载命令文件可以节省键入时间。但是,调试提示符没有任何内置的编程元素-它只能顺序执行一个接一个的命令。换句话说,调试提示符本身并不是图灵完整的语言。幸运的是,我们已经在模拟器中以Tcl提示符的形式提供了功能齐全的编程环境。 uvm_debug库创建Tcl包装函数,该函数调用调试提示符,传入并执行一个调试命令,然后返回到Tcl提示调试命令的返回值。uvm_debug库与模拟器的Tcl提示一起将UVM转换为脚本语言。

 

Tcl提示符

请注意,Tcl脚本集成仍处于试验阶段。作者仅尝试在简单的if-else分支和控制循环中运行调试命令。 Tcl提示符实际上是在单个线程下运行所有​​程序。如果有多个线程同时调用多个调试命令,那么uvm_debug库的运行情况看起来将很有趣。但是,Tcl从未计划成为一种严格的多线程安全编程语言,那么为什么我们不简单地在本机SV代码中实现那些复杂的场景呢?

 

h. 测试平台示例

uvm_debug库带有一个测试平台示例。我们选择了每个UVM发行版附带的uvm_example / integrated / codec示例,以演示设置uvm_debug库有多么容易。我们选择此测试平台作为示例,因为它很简单,并且验证社区可以很好地理解,但是它提供了功能齐全的测试平台的框架。它具有APB寄存器接口和简单的串行VIP序列。

 

集成非常简单。除了要在示例中添加额外的信息消息之外,以突出显示当前正在运行的调试命令,它仅需要三行代码。用户可以在uvm_debug库中运行demo.sh以查看实际的调试提示。该示例还具有以下简单的调试命令文件,以演示最常用的功能。

 

debug.cmd

 

调试和回归方法

一旦我们建立了更改SV中已编译模拟的行为的能力,就可以采用之前论文[8]中概述的经过验证的调试方法,以加快调试周转时间并最大程度地减少回归运行时间。图1说明了uvm_debug库的三个最常用的模型。

 

图1 交互式调试方法示例

首次使用模型类似于传统的调试流程,即在模拟器的测试台中设置断点或在实验室的后硅调试环境中设置软装中的断点。一旦在测试平台中检测到错误,它将暂停模拟并触发调试提示符。用户可以交互地读取或写入寄存器和/或注入简单的激励来诊断问题。现在通过执行更高级别的调试命令来代替以前手动查看和peek/poke RTL信号所完成的工作,从而使验证工程师免于在Tcl中处理RTL信号的繁琐工作。

 

有时,使用现有模拟中的波形并仅在故障点之后对设计进行调整可能无法提供足够的信息来根源导致问题。可能需要工程师尝试不同的极端情况来对错误进行定位。如果错误发生在模拟运行了几个小时后,那么从零时开始重新运行模拟以重现错误可能会耗费工程师一整天的时间。典型的故障需要多次迭代才能隔离并正确识别错误,因此在每次迭代中重新运行仿真都是不可接受的,并且效率极低。我们需要复制故障条件,并在更短的时间内尝试不同的激励。

 

第二个使用模型将uvm_debug库与模拟器的快照保存/恢复功能结合在一起。在模拟运行中,测试台将定期(例如每30分钟)保存一个检查点快照。其目标是,每当发生故障时,用户就可以快速还原最后保存的检查点,因为它最多需要30分钟来复制发生故障场景。将模拟倒回保存的检查点后,用户可以使用uvm_debug库通过启动新序列修改现有激励来交互地尝试各种“假设”方案。用户还可以加载命令文件并以批处理模式运行仿真,以并行测试不同的场景,而不必在交互式提示中手动键入调试命令。

 

第三种使用模型采用批处理模式调试应用程序,并将其应用于例行回归。我们观察到,在我们的项目中,大多数测试用例花费了大量时间来配置设备并等待设备稳定下来,然后我们才能开始注入有趣的流量模式来加强不同极端情况的测试。如果我们必须使用相同的启动顺序来测试十种不同的流量模式,那么过去,我们必须从零时开始运行模拟十次。借助uvm_debug库和命令文件,我们可以在设备稳定后保存检查点,然后加载不同的命令文件以扩展基本测试用例并更改流量模式。例如,我们项目中的初始化序列平均至少消耗25%的模拟时间,因此使用命令文件将我们的回归速度提高25%。

 

未来的发展

我们主要在Cadence IES模拟器上实现并测试了uvm_debug库。尽管我们从未尝试过,但大多数代码都与模拟器无关,并且可以在其他模拟器上进行部署。但是,有一小部分代码依赖于对特定于模拟器的Tcl命令的函数调用。这些功能-例如从tcl提示符触发调试提示符,tcl程序以获取debug命令的返回值,保存模拟的检查点快照以及在调试提示符创建的序列内设置UVM字段的值-在当前版本中不能在其他模拟器上使用。我们计划在将来支持三大模拟器。 Mentor Modelsim集成的基础已经存在于现有代码中-SV域可以通过SV-DPI C代码与Modelsim中的Tcl域进行通信。剩下的工作是用modelsim Tcl命令替换ncsim Tcl命令。但是,作者无法访问Synopsys VCS模拟器的Tcl提示符。如果VCS专家能够分享他们对Tcl集成的知识并为该项目做出贡献,将不胜感激。

 

由于Accelera与IEEE合作将UVM 1.2改正为IEEE 1800.2标准,因此UVM的新发展目前处于搁置状态。当UVM愿意接受新的开发时,作者希望将uvm_debug库贡献给Accelera,并将其纳入UVM发行版。如果UVM带有内置的交互式命令行解析器和一组标准的SV-Tcl接口,则将极大地有益于验证社区。 uvm_debug库的当前实现侧重于对现有测试平台代码不侵入,因此它将库可访问的数据结构限制为仅在uvm_root树层次结构下的以字符串命名的对象。如果uvm_debug库被Accelera用作UVM的一部分,它将允许与带有自省支持的UVM基类更紧密地集成,并开辟了许多新的可能性,例如用于控制运行阶段域的调试命令,精细的序列线程管理和动态函数重载。理想情况下,用户可以从调试提示中调用uvm_component或uvm_object中的任何函数。

 

总结

UVM交互式调试库易于设置。它是非侵入性的-除了加载程序包文件和调用initialize函数之外,无需更改测试台。借助uvm_debug库,我们将典型模拟故障的调试周转时间减少了90%。运行回归的总模拟时间减少了25%。交互式调试可增强现有的验证方法,从而提高验证工程师的工作效率,降低模拟器许可证的使用成本,并缩短项目进度。

 

致谢

作者要感谢他的雇主Microsemi Corporation对uvm_debug库开发和本文撰写的支持。他还要感谢开源项目cluelib [9]提供了库中使用的一些文本字符串处理代码。

 

参考文献

[1] Accellera Systems Initiative, “Universal Verification Methodology (UVM) 1.2 Class Reference,” 2014.

[2] IEEE Standard for SystemVerilog – Unified Hardware Design, Specification, and Verification Language, IEEE Std. 1800-2012, 2013

[3] UVM Interactive Debug Library, Available: https://github.com/uvmdebug/uvm_debug

[4] IEEE Standard for the Functional Verification Language e, IEEE Std. 1647-2011, 2011.

[5] Cocotb Documentation, PotentialVentura, 2016, Available: http://cocotb.readthedocs.io

[6] M. Peryer, “Command Line Debug Using UVM Sequences”, DVCON2011

[7] J.McNeal and B. Morris, “RESSL UVM Sequences to the Mat”, SNUG2015

[8] H. Chan and B. Vandergriend, “Can You Even Debug a 200M+ Gate Design?”,DVCON2013

[9] K. Shimizu, “Sharing Generic Class Libraries in SystemVerilog Makes Coding Fun Again”, DVCON2014

原文来自:DVCon2017_USA, 去路科官网下载DVCon2017论文合集,还有更多资料等你来哦

扫描上图二维码可直达课程页面,马上试听

 

往期精彩:

V2Pro春季班普遍学撑了,秋季班7月报名你还敢来么

30w+还送股送房?60+IC企业2019薪资全面攀升!

UVM RAL模型:用法和应用

我们准备做第二期线下培训,依旧认真且严肃

如果你突然被裁员了,你的Plan B是什么?

[彩虹糖带你入门UVM]

理解UVM-1.2到IEEE1800.2的变化,掌握这3点就够

发表评论

邮箱地址不会被公开。 必填项已用*标注

陕ICP备18003383号-1