Preface

译者的话:本文是synthensizing SystemVerilog, Busting the Myth that SystemVerilog is only for Verification的译文,计划分成三到四个部分完成,作为译者学习RTL设计的入门纪念。参考文献将以脚注的形式给出。

关于作者:

以下是译文正文。

摘要

SystemVerilog绝不仅仅是用于验证的!它最初被发明的时候,其中一个主要目标就是使人们可以使用更少的代码更准确地描述复杂硬件设计的可综合模型。这个目标 已经实现了,而且Synopsys已经在Design Compiler(DC)和Sysnplify-Pro中完成了实现SystemVerilog的主要工作。这篇文章主要测试了SystemVerilog可综合子集的细节,以及展示了相对传统Verilog而言,使用这些新的结构的优势。读者将从本文中学到新的RTL建模技能,这些技能确实可以用更少的代码行进行建模,同时减少潜在的设计错误并实现高综合结果质量。

目标受众:使用ASIC和FPGA参与RTL设计和综合的工程师们。

1.0 引言 —— SystemVerilog v.s. Verilog的真相

总有一种常见的误解,认为Verilog是一种可综合的硬件建模语言,而SystemVerilog是一种不可综合的验证语言。这是完全错误的!

自从1984年Verilog语言被引入以来,它就肩负了两个目标,分别是为硬件建模以及描述硬件测试平台(testbench)。许多Verilog语言的结构,例如if...else...,既可用于建模,也可用于验证。此外Verilog中也存在大量仅可用于验证的结构,例如$display,不存在与它们直接对应的硬件结构。综合关心的是语言中与硬件建模相关的部分,因此原始的Verilog语言中仅有一个子集支持综合。

IEEE于1995年正式将Verilog标准化,标准号是1364-1995,因此它也被称为Verilog-1995[1]。然后IEEE开始着手扩充Verilog,使之同时支持验证和设计,2001年,标准1364-2001[2],或称Verilog-2001发布。一年后,IEEE发布1364.1-2002 Verilog RTL Synthesis[3]标准,正式定义了Verilog-2001中可综合的子集。

IEEE后来也升级Verilog标准到1364-2005,又称Verilog-2005[4]。然而,集成电路的功能、复杂度和时钟速率在2000年后演化的是如此迅速,以至于Verilog标准的增量更新已经不足以跟上硬件模型和验证测试对语言能力所持续增长的需求。IEEE为增强Verilog语言而定义的新特性越来越多,以至于有必要为其分配一个新的标准编号描述语言的扩展内容,即1800-2005,又称SystemVerilog [5]SystemVerilog并非是一种全新的语言 —— 它只是在Verilog-2005基础上提供了一组扩展。 分成两份文档的原因之一是为了帮助那些供应Verilog仿真器和综合编译器的公司能专注实现所有的新功能。

然后是混乱的名称演变…… 2009年,IEEE将Verilog 1364-2005和SystemVerilog扩展(1800-2005)合并为一个文档。出于作者一直无法理解的原因,IEEE选择停用原来的Verilog名称,而将合并后的新标准改称为SystemVerilog。从此,原始的1364 Verilog标准结束了,IEEE批准了1800-2009,即SystemVerilog-2009[6]作为一个完整的硬件设计和验证语言。在IEEE的术语里,Verilog标准已经不复存在,现在只有SystemVerilog标准。2009年以来,你再也没有使用过Verilog…… 而是使用SytemVerilog进行设计和综合工作!(IEEE随后发布了SystemVerilog-2012标准,其中包括了对原始的、现已不复存在的Verilog标准的额外增强)

Figure 1. Verilog到SystemVerilog的发展

值得注意的是,SystemVerilog同时扩展了Verilog在硬件建模和测试验证的能力。Figure 1是语言的发展路线,虽不完善,但是也足以说明SystemVerilog对原始Verilog的大量扩展增强了硬件建模能力。本文的重点就是这些语言结构是如何综合的,以及使用这些SystemVerilog扩展在硬件设计中的优势。

本文的目的是提供一个在Synopsys Desgin Compiler和/或Synplify-Pro中可综合部分的全面的清单。重点是SystemVerilog增加的那部分组件,以及作为用户如何利用这些改进。为完整起见,本文也会提及不同版本Verilog中的可综合组件,但不会过多讨论其细节。

