SVLIB用户手册中文版1.0

SVLIB用户手册中文版1.0 by zt5169@126.com


zt5169@126.com

第一章 概述

systemverilog开源扩展库svlib的说明和编程指导。

本文档介绍了systemverilog的实用程序库svlib。svlib为日常验证工作提供了SystemVerilog所缺乏的功能:字符串处理、操作系统接口和许多其他有用的函数。

第二章 简介

第三章介绍了如何在仿真器和验证环境中使用svlib
第四章介绍了了svlib的一些基本原则和规则。
第五章介绍了svlib的特性,这些特性补充了SystemVerilog语言中所没有的字符串处理与操作。
第六章介绍了正则匹配与替换的特性。
第七章给出了Pathname类的详细信息,借助这个类,可以更加简单的进行常见的文件操作,例如确定目录、根据目录生成路径名、查找文件的扩展名等。这些操作只是专用的字符串函数,并不能对文件系统进行任何读写。
第八章介绍了用于查询文件系统的svlib工具。通过这些函数可以查询文件的属性,如“这个文件存在吗”,“文件最后修改的日期是什么”,“是否拥有写权限”,“它是一个目录”和许多其他属性。
第九章介绍了操作系统查询函数。通过这些函数可以轻松地获取当前时间和日期,以各种人类可读的格式呈现日期,获取操作系统的环境变量,以字符串队列的形式获取目录内容,并读取计时器。
第十章讨论如何在svlib中处理错误。默认情况下,错误会报告在仿真器的控制台,但通过svlib可以以各种方式自定义错误处理,甚至可以通过自己的SystemVerilog代码处理错误。
第十二章详细介绍了支持以.ini或YAML格式读写配置文件的类和函数,并说明了如何在自定义配置类和svlib的内部文档对象模型(DOM)表示之间传输配置数据。
第十三章介绍了一个通过运行仿真来查询仿真环境的功能。
第十四章介绍了一些实用的函数,这些函数优化了SystemVerilog枚举类型的使用体验。
第十五章介绍了以SystemVerilog宏的形式提供的一些实用特性。

第三章 编译并运行svlib

svlib代码分为三个不同的部分,都可以src/目录中找到:
宏定义,如果想使用宏相关的功能,请在代码中添加

    `include "svlib_macros.svh"

systemverilog代码,已经打包成一个svlib_pkg,使用前需要用编译器编译svlib_pkg.sv文件。
C代码,通过systemverilog DPI调用,包含了多个库,目前打包成了一个文件dpi/svlib_dpi.c。
要使用svlib,必须编译svlib_pkg.sv和dpi/svlib_dpi.c。已经在使用DPI的用户可以直接拓展已经有的DPI,或者也可以链接到动态链接库文件。对于新用户或者只是想试试的用户,建议按照仿真器的编译、链接以及运行的流程使用svlib。为了简化流程,请参考文件src目录下的svlib.f。

占位符 <svlibroot> 代表svlib的目录。

下面以三种主流仿真器为例,说明使用方法

3.1 Mentor Graphics QuestaSim

qverilog的一步流程

qverilog +incdir+<svlibRoot>/src –f <svlibRoot>/src/svlib.f <user_options> <user_files>

3.2 Cadence Incisive

irun的一步流程

irun +incdir+<svlibRoot>/src –f <svlibRoot>/src/svlib.f <user_options> <user_files>

3.3 Synopsys VCS

vcs的一步流程。请注意附加的-LDFLAGS选项,它是链接VCS默认没有链接的C库时所必需的选项。-R选项不是强制性的,它只是使simv可执行文件在编译和链接完成后自动开始运行。-LDFLAGS –lrt先后顺序不要变。

vcs –sverilog –R +incdir+<svlibRoot>/src –f <svlibRoot>/src/svlib.f \
 –LDFLAGS –lrt  <user_options> <user_files>

如果要简化这个命令,我们准备了了一个vcs.f文件,它包含所需的-LDFLAGS和-sverilog选项以及svlib的其他内容,然后就可以使用下面的命令运行:

vcs–R +incdir+<svlibRoot>/src –f <svlibRoot>/src/vcs.f <user_options> <user_files>

第四章 一些使用规则与约定

svlib被设计成在任何SystemVerilog环境都尽可能不影响原环境并且都能正常运行。为了实现这些目标,有必要引入一些对整个库都通用的底层行为。对于用户来说,了解这些行为,避免意外,是很重要的。

4.1 库的结构概述

4.1.1 package

svlib已经封装成了一个叫svlib_pkg的SystemVerilog包。仿真器编译之后,用户应该把这个包导入到自己的代码中,这样svlib的工具就可以随时使用。pkg的import语句应该在任何需要它的模块或包的域中,就在module或package的开头。不要把import语句放在任何module或package的外侧,会使svlib导入到$unit空间,存在潜在风险。

4.1.2 macro

除了包之外,svlib还有一些在使用包特性时有用或必要的宏。为了使这些宏定义生效,用户应该在代码中添加:

systemverilog

`include "svlib_macros.svh"

