包 package
为什么引入包?
在verilog中,变量、任务和函数的声明都在module..endmodule之间,声明的对象都是局部的。不能全局声明。如果一个声明在多个设计块中用到,必须在每个块中声明。
Systemverilog可以使用typedef用户自定义类型,会在多个模块中使用这个类型,verilog需要在多个模块中使用这个类型。引入了包的概念。
1.1 包的定义
可以使多个模块共享用户自定义类型。在package和endpackage之间定义。
包中可综合的结构有
①paramater和 localparam 常量定义(不能被重新定义,在包中,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
1.2 引用包的内容
①使用作用域解析操作符 “::”
可以通过包的名称直接引用包,选择包中特定的定义或声明
module ALU(
input definitions::instruction_t IW,
input logic clock,
output logic[31:0] result
);
always_ff @(posedge clock)
case(IW.code)
definitions::ADD:result=IW.a + IW.b;
definitions::SUB:result=IW.a - IW.b;
definitions::MUL:result=definitions::multiplier(IW.a,IW.b);
endcase
endmodule
当包中的一项或者多项在模块中多次引用,每次显式地引用包的名称太麻烦。
②导入包中的特定子项
import语句导入特定子项。
import definitions::ADD;
import definitions::multiplier;
case(IW.code)
ADD:result = IW.a + IW.b;
MUL:result = multiplier(IW.a,IW.b);
endcase
但是导入枚举类型并不导入里面的元素。
例如:import definitions::opcode_t;会使用户定义的类型opcode_t在模块中可见,但是它不会时其使用的枚举元素可见。每个枚举元素必须显式导入。
为此,使用通配符导入更实用。
③包中子项的通配符导入
import definations::*
通配符导入并不能自动导入包中所有内容(只有在模块或接口中实际使用的子项才会被真正导入,没被引用的包中的定义和声明不会被导入)
import definition::*;
always_comb
case(IW.opcode)
ADD:result=;
endcase
对于模块端口IW,包名必须显式引用,因为不能在module和端口定义之间加入一个import语句。
可以使用$unit声明域
综合:
为了能够综合,包中定义的任务和函数必须声明为automatic,并且不能包含静态变量。
$unit编译单元声明
编译单元是同时编译的所有源文件,为软件工具提供了一种对整个设计的各个子块单独编译的方法。
sv允许在包、模块、接口和程序块的外部进行声明,扩展了verilog的声明域。外部声明在“编译单元域”(不可综合!!!)
编辑单元(unit):同时编译的所有文件
编辑单元域($unit域):编译单元在package\module\接口和程序块外部的声明域
编译单元域包含:
(1)时间单位和精度声明
(2)变量声明
(3)net声明
(4)常量声明
(5)用户定义数据类型
(6)任务和函数定义
///外部声明/
parameter VERSION = "1.2a"; //外部常量
reg resetN = 1; //外部变量(低有效)
typedef struct packes{ //外部用户定义类型
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
注:外部编译单元域声明不是全局的
SV的编译单元域只作用于同时编译的源文件。每次编译源文件,就创建一个唯一针对此次编译的编译单元域。
2.1编码建议
①不要在$unit中进行任何声明,而应在package内。
②必要时可以将package导入到$unit中。
未命名语句块中的声明
verilog中允许在命名的begin...end 和 fork...join块中声明局部变量。
局部变量声明的通常用法是声明一个临时变量进行循环控制。避免了对同名但用途不同的模块级变量的无意访问。
![](https://img-blog.csdnimg.cn/img_convert/1d2950a847365549ec6e982e1dd8671c.png)
3.1 未命名块中的局部变量
可以在未命名块中声明变量,被隐藏起来,不会被块以外的代码读取和修改。
![](https://img-blog.csdnimg.cn/img_convert/35f7113f2c7fca1844861440b5ffcbab.png)
仿真时间单位和精度
4.1 verilog编译指令
`timescale 1ns/10ps
4.2 包含时间单位的时间值
SV扩展了Verilog语言,可以给时间值指定时间单位
forever #5ns clock=~clock;
4.3 范围级时间单位和精度
SV允许指定局部性的时间单位和精度,作为模块、接口或程序块的一部分,而不是作为软件工具的指令。
![](https://img-blog.csdnimg.cn/img_convert/2959d49fa21569b7b38ec5477ec31a74.png)
timeunit和timeprecision语句必须在其他任何声明或语句之前、紧随模块,必须先于其他任何声明。