还应该注意的是,现在没有一份关于SystemVerilog综合的标准。IEEE选择不更新1364.1以反映
SystemVerilog中新增的可综合扩展。作者认为这是短视的,同时也是在损害工程社区,但是也系希望本文与旧的1364.1-2002 Verilog综合标准结合使用,能作为一个SystemVerilog可综合子集的官方标准。

2 数据类型

注意:本文使用的术语“值集”(Value Sets)用于表示二态值(0, 1)和四态值(0, 1, X, Z)。而术语“数据类型”是一个通用术语,泛指所有的线网类型、变量类型和用户定义类型。在IEEE正式的SystemVerilog标准[7]中,这两个术语的用法与此处不同,这是因为标准文档主要是面向提供仿真器或者综合编译器一类软件工具的公司和组织。标准中使用的术语,诸如“类型”(types)、“对象”(objects)和“类别”(kinds),对于工具的实现者具有特殊含义,但是作者认为它们对于使用SystemVerilog的工程师们既不通俗也不直观。

2.1 值集

原始Verilog中仅存在四态值,即向量中的每个比特可以取值0, 1, X, Z。SystemVerilog在此基础上增加了表示二态值的能力,即每个比特只能取值0或1。相应地,增加了新的关键字bitlogic分别用于表示二态值和四态值。SystemVerilog中的线网类型,如wire,只能使用logic的四态值集。对变量而言,四态值类型和二态值类型都是可用的。(对于仿真器和综合编译器的实现者而言,这两个关键字还有更多的含义,不过对于理解如何通过SystemVerilog进行模拟设计而言已经足够了。)

关键字bitlogic也可以在不明确定义线网类型或变量类型的情况下使用,此时具体是线网还是变量是通过上下文自动推导得到的。关键字bit总是被推导为变量类型,而大多数情况下,关键字logic会被推导为变量类型,而在与关键字inputinout一起使用时,才会被推导为线网类型。下面的声明展示了这些推导规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
module A;
...
endmodule

module M (
// 带推导的模块端口声明
input i1, // 推导为四态线网
input logic i2, // 推导为四态线网
input bit i3, // 推导为二态变量
output o1, // 推导为四态线网
output logic o2, // 推导为四态变量
output bit o3 // 推导为二态变量
);

// 带推导或明确声明的内部信号
bit clock; // 推导为二态变量
logic reset; // 推导为四态变量
logic [7:0] data; // 推导为四态变量
wire [7:0] n1; // 显式声明为现网类型,推导为四态logic
wire logic [7:0] n2; // 显式声明为四态线网
var [7:0] v1; // 显式声明为变量,推导为logic
var logic [7:0] v2; // 显式声明为四态变量
...
endmodule

重要: 在综合中,bitlogic是等同的,四态和二态值集仅用于仿真,对综合无意义。

  • SystemVerilog的优势之一:不再需要担心模块端口应该声明为wirereg(更明确些,现网类型还是变量类型)。在SystemVerilog中,你可以将模块端口和局部信号都声明为logic,它们将会被正确地推导为合适的线网类型或者变量类型(可能偶有例外,工程师有时也会希望明确地使用和推导结果不同的类型,但是此种例外较少见)。

注意验证代码略有不同。在testbench中,随机生成的变量应该被声明为bit(二态)而非logic(四态)。关于在设计与验证代码中二态和四态类型的详细讨论,见Sutherland[8]

2.2 线网类型

可综合的线网类型有:

  • wiretri:允许并支持多驱动的互联线网
  • supply0supply1:分别为带有常量0和常量1的互联线网
  • wandtriandwortrior:将多个驱动相与(AND)或相或(OR)的互联线网

由于它们一直都是Verilog的一部分,本文不讨论这些线网类型进行综合时的细节。请参考Verilog RTL综合标准1364.1或者综合编译器的文档以获取更多传统Verilog类型综合的信息。

  • SystemVerilog优势之二(至少它应该算作优势):SystemVerilog同时提供了能对设计工作带来巨大潜在优势的线网类型uwire,但是它现在尚不支持综合。本文的第12章将详细介绍为什么在设计工作中uwire是一个重要的优势。

2.3 变量类型

