接口(Interface)
Verilog模块之间的连接是通过模块端口进行的。 为了给组成设计的各个模块定义端口,我们必须对期望的硬件设计有一个详细的认识。 不幸的是,在设计的早期,我们很难把握设计的细节。 而且,一旦模块的端口定义完成后,我们也很难改变端口的配置。 另外,一个设计中的许多模块往往具有相同的端口定义,在Verilog中,我们必须在每个模块中进行相同的定义,这为我们增加了无谓的工作量。
SystemVerilog提供了一个新的、高层抽象的模块连接,这个连接被称为接口(Interface)。 接口在关键字interface和endinterface之间定义,它独立于模块。 接口在模块中就像一个单一的端口一样使用。 在最简单的形式下,一个接口可以认为是一组线网。 例如,可以将PCI总线的所有信号绑定在一起组成一个接口。 通过使用接口,我们在进行一个设计的时候可以不需要首先建立各个模块间的互连。 随着设计的深入,各个设计细节也会变得越来越清晰,而接口内的信号也会很容易地表示出来。 当接口发生变化时,这些变化也会在使用该接口的所有模块中反映出来,而无需更改每一个模块。 下面是一个接口的使用实例:
1 interface chip_bus;// 定义接口
2
3 wireread_request, read_grant;
4
5 wire [7:0]address, data;
6
7 endinterface: chip_bus
8
9
10
11 module RAM(chip_bus io, // 使用接口
12
13 inputclk);
14
15 //可以使用io.read_request引用接口中的一个信号
16
17 endmodule
18
19
20
21 module CPU(chip_busio, input clk);
22
23 ...
24
25 endmodule
26
27
28
29 module top;
30
31 reg clk = 0;
32
33 chip_busa; // 实例接口
34
35 //将接口连接到模块实例
36
37 RAM mem(a,clk);
38
39 CPU cpu(a,clk);
40
41 endmodule
实际上,SystemVerilog的接口不仅仅可以表示信号的绑定和互连。 由于SystemVerilog的接口中可以包含参数、常量、变量、结构、函数、任务、initial块、always块以及连续赋值语句,所以SystemVerilog的接口还可以包含内建的协议检查以及被使用该接口的模块所共用的功能。
全局声明和语句
在Verilog中,除了一个模块可以作为模块实例引用其他模块外,并不存在一个全局空间。 另外,Verilog允许任意数目的顶层模块,因此会产生毫无关联的层次树。
SystemVeriog增加了一个被称为root的隐含的顶级层次。 任何在模块边界之外的声明和语句都存在于root空间中。 所有的模块,无论它处于哪一个设计层次,都可以引用$root中声明的名字。 这样,如果某些变量、函数或其它信息被设计中的所有模块共享,那么我们就可以将它们作为全局声明和语句。 全局声明和语句的一个使用实例如下:
1 reg error_flag; // 全局变量
2
3 function compare(...); // 全局函数
4
5 always@(error_flag) // 全局语句
6
7 ...
8
9 module test;
10
11 chip1 u1(...)
12
13 endmodule
14
15
16
17 module chip1(...);
18
19 FSM u2(...);
20
21 always@(data)
22
23 error_flag= compare(data, expected);
24
25 endmodule
26
27
28
29 module FSM(...);
30
31 ...
32
33 always @(state)
34
35 error_flag= compare(state, expected);
36
37 终端模块
时间单位和精度
在Verilog中,表示时间的值使用一个数来表示,而不带有任何时间单位。例如:
forever #5clock= ~clock;
从这一句中我们无法判断5代表的是5ns? 5ps? 还是其他。 Verilog的时间单位和精度是作为每一个模块的属性,并使用编译器指令timescale来设置。使用这种方法具有固有的缺陷,因为编译器指令的执行依赖于源代码的编译顺序,编译器总是将它遇到的最后一个timescale设置的时间单位和精度作为之后的标准。 那么,假如有些模块之前没有使用`timescale设置时间单位和精度,这就有可能出现同一个源代码的不同仿真会出现不同结果的情况。
SystemVerilog为了控制时间单位加入了两个重要的增强。首先,时间值可以显式地指定一个单位。时间单位可以是s、ms、ns、ps或fs。时间单位作为时间值的后缀出现。例如:
forever #5nsclock= ~clock;
其次,SystemVerilog允许使用新的关键字(timeunits和timeprecision)来指定时间单位和精度。 这些声明可以在任何模块中指定,同时也可以在$root空间中全局指定。 时间单位和精度必须是10的幂,范围可以从s到fs。 例如:
时间单位 1ns;
timeprecision 10ps;
4. 抽象数据类型
Verilog提供了面向底层硬件的线网、寄存器和变量数据类型。这些类型代表了4态逻辑值,通常用来在底层上对硬件进行建模和验证。线网数据类型还具有多个强度级别,并且能够为多驱动源的线网提供解析功能。
SystemVerilog包括了C语言的char和int数据类型,它允许在Verilog模型和验证程序中直接使用C和C++代码。VerilogPLI不再需要集成总线功能模型、算法模型和C函数。SystemVerilog还为Verilog加入了几个新的数据类型,以便能够在更抽象的层次上建模硬件。
l char:一个两态的有符号变量,它与C语言中的char数据类型相同,可以是一个8位整数(ASCII)或short int(Unicode);
l int:一个两态的有符号变量,它与C语言中的int数据类型相似,但被精确地定义成32位;
l shortint:一个两态的有符号变量,被精确地定义成16位;
l longint:一个两态的有符号变量,它与C语言中的long数据类型相似,但被精确地定义成64位;
l byte:一个两态的有符号变量,被精确地定义成8位;
l bit:一个两态的可以具有任意向量宽度的无符号数据类型,可以用来替代Verilog的reg数据类型;
l logic:一个四态的可以具有任意向量宽度的无符号数据类型,可以用来替代Verilog的线网或reg数据类型,但具有某些限制;
l shortreal:一个两态的单精度浮点变量,与C语言的float类型相同;
l void:表示没有值,可以定义成一个函数的返回值,与C语言中的含义相同。
SystemVerilog的bit和其他数据类型允许用户使用两态逻辑对设计建模,这种方法对仿真性能更有效率。由于Verilog语言没有两态数据类型,因此许多仿真器都通过将这种功能作为仿真器的一个选项提供。这些选项不能够在所有的仿真器之间移植,而且在需要时用三态或四态逻辑的设计中强制使用两态逻辑还具有副作用。SystemVerilog的bit数据类型能够极大改进仿真器的性能,同时在需要的时候仍然可以使用三态或四态逻辑。通过使用具有确定行为的数据类型来代替专有的仿真器选项,两态模型能够在所有的SystemVerilog仿真器间移植。
SystemVerilog的logic数据类型比Verilog的线网和寄存器数据类型更加灵活,它使得在任何抽象层次上建模硬件都更加容易。logic类型能够以下面的任何一种方法赋值:
l 通过任意数目的过程赋值语句赋值,能够替代Verilog的reg类型;
l 通过单一的连续赋值语句赋值,能够有限制地替代Verilog的wire类型;
l 连接到一个单一原语的输出,能够有限制地替代Verilog的wire类型;
由于logic数据类型能够被用来替代Verilog的reg或wire(具有限制),这就使得能够在一个更高的抽象层次上建模,并且随着设计的不断深入能够加入一些设计细节而不必改变数据类型的声明。logic数据类型不会表示信号的强度也不具有线逻辑的解析功能,因此logic数据类型比Verilog的wire类型更能有效地仿真和综合。
有符号和无符号限定符
缺省情况下,Verilog net和reg数据类型是无符号类型,integer类型是一个有符号类型。Verilog-2001标准允许使用signed关键字将无符号类型显式地声明成有符号类型。SystemVerilog加入了相似的能力,它可以通过unsigned关键字将有符号数据类型显式地声明成有无符号数据类型。例如:
int unsigned j;
....
SystemVerilog语言简介
SystemVerilog是一种硬件描述和验证语言(HDVL),它基于IEEE1364-2001 Verilog硬件描述语言(HDL),并对其进行了扩展,包括扩充了C语言数据类型、结构、压缩和非压缩数组、 接口、断言等等,这些都使得SystemVerilog在一个更高的抽象层次上提高了设计建模的能力。 SystemVerilog由Accellera开发,它主要定位在芯片的实现和验证流程上,并为系统级的设计流程提供了强大的连接能力。 下面我们从几个方面对SystemVerilog所作的增强进行简要的介绍,期望能够通过这个介绍使大家对SystemVerilog有一个概括性的了解。
接口(Interface)
Verilog模块之间的连接是通过模块端口进行的。 为了给组成设计的各个模块定义端口,我们必须对期望的硬件设计有一个详细的认识。 不幸的是,在设计的早期,我们很难把握设计的细节。 而且,一旦模块的端口定义完成后,我们也很难改变端口的配置。 另外,一个设计中的许多模块往往具有相同的端口定义,在Verilog中,我们必须在每个模块中进行相同的定义,这为我们增加了无谓的工作量。
SystemVerilog提供了一个新的、高层抽象的模块连接,这个连接被称为接口(Interface)。 接口在关键字interface和endinterface之间定义,它独立于模块。 接口在模块中就像一个单一的端口一样使用。 在最简单的形式下,一个接口可以认为是一组线网。 例如,可以将PCI总线的所有信号绑定在一起组成一个接口。 通过使用接口,我们在进行一个设计的时候可以不需要首先建立各个模块间的互连。 随着设计的深入,各个设计细节也会变得越来越清晰,而接口内的信号也会很容易地表示出来。 当接口发生变化时,这些变化也会在使用该接口的所有模块中反映出来,而无需更改每一个模块。 下面是一个接口的使用实例:
interface chip_bus;// 定义接口
wireread_request,read_grant;
有线 [7:0] 地址、数据;
端界面:chip_bus
module RAM(chip_bus io, // 使用接口
输入);
//可以使用io.read_request引用接口中的一个信号
终端模块
模块CPU(chip_busio,输入方);
...
终端模块
模块顶部;
注册 clk = 0;
chip_busa; // 实例接口
//将接口连接到模块实例
RAM mem(a,clk);
中央处理器(a,clk);
终端模块
实际上,SystemVerilog的接口不仅仅可以表示信号的绑定和互连。 由于SystemVerilog的接口中可以包含参数、常量、变量、结构、函数、任务、initial块、always块以及连续赋值语句,所以SystemVerilog的接口还可以包含内建的协议检查以及被使用该接口的模块所共用的功能。
全局声明和语句
在Verilog中,除了一个模块可以作为模块实例引用其他模块外,并不存在一个全局空间。 另外,Verilog允许任意数目的顶层模块,因此会产生毫无关联的层次树。
SystemVeriog增加了一个被称为root的隐含的顶级层次。 任何在模块边界之外的声明和语句都存在于root空间中。 所有的模块,无论它处于哪一个设计层次,都可以引用$root中声明的名字。 这样,如果某些变量、函数或其它信息被设计中的所有模块共享,那么我们就可以将它们作为全局声明和语句。 全局声明和语句的一个使用实例如下:
reg error_flag; // 全局变量
function compare(...); // 全局函数
always@(error_flag) // 全局语句
...
模块测试;
芯片 1 u1(...)
终端模块
模块 chip1(...);
FSM u2(...);
总是@(数据)
error_flag=比较(数据,预期);
终端模块
模块有限状态机(...);
...
总是@(状态)
error_flag=比较(状态,预期);
终端模块
时间单位和精度
在Verilog中,表示时间的值使用一个数来表示,而不带有任何时间单位。例如:
永远 #5clock= ~clock;
从这一句中我们无法判断5代表的是5ns? 5ps? 还是其他。Verilog的时间单位和精度是作为每一个模块的属性,并使用编译器指令timescale来设置。使用这种方法具有固有的缺陷,因为编译器指令的执行依赖于源代码的编译顺序,编译器总是将它遇到的最后一个timescale设置的时间单位和精度作为之后的标准。那么,假如有些模块之前没有使用`timescale设置时间单位和精度,这就有可能出现同一个源代码的不同仿真会出现不同结果的情况。
SystemVerilog为了控制时间单位加入了两个重要的增强。首先,时间值可以显式地指定一个单位。时间单位可以是s、ms、ns、ps或fs。时间单位作为时间值的后缀出现。例如:
永远 #5nsclock= ~clock;
其次,SystemVerilog允许使用新的关键字(timeunits和timeprecision)来指定时间单位和精度。这些声明可以在任何模块中指定,同时也可以在$root空间中全局指定。时间单位和精度必须是10的幂,范围可以从s到fs。例如:
时间单位 1ns;
时间精度10ps;
抽象数据类型
Verilog提供了面向底层硬件的线网、寄存器和变量数据类型。这些类型代表了4态逻辑值,通常用来在底层上对硬件进行建模和验证。线网数据类型还具有多个强度级别,并且能够为多驱动源的线网提供解析功能。
SystemVerilog包括了C语言的char和int数据类型,它允许在Verilog模型和验证程序中直接使用C和C++代码。VerilogPLI不再需要集成总线功能模型、算法模型和C函数。SystemVerilog还为Verilog加入了几个新的数据类型,以便能够在更抽象的层次上建模硬件。
l char:一个两态的有符号变量,它与C语言中的char数据类型相同,可以是一个8位整数(ASCII)或short int(Unicode);
l int:一个两态的有符号变量,它与C语言中的int数据类型相似,但被精确地定义成32位;
l shortint:一个两态的有符号变量,被精确地定义成16位;
l longint:一个两态的有符号变量,它与C语言中的long数据类型相似,但被精确地定义成64位;
l byte:一个两态的有符号变量,被精确地定义成8位;
l bit:一个两态的可以具有任意向量宽度的无符号数据类型,可以用来替代Verilog的reg数据类型;
l logic:一个四态的可以具有任意向量宽度的无符号数据类型,可以用来替代Verilog的线网或reg数据类型,但具有某些限制;
l shortreal:一个两态的单精度浮点变量,与C语言的float类型相同;
l void:表示没有值,可以定义成一个函数的返回值,与C语言中的含义相同。
SystemVerilog的bit和其他数据类型允许用户使用两态逻辑对设计建模,这种方法对仿真性能更有效率。由于Verilog语言没有两态数据类型,因此许多仿真器都通过将这种功能作为仿真器的一个选项提供。这些选项不能够在所有的仿真器之间移植,而且在需要时用三态或四态逻辑的设计中强制使用两态逻辑还具有副作用。SystemVerilog的bit数据类型能够极大改进仿真器的性能,同时在需要的时候仍然可以使用三态或四态逻辑。通过使用具有确定行为的数据类型来代替专有的仿真器选项,两态模型能够在所有的SystemVerilog仿真器间移植。
SystemVerilog的logic数据类型比Verilog的线网和寄存器数据类型更加灵活,它使得在任何抽象层次上建模硬件都更加容易。logic类型能够以下面的任何一种方法赋值:
l 通过任意数目的过程赋值语句赋值,能够替代Verilog的reg类型;
l 通过单一的连续赋值语句赋值,能够有限制地替代Verilog的wire类型;
l 连接到一个单一原语的输出,能够有限制地替代Verilog的wire类型;
由于logic数据类型能够被用来替代Verilog的reg或wire(具有限制),这就使得能够在一个更高的抽象层次上建模,并且随着设计的不断深入能够加入一些设计细节而不必改变数据类型的声明。logic数据类型不会表示信号的强度也不具有线逻辑的解析功能,因此logic数据类型比Verilog的wire类型更能有效地仿真和综合。
有符号和无符号限定符
缺省情况下,Verilog net和reg数据类型是无符号类型,integer类型是一个有符号类型。Verilog-2001标准允许使用signed关键字将无符号类型显式地声明成有符号类型。SystemVerilog加入了相似的能力,它可以通过unsigned关键字将有符号数据类型显式地声明成有无符号数据类型。例如:
不带符号的 j;
值得注意的是unsigned在Verilog中是一个保留字,但并没有被Verilog标准使用。
用户定义的类型
Verilog不允许用户定义新的数据类型。SystemVerilog通过使用typedef提供了一种方法来定义新的数据类型,这一点与C语言类似。用户定义的类型可以与其它数据类型一样地使用在声明当中。例如:
typedefunsigned int uint;
单元 a, b;
一个用户定义的数据类型可以在它的定义之前使用,只要它首先在空的typedef中说明,例如:
typedef int48; // 空的typedef,在其他地方进行完整定义
int48 c;
枚举类型
在Verilog语言中不存在枚举类型。标识符必须被显式地声明成一个线网、变量或参数并被赋值。SystemVerilog允许使用类似于C的语法产生枚举类型。一个枚举类型具有一组被命名的值。缺省情况下,值从初始值0开始递增,但是我们可以显式地指定初始值。枚举类型的例子如下:
枚举{红、黄、绿} RGB;
枚举 {WAIT=2'b01, LOAD, DONE} 状态;
我们还可以使用typedef为枚举类型指定一个名字,从而允许这个枚举类型可以在许多地方使用。例如:
typedefenum {FALSE=1'b0, TRUE} 布尔值;
布尔值准备就绪;
布尔测试完成;
结构体和联合体
在Verilog语言中不存在结构体或联合体,而结构体或联合体在将几个声明组合在一起的时候非常有用。SystemVerilog增加了结构体和联合体,它们的声明语法类似于C。
结构{
reg [15:0] 操作码;
注册 [23:0] 地址;
} 和;
工会{
诠释我;
短实 f;
} N;
结构体或联合体中的域可以通过在变量名和域名字之间插入句点(.)来引用:
IR.opcode = 1; // 设置IR变量中的opcode域
N.f = 0.0; // 将N设置成浮点数的值
我们可以使用typedef为结构体或联合体的定义指定一个名字。
typedefstruct{
reg [7:0] 操作码;
注册 [23:0] 地址;
} instruction; // 命名的结构体
instruction IR; // 结构体实例
一个结构体可以使用值的级联来完整地赋值,例如:
指令={5,200};
结构体可以作为一个整体传递到函数或任务,也可以从函数或任务传递过来,也可以作为模块端口进行传递。
数组
在Verilog中可以声明一个数组类型,reg和线网类型还可以具有一个向量宽度。在一个对象名前面声明的尺寸表示向量的宽度,在一个对象名后面声明的尺寸表示数组的深度。例如:
reg [7:0] r1 [1:256]; // 256个8位的变量
在SystemVerilog中我们使用不同的术语表示数组:使用“压缩数组(packed array)”这一术语表示在对象名前声明尺寸的数组;使用“非压缩数组(unpacked array)”这一术语表示在对象名后面声明尺寸的数组。压缩数组可以由下面的数据类型组成:bit、logic、reg、wire以及其它的线网类型。无论是压缩数组还是非压缩数组都可以声明成多维的尺寸。
bit [7:0] a; // 一个一维的压缩数组
bit b [7:0]; //一个一维的非压缩数组
bit [0:11] [7:0] c; //一个二维的压缩数组
bit [3:0] [7:0] d [1:10]; // 一个包含10个具有4个8位字节的压缩数组的非压缩数组
非压缩尺寸在压缩尺寸之前引用,这就允许将整个压缩数组作为一个单一的元素进行引用。在上面的例子中,d[1]引用非压缩数组的一个单一元素,这个元素是一个包含4个字节的数组。
在为命名的块中声明
Verilog允许变量在一个命名的begin-end或fork-join语句组中声明。相对于语句组来说,这些变量是本地的,但它们可以被层次化地引用。在SystemVerilog中,既可以在命名的块中也可以在未命名的块中声明。在未命名的块中,不能够使用层次名来访问变量。所有的变量类型,包括用户定义的类型、枚举类型、结构体和联合体都可以在begin-end或fork-join语句组中声明。
常量
在Verilog中有三种特性类型的常量:parameter、specparam和localparam。而在SystemVerilog中,允许使用const关键字声明常量。例如:
常量字符=“:”;
可重定义的数据类型
SystemVerilog扩展了Verilog的parameter,使其可以包含类型。这个强大的功能使得一个模块中的数据类型在模块的每一个实例中重新定义。例如:
模块 foo;
(参数 typeVAR_TYPE = 短线;)
(输入逻辑[7:0] i,输出逻辑[7:0] o);
VAR_TYPE j = 0; // 如果不重新定义,j的数据类型为shortint
……
终端模块
模块栏;
逻辑 [3:0] i, o;
foo#(.VAR_TYPE(int)) u1 (i, o); // 重新将VAR_TYPE定义成int类型
终端模块
模块端口连接
在Verilog中,可以连接到模块端口的数据类型被限制为线网类型以及变量类型中的reg、integer和time。而在SystemVerilog中则去除了这种限制,任何数据类型都可以通过端口传递,包括实数、数组和结构体。
字母值
在Verilog中,当指定或赋值字母值的时候存在一些限制。而SystemVerilog则为字母值如何指定作了下面的增强:
l 一个字母值的所有位均可以使用0、1、z或x作相同的填充。这就允许填充一个任意宽度的向量,而无需显式地指定向量的宽度,例如:
位 [63:0] 数据;
data = `1; //将data的所有位设置成1
l 一个字符串可以赋值成一个字符数组,象C语言一样加入一个空结束符。如果尺寸不同,它象C中一样进行左调整,例如:
char foo[0:12] = “你好世界”;
l 加入了几个特殊的串字符:
v:垂直TAB
f:换页
a:响铃
x02:用十六进制数来表示一个ASCII字符
l 数组可以使用类似于C初始化的语法赋值成字符值,但它还允许复制操作符。括号的嵌套必须精确地匹配数组的维数(这一点与C不同),例如:
int n[1: 2] [1:3] = {{0, 1, 2}, {3{4}}};
强制类型转换
Verilog不能将一个值强制转换成不同的数据类型。SystemVerilog通过使用’操作符提供了数据类型的强制转换功能。这种强制转换可以转换成任意类型,包括用户定义的类型。例如:
int’ (2.0 *3.0) // 将结果转换为int类型
mytype’ (foo) // 将foo转换为mytype类型
一个值还可以通过在强制转换操作符前指定一个10进制数来转换成不同的向量宽度,例如:
17’(x- 2) // 将结果转换为17位宽度
也可以将结果转换成有符号值,例如:
signed’(x) // 将x转换为有符号值
操作符
Verilog没有C语言的递增(++)和递减(--)操作符。 而SystemVerilog加入了几个新的操作符:
l ++和--:递增和递减操作符;
l +=、-=、*=、/=、%=、&=、^=、|=、<<=、>>=、<<<=和>>>=赋值操作符;
唯一性和优先级决定语句
在Verilog中,如果没有遵循严格的编码风格,它的if-else和case语句会在RTL仿真和RTL综合间具有不一致的结果。 如果没有正确使用full_case和parallel_case综合指令还会引起一些其它的错误。
SystemVerilog能够显式地指明什么时候一条决定语句的分支是唯一的,或者什么时候需要计算优先级。 我们可以在if或case关键字之前使用unique或requires关键字。 这些关键字可以向仿真器、综合编译器、以及其它工具指示我们期望的硬件类型。 工具使用这些信息来检查if或case语句是否正确建模了期望的逻辑。 例如,如果使用unique限定了一个决定语句,那么在不希望的case值出现的时候仿真器就能够发布一个警告信息。
位 [2:0]a;
唯一 if((a==0) ||(a==1))y= in1;
否则如果 (a==2) y=in2;
else if (a==4) y=in3; // 值3、5、6、7会引起一个警告
优先级如果 (a[2:1]==0) y = in1;a是0或1
否则如果 (a[2]==0) y = in2;a是2或3
else y = in3; // 如果a为其他的值
独特案例 (A)
0, 1: y = in1;
2: y = in2;
4: y = in3;
endcase // 值3、5、6、7会引起一个警告
优先级案例z(a)
2'b00?: y = in1;a是0或1
2'b0??: y = in2;a是2或3
default : y = in3; //如果a为其他的值
尾壳
底部检测的循环
Verilog包含for、while和repeat循环,这几个循环都是在循环的起始处检测循环条件。 SystemVerilog加入了一个do-while循环,这种循环在执行语句的结尾处检测循环条件。
跳转语句
在语句的执行过程中,C语言提供了几种方式来跳转到新的语句,包括:return、break、continue和goto。 在Verilog中除了通过使用disable语句跳转到语句组的尾部外,没有提供任何其它跳转语句。 使用disable语句执行中止和继续功能要求加入块的名字,并且会产生不直观的代码。 SystemVerilog加入了C语言的break和continue关键字,这两个关键字不要求使用块名字。 另外,SystemVerilog还加入了一个return关键字,它可以用来在任何执行点上退出一个任务或函数。
l break:退出一个循环,与C语言相同;
l continue:跳转到一个循环的尾部,与C语言相同;
l return 表达式:退出一个函数;
l return:退出一个任务或void类型的函数。
SystemVerilog没有包含C语言中的goto语句。
块名字和语句标签
在Verilog中,我们可以通过在begin或fork关键字之后指定名字来为begin-end或fork-jion语句指定名字。 这个指定的名字代表整个语句块。 SystemVerilog还允许在end或jion关键字之后指定一个匹配的块名字。 这种机制很容易将end或jion与对应的begin或fork联系起来,尤其是在一个长的块或嵌套的块中。 块结尾处的名字是可选的,但如果使用的话,它必须与块起始处的名字相同。 例如:
begin: foo // 在begin之后的块名字
……
fork: bar // 具有名字的嵌套的块
……
jion: bar // 必须具有相同的名字
……
end: foo // 必须具有相同的名字
SystemVerilog还允许像C语言一样为单个语句设置标签。 语句标签放置在语句的前面,用来标识这条语句。 例如:
初始开始
测试1: read_enable = 0;
……
测试2:对于(I=0;I<=255;I++)
……
结束
对事件控制的增强
Verilog使用@标记来控制基于特定事件的执行流,SystemVerilog增强了@事件控制。
l 有条件的事件控制
@标记的一个基本应用就是推断一个具有使能输入的锁存器。 下面的例子演示了一个锁存器建模的基本风格。
始终 @(数据或 en)
如果 (en)y<=data;
这种编码风格对仿真来说是效率低下的,因为即使在使能输入无效的时候,数据输入的每次改变都会触发事件控制。
SystemVerilog在事件控制中加入了一个iff条件。 只有iff条件为真的条件下,事件控制才会被触发。 通过将使能判断移入到事件控制里面,使得只有在锁存器输出能够改变的时候事件控制才会被触发。 例如:
始终 @(a 或 en iff en==1)
y<=a;
l 事件控制中的表达式
Verilog允许在@事件控制列表中使用表达式,例如:
始终 @((a * b))
总是@(内存[地址])
在第一个例子中,是当操作数发生改变的时候还是只有当运算结果发生改变的时候才会触发事件控制? 在第二个例子中,是当memory的地址发生变化的时候还是只有当memory的值发生变化的时候才会触发事件控制? 当@事件控制中包含表达式的时候,IEEE Verilog标准允许仿真器进行不同的优化。 这就可能导致在不同的仿真器间有不同的仿真结果,可能还会导致仿真与综合之间的结果不一致。 SystemVerilog加入了一个changed关键字,在事件控制列表中它被用作一个修饰符。 @(changed (表达式))能够显式地定义只有当表达式的结果发生改变的时候才会触发事件控制。 例如:
总是 @(更改 (a * b))
总是@(更改了内存[地址])
l 事件控制中的赋值
Verilog不允许在事件控制中使用赋值。 SystemVerilog允许在事件控制中使用赋值表达式。 事件控制仅仅敏感于赋值表达式右侧的变化。 例如:
始终 @(y = a * b)
新的过程
Verilog使用always过程来表示时序逻辑、组合逻辑和锁存逻辑的RTL模型。 综合工具和其它软件工具必须根据过程起始处的事件控制列表以及过程内的语句来推断always过程的意图。 这种推断会导致仿真结果和综合结果之间的不一致。 SystemVerilog增加了三个新的过程来显式地指示逻辑的意图。
l always_ff:表示时序逻辑的过程;
l always_comb:表示组合逻辑的过程;
l always_latch:表示锁存逻辑的过程。
例如:
always_comb@(aor b 或 sel) 开始
如果 (sel) y = a;
否则 y = b;
结束
软件工具能够检查事件控制敏感列表和过程的内容来保证逻辑的功能匹配过程的类型。 例如,工具能够检查一个always_comb过程能够敏感过程内读取的所有外部值,对逻辑的每一个分支的相同变量进行赋值,并且检查分支是否覆盖了所有可能的条件。 如果任何一个条件没有满足,软件工具均会报告该过程没有正确建模组合逻辑。
动态过程
Verilog通过使用fork-jion提供了一种静态的并发过程。 每一个分支都是一个分离的、并行的过程。 fork-jion中任何语句的执行必须在组内的每一个过程完成后才会执行。 例如:
初始开始
叉
send_packet_task (1,255, 0);
send_packet_task(7 128人,5人);
watch_result_task (1,255, 0);
watch_result_task(7 128人,5人);
jion // 所有的任务必须完成后才会到达这里
结束
SystemVerilog通过process关键字加入了一个新的、动态的过程。 它为一个过程产生分支,然后继续执行而无需等待其他过程完成。 过程不会阻塞过程或任务内的语句执行。 这种方式能够建模多线程的过程。 例如:
初始开始
进程send_packet_task (1,255, 0);
processsend_packet_task(7, 128, 5);
processwatch_result_task(1, 255, 0);
processwatch_result_task(7, 128, 5);
end //所有的过程并行运行
任务和函数增强
SystemVerilog为Verilog的任务和函数作了几个增强。
l 静态和自动的存储
缺省情况下,在Verilog任务或函数内的所有存储都是静态的。 Verilog-2001允许将任务和函数声明成自动的。 在SystemVerilog中:(1). 在一个静态任务和函数内的特定数据可以显式地声明成自动的。 声明成自动的数据在块中具有完整的生命周期,并且在任务和函数调用的入口处初始化; (2). 在一个自动的任务或函数中的特定数据可以显式地声明成静态的。 自动的任务或函数中声明成静态的数据在一个块的本地范围内具有静态的生命周期。
l 从任何点返回
Verilog在一个任务或函数中执行到endtask或endfunction关键字的时候返回。 函数的返回值是给函数名赋的最后一个值。 SystemVerilog加入了一个return关键字,使用这个关键字,一个任务或函数可以在任何点上返回。
l 多语句
Verilog要求一个任务或函数只具有一个语句或语句块。 多条语句必须组合到一个单一的begin-end或fork-jion块中。 SystemVerilog去除了这种限制。 因此,多条语句可以在一个任务或函数中列出而无需使用的begin-end或fork-jion。 每有分组的语句就像在begin-end中一样顺序执行。 我们还可以产生一个没有语句的任务或函数定义。
l void函数
Verilog要求一个函数具有一个返回值,函数的调用接收这个返回值。 SystemVerilog加入了一个void数据类型,这个数据类型可以作为一个函数的返回值类型。 void函数可以像Verilog任务一样进行调用,而无需接收一个返回值。 void函数和任务的差别在于函数存在几个限制,例如没有时间控制等。
l 函数的输入和输出
Verilog标准要求一个函数至少具有一个输入并且函数只能具有输入。 SystemVerilog去除了这些限制。 函数可以具有任意数目的输入、输出以及输入输出,也可以什么也没有。
连续赋值的增强
在Verilog中,连续赋值语句的左侧只能是线网类型,例如wire。 连续赋值语句被认为是线网的驱动源,而线网可以拥有任意数据的驱动源。 SystemVerilog允许除reg类型以外的任何数据类型用于连续赋值语句的左侧。 与线网不同,所有其它数据类型被限制为只能有一个连续赋值语句驱动。 为相同的变量混合使用连续赋值语句和过程赋值语句是不被允许的。
$bit系统函数
在Verilog中没有类似于C语言中sizeof的函数。 SystemVerilog加入一个新的$bit内建函数。 这个函数返回保存一个值所需的硬件位的数目(一个四态值要求一个硬件位),这个函数还可以用来确定一个结构体所代表的硬件位的数目。
`define的增强
SystemVerilog增强了define编译器指令的能力以便支持将字符串作为宏的参数。宏的文本字符串中可以包含一个隔离的引号,它的前面必须具有一个反勾号(”),这就允许字符串中包含宏参数。 宏文本可以在行的尾部包含一个反斜杠(’’)来表示在下一行继续。 如果宏文本字符串中包含反斜杠,则反斜杠应该被放在两个反勾号之间,这样它就不会被认为是Verilog转义标识符的开始。 宏文本字符串还可以包含双反勾号(``),它允许标识符能够从参数中构建。 这些增强使得define指令更加灵活。例如:include指令后可以紧跟一个宏名字来替代一个字符串。
“定义 F1”../project_top/opcode_defines”
include F1
状态机建模
SystemVerilog允许在更高的抽象层次上对状态机建模。 这些结构包括:
l 枚举类型
l 一个特殊的state数据类型;
l 一个迁移语句
l 一个迁移操作符
断言
SystemVerilog中加入了断言的功能来改善系统的验证过程。
结论
SystemVerilog为Verilog-2001标准提供了一系列的扩展。 这些扩展使得大型设计的建模和验证更加容易。
-
模块
+关注
关注
7文章
2666浏览量
47327 -
接口
+关注
关注
33文章
8486浏览量
150804 -
Verilog
+关注
关注
28文章
1343浏览量
109970 -
System
+关注
关注
0文章
165浏览量
36877
原文标题:System Verilog的概念以及与verilog的对比
文章出处:【微信号:Hack电子,微信公众号:Hack电子】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论