· 代码应该添加在最外层($unit)范围内,在任何模块或包之外。推荐在整个环境的顶层添加这行代码,此外,代码使用了ifdef语句,避免了二次定义。

4.2 类或者包内的函数

几乎所有的svlib功能都是由包中定义的类提供的。用户可以根据需要创建这些类的实例(见下文4.3节)。然而,在某些情况下,简单地调用一个函数,比创建一个对象、配置数据,然后调用它的方法并最终从对象中提取处理过的数据更方便。很多特性这两种形式都有,因此可以选择更方便的一种。有关更多细节,请参阅每个特性的文档。

4.3 构造svlib对象

svlib的许多部分都使用定义了SystemVerilog类。因此,为了使用svlib特性,用户的代码中必须创建这些类型的新对象。然而,为了稳定性,提高内存管理效率,用户代码不应该直接调用任何svlib类的new函数。所有的对象都应该用内建的静态函数create创建,每个类的create函数在后续章节都有介绍。

这个问题在参考的会议论文[1]中有更详细的讨论。所有主流的SystemVerilog仿真器现在都提供了对受保护的构造函数的全面支持。因此,所有的svlib类构造函数都声明为protected的,因此用户代码不可能直接调用它们。

4.4 错误处理

偶尔,svlib函数可能会导致内部错误。尤其是函数调用C库时,在C库中可能存在内存分配、文件权限甚至文件存在等问题。这样的错误总是被传回到SystemVerilog中进行处理,但是错误处理的具体斜街在某种程度上是由程序员控制的。svlib的默认行为是抛出断言的错误,但是还有更加细致的控制。详情见第十章。

4.5 svlib的内部隐藏特性

svlib的一些特性被设计为对用户保持隐藏。这样做是为了让包能够保持DPI的C端和SystemVerilog端数据的一致性。但是,SystemVerilog没有提供任何方法在语言中实现强制隐藏。为了帮助用户避免意外地破坏这种封装,svlib的隐藏部分被放置在一个单独的包svlib_private_base_pkg中。用户代码不应该直接导入这个包,也不应该尝试使用其中的任何数据、函数、类或DPI导入。

4.6 命名约定

整个svlib中尽可能使用了一致的命名方案,因此更容易记住或猜测给定特性的名称。为了方便使用,命名尽可能短,但有时由于与systemverilog关键字或者其他包冲突(例如UVM),或者为了在包中保持一些独特的名字,命名会比较长。

4.6.1 类

几乎所有的svlib类都是以大写字母开头的短名称,或者都是小写的。例如,表示正则表达式的类是Regex,也有一些例外,比如,配置特性有几个以cfg前缀命名的类,比如cfgNode。

4.6.2 类的方法

svlib类的方法的名称尽可能短,同时尽量好记。如果一个名字是由多个单词组成的,那么这个名字用驼峰式拼写(没有下划线,除了第一个单词外都大写),比如,cfgNode类的addNode函数。

4.6.3 pkg级函数的前缀

许多svlib函数可以很自然地分组。例如,有几个与操作系统交互有关的pkg级函数。这些函数的名称都以前缀sys开头,与名称的主要部分用下划线分隔,如sys_dayTime中所示。

第五章 字符串处理

SystemVerilog语言本身提供了许多字符串操作。然而,经验表明,内置方法不足以满足工作中的字符串处理任务,svlib提供了进一步的操作集来帮助满足这些需求。

在大多数情况下,字符串操作有两种不同的形式,用户可以自由选择更适合自己需要的形式。

第一种形式

关于字符串变量的简单函数,通常(但不总是)返回字符串结果。这些函数在svlib包中定义,名称都以str_开头。

第二种形式

Str类对象的方法(注意大写的S)。Str类是SystemVerilog字符串的wrapper,通过引用传递字符串,并使一些操作更方便。

对比使用简单函数,使用Str对象必须在所有操作之前构造对象。不过通过Str对象的许多操作的效率和便利性通常收益是利大于弊的。程序员可以自由选择对他们来说最方便的方法。如果只需要对一个字符串执行一个操作,那么pkg级函数可能是最方便的。如果要对同一个字符串执行许多连续操作,最好创建一个Str对象来进行处理。

5.1 Str类

5.1.1 处理Str对象和成员的方法

static function Str Str::create(string s = "");

function void set (string s);

function string get ();

function Str copy ();

function int len ();

前文提到过,用户不能直接通过new函数创建对象,必须使用Str::create方法。当然,创建对象时可以无视参数s。

对象创建以后,随时可以使用set方法更新字符串成员。而get方法则返回对象保存的字符串。len方法则返回字符串长度。copy函数则返回一个新的对象,并且它的内容与调用的对象一致。

5.1.2 枚举类型

typedef enum {NONE, LEFT, RIGHT, BOTH} side_enum;

typedef enum {START, END} origin_enum;

这两个枚举用于指定某些方法的各种可选行为。side_enum用于指定字符串的哪一侧将参与各种操作,特别是trim和pad。origin_enum用于指定在range和replace操作时从字符串的哪端计数。START指定字符串最左端,END指定最右端。这些选项的细节将在后面的小节中展开。