变量用于过程代码,或称alway块中。Verilog/SystemVerilog均要求过程赋值语句的左侧必须是变量类型。SystemVerilog中可综合的变量类型如下:

  • reg:用户自定义长度的通用四态变量
  • integer:一个32比特的四态变量
  • logic:推导为用户自定义长度的通用四态变量,除了位于模块的input/inout端口上
  • bit:推导为用户自定义长度的通用二态变量
  • byteshortintintlongint:长度分别为8比特、16比特、32比特和64比特的二态变量

reginteger早已是Verilog标准的一部分,本文不再详细讨论。

logic严格意义上不能算是变量类型,但是在绝大多数上下文中,它通常被推导为reg类型。因此它可以被用于替代reg,使语言推导出变量。

byteshortintintlongint只可用于存储二态值。综合会将其视作具有相同向量长度的四态reg变量。注意: 由于综合过程不回为耻二态的行为,因此可能会存在仿真和综合后功能不匹配的风险。一个潜在的区别是,二态变量在综合中每个比特的初始值总是0,而综合实现时每个比特可能是0也可能是1。

  • 建议: 在绝大多数声明中使用logic,使语言基于上下文自动推导该声明的具体类型。避免在RTL模型中使用二态类型。这些类型可能会掩盖某些设计问题(见Sutherland[9]),而且可能会导致仿真和综合的不匹配。一个例外是在for循环中使用int变量作为迭代器。

2.4 向量声明(压缩数组)

向量由方括号内明确的比特范围声明,后面跟随向量名。范围的声明形式为[most-significant_bit_number : least-significant_bit_number]。其中最高有效位(msb)和最低有效位(lsb)可以是任何数字,最高有效位可以是二者间最大或最小的数字。

wire  [31:0] a;   // 32位向量,小端序
logic [1:32] b; // 32位向量,大端序

向量的声明、位选以及部分选择(多比特选择)一直是Verilog标准的一部分,且可综合。Verilog-2001新增了对变量的部分选择,同样可综合。

SystemVerilog称向量为压缩数组,以表示向量是有多个连续存储的比特构成的数组。其中一个重要改进是,SystemVerilog允许使用多个范围声明将向量划分为多个子域。例如:

logic [3:0][7:0] a;   // 32比特向量,划分为4个8比特的子域

a[2] = 8’hFF; // 对子域2赋值
a[1][0] = 1’b1; // 选择子域1中的单个比特

多维压缩数组及其选择操作是可综合的。在设计中需要频繁访问某个向量的子域时,该特性会十分有用。这一特性会很有用。例如在上面的例子中,这一特性就使得我们能够更加容易地从32比特的向量中选择字节。

2.5 数组(非压缩数组)

SystemVerilog允许为线网类型、变量类型和用户定义类型(见2.6节)声明一维或多维数组。数组的维度声明在数组名之后。例如:

logic [7:0] LUT [0:255];              // 256字节的一维数组
logic [7:0] RGB [0:15][0:15][0:15]; // 多字节的三维数组

使用索引访问数组中的元素:

data = LUT[7];          // 访问索引为7的字节
RGB[0][0][0] = 8’h1A; // 为索引为0, 0, 0的字节赋值

Verilog的数组和选择操作时可综合的。

SystemVerilog对Verilog数组进行了多个方面的扩展,其中有一些对复杂设计建模非常重要。这些将在下文中讨论。

2.5.1 C语言风格的数组声明

Verilog数组由数组的地址范围声明,语法是[first_addr : last_addr],如[0:255]。SystemVerilog则允许使用和C语言相同的方式,只声明数组的大小。如:

logic [7:0] LUT [256];          // 256字节的一维数组
logic [7:0] RGB [16][16][16]; // 多个字节构成的三维数组

当使用这种语法是,数组寻址总是从0开始,结束于数组的大小减一。这个方便的小改动是可综合的。

2.5.2 数组复制

Verilog只允许在单次操作中访问一个数组元素。如果要拷贝一个数组,则需要使用循环遍历数组的每个元素。SystemVerilog则允许通过单个语句拷贝数组,无论是整个数组还是数组的一部分都可以如此操作,例如:

logic [31:0] big_array    [0:255];  // array with 256 32-bit elements
logic [31:0] small_array [0:15]; // array with 16 32-bit elements assign

small_array = big_array[16:31]; // copy 16 elements of big_array

数组拷贝要求操作数具有相同的维度,以及每个维度上操作的元素数量相同。每个元素的位宽也必须相同,或者是兼容的数据类型。该操作是可综合的,也可以有效降低将块数据移动到其他数组的设计的复杂度。

