IC验证培训——SystemVerilog通用程序库(下)

2022-10-28 09:02:41 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

路桑的个人网址:路科验证 -IC验证培训-数字芯片验证

五、类方法还是包函数?

我们最初的直觉是将svlib作为一组SystemVerilog类呈现给用户。 我们假设由一个类来表示一个正则表达式,另一个类来表示一个文件名,等等。 从库写作者的角度来看,以这种方式打包用户数据是非常有吸引力的,因为它允许我们将任意隐藏数据与每个对象相关联。

我们在编写面向用户的API时,上遇到了一个严重的可用性问题。 SystemVerilog用户通常期望库操作接受自定义类型(特别是字符串)的输入参数,以各种方式处理它们,并将其结果也作为本机数据类型返回。例如string trim函数,它从字符串中删除前导和/或尾部空格。 现在至少有四种合理的方法来提供这种功能:

  1. 作为一个简单的函数它能够接受字符串参数并且返回字符串结果
  2. 作为对所述字符串对象的操作,能够改变对象的内容来表示缩减字符串
  3. 作为对所述字符串对象的操作,能够保留所述对象的原始内容不变并返回包含缩减字符串的新对象
  4. 作为对所述字符串对象的操作,保留对象的原始内容不变,并返回包含修整值的SystemVerilog字符串

这四种方法中,只有第一种方法可能对已经熟悉语言的本地字符串数据类型的行为的SystemVerilog用户有吸引力。 但是对基于对象的表示的库写作者的优点太大,不能忽略,特别是对于必须在DPI的C侧做重要工作的函数。

我们最后决定采用一种混合方法,其中由svlib定义的类用于表示几乎所有用户数据。 这些类的方法适用于任何偏好基于用类表示的用户。然而,我们预计许多用户将更喜欢在包级别的简单的函数调用接口。因此,我们实现了更加注重库管理的效率和易用性的类,而不是可用性,我们已经为这些类提供了一套程序包级别的便利函数。

这样导致的结果是每个面向用户的函数需要一个或多个新对象被使用。如果这是通过根据需要的构建的对象显式完成的,那么它可能会对SystemVerilog的内存分配和垃圾回收造成不合理的负担。

为了向用户展示如何使用这个模型,这里有一些来自str类(字符串的wrapper)的声明和部分函数,以及用户如何应用它们的示例。

Str类中的cread方法用作构造函数,但具有一些有用的附加属性,将在第七节中更详细地讨论。 不鼓励用户直接调用任何svlib类的构造函数。Get方法,其返回对象的字符串内容作为本地SystemVerilog字符串。

值得注意的是,我们选择将枚举类型side_enum的定义放入Str类中,而不是包级别。如果它是包级定义,则短名称NONE,LEFT,RIGHT,BOTH将通过包的通配符导入注入到用户的命名空间中。 这将很可能导致名称冲突。通过在类中隐藏它的typedef,我们保持简短和描述性的名称,同时当使用它们时只需一个简单的str:: 前缀。

这样声明之后,我们现在可以以两种不同的方式对字符串进行修剪。

一次性从字符串的开始后者结尾去除剩余空间:

需要对字符串执行许多连续操作或者希望能够通过引用传递字符串对象,更适合使用str对象表示:

六、单个进程错误处理

如前所述,我们通过最近发生错误的库函数的错误报告来提供查询功能。为了避免一个进程的错误信息被其它进程破坏,我们独立的为每个进程记录错误信息。

这可以通过把进程句柄的错误对象放入一个关联数组中来实现。不幸的是,广泛使用的商业SystemVerilog模拟器不支持由类或过程句柄索引的关联数组。幸运的是,它们为任何进程提供了唯一的字符串名称,可以通过使用%p格式化程序将进程句柄本身格式化为字符串获得。虽然对每个进程的信息进行记录会造成轻微的损失,但是我们认为这是值得和可接受的。

七、对随机稳定性的影响

在SystemVerilog中,任何新对象的class的类型的创建影响了它的随机。这对我们的库是有影响的,因为虽然svlib本身不以任何方式参与随机数生成,但是它以用户难以预测或控制的方式即时创建对象。由于svlib非常重视字符串和文件的处理,它很可能在信息诊断中大量使用,随着开发和调试的进行,可能被插入到用户的代码库中或者从用户的代码库中移除。如果调试代码的引入对随机化的稳定性产生影响,那么调试工作将变得更加困难,因为在加入这些代码后不可能再重现出现的问题。

