SystemVerilog package
在Verilog中,由于不能进行全局声明,因此当一个声明在多个设计块中用到,那么这个声明必须在每个块中进行声明,这样不仅显得冗余,而且如果在其中一个块中对声明进行了更改,却忘记修改其他设计块中出现的该声明,就会导致错误
基于这一点,SystemVerilog增加了包(package)
1. 包的定义
package是为了使多个块共享用户定义类型的定义
package定义在package
和endpackage
之间
package中可综合的结构有一下几种:
- parameter和localparam常量定义
- const常量定义
- typedef用户定义类型
- 全自动task和function定义
- 从其他package中import语句
- 操作符重载定义
package中可以进行全局变量声明、静态任务定义和静态函数定义
package是一个独立的声明空间,不需要包含在Verilog模块中
下面是一个简单的package定义的例子:
package definitions;
parameter version = "1.1";
typedef enum {ADD, SUB, MUL} opcodes_t;
typedef struct {
logic [31:0] a, b;
opcodes_t opcode; //声明的opcode中包含 ADD, SUB和MUL
} instruction_t;
function automatic [31:0] multiplier (input [31:0] a, b);
return a * b;
endfunction
endpackage
在Verilog中,parameter常量可以重新定义,localparam常量不能重新定义
在package中,parameter常量和localparam常量均不可重新定义
2. 应用package的内容
引用package的方式有如下四种:
- 用范围解析操作符直接引用
- 将package中特定子项导入到模块或接口中
- 用通配符导入包中的子项到模块或接口中
- 将包中子项导入到
$unit
声明域中
2.1 作用域解析操作符引用package
作用域解析操作符为: :
package名和package中子项名有双冒号 (: 😃 隔开
借用上例中定义的package,用作用域解析操作进行引用:
module ALU (
input definitions: : instruction_t IW, //用IW代表上例中instruction_t
input logic clk,
output logic [31:0] result
);
always_ff @ (posedge clk) begin
case (IW.opcode) //IW即可代表上例中instruction_t
definitions: : ADD : result = IW.a + IW.b;
definitions: : SUB : result = IW.a - IW.b;
definitions: : MUL : result = definitions: : multiplier(IW.a, IW.b); //调用上例中的function
endcase
end
endmodule
2.2 导入package中特定子项
SV允许用import
语句将package中特定子项导入到模块中
import
语句是package中子项局部可见,导入的package定义或声明就和模块或接口中的一个局部定义名一样
通过导入特定子项的方法,将上例进行简化:
module ALU (
input definitions: : instruction_t IW, //用IW代表上例中instruction_t
input logic clk,
output logic [31:0] result
);
import definitions: : ADD;
import definitions: : SUB;
import definitions: : MUL;
import definitions: : multiplier;
always_ff @ (posedge clk) begin
case (IW.opcode) //IW即可代表上例中instruction_t
ADD : result = IW.a + IW.b;
SUB : result = IW.a - IW.b;
MUL : result = multiplier(IW.a, IW.b); //调用上例中的function
endcase
end
endmodule
导入特定子项时,对于导入枚举类型只会导入枚举类型定义并不会导入那个定义使用的元素
例如import definitions: : opcode_t;
,这个导入不会起作用
为了局部引用,每个枚举元素必须显示导入
2.3 通配符导入
通配符为*
通配符可以使package中所有子项都成为可见的,但通配符并不会自动导入整个package,只有实际使用的子项才真正被导入
下面使用通配符将上例简化:
module ALU (
input definitions: : instruction_t IW, //用IW代表上例中instruction_t
input logic clk,
output logic [31:0] result
);
import definitions: : *; //通配符导入
always_ff @ (posedge clk) begin
case (IW.opcode) //IW即可代表上例中instruction_t
ADD : result = IW.a + IW.b;
SUB : result = IW.a - IW.b;
MUL : result = multiplier(IW.a, IW.b); //调用上例中的function
endcase
end
endmodule
$unit声明域内容较多,单独记录
本文主要摘自《SystemVerilog硬件设计与建模》