2.5.3 将值列表赋值给数组

数组的全部或者多个元素可以由{}包裹的值列表(list of values)赋值。该列表中的元素可以是独立的数组元素,也可以是数组的默认值。

logic [7:0] a, b, c;
logic [7:0] d_array [0:3]; // array with 4 32-bit elements

always_ff @(posedge clock or negedge rstN)
if (!rstN) d_array <= ’{default:0}; // reset all elements of the array
else d_array <= ’{8’h00, c, b, a}; // load the array

数组列表是可综合的。数组的维数和数组列表中值得数量必须匹配。数组中每个元素的位宽必须与列表中的每个值相同。

2.5.4 将数组传递给模块端口、任务和函数

可以为数组中的多个元素赋值的能力也使得使用数组作为模块端口,以及任务或者函数的入参成为可能。下面的例子定义了一个用于表示8*256,位宽为32比特的二维数组的用户自定义类型,然后将其作为函数的入参和返回值,以及模块端口。2.6节将会讨论用户自定义类型的更多细节,4.1节将会讨论用户自定义类型的合适用法。

typedef logic [31:0] d_array_t [0:7][0:255];

module block_data (input d_array_t d_in, // input is an array
output d_array_t q_out, // output is an array
input logic clock, rstN);

function d_array_t transform (input d_array_t d); // input is an array
// ... perform operations on all elements of d
return d; // return is an array
endfunction

always_ff @(posedge clock or negedge rstN)
if (!rstN) q_out <= ’{default:0}; // reset entire q_out array
else q_out <= transform(d_in); // transform and store entire array
endmodule

注意SystemVerilog要求通过模块端口或者任务、函数参数传递的值必须拥有相同的维数,它们的每个元素必须拥有相同的向量大小和兼容类型。

2.5.5 数组查询系统函数

SystemVerilog提供了丰富的系统函数,使操作非硬编码大小的数组变得更容易。可综合的数组查询函数有:$left()$right()$low()$high()$increment()$size()$dimensions()以及$unpacked_dimensions()。下面是一些简单使用它们的例子:

typedef logic [31:0] d_array_t [0:15][0:15];

function d_array_t transform (input d_array_t d);
for (int i = $low(d,1); i <= $high(d,1); i++) begin: outer_loop
for (int j = $low(d,2); j <= $high(d,2); j++) begin: inner_loop
// ... perform some sort of operation on each element of d
end: inner_loop
end: outer_loop
return d; // function return is an array
endfunction

注意:此例可以使用foreach循环进行更进一步的简化。不幸的是,foreach尚且不被DC和Synplify-Pro支持,关于它的更多细节,详见12.2节。

2.5.6 不可综合的数组改进

SystemVerilog以其他几种不可综合的方式扩展了Verilog数组。这些改进包括foreach迭代器循环、数组操作函数、数组定位器函数、数组排序函数以及数组位流转换等。

2.6 用户定义类型

原生Verilog语言只有内建数据类型。SystemVerilog允许设计和验证工程师创建新的,用户自定义的数据类型,且变量和线网都可以由用户自定义类型声明。如果声明中没有明确指定类型,那么它们总是被假定为变量类型。可综合的用户自定义类型如下:

  • enum:具有合法值枚举列表的变量或网络,见2.6.1节;
  • struct:组合多个线网或者变量的结构体,见2.6.2节;
  • union:可以分时容纳不同类型数据的变量,见2.6.3节;
  • typedef:类型定义,见2.6.4节。
2.6.1 枚举类型

枚举类型允许变量和线网被具名值(named valus)的特殊集合定义。本文仅介绍枚举类型的可综合方面。声明枚举类型的基本语法是:

// a variable that has 3 legal values
enum {WAITE, LOAD, DONE} State;

枚举类型一般拥有一个基础数据类型,默认情况下是int(二态值,32比特)。上例中,Stateint类型,而WAITELOADDONE是32比特的int值。枚举列表中的标签均为常量且顺序递增。一般情况下,第一个标签值为逻辑上的0,此后的标签依次递增1。因此,上例中的WAITE值为0,LOAD值为1,而DONE值为2。

设计者可以明确声明基础类型,以允许枚举类型使用特殊的硬件模型。设计者也可以为枚举列表中的部分或者全部标签显式声明特定的值。例如:


// Two 3-bit, 4-state enumerated variables with one-hot values
enum logic [2:0] {
WAITE = 3’b001,
LOAD = 3’b010,
DONE = 3’b100
} State, NextState;

相对于内建的变量和线网类型,枚举类型拥有更健壮的检查规则:

  • 枚举列表中的每个标签的值必须是唯一的;
  • 变量值的大小和标签的值必须相同;
  • 枚举变量仅可通过以下方式赋值:
    • 枚举列表中的标签
    • 拥有相同枚举类型的另一个变量

这些健壮的规则使得枚举类型相比传统的Verilog提供了巨大的优势。下面的两个例子对比了分别由Verilog和SystemVerilog建模的简单状态机。这两个模型均有数个编码错误,注意看注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

// Names for state machine states (one-hot encoding)
parameter [2:0] WAITE=3'b001, LOAD=3'b010, DONE=3'b001; // FUNCTIONAL BUG

// Names for mode_control output values
parameter [1:0] READY=3'b101, SET=3'b010, GO=3'b110; // FUNCTIONAL BUG

// State and next state variables
reg [2:0] state, next_state, mode_control;

// State Sequencer
always @(posedge clock or negedge resetN)
if (!resetN) state <= 0; // FUNCTIONAL BUG
else state <= next_state;

// Next State Decoder (sequentially cycle through the three states)
always @(state)
case (state)
WAITE: next_state = state + 1; // DANGEROUS CODE
LOAD : next_state = state + 1; // FUNCTIONAL BUG
DONE : next_state = state + 1; // FUNCTIONAL BUG
endcase

// Output Decoder
always @(state)
case (state)
WAITE: mode_control = READY;
LOAD : mode_control = SET;
DONE : mode_control = DONE; // FUNCTIONAL BUG
endcase

上例中的六个问题均是合法可综合的,且仿真也可以正常编译和运行。如果顺利的话,验证代码也许可以捕捉到功能问题。综合过程中也许会对一些编码错误提出警告,但一些错误仍会在最终的门级实现中保留。

下面的例子展示了同样的编码错误,但是使用枚举类型替代了Verilog中的parameterreg类型(该例也使用了部分后文介绍的SystemVerilog结构)。注释显示,每个Verilog中被认为是功能问题的地方都变成了语法错误 —— 编译器捕获了这些错误,而不是不得不检测、调试和重新验证这些问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
module bad_fsm_systemverilog_style (...); // only relevant code shown
enum logic [2:0] {WAITE=3'b001, LOAD=3'b010, DONE=3'b001} // SYNTAX ERROR
state, next_state;

enum logic [1:0] {READY=3'b101, SET=3'b010, GO=3'b110} // SYNTAX ERROR
mode_control;

// State Sequencer
always_ff @(posedge clock or negedge resetN)
if (!resetN) state <= 0; // SYNTAX ERROR
else state <= next_state;

// Next State Decoder (sequentially cycle through the three states)
always_comb
case (state)
WAITE: next_state = state + 1; // SYNTAX ERROR
LOAD : next_state = state + 1; // SYNTAX ERROR
DONE : next_state = state + 1; // SYNTAX ERROR
endcase

// Output Decoder
always_comb
case (state)
WAITE: mode_control = READY;
LOAD : mode_control = SET;
DONE : mode_control = DONE; // SYNTAX ERROR
endcase
endmodule

注意:DC无法捕获到上例中的第二个语法错误,但是VCS可以。

SystemVerilog也提供了数个和枚举类型配合使用的方法。可综合的方法有:.first.last.next.prev以及.num。枚举类型的排序基于其声明的顺序(而非标签的值)。上例中的循环逐次状态解码就可以使用这些方法进行简化:

always_comb
next_state = state.next; // transitions from WAITE to LOAD, from LOAD to
// DONE, and from DONE back to WAITE

尽管枚举方法可以在某些情境下简化代码,但是在真实的设计应用它们依然是有某种程度的限制的。本文作者认为使用枚举标签赋值是更好的代码代码风格,而非使用枚举方法。使用枚举标签可以使代码更好地进行自注释,以及提供更灵活的状态机分支。

注意:本文写作时,Synplify-Pro尚不支持枚举方法。