为了减轻这个问题,我们建立这个库来确保它无论创建多少对象都不会对随机化的稳定性产生影响。我们通过这种方法来实现:取消使用任何svlib类的构造函数,而不是将构造函数放到每个类提供的静态方法中。这里给出一些假象的svlib_C类的创建方法。

通过在对象构造中保存和恢复调用进程的随机化状态,我们保证svlib不会干扰用户的随机化,因此用户可以自由地添加或删除基于svlib的调试代码。

在编译的时候可以使用宏来取消这种机制,消除其(相当小的)性能损失,但牺牲了调试期间对随机化稳定性的保证。

八、使用配置文件

正如前文所说,svlib开发的一个重要目的就是希望能够以诸如YAML的结构化格式对数据配置文件进行读取和写入。根据我们的经验,这样的文件通常都是通过脚本创建的。验证环境应该能够读取这样的文件并且对配置数据对象进行填充。

乍一看,这个要求似乎集中在读取和写入所需文件格式的问题上。但是,仅仅能够解读这些文件是不够的。还需要实现从本地数据结构到文件对象映射的自动化。例如,在一些SystemVerilog类和这些类的对象中,一些值储存在他们的成员变量中。

如果我们将greaterConfig对象写入YAML文件,我们期望文件的内容大致如下显示:

虽然YAML数据结构非常清晰,但是不能直接将它映射到我们的两个SystemVerilog对象的内容中。对于更复杂的对象,问题会更加麻烦。此外,很可能不应该在外部配置文件中表示对象的所有数据成员。由于所有这些原因,显然我们需要一个中间表示。 借用其他软件环境,我们选择了文档对象模型(DOM)。

A、svlib DOM的特性

在svlib中,DOM是具有单个根节点的树状结构。 树中的节点可以具有三种基本形式之一:

  1. 一个映射,其值是一个无序的节点集合,每个节点由一个字符串名称(key)
  2. 一个序列,其值是节点的有序列表,其中每个节点由其在列表中的位置标识
  3. 一个标量,其值是一个“标量值”对象

显然,标量是节点树的叶子。 我们使得每个标量的值能够由另一个对象表示,以便在将来的版本中轻松添加新的标量数据类型。目前只支持整数和字符串标量。在UVM中,数据资源库使用参数化的SystemVerilog类为任何用户自定义数据类型提供资源存储方式。与此相同,标量值通过它们的原始数据类型进行参数化。

这两个结构化节点类型(序列和映射)足以表示几乎任何合理的数据结构。 DOM映射仅仅是由字符串索引的节点的关联数组; DOM序列是节点的队列。以这种方式,我们能够在SystemVerilog中创建一个只包含几个新类的DOM,其它的类都从两个基类派生。

  1. cfgNode及其子类cfgNodeMap,cfgNodeSequence,cfgNodeScalar表示不同种类的DOM节点。 cfgNodeScalar包含表示节点上标量值的cfgScalar对象。
  2. 1、cfgScalar及其子类(如cfgScalarString)是标量值的容器。

库的用户通常不期望与DOM直接交互。 相反,他们将使用内置的svlib功能将DOM复制到任何被支持的文件类型(当前为YAML和更受限的INI文件格式)。同时,库的类提供了可以访问和操纵DOM的各个节点的方法。

B、将用户定义的数据表示为DOM,或将DOM表示为用户定义数据

虽然DOM结构是非常灵活的,并且提供了一种表示任何所需数据结构的一致方式,但是对于日常的使用来说太笨拙。用户当然期望以最方便的方式创建自己的配置数据对象(通常通过类实现)。为了处理这个问题,我们从UVM提取了我们的提示,并提供了现场自动化宏来实现。 使用这种方法,我们的greaterConfig对象变成:

另一个类simpleConfig也以类似的方式处理。

这些宏自动为类的两个新方法创建代码:

toDOM构造并返回一个新的DOM映射节点,其中包含对象内容的表示,以及它的下级对象。这个DOM可以写入任何支持的格式的文件,只需将其传递给适当的cfgFile对象的serialize方法即可; 在当前版本的svlib中,提供了子类cfgFileYAML和cfgFileINI。

fromDOM使用给定的DOM的内容来填充对象的数据成员,再次根据需要映射到内部的objectSC对象中。以下示例显示如何从源文件src.yaml中填充largeConfig对象,然后将其以不同的文件格式写入另一个文件dst.ini:

