Chapter 2 SystemVerilog 声明的位置
2.1 package
2.1.1 package内容
package中的内容在package和endpackage之间定义,可以包含的可综合结构有:
- parameter和localparam常量定义
- const变量定义
- typedef用户自定义类型
- 全自动task和function定义
- import语句
- 操作符重载定义
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);
return a * b;
endfunction
endpackage
2.1.2 package内容的引用
- 用范围解释操作符直接引用。
definitions::ADD: result = IW.a + IW.b;
- 将package中特定子项导入到模块或接口中。
import definitions::ADD;
- 用通配符导入package中的子项到模块或接口中,通配符导入并不自动导入整个package,只是相当于添加了一条搜索路径。
import definitions::*;
- 将package中子项导入到$unit中。
2.1.3 综合指导
- package中的的任务和函数必须说明为自动的才能被综合,并且不能包括静态变量。自动任务或函数的存储区在每次调用时才会分配,引用自动package中的自动任务或函数的每个模块看到的是不被其它模块共享的唯一副本,保证了综合前后行为相同。
- package中的变量声明是不可综合的!
- 为什么package中的静态函数和变量不可综合?
2.2 $unit编译单元声明
- 编译单元是同时编译的所有源文件。编译单元为软件工具提供了一种对整个设计的各个子块单独编译的方法。
- SystemVerilog可以在package,模块、接口和程序块的外部进行声明,这些外部声明在“编译单元域”中,对所有同时编译的模块都是可见的。
$unit中可包含的结构 | $unit可综合结构 |
---|---|
时间单位和精度声明 | typedef用户定义类型 |
net声明 | 自动函数、任务 |
变量、常量声明 | parameter和localparam常量 |
用户定义数据类型,使用typedef、enum和class | package导入 |
任务和函数 | 共享变量,不可综合 |
静态任务和函数也是不可综合 |
- 编译单元域只作用于同时编译的源文件。每次编译源文件就创建一个唯一仅针对此次编译的编译单元域
- 假定模块CPU和模块controller都引用外部声明的变量reset,考虑以下两种情况:如果两个模块同时编译、如果每个模块分别编译。
parameter VERSION = “1.2a”;
reg resetN = 1;
typedef struct packed {
reg [31:0] address;
reg [31:0] data;
reg [31:0] opcode;
} instruction_word_t;
function automatic int log2(input int n);
if (n < 1) return(1);
log2 = 0;
while (n > 1) begin
n = n / 2;
log2 ++;
end
return(log2);
endfunction
module register(
output instruction_word_t q;
input instruction_word_t d;
input wire clock);
always @(posedge clock, negedge resetN)
if (!resetN) q <= 0;
else q <= d;
endmodule
- 第(2)种情况第二次编译时看不到第一次编译中的reset声明,可能编译失败,也可能使reset成为隐式的net,如果是后一种情况,那么就有了两个叫reset的不同信号
2.2.1 编码指导
- 不要在$unit空间进行任何声明,所有共享的声明都要在package中进行
- 需要时可以将package导入到$unit中
- 如果声明分散在多个文件中,会使代码结构混乱、逻辑性差、难于调试、产生错误
2.2.2 标识符搜索规则
- 搜索按IEEE1364 Verilog标准定义的局部声明
- 搜索通配导入到当前作用域的package中的声明
- 搜索编译单元域中的声明
- 搜索设计层次中的声明,遵循IEEE Verilog搜索规则
2.2.3 源代码顺序
module parity_gen(input wire [63 :0] data);
parity = ^data;
endmodule
reg parity;
module parity_check(input wire[63 : 0] data,
output logic err);
assign err = (^data != parity);
endmodule
编译不会有错,但两个模块中的parity其实并不相同,第一个是隐式的线网,第二个是$unit变量。
2.2.4 将package导入$unit原则
- package也可以通过通配符导入到$unit域中。通配符导入只是将package加到SystemVerilog源路径中。
- 这样就可以保证后续的模块可以在端口声明时直接使用package中的内容。
- 将package导入到$unit同样会遇到在$unit中进行声明和定义时的问题
文件编译顺序信赖性 |
---|
多文件编译与单文件编译 |
每个文件使用导入语句(同时编译时非法!!) |
条件编译 |
2.3 未命名语句块中的声明
- 在命名块中声明的变量可以用包含块名的层次路径引用,层次化引用不可综合,通常在验证程序使用。
$display(“chip.loop.i = %0d”, chip.loop.i);
- SystemVerilog扩展了Verilog,允许在未命名块中声明变量,语法与在命名块中声明相同,未命名块中声明的变量不能被层次化引用。未命名块中声明的变量虽然不能被层次化引用,但软件工具会给未命名块一个推断名,以便于波形显示工具能引用未命名块中的局部变量!
2.4 仿真时间单位和精度
2.4.1 编译指令`timescale
- 编译指令`timescale包括两部分:时间单位和时间精度,时间精度表明仿真时时间的最小取值。`timescale对文件编译顺序有依赖性。
2.4.2 SystemVerilog时间单位和精度
- timeunit和timeprecision使模块、接口或程序块与时间单位和精度信息直接绑定,解决了timescale存在的不确定性和对文件顺序的依赖性。它必须在其它声明或语句之前,紧随模块、接口或程序的声明之后。
2.4.3 编译单元的时间单位和精度
- timeunit和timeprecision的声明可以在编译单元域中,但必须在其它声明的前面。
- 时间单位和精度搜索次序:
- 如果时间值带时间单位,使用指定的单位
- 使用在模块、接口和程序块内部指定的时间单位和精度
- 使用父模块或接口指定的时间单位和精度
- 使用有效的`timescale时间单位和精度
- 使用在编译单元域中定义的时间单位和精度
- 使用仿真器默认的时间单位和精度
timeunit 1ns; //外部声明的时间单位和精度
timeprecision 1ns;
module my_chip(…);
timeprecision 1ps; //局部精度
always @(posedge data_request) begin
#2.5 send_packet; // 使用外部单位和局部精度
#3.75ns check_crc; //使用指定的单位
end
……
endmodule
`timescale 1ps/1ps //优先于外部声明
module FSM();
timeunit 1ns; //优先于`timescale的指定
always @(state) begin
#1.2 case(state) //使用局部单位和`timescale的精度
WAIT #20ps ……; //使用此处指定的单位
end
endmodule