路桑的个人网址:路科验证 -IC验证培训-数字芯片验证
作为许多验证工程师的首选语言,SystemVerilog其实并不是专门为验证设计的语言,它还是一种硬件描述语言和通用的编程语言。尽管SystemVerilog面向对象的编程特性和丰富的数据类型为通用编程提供了极好的支持,但是仍然缺少一些在其它编程语言中被认为是理所应当的实用程序。在本文中,我们介绍了一个为SystemVerilog开发的一个全面、一致、易于使用的实用程序库。
一、介绍
本文以临时命名为svlib的项目介绍SystemVerilog的类和实用的函数。它是有开源授权的,用户可以任意使用源代码、文档来轻松的为当今流行的SystemVerilog模拟器创建脚本和示例。
Svlib通过提供各种实用的功能来补充SystemVerilog的现有功能。它旨在用于各种编程问题,并不限于SystemVerilog的验证领域。Svlib中没有什么是验证特定的,因为那些特性已经被诸如UVM的方法工具很好地支持。
根据作者的经验,考虑DUT的配置文件格式为JSON或者YAML。这些文件的位置由操作系统的环境变量制定。定位文件需要测试平台能够读取环境变量。检查文件是否是最新的需要具备读取当前的日期和时间、检查目录内容和确定文件修改时间的能力。最后,要读取配置,测试台必须从选定的文件格式中提取信息,可能还需要进一步的字符串处理,来获得所需的信息。对于类似任务的支持,SystemVerilog不如C和Java,更不如Python,Ruby,Tcl和Perl等脚本语言。SystemVerilog用户不得不自己创建,可能通过DPI借助于外部软件实现,因为SystemVerilog缺乏一个足够全面的库。一些类似的程序可以与主要供应商的模拟器绑定,但是这些供应商特定的库本质上是不可移植的,并不一定完全如人们所愿。还有一些与UVM代码库一起提供的实用函数(特别是正则表达式字符串匹配),但是这些函数可能不如用户期望的那样完整。
我们的目标是提供尽可能便于实用的SystemVerilog DPI,并允许用户评估该库对自己应用程序的适用性。
二、SVLIB的框架
svlib功能分为几个大类,它们之间有一些不可避免的交互。例如,操作文件路径名的函数已与其它处理文件系统的函数分组在一起,虽然它们只是专门的字符串操作。通过简单导入软件包svlib_pkg为用户提供的可用功能可以细分如下:
通用的字符串操作,包括正则表达式
操作文件和目录名的函数,包括目录查找,目录列表和文件属性查询
用于与操作系统交互的函数,包括环境变量,命令行参数和系统时钟的时间和日期
实用程序函数,包括操作枚举类型的便利功能
用于读和写储存在.ini或者YAML文件中的配置数据的工具包
最后,头文件svlib_macros.svh中定义了一些SystemVerilog宏,用于简化不方便的操作。
包的完整内容的完整详细信息可以在发行版中找到,以下简要说明说明几个亮点,并提供一些库的使用的简单示例。
A、字符串操作和正则表达式
Svlib支持正则表达式匹配的所有功能。它可以实现匹配和子匹配的捕获,自动的搜索和替换,包括子匹配的占位符以及全局匹配和替换(在源字符串中找到所有可能的匹配)。正则表达式可以保存为对象,使其更快更方便地用于后续匹配尝试。
该包还提供一系列字符串操作函数,包括在字符串中查找子字符串,将一个字符串插入另一个字符串,在每次出现分隔符字符时将字符串拆分为字符串队列,以及许多脚本语言中熟悉的字符串连接函数。
最后,提供了一个可以在Verilog数字化字符中读取数值的函数,例如读取16’hFF。这个功能会在现有的I/O格式支持中插入一个小小的间隙。
B、与文件系统交互
传统上,SystemVerilog不能进行文件系统查询,例如确定文件的最后修改时间,或者获取目录中的所有文件的列表。 该软件包提供了可以直接从SystemVerilog轻松访问这些文件的操作。例如,要获取当前目录下所有以sv结尾的文件名称的字符串队列,可以使用以下代码:
通过这个文件列表,我们可以通过添加条件很容易的找到最近的文件。这个例子使用了来自svlib的sys_dayTime函数用来产生当前时间。用file_mTime函数来提供最后一次修改文件的时间。最后,用sys_formatTime函数来将这个时间转化成人们可读的格式。
Svlib_pkg还包括操作文件路径名的函数,例如它可以轻松的在完整的路径名中提取文件名。我们没有提供通过SystemVerilog代码直接对文件进行重命名,移动和删除的功能,因为这个功能已经可以通过调用$ system()函数轻松实现。
C、与操作系统交互
这些函数可用于查找当前工作目录,使用环境变量,并找到用于启动模拟器的完整命令行。
如前文所述,svlib还包括了用于读取系统时间和日期的函数,并能够把它们转化为人们可读的格式。
D、配置数据文件
库中的函数提供读取ini或YAML格式的配置文件的功能,以便你的SystemVerilog代码可以读取任何在配置文件中命名过的单元。它们通常用于仿真开始时对一些选项进行配置。同样的,任意的用户数据也可以以相同的格式输出到文件中。暂不支持逗号分割值(CSV)和其它的文件格式。配置文件的特性将在第八节中进行详细的描述。
三、语言和库之间功能的分离
A、漏掉的一些事项
使用通用编程语言(如C或Java)或脚本语言(如Python或Perl)工作的用户当然希望访问一个巨大而成熟的函数集合,用于字符串操作、文件访问、系统接口操作、数据结构操作和类似的功能。但是SystemVerilog提供这样的功能是很麻烦的:例如,虽然文本文件I/O得到了很好的支持,但是不能对字符串进行操作,并且也不能很好的支持操作系统的接口。
造成SystemVerilog在这方面很薄弱的一个原因可能是在它的LRM指定的核心语言中已经提供了一些有用的操作,以便在更通用的语言中可以作为库函数被提供。例如大量的格式化和I/O的文本信息可以通过$ display,$ scan和$ sformat系列函数获得。然而,将这样的功能内置到核心语言中使得添加新特征变得很困难,添加它是一个语言标准化的长期过程。作者认识到,SystemVerilog的用户库已经习惯于将通用的程序内置,并且只能通过扩展这个语言才能添加新的操作。
主要的模拟器都附带一些提供这些功能的通用程序库。 然而,在不同情况下,功能集是不完整的,更重要的是,许多用户喜欢避免特定供应商的产品,因为商业或技术限制要求他们能够使用多个工具,并且保持通用的代码库。
B、DPI会使库变得多余吗?
SystemVerilog具有全面和良好标准化的C语言接口,VPI和DPI,用户现在可以通过DPI直接调用通常的C库中的任何函数,如果其参数和返回数据类型是DPI接口机制所支持的类型之一。 以下代码示例显示如何使用DPI在SystemVerilog中直接提供标准C数学库函数:
不需要进一步的编码工作,DPI参数传递机制自动确保real参数的传递并返回一个在C语言中为double类型的值,并且用户只需要确保SystemVerilog仿真器可以使用适当的命令行选项访问必要的C语言库目标代码。
这样看来似乎没有必要在SystemVerilog中提供任何类型的通用程序库,然而事实并非如此。很多C语言的库函数很大程度上依赖于不能直接映射到SystemVerilog上的数据结构,因此需要间接的映射。此外,如果svlib被移植到其它平台,一些底层的C语言函数可能发生了改变,但是我们必须保留未更改的SystemVerilog API。
例如,查询函数file_mTime返回文件的最后修改的时间戳。 在svlib中,系统时间/日期始终以longint类型表示为自1970年开始以来的秒数。在C语言库中,文件的时间戳信息(以及其他状态)必须通过用户编写代码来收集返回值。我们不希望使用这些特定于C语言库的细节来麻烦SystemVerilog程序员,因此提供了封装好函数。一些封装的功能在C语言中实现,一些在私有的SystemVerilog代码中实现。具体的分割对用户是不可见的,并且可能会在以后的svlib版本中更改,但面向用户的file_mTime函数能够始终保持不变。
同样重要的是,库作为一个整体不应该存在分配内存但从不释放的情况。这在svlib中可能是难以实现的,因为一些C语言库函数必须在将该信息返回到SystemVerilog之前在C存储器中分配数据结构。在将控制权返回到SystemVerilog之后,C代码不再运行,并且无法释放之前所需的内存。在svlib中,我们通过仔细管理SystemVerilog和C之间的共享职责来处理这个问题,以便C代码总是有机会释放它使用的任何内存。这需要在该库的C语言和SystemVerilog部分之间建立严格的协议来完成,这反过来意味着该库基础结构的某些部分必须保持对用户隐藏,以便确保它们的完整性。整个库架构如图1所示。
四、指定库
我们从一开始就考虑了几个设计原则。 我们发现,当指定svlib时,遵守这些原则是我们任务中最具挑战性的部分。 相比之下,第十节中描述的实现和测试是相对直接的,并且容易地适合我们的通常的编码实践。
我们的核心目标如下:
方便SystemVerilog程序员使用的API,不需要C或者DPI编程
一致且统一的错误情况处理
一致的命名约定
模拟器,操作系统和主机系统架构之间的可移植性
没有对任何其他SystemVerilog包的依赖性
与所有流行的验证方法兼容
所有可用成果都是开源的,不受任何限制性许可协议的约束
此外,我们尽量确保svlib没有错误以减少不当的性能损失。我们应该从一开始就满足我们的核心目标,以避免svlib的规范在未来发生变化是可能对用户产生的不便。
A、方便SystemVerilog程序员使用的API
我们从来不指望svlib提供其它语言不具备的独特功能,Svlib提供的每个功能都是可以在其它的语言或者库中找到的。程序员可以在导入package后直接使用它。
除了这个简单的使用模型,我们还话费了大量的精力考虑这些函数实现的形式。其中有两个比较麻烦的问题:下面第四节B部分的错误处理条件和是否使用面向对象的方法。我们已经通过提供面向对象的API和用于其许多特征的直接函数调用的API。用户可以自由选择更适合其目的和编码风格的方法。
B、一致且统一的错误情况处理
任何的实用程序库中,都可能会有某些函数导致错误的情况。有时是库的内部造成的(例如,一个字符串溢出到预先分配的缓存空间),有些是用户使用时造成的(例如,尝试发现一个文件的修改日期,但它并不存在),并且一些错误可能并不认为是用户的错误(例如,尝试检查用户并没有读取或执行权限的目录中的文件)。程序库的用户必须能够正常处理任何类型的错误。
1. 输出参数中的错误代码
我们简要地考虑了每个函数通过输出参数传递其错误代码的可能性。 然而,我们很快就发现这种方法是不切实际的。 它强制用户声明一个合适的结果变量,并将其显式传递给每个函数调用。在2009版本的SystemVerilog标准中引入的默认输出参数可以使此方法易于理解。 不幸的是,这种有价值的语言特征在写作时尚未在一些商业上可获得的模拟器中实现。
2. 错误代码作为函数返回值
我们尝试基于每一个svlib的函数实现一致性,它们都是返回一个整数错误代码作为其结果。在大多数情况下有很多导致错误的情况,但只有正确的代码是被需要的。因此,我们选择让这些函数在成功时返回零,并为任何类型的错误制定适当的整数代码。
例如,使用以字符串形式打印当前工作目录的函数,函数原型和使用例子为:
虽然它具有一致性,但这种方法在实践中很不方便。它需要通过一些测试途径捕获每个函数的错误变量,因此给程序员增加了很大的负担,当使用很多这个库的函数时,将很自然的返回很多函数值。
并且,这个函数基本上没有可能出错的情况,并且如果这样写的话将更加自然:
此外,使用零作为成功代码的标志容易出错并且难以阅读,并且增加了一些函数命名的难度。例如,我们用一个regex_match函数来根据正则表达式测试一个字符串,在成功的情况下返回一个零是不合适的。
虽然没有提供这种完全一致性的解决方案是一个很艰难的决定,但我们认为这种方法太笨拙,因此放弃了它。
3. 我们选择的解决方案
基于我们的目标,我们选择了如下的方案。
函数被设计为在用户出现错误的情况下尽可能的返回最重要的而不是具有误导性的结果。对于那些用户有明确责任预测和处理错误情况的少数函数,我们提供了一个输出参数,用户必须填充一个错误代码变量。其他功能不受错误报告机制的影响。
这造成了如何处理那些用户通常不会预期的错误的问题。在这些情况下,我们检测库中的过程代码中的错误,然后使用SystemVerilog立即断言抛出错误并在控制台提供详细的诊断。以这种方式使用立即断言使得程序员可以使用$ assertoff或一些等效的工具命令来抑制错误。 我们还提供package配置功能,允许用户做同样的事情,而不需要知道有问题的断言的名称。
对于希望自己处理错误的用户,我们还提供一种方法来测试在svlib函数调用中可能发生的任何错误。
每个程序的错误信息的记录机制在第六节中描述。它在稳定性,方便性和灵活性的冲突上采取了一些折中:
默认情况下,库的错误会产生断言类型的错误信息,导致错误的函数会采取默认的保护行为并返回一些默认结果。
如果需要,用户可以轻松地屏蔽掉这些错误消息,如果程序员没有明确测试这些屏蔽掉的错误,则需要承受偶然意外行为的风险。
保留了高级用户对库错误进行显式测试的能力,他们能够选择任何方式处理错误结果。
C、一致的命名约定
svlib提供了用户可能需要在其代码中提到的各种命名项目:包,类,函数及其参数,以及枚举类型。用户命名的一致性不但是项目易于理解,而且也能让它在文档中易于找到。
但是这样也有些不足,描述性名称不可避免地更长,因此更麻烦。对于每个主要功能组,我们试图准备其使用的实际示例。一般来说,对于什么名字最合适一般遵循以下准则:
camelCase用于由多个字组成的名称,例如matchStart
在前缀或后缀(标识一组相关的想法或特征)和名称的其余部分之间使用下划线分隔符,如regex_match和file_mTime;
在类方法中尽量使用短的名称,并始终以对象名称为前缀
包的名称要紧凑,如果你想避免通配符匹配到包中所有的名称,则为其显式的加上前缀
一些用于内部函数和其它项目的长且不好描述的名称,例如DPI导入函数名称,用户不应该引用它们,以最小化与普通用户定义的名称冲突的风险。
D、模拟器和平台的可移植性
从一开始,我们致力于确保svlib在所有主要模拟器中方便使用。 来自主要供应商的SystemVerilog模拟器现在已经非常成熟,并且只需要非常少的特定工具的处理来实现与我们使用工具的兼容行为。
然而,这些工具现在广泛部署在各种平台上,通常但并不总是使用Linux操作系统,其中32位和64位平台之间的区别很重要。 我们已经测试了在一系列平台上的当前版本,但我们不能确定已经涵盖了所有的可能性。我们希望让社区参与帮助扩展一组完全支持的平台。
Microsoft Windows®也与一些SystemVerilog仿真器一起使用,但在编写本文时,我们无法将测试扩展到该平台。 Windows和Linux C库之间的差异也可能会带来一些挑战。
E、对任何其他SystemVerilog软件包没有依赖性
svlib旨在为任何SystemVerilog程序员提供服务。 因此,它必须是一个真正独立的库,能够在任何用户环境中部署,而不管环境使用的是什么其他包。
当然,svlib也存在一些依赖性。它大量使用来自标准C库的函数,我们假设这些函数存在于任何实际的仿真平台上。它还使用一个第三方C包,libyaml开源的YAML parser/writer。在发布svlib时会包含这个包的复制版本。我们认为这两种依赖都不会对SystemVerilog程序员造成负担。
尽管在新包和用户现有代码之间始终存在名称冲突的可能性,但如果必要,可以通过使用完全限定的包名称,或者显式导入个别名称而不是通配符来避免这种冲突。
最后,我们应该注意,任何希望扩展或修改库的开发人员都希望像我们所做的那样使用SVUnit单元测试框架。 与其他依赖项一样,我们预计这将没有显着的障碍。
F、与所有流行的验证方法兼容
svlib不试图侵入验证方法库的领域,如UVM。 它旨在补充他们,提供功能,在任何其他基于UVM测试平台的验证代码中使用。它可以与任何已发布或专有的验证方法一起使用,无需特殊考虑。
不可避免地,在svlib和验证基类库(例如UVM)之间存在一些特征的重叠。 例如,UVM已经提供了模拟器命令行的正则表达式匹配和查询(尽管在这两种情况下svlib提供的支持是相当全面和灵活的)。虽然没有由这种重叠引起的根本问题,我们试图最小化这些具有重叠特征的函数。
G、所有可用成果都是开源的
svlib最初是由我们公司内的一个小组开发的。 但它的野心是提供一个实用程序包,将在SystemVerilog用户群中广泛采用。 只有当它是完全开源的时,才能满足这个目标。 我们已经应用了相当宽松的Apache开源许可证,就像Accellera对UVM所做的那样。
我们使用第三方libyaml包。 该软件可以在MIT开源许可证下获得,这是类似的许可。
谢谢你对路科验证的关注,也欢迎你分享和转发真正的技术价值,你的支持是我们保持前行的动力。