5.1.3 在Str对象的字符串后面拼接一个字符串

function void append(string s);

这个函数通过使用简单的字符串连接,将指定的字符串拼接到一个Str对象的字符串成员后面,从而修改该对象的现有字符串内成员。

5.1.4 查找子字符串

function int first (string substr, int ignore=0);

function int last (string substr, int ignore=0);

first()在对象的字符串内容中搜索字符串substr的第一次出现的位置。它返回子字符串的最左边字符在原始字符串中的位置。如果搜索失败(在原始字符串中没有出现子字符串),则函数返回-1。这个方法的搜索是精确的文字匹配,不使用通配符或正则表达式匹配。

参数ignore指定搜索从哪里开始。默认值(ignore=0)将扫描整个字符串,并返回第一个匹配项。如果ignore大于零,搜索将从指定的字符位置开始。不管ignore的值是多少,成功匹配后的返回值都是匹配在原始字符串中的绝对起始位置。

last的行为方式类似,但它从字符串的最右端开始扫描,因此,如果查找的子字符串在原始字符串中出现多次,它将返回最后一个可能的匹配结果。最后,ignore参数指定在字符串最右端的要忽略的字符数——它的作用等效于这部分字符不存在。

注意:Str类的first和last方法提供了一个简单快速的子字符串搜索方法。在第六章中,使用正则表达式匹配可以更灵活地进行搜索匹配,但这种灵活性的代价是参数配置增加和速度下降。在大多数情况下,是利大于弊的,正则表达式是首选。

5.1.5 切割和连接操作

function string sjoin (qs elements);

function qs split (string splitset="", bit keepSplitters=0);

注意:svlib内部定义了类型名qs,表示“queue of strings”,但用户代码不能调用它。如果你需要一个类型名来表示字符串队列,你应该自己定义类型名,能完全兼容(类型等效)qs。另外,也可以简单地声明字符串队列的变量,并使用它们作为参数和结果变量。

sjoin方法(不使用join作为名称,是因为和SystemVerilog关键字冲突)使用Str对象的内容作为“joiner”,将字符串队列中的元素组装成单个字符串。例如,它可以方便地创建逗号分隔的列表。

split方法获取Str对象的现有字符串(保持不变),并使用单个字符分割标记(“splitter”)将其分割成字符串队列。参数splitset是一个字符串,但它被视为一组单独的字符;对象的字符串变量被分割,分割的位置是出现splitset中字符的位置。如果splitset是一个空字符串,那么对象的字符串会被分割后的字符串队列的每个元素都将是单个字符。

如果keepsplitter为true(1)且splitset不是空字符串,则拆分字符将作为结果队列的单个成员出现在其对应的位置。如果keepsplitter为false(0,默认值),拆分字符将不会出现在结果中。

注意:从svlib的0.5版开始,Regex类中有一个新的split方法(见第6章)。它提供了比这里的Str::split方法灵活得多的功能,在大多数情况下是首选方法。

5.1.6 提取子字符串和替换操作

function string range (int p, int n, origin_enum origin=START);

function void replace(string rs, int p, int n, origin_enum origin=START);

range提供了比SystemVerilog原生字符串的substr操作的更通用和统一的方法。当其中一个边界超出字符串时,它的表现会更加正常。在第5.3节中,详细地介绍了如何使用p、n和origin参数指定字符串的一个切片的详细信息。range只返回指定的子字符串,返回类型为SystemVerilog的字符串类型。

replace以完全相同的方式指定子字符串,然后用rs替换该子字符串,并修改Str对象的内容。replace非常灵活,有时可以单独使用。例如:

• 通过传入空的rs参数,删除指定子字符串

• 通过下面的方式可以实现在尾部添加一个字符串

s.replace(append_string, 0, 0, Str::END);

• 通过下面的方式可以实现在开头添加一个字符串

s.replace(prefix_string, 0, 0, Str::START);

传入的rs字符串的长度没有限制,不需要和被替换的字符串长度一致。

5.1.7 在字符串的开头和结尾删除或添加空白字符

function void trim (side_enum side=BOTH);

function void pad (int width, side_enum side=BOTH);

trim删除字符串的开头或者结尾的所有空白字符,它会修改Str对象的现有内容。参数side指定要修剪字符串的哪一端。如果side是Str::LEFT,则从字符串的左端删除空白;如果是RIGHT,删除尾随空格;如果是BOTH,删除两端的空格。最后,如果指定了NONE,就不会产生任何效果。

空白字符包括任何空格、制表符、换行符、回车符和不间断空格(ASCII码160)。

如果字符串完全由空格组成,并且side参数不是NONE,则结果将是一个空字符串。

pad会在开头或者结尾添加空白字符(使用空格字符),使结果字符串的长度正好是width。如果字符串已经大于width,则不进行任何操作。如果side为NONE,则字符串不变。否则,将根据需要在指定的字符串末尾添加空格。如果side为BOTH,则在两边添加相同数量的空格(必要时在右侧添加一个额外的空格)。此方法对于以表格格式打印的文本对齐非常有用。

5.1.8 删除字符串中不想要的字符

function void strip (st
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值