给定先前提供的YAML文件,dst.ini中的结果输出如下:

我们可以看到INI文件格式使用[section]来表示本身是映射的第一级映射条目。 INI文件不支持更深的嵌套; 类似地,序列节点不能被写入INI文件。YAML等格式更灵活,更富表现力,可以代表全方位的DOM功能。

C、DOM的模糊性

我们选择了一个非常轻巧的DOM,强调灵活性和简单性。因此,不能完全表示典型用户的数据结构。需要注意的是,映射节点用于两个截然不同的目的:表示由字符串索引的关联数组,并且表示对象的各种命名数据成员。在实践中,这种模糊性不存在任何实际的困难,因为目标对象应该被设计为与预期的数据结构匹配。自动化宏的一个关键优点是,当DOM被复制到对象或从对象复制时,执行各种语法和语义检查,因此任何不匹配都将被识别以供用户校正。

D、可序列化对象作为公共基类

在UVM中,字段自动化宏创建了重写uvm_object基类的虚方法的方法,如copy()和compare()。这有很大的优势,你可以创建基础结构代码,使用变量uvm_object类型,允许你处理任何派生对象。在svlib的情况是非常不同的。 我们不能期望用户从一些常见的svlib基类派生所有类。因此,对于每个用户定义类,宏生成的toDOM和fromDOM方法是完全独立的,并且不可能建立公共基础设施来以一致的方式为所有对象处理序列化。

然而,2012版本的SystemVerilog提供了一个完全匹配这个问题的解决方案:新的接口类结构。这种形式的多重继承,受到Java的接口特性的启发,允许用户类实现而不是继承自定义一组虚拟 方法与虚拟基类可能做的方式大致相同。然后,用户类必须提供由接口类定义的所有虚拟方法的具体实现。一旦这完成,接口类类型的类句柄引用实现该接口类的任何类的对象是合法的。以下代码示例显示了这在实践中如何工作。

首先我们展示svlib如何定义其接口类,指定任何实现类需要覆盖的虚方法:

接下来,我们修改用户的配置类,以便定义它来实现接口类。注意,用户的原始类层次结构不受干扰:greaterConfig像以前一样从someUserCfgClass派生。implements关键字让新类执行了DOM和fromDOM方法,它由宏释放,因此不需要额外的用户代码:

最后,我们演示了一个虚构的用户基础设施代码,可以序列化和反序列化任何对象,如果它的类的类型是svlib_serializable接口类:

我们已经将这种方法原型化,得到非常令人满意的结果。不幸的是,一个流行的SystemVerilog模拟器在写入时还不能支持接口类功能,因此我们认为在初始版本中不能包含这个有吸引力的功能。

九、模拟器重新启动,检查点和恢复

模拟器复位/重新启动的管理,检查点和恢复到保存的检查点通常是任何基于VPI / DPI的包的实现者的麻烦的问题。如果包要维护C内存中的任何状态,那么该状态必须在重新启动时清除(不是特别困难),并在模拟器检查点或恢复时上保存和恢复(非常困难)。我们的解决方案是在SystemVerilog中管理所有软件包状态,它由模拟器的内置机制自动保存和恢复。

然而,保持SystemVerilog端的所有状态会增加性能损失,因为这可能意味着需要跨越DPI边界重复地复制各种数据对象。为了降低这个成本,我们在C中实现了缓存状态,隐藏了SystemVerilog包状态。因为跨DPI边界的所有交互都是通过隐藏的私有函数而不是用户可调用的代码来执行的,我们可以自信地做到这一点。

任何模拟器重新启动时(无论是时间归零还是保存的检查点)我们的C代码管理的所有内存被释放,有效地擦除我们的状态缓存。

因为这个内存缓存对于用户是完全不可见的,所以我们将能够在后续实现中更加积极地使用它,而不会有破坏用户可见功能的风险。我们的初始实现具有最少的缓存,因此具有次优性能,但是功能完备,并允许我们进行可用性测试。在适当的时候,缓存机制将实现那些功能(特别是正则表达式处理和YAML文件读取器),它将明显带来好处。

十、执行,测试和文件

如前所述,svlib的实现通常是无故障的。到目前为止,其开发的最具挑战性的方面是建立一个方便的面向用户的API,这是SystemVerilog程序员自然会使用到的。

A、测试

