引言
本文简单介绍 SystemVerilog 的其他程序结构。
前文链接:
我的 System Verilog 学习记录(1)
我的 System Verilog 学习记录(2)
我的 System Verilog 学习记录(3)
我的 System Verilog 学习记录(4)
我的 System Verilog 学习记录(5)
我的 System Verilog 学习记录(6)
我的 System Verilog 学习记录(7)
我的 System Verilog 学习记录(8)
我的 System Verilog 学习记录(9)
我的 System Verilog 学习记录(10)
program 块
模块是用于构建设计的基本结构,每个模块可以包含其他模块、线网、变量和其他程序块的层次结构,以描述任何硬件功能。另一方面,testbench 是一个验证设计的完整环境,因此重点放在它的建模方式上,以使其更可重用和有效。必须正确地初始化和同步,避免设计和 testbench 之间的竞争条件。
program 块的需求是啥?
SV 的 program 块被引入的原因如下:
- 为执行测试工作台提供一个入口点
- 创建一个容器来保存所有其他的测试台数据,如任务、类对象和函数
- 通过在仿真周期的反应区域内执行来避免设计中的竞争条件
反应区域是模拟时间提前之前的最后几个阶段之一,到那时,所有的设计元素语句都已经被执行,测试台将看到更新的值。在设计的执行和测试台语句之间有这种划分是很重要的,因为它将在模拟器之间提供更具确定性的输出。
语法
示例
程序块可以嵌套在模块和接口中,因此同一模块中的多个程序可以共享该作用域的本地变量。在下面的示例中,模式是TB内的局部变量,可以由程序p1和p2访问。
动态的强制类型转换
当需要在两个不同的数据类型变量之间分配值时,普通分配可能无效,而应该使用一个名为$cast的系统任务。
$cast 可以用作任务或函数,区别是,当作为函数使用时,如果转换是合法的,它返回1。它在处理无效的赋值时变得很有用。
语法
在这里,targ_var是目标变量,而source_exp是应该计算并分配给目标变量的源表达式。
以 函数 / 任务 调用
当$cast作为任务调用时,它将尝试将源表达式分配给目标变量,如果它无效,将会发生运行时错误,而目标变量将保持不变。
当$cast作为函数调用时,它将尝试将源表达式分配给目标变量,如果成功则返回1。如果它失败并返回0,则不会进行分配。请注意,在这种情况下,将不会出现运行时错误,并且仿真将以目标变量的不变值继续进行。
示例
使用 $cast
不用 $cast
有一些仿真器会给出编译警告,有些则会报错。
强转无效数据
枚举变量里面没有数值为 75 的元素,运行期间会报错。
以函数形式调用
package
package 提供了一种存储和共享数据、方法、属性、参数的机制,这些数据、方法、属性和参数可以在多个其他模块、接口或程序中重复使用。它们显式命名了与顶级模块位于同一级别的作用域。因此,所有参数和枚举都可以通过该作用域引用。将这样的定义和声明放在包中还可以避免使全局名称镜变得混乱。然后,可以将包导入到可以使用该包中的项的当前范围中。请注意,包中的项不能具有对标识符的分层引用,除非那些在包中创建的项或通过导入另一个包而可见的项。
示例
上面显示的包可以导入到其他模块和类范围中,以便可以重用其中定义的项。这可以使用关键字import ,然后使用范围解析操作符 :: ,然后指定要导入的内容。在下面的示例中,我们导入包中定义的所有内容,如下面的 * 运算符所示,以便能够使用任何项。
请注意,必须导入程序包,编译器才能识别绿色的定义位置。如果没有导入程序包,则会出现如下所示的编译器错误。
如果您确切地知道代码中使用了什么,也可以单独导入它们,而不是导入包中的所有定义。但是,这被视为一种开销,特别是当从包访问更多成员时。
省略这三个导入语句中的任何一个都会导致编译器错误,因为它不知道它们是在哪里定义的,除非它是导入的。
namespace 冲突
考虑下面的示例,其中存在相同的定义,一个在顶层,另一个通过导入的包。
请注意,即使my_pkg是导入的,e_rd_wr变量OPC2的值Read也是1,这意味着不考虑包中的枚举值。
为了让仿真器应用来自包的值,应该使用 :: 操作符显式地提到包名,如下所示。
命令行输入
有时,您需要避免重新编译testbench,而是能够像任何脚本语言(如bash或perl)一样接受来自命令行的值。在SystemVerilog中,此信息作为始终以字符开头的可选参数提供给仿真。这些从命令行传递的参数可在SV代码中通过以下称为plusargs的系统函数进行访问。
语法
$test$plusargs
函数 $test$plusargs 通常在不需要参数的值时使用。它在加号列表中搜索用户指定的字符串。变量也可以用来指定字符串,任何空字符都将被忽略。如果提供的加号之一的前缀与提供的字符串中的所有字符匹配,该函数将返回一个非零整数,否则返回零。
示例
当使用运行时参数+STANDBY编译和模拟上面显示的代码时,其中STANDBY是提供给模拟工具的字符串plusarg,我们会得到如下所示的输出。请注意,plusarg区分大小写,即使提供的字符串是“STANDBY”,它也匹配“S”和“STAND”。
$value$plusargs
$value$plusargs 系统函数也搜索plusargs列表,和$test$plusargs一样,但它有能力获取指定用户字符串的值。如果提供的一个plusarg的前缀与给定用户字符串中的所有字符匹配,该函数将返回一个非零值,并将结果值存储在提供的变量中。如果没有找到用户字符串,则该函数将返回一个非零值,并且该变量将不会被修改。
用户字符串应为“plusarg_string format_string”,其中格式字符串与$display 任务相同。这些格式标识符用于将通过命令行提供的值转换为给定的格式,并存储在一个变量中。
示例:
对于不同的输入参数,我们将得到不同的输出。还要注意,用户字符串、= 和命令行表达式中的值之间不应该有任何空格。
具体示例:
SystemVerilog Command Line Arguments
文件操作
打开/关闭文件
可以使用$fopen()系统任务打开文件以进行读取或写入。此任务将返回一个称为文件描述符的32位整数句柄。此句柄应用于读取和写入该文件,直到它关闭。文件描述符可以使用$flose()系统任务关闭。一旦关闭,就不允许对文件描述符进行进一步的读取或写入。
示例
如何在读取和追加模式下打开?
示例
如何对文件读写?
写文件时,文件的打开方式为 w (w+) 或者 a (a+) ;写入文字使用 $fdisplay() 或者 $fwrite() ;
读文件时,在 r 或者 r+ 模式下打开文件,$fgets() 每调用一次读取一行;
示例
怎么知道有没有读到文件尾?
使用 $feof(); 系统任务。如果读到文件尾,返回1;
示例
如何解析行内数据?
使用 $fscanf() 系统任务。示例如下:
SV的域作用符
域作用操作符 :: 用于引用类的范围内的标识符。
作用域解析运算符 :: 的左侧应该是类类型名称、程序包名称、覆盖组类型名称、覆盖点或交叉名称、类型定义(typedef)名称。运算符的右侧应该是类似于变量或方法名称的标识符。
域作用符的意义何在?
类和其他作用域可以有相同的标识符的名称,可能创建一个名称空间冲突如果不指定范围。
范围解析操作符唯一地标识一个给定的类的一个成员或参数。
他们也用于访问静态变量和方法,参数和局部参数以外的类的类。它还允许访问公共基类的成员和保护成员在子类中。
示例
定义外部函数
访问静态方法/函数
使用package
避免命名空间冲突