  • SystemVerilog优势之三:枚举类型可以预防一些难以检测和调试的编码错误!在变量和线网只拥有数个合法值得时候,无论何时都应该优先选择枚举类型。
2.6.2 结构体

SystemVerilog的结构体(Structure)提供了一种将多个变量置于一个公共名称的机制。结构体使可综合的,前提是结构体中的成员的类型是可综合的。

struct {
logic [31:0] source_address;
logic [31:0] destination_address;
logic [63:0] data;
logic [3:0] ecc;
} packet;

使用点操作符.可以单独访问结构体中的各个成员:

packet.source_address = 32'h0000dead;

更有用的是,结构体可以作为一个整体读写,如果两个结构体实例的定义相同,则它们可以整体拷贝。这个操作需要使用在2.6.4节介绍的typedef。使用typedef定义的结构体也可以作为一个整体,传递给模块端口、认为或者函数。

结构体中的所有成员可以使用由{}的值列表赋值,该列表可以包含每个成员的值,或者任意成员的默认值。

always_ff @(posedge clock or negedge rstN)
if (!rstN) packet <= ’{default:0}; // reset all members of packet
else packet <= ’{old_addr, new_addr, data_in, ecc_func(data_in)};

默认情况下,结构体的成员以软件工具认为最佳的任何方式存储。大多数时候,这将会产出最佳的仿真和综合质量。设计者可以通过把结构体声明为packed以控制成员的存储方式。声明为packed的结构体将会连续存储所有的成员,此时第一个成员将会位于存储的最左侧,即LSB模式。和packed union(见下节)联合使用时,这是非常有用的特性。

struct packed { // members will be stored contiguously
logic [ 1:0] parity;
logic [63:0] data;
} data_word;
  • SystemVerilog优势之四:使用结构体将所有相关变量聚拢到一起,这个集合可以被整体传输或赋值,这可以有效减少代码行数以及保证一致性。仅在需要在union中使用结构体的时候将其声明为packed
2.6.3 联合体

联合体(Union)允许一块单独的存储空间表现出多种存储格式。SystemVerilog拥有三种类型的联合体:简单联合体(Simple Union)、压缩联合体(Packed Union)和标记联合体(Tagged Union)。只有压缩联合体可以被综合。压缩联合体要求所有成员都是具有相同位数的压缩类型。压缩类型包括位向量(压缩数组)、整数类型和压缩结构体。由于在压缩联合体中所有成员的大小相同,因此将数据写入联合的一个成员(格式),然后从另一个成员读取数据(的操作)是合法的。

下面的例子展示了一个64比特的寄存器,它可以存储一个数据包或者指令包:

union packed {
struct packed {
logic [31:0] data;
logic [31:0] address;
} data_packet;
struct packed {
logic [31:0] data;
logic [31:0] operation;
} instruction_packet;
} packet_u;

always_ff @(posedge clock or negedge rstN)
if (!rstN) packet_u <= {’0, ’0}; // reset
else if (op_type == DATA) packet_u.data_packet <= {d_in, addr};
else packet_u.instruction_packet <= {d_in, instr};

上例最后三行中的赋值语句把两个值拼接后赋值给结构体data_struct或者instruction_packet。由于这两个结构体都声明为packed,因此它是合法且功能正确的。

  • *注意:*将值列表赋值给结构体是合法可综合的,也是推荐的代码风格,见2.1节,但DC并不支持将值列表赋值给联合体中的结构体成员。
2.6.4 类型定义typedef

由内置类型和其他用户自定义类型构成的新的数据类型可以像C语言一样使用typedef,例如:

typedef logic [31:0] bus64_t; // 64-bit bus

typedef struct { // typed structure
logic [ 1:0] parity;
logic [63:0] data;
} data_t;

typedef enum logic {FALSE=1’b0, TRUE=1’b1} bool_t;

module D (input data_t a, b,
output bus64_t result,
output bool_t aok );
// ...
endmodule

SystemVerilog还提供了一种名为package的结构用于封装typedef和其他定义。关于在综合中使用package的更多细节见4.1节

  • SystemVerilog优势之五:用户自定义的类型,即便是最简单的向量类型别名,也可以保证该类型在整个项目中的一致性。使用用户自定义类型可以避免类型和大小的不匹配。