我们旨在使用测试驱动的代码开发方法。SVUnit框架非常适合测试由许多由自包含函数组成的类和包,这些函数可以单独测试,因此我们从一开始就使用它。在大多数情况下,我们能够在每个新功能开发之前为其构建至少一个简单的测试工具,并且确保所生成的SVUnit测试用例提供了非常有用的完整性检查,在每种情况下我们都有一个明智的API。作为一个例子,这里是一个我们的单元测试的字符串“find substring”函数的第一个和最后一个片段。

这些方法的原型:

int first(string seeking,int ignore = 0)where seek是要搜索的字符串,ignore是字符串开始处(或结束时,对于last())的字符数,应该跳过 通过搜索。 它们的结果是找到的字符串在原始字符串中最左边的字符的位置。

1. 执行成功是快速反馈

这种方法给我们即时反馈小的执行错误,否则可能很难找到。 每个单元测试运行得非常快,它可以给出任何运行时间在20秒以内的代码改变时的成功或失败信息,即使是在三种主流的模拟器上运行。有了这个能力,在所有支持的平台上测试每一个新的变化只需要不到一杯咖啡的功夫,我们因此能高度的保持代码库在任何时候都能正常工作。SVUnit的简单明了的报告机制将执行错误迅速锁定到单个测试用例。

2. API库的连续完整性检查

单元测试还为我们提供了面向用户的API是否方便智能的预判,因为我们不得不在实现之前使用该API编写测试代码。糟糕的设计因此会被迅速的凸显出来。例如,我们之前尝试在一个函数中包含多个可选参数(比如参数具有默认值)。错误很快凸显出来,这对于用户来说是极易出错的,因为它很容易提供一个值到错误的可选参数,并因此导致令人惊讶的结果。

3. 单元测试组成的文档池

最后,不断增加的测试组件持续提供各种示例函数的有用信息,这样的一个好处就是每个示例的测试结果都是被单独记录的。

B、文档

使用自动文档生成器(例如Doxygen或NaturalDocs)来记录这些包已经变得很流行了。它们具有显而易见的重要优点,即代码有改变时文档将自动更新。然而,我们选择了减少这种趋势,并恢复为传统的手动生成的文档。 这个似乎倒退的步骤是在仔细思考后才采取的。 从代码及其注释生成的文档往往专注于单个代码片段的属性,直到功能级别。但是我们认为人们更有可能对包的总体理论感兴趣:其组件之间的关系, 以及它作为一个整体所遵循的任何指导原则。 我们相信这些信息更好地捕捉在人工手写的材料中。

当代码库快速变化时,会发现自动生成的文档的巨大优势,因为必须不断审查层次结构中的类之间的复杂关系。 但是对于像svlib这样的包的用户面向行为应该被做的非常稳定,因此这个优势不太明显。

十一、性能

虽然svlib提供了一些SystemVerilog难以实现的功能性接口,但是如果其性能不可接受地慢,那么该方便性将是无用的。我们已经测量了所选svlib功能的性能成本,我们认为这是可以接受的,因为诸如文件访问和操作大字符串的性能耗竭操作很可能只会很少使用。

十二、未来的工作

我们有一个广泛的未来工作的“购物清单”,我们希望在资源允许的情况下添加,以及如果对它们有任何需求。其中我们应该提到:

  1. 完全支持MicrosoftWindows®平台
  2. 支持逗号分隔值(CSV)表格数据文件
  3. 支持对数据库的SQL访问
  4. 将已打开的C文件句柄与Verilog文件标识符相关联。通过这种机制,将提供广泛的有趣的新功能,包括对临时文件的支持,以及通过TCP / IP套接字和其他网络机制的通信。
  5. 如果可以在SystemVerilog和诸如Python之类的脚本语言之间建立紧密的连接,将会产生大量激动人心的可能性。这个想法已经通过PyHVL项目以略微不同的方式进行了探索,我们几乎没有开始考虑这一领域可能的相互作用。

十三、小结

初始版本可在发布时下载,可通过访问www.verilab.com/resources下的svlib链接找到。

我们认为这是一个beta质量版本:它有一个完整的功能集,我们自己已经在大量使用它,但它还没有在项目工作中的大量使用,因此可能不够成熟。因此,我们欢迎潜在用户的反馈,我们希望使用DVCon来收集用户对这样的库的反馈。

如果这项倡议得到热烈欢迎,我们将开始寻求办法使更广泛的社会支持它并推动它。本篇论文原作者欢迎任何反馈,包括svlib的技术内容及其在实践中的实用性。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/197455.html原文链接:https://javaforall.cn

0 人点赞