声明空间

verilog中,reg 、wire、task、function等的定义都必须在module之内,这些声明都是局部的,因此只能在模块内部使用,无法跨模块使用。如果希望在多个模块中使用某个function,则需要在每一个模块内都重新定义。特别是使用user-defined type,需要在每一个模块内都重复进行typedef,非常的冗余,且容易出错。

为此system verilog拓展了声明空间。

package

package的定义

package是一个独立的声明空间,因此不能嵌入在module内部。

package是通过两个关键字进行定义的——packageendpackage可综合package可以包含如下的内容:

  • parameterlocalparam定义的常数(在package中,两个是一样的,都不能在外部被修改)。
  • const定义的变量。
  • typedef定义的自定义类型。
  • automatic taskautomatic function
  • import导入其他package
  • 操作符重载。

package还可以包含全局变量声明、静态任务定义和静态函数定义。然而,这些都是不可综合的。

一个package的实例如下:

1
2
3
4
5
6
7
8
9
10
11
12
package definitions;  
parameter VERSION = "1.1";
typedef enum {ADD, SUB, MUL} opcodes_t;
typedef struct {
logic [31:0] a, b;
opcodes_t opcode;
} instruction_t;
function automatic [31:0] multiplier (input [31:0] a, b);
// code for a custom 32-bit multiplier goes here
return a * b; // abstract multiplier (no error detection)
endfunction
endpackage

package的引用

package可以在moduleinterface中,通过如下四种方式进行引用。

  1. 直接使用名称空间决议运算符(::
  2. package中特定的item通过import导入到moduleinterface
  3. 使用通配符*package中的item通过import导入到moduleinterface
  4. package中的内容import$unit声明空间

名称空间决议运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
module ALU  (
input definitions::instruction_t IW,
input logic clock,
output logic [31:0] result
);
always_ff @(posedge clock) begin
case (IW.opcode)
definitions::ADD : result = IW.a + IW.b;
definitions::SUB : result = IW.a - IW.b;
definitions::MUL : result = definitions::multiplier(IW.a, IW.b);
endcase
end
endmodule

import特定item

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module ALU  (
input definitions::instruction_t IW,
input logic clock,
output logic [31:0] result
);
import definitions::ADD;
import definitions::SUB;
import definitions::MUL;
import definitions::multiplier;
always_comb begin
case (IW.opcode)
ADD : result = IW.a + IW.b;
SUB : result = IW.a - IW.b;
MUL : result = multiplier(IW.a, IW.b);
endcase
end
endmodule

Note

Importing an enumerated type definition does not import the labels used within that definition.

使用通配符导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module ALU  (
input definitions::instruction_t IW,
input logic clock,
output logic [31:0] result
);
import definitions::*; // wildcard import
always_comb begin
case (IW.opcode)
ADD : result = IW.a + IW.b;
SUB : result = IW.a - IW.b;
MUL : result = multiplier(IW.a, IW.b);
endcase
end
endmodule

导入到$unit

1
2
3
4
5
6
7
// import specific package items into $unit 
import definitions::instruction_t;
module ALU (
input instruction_t IW,
input logic clock,
output logic [31:0] result
);

或者使用下面的通配符导入:

1
2
3
4
5
6
7
// import specific package items into $unit 
import definitions::*;
module ALU (
input instruction_t IW,
input logic clock,
output logic [31:0] result
);

导入到$unit注意事项

文件导入顺序

如果在A文件中使用import导入声明到$unit中,而B文件不使用import语句来导入。这时候,只有B文件在A之后导入,B中才能使用导入的声明,如果在A之间被导入,则为报错(或者使用隐式声明)。

每个文件都import
虽然每个文件都是用import导入声明到$unit,可以解决上述的问题。但是,这几个文件同时编译时,会导致重复定义的错误。

解决办法
与c当中的头文件类似,使用宏来防止被重复导入。

一个可行的示例如下:

创建一个名为definitions.pkg的文件,其内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
`ifndef DEFS_DONE // if the already-compiled flag is not set... 
`define DEFS_DONE // set the flag
package definitions;
parameter VERSION = "1.1";
typedef enum {ADD, SUB, MUL} opcodes_t;
typedef struct {
logic [31:0] a, b;
opcodes_t opcode;
} instruction_t;
function automatic [31:0] multiplier (input [31:0] a, b);
// code for a custom 32-bit multiplier goes here
return a * b; // abstract multiplier (no error detection)
endfunction
endpackage
import definitions::*; // import package into $unit
`endif

然后将下面的语句放在每一个需要导入该package的文件开头

1
include "definitions.pkg"

$unit compilation-unit

SystemVerilog语言在Verilog中增加了一个称为compilation-unit(编译单元)的概念——一个编译单元是同时编译的所有源文件。compilation-unit为软件工具提供了一种对整体设计的子块进行单独编译的手段。一个子块可能包含单个模块,也可能包含多个模块。模块可能包含在单个文件中,也可能包含在多个文件中。

SystemVerilog语言扩展了Verilog的声明空间,允许声明在packagemoduleinterfaceprogram block边界之外进行。这些外部声明处于一个compilation-unit范围内,对于同时编译的所有模块都是可见的。

compilation-unit示例

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
31
32
33
34
/******************* External declarations *******************/ 
parameter VERSION = "1.2a"; // external constant

reg resetN = 1; // external variable (active low)

typedef struct packed { // external user-defined type
reg [31:0] address;
reg [31:0] data;
reg [ 7:0] opcode;
} instruction_word_t;

function automatic int log2 (input int n); // external function
if (n <=1) return(1);
log2 = 0;
while (n > 1) begin
n = n/2;
log2++;
end
return(log2);
endfunction

/********************* module definition *********************/
// external declaration is used to define port types
module register (
output instruction_word_t q,
input instruction_word_t d,
input wire clock
);
always @(posedge clock, negedge resetN)
if (!resetN)
q <= 0; // use external reset
else
q <= d;
endmodule

Note

External compilation-unit scope declarations are not global

Tip

A declaration in the compilation-unit scope is not the same as a global declaration. A true global declaration, such as a global variable or function, would be shared by all modules that make up a design, regardless of whether or not source files are compiled separately or at the same time.

数据类型