SystemVerilogunit编译单元介绍
SV $unit 编译单元
SV中增加了编译单元的概念,就是 SV源文件编译的时候,一起编译的所有源文件 当多个源文件一起编译时,**只有一个unit域中的所有子项对这些源文件都是可见的 当多个源文件单独编译时,**会产生多个unit中导入的包对其他$unit是不可见的 1. 编译单元域搜索规则
编译单元域在搜索顺序中排第三位
举个栗子 package a_dpk; //创建一个名为a_dpk的包 function print(); $display(" This is in a_dpk!"); endfunction endpackage package b_dpk; //创建一个名为b_dpk的包 function print(); $display(" This is in b_dpk!"); endfunction endpackage import b_dpk:: *; //导入$unit编译单元域 module test_tb; import a_dpk:: *; //通配符导入 function print(); $display(" This is in module!"); endfunction initial begin $display(" **********************"); print; $display(" ***********End*********"); end endmodule
这里我们创建了两个包(a_dpk和b_dpk),两个包中的都只有一个打印函数,同时module中也有一个打印函数,(打印函数用于指示所在位置)
打印结果如下:
image-20211216201944989
从打印结果可以看出,当三者都存在时,打印的是module中声明的函数
那么如果把module中声明的函数注释掉会打印哪个函数呢? package a_dpk; //创建一个名为a_dpk的包 function print(); $display(" This is in a_dpk!"); endfunction endpackage package b_dpk; //创建一个名为b_dpk的包 function print(); $display(" This is in b_dpk!"); endfunction endpackage import b_dpk:: *; //导入$unit编译单元域 module test_tb; import a_dpk:: *; //通配符导入 //function print(); // $display(" This is in module!"); //endfunction initial begin $display(" **********************"); print; $display(" ***********End*********"); end endmodule
运行结果如下:
可以看到此时打印的是在module中导入的包
那么接下来我们把在module中导入的包注释掉,即把 import a_dpk::*; 注释掉package a_dpk; //创建一个名为a_dpk的包 function print(); $display(" This is in a_dpk!"); endfunction endpackage package b_dpk; //创建一个名为b_dpk的包 function print(); $display(" This is in b_dpk!"); endfunction endpackage import b_dpk:: *; //导入$unit编译单元域 module test_tb; // import a_dpk:: *; //通配符导入 //function print(); // $display(" This is in module!"); //endfunction initial begin $display(" **********************"); print; $display(" ***********End*********"); end endmodule
下面运行结果:
此时终于把导入$unit编译单元域的内容打印出来了
通过这个过程不难发现,在运行过程中, 先从module内部找有没有声明,如果没有声明再从module内部import的包找,如果还是没有才会在module外部import的包中找
这也就是为什么 编译单元域在搜索规则中排第三
那么为什么编译单元域叫 $unit 呢?我们可以再把代码更改一下package a_dpk; //创建一个名为a_dpk的包 function print(); $display(" This is in a_dpk!"); endfunction endpackage //package b_dpk; //创建一个名为b_dpk的包 // function print(); // $display(" This is in b_dpk!"); // endfunction //endpackage import b_dpk:: *; //导入$unit编译单元域 module test_tb; // import a_dpk:: *; //通配符导入 //function print(); // $display(" This is in module!"); //endfunction initial begin $display(" **********************"); print; $display(" ***********End*********"); end endmodule
我们把 b_dpk 注释掉,但依旧将其导入,看看运行结果
这里直接显示错误在 $unit ,或许$unit只是一个名称,就好像上例中module命名为test_tb 一样2. 单独编译将包导入到$unit中(条件编译)
格式为: `ifndef xxx `define xxx `endif
这是C语言中常用的技巧,如果第一次遇到导入语句将其编译到$unit中,再次出现则不会编译
下面我们将上篇笔记中的包用这种方式仿真一下,先给出上篇笔记中包的内容 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
我们将文件名命名为 definitions.dpk ,其中后缀.dpk 是随便起的`ifndef PACK `define PACK 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 import definitions:: *; //将包导入到$unit中 `endif
下面是源码和测试文件 `include "definitions.dpk" //编译包文件 module ALU ( input instruction_t IW, input logic clk, output logic [31:0] result ); always @ (posedge clk) 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 `include "definitions.dpk" //编译包文件 module ALUtb; instruction_t IW; logic clk = 1; logic [31:0] result; always #10 clk = ~clk; //生成时钟,周期为20ns initial begin IW.a = "d10; IW.b = "d5; IW.opcode = ADD; repeat(2) @(negedge clk); IW.a = "d3; IW.b = "d7; IW.opcode = MUL; repeat(2) @(negedge clk); IW.a = "d5; IW.b = "d1; IW.opcode = SUB; repeat(3) @(negedge clk); $finish; end initial begin $monitor ($time, ,"a->%d, b->%d, opcode->%s, result->%d",IW.a, IW.b, IW.opcode, result); end ALU tb(.IW(IW), .result(result), .clk(clk)); //例化 endmodule
运行结果如下
本文主要参考 《SystemVerilog硬件设计及建模》 人人都会用到,但是大部分人不清楚是什么的"神秘空间" (baidu.com)