  • 推荐 尽可能使用typedef。即使是项目中一个特定大小的向量类型,例如地址或数据向量,也应该被定义为一种用户自定义类型。所有的typedef应该被封装在package中。

3 参数化建模

Verilog具有使用参数和参数重定义使得模型可配置和可扩展的能力。参数化模型也是Verilog标准的一部分,本文不予讨论。

SystemVerilog扩展了Verilog中参数的定义和重定义,以允许参数化的数据类型,例如:

module adder #(parameter type dtype = logic [0:0]) // 默认为1比特位宽
(input dtype a, b,
output dtype sum);
assign sum = a + b;
endmodule

module top (
input logic [15:0] a, b,
input logic [31:0] c, d,
output logic [15:0] r1,
output logic [31:0] r2);

adder #(.dtype(logic [15:0])) i1 (a, b, r1); // 16位加法器
adder #(.dtype(logic signed [31:0])) i2 (c, c, r2); // 32位有符号加法器
endmodule

参数化数据类型是可综合的。注意SystemVerilog-2009使关键字parameter在模块参数列表#(...)中成为可选项,但是DC依然要求该关键字。

4 共享声明空间 —— packages$unit

4.1 包

原始的Verilog标准中并没有共享的生命空间。每个模块包含所有在此模块中使用的声明。这是Verilog的一个主要的局限性。如果相同的参数、任务或函数定义被多个模块需要,设计者必须采用笨拙而糟糕的方法,通常是配个编译器指令 `ifdef `include。SystemVerilog添加了一系列用户定义类型、面向对象的类定义以及随机约束,这使得缺少共享命名空间的问题成为了一个严峻的问题。

SystemVerilog添加了用户自定义包,以克服Verilog的这个缺点。包提供了一个可以被多个设计模块,也包括验证代码,引用的声明空间。可以在包中出现的可综合项目如下:

  • parameterlocalparam常量的定义
  • const变量定义
  • typedef用户自定义变量
  • 完全自动化的taskfunction定义
  • 引用其他包的import语句
  • 用于包链的export语句

一个包的简单示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package alu_types;
localparam DELAY = 1;

typedef logic [31:0] bus32_t;
typedef logic [63:0] bus64_t;

typedef enum logic [3:0] {ADD, SUB, ...} opcode_t;

typedef struct {
bus32_t i0, i1;
opcode_t opcode;
} instr_t;

function automatic logic parity_gen(input d);
return ^d;
endfunction
endpackage

注意:

  • 在包中定义了的parameter不可以被重定义,localparam同理
  • 综合要求包中定义的taskfunction必须声明为automatic
4.1.1 引用包定义

包内的定义可以被设计块(例如moduleinterface)通过以下三种方式引用:

  • 显式包引用
  • 显式import语句
  • 通配import语句

通过packages::item的方式,可以对包内定义的条目进行显式引用。例如:

module alu
(input alu_types::instr_t instruction, // 端口列表中的引用
output alu_types::bus64_t result );
alu_types::bus64_t temp; // 模块内部的引用
...
endmodule

对包内条目的显式引用不会使之在该模块内其他位置可见。因此每次使用该定义时,都必须显式进行引用。

通过import语句显式导入包内条目。条目一旦被导入,即可在该模块中被多次引用。如:

module alu
import alu_types::bus64_t;
(input alu_types::instr_t instruction, // 显式引用
output bus64_t result ); // bus64_t已被导入
bus64_t temp; // bus64_t已被导入
...
endmodule

通配导入使用*表示包内所有的条目。通配导入使得包内所有条目在模块中均可见。如:

module alu
import alu_types::*;
(input instr_t instruction, // instr_t已被导入
output bus64_t result ); // bus64_t已被导入
bus64_t temp; // bus64_t已被导入
...
endmodule
  • SystemVerilog优势之六: 包可以消除冗余代码,消除不同设计块之间可能存在不匹配的风险,以及减少维护冗余代码的难度。

建议: 现在开始使用包!包提供了干净、简单的方法在整个项目中重用任务、函数以及用户定义类型。

4.1.2 import语句的位置

import语句的位置在前两个例子中是很重关于的。为了在端口列表中引用包内条目的定义,该条目必须在引用前被导入。在SystemVerilog-2005中,import语句只能出现在端口列表之后,这太晚了。SystemVerilog-2009允许import语句出现在端口列表之前,甚至是参数列表之前。这一增强已经发布了三年之久,但是Synopsys仍没有实现这一微小而重要的改进。

注意:DC支持在端口列表前放置import语句,但是需要手动开启set hdlin_sverilog_std_2009。截至本文撰写时,Synplify-Pro仍不支持这一特性。

4.1.3 将一个包导入另一个包内

一个包可以导入或者引用来自其他的包的定义。将包导入其他的包也是可综合的。SystemVerilog也允许包链(Package Chaining),一种简化包间引用的方法。

注意: DC尚不支持包链。关于包链的更多信息,参考第12.6节。

4.1.4 包的编译顺序

SystemVerilog的语法要求包内条目在被引用前必须先被编译。这意味着编译时存在文件依赖顺序,同时也意味着引用了包内条目的模块无法被单独编译 —— 包必须在模块之前被编译(或者预编译,如果工具支持增量编译的话)。

4.2 $unit

在包被添加到标准之前,SystemVerilog还提供了一种不同的机制用于创建多模块共享的定义,即$unit,一个伪全局命名空间。任何在具名声明空间外定义的声明都属于$unit包。在下面的例子中,bool_t的定义在两模块之外,因此它属于$unit声明空间。

typedef enum bit {FALSE, TRUE} bool_t;

module alu (...);
bool_t success_flag;
...
endmodule

module decoder (...);
bool_t a_ok;
...
endmodule

$unit可以包含的用户定义的类型与具名包是相同的,综合限制也相同。

注意: $unit是一个危险的共享命名空间,充斥着各种风险。简单来说,它带来的风险如下:

  1. $unit中的定义可以散落在多个文件中,使其维护如噩梦一般
  2. $unit中的定义分布在多个文件中时,这些文件必须以特定的顺序被编译,以保证每个定义在引用前都被编译
  3. 每次调用编译器都会开启一个新的$unit空间,它不共享其他空间内的声明。因此同时编译多个文件的编译器(如VCS)将看到多个$unit空间,而独立编译每个文件的编译器(如DC)将看到多个独立的$unit空间。
  4. 在同一个命名空间中多次定义相同的名字在SystemVerilog中是非法的。因此如果一个文件在$unit中定义了bool_t,而另一个文件也在$unit中定义了bool_t,并且这两个文件一起编译的话,就会发生编译错误或elaboration错误。
  5. 具名包可以被导入$unit,但是将同一个包多次导入同一命名空间是非法的

建议:应当完全避免使用$unit 通过具名包共享定义可以有效避免$unit带来的风险。


  1. “1364-1995 IEEE Standard Verilog Hardware Description Language”, IEEE, Pascataway, New Jersey. Copyright 1995. ISBN:.1-55937-727-5 ↩︎

  2. “1364-2001 IEEE Standard Verilog Hardware Description Language”, IEEE, Pascataway, New Jersey. Copyright 2001. ISBN: 0-7381-2826-0. ↩︎

  3. “1364.1-2002 IEEE Standard for Verilog Register Transfer Level Synthesis”, IEEE, Pascataway, New Jersey.Copyright 2002. ISBN: 0-7381-3501-1. ↩︎

  4. “P1364-2005 Draft Standard for Verilog Hardware Description Language”, IEEE, Pascataway, New Jersey. Copyright 2005. ISBN: 0-7381-4850-4 ↩︎

  5. “1800-2005 IEEE Standard for SystemVerilog: Unified Hardware Design, Specification and Verification Language”, IEEE, Pascataway, New Jersey. Copyright 2005. ISBN: 0-7381-4811-3. ↩︎

  6. “1800-2009 IEEE Standard for SystemVerilog: Unified Hardware Design, Specification and Verification Language”, IEEE, Pascataway, New Jersey. Copyright 2009. ISBN: 978-0-7381-6129-7. ↩︎

  7. “1800-2012 IEEE Standard for System Verilog: Unified Hardware Design, Specification and Verification Language”, IEEE, Pascataway, New Jersey. Copyright 2013. ISBN: 978-0-7381-8110-3 (PDF), 978-0-7381-8111-0 (print). ↩︎

  8. Sutherland, “I’m Still in Love with My X!”, Design and Verification Conference (DVCon), San Jose, California, February 2013. ↩︎

  9. Sutherland, “I’m Still in Love with My X!”, Design and Verification Conference (DVCon), San Jose, California, February 2013. ↩︎