SV中$unit编译单元

本文介绍了SystemVerilog中的编译单元($unit)概念,解析了编译单元域的搜索规则,并通过实例展示了函数查找的优先级。同时,讲解了使用`ifndef`和`define`进行条件编译,以避免重复导入包,确保代码正确性。内容涉及SystemVerilog的包导入、模块定义以及仿真过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SV $unit编译单元

SV中增加了编译单元的概念,就是SV源文件编译的时候,一起编译的所有源文件

  • 当多个源文件一起编译时,只有一个$unit域,包导入$unit域中的所有子项对这些源文件都是可见的
  • 当多个源文件单独编译时,会产生多个$unit域,一个$unit中导入的包对其他$unit是不可见的

1. 编译单元域搜索规则

编译单元域在搜索顺序中排第三位

举个栗子

package a_dpk;  //创建一个名为a_dpk的包
    function print();
        $display("\t This is in a_dpk!");
    endfunction
endpackage

package b_dpk;  //创建一个名为b_dpk的包
    function print();
        $display("\t This is in b_dpk!");
    endfunction
endpackage

import b_dpk:: *; //导入$unit编译单元域

module test_tb;
    
    import a_dpk:: *; //通配符导入

    function print();
        $display("\t This is in module!");
    endfunction

    initial begin
        $display("\t**********************");
        print;
        $display("\t***********End*********");
    end
endmodule

这里我们创建了两个包(a_dpk和b_dpk),两个包中的都只有一个打印函数,同时module中也有一个打印函数,(打印函数用于指示所在位置)

打印结果如下:

image-20211216201944989

从打印结果可以看出,当三者都存在时,打印的是module中声明的函数

那么如果把module中声明的函数注释掉会打印哪个函数呢?

package a_dpk;  //创建一个名为a_dpk的包
    function print();
        $display("\t This is in a_dpk!");
    endfunction
endpackage

package b_dpk;  //创建一个名为b_dpk的包
    function print();
        $display("\t This is in b_dpk!");
    endfunction
endpackage

import b_dpk:: *; //导入$unit编译单元域

module test_tb;
    
    import a_dpk:: *; //通配符导入

    //function print();
    //    $display("\t This is in module!");
    //endfunction

    initial begin
        $display("\t**********************");
        print;
        $display("\t***********End*********");
    end
endmodule

运行结果如下:

image-20211216203025254

可以看到此时打印的是在module中导入的包

那么接下来我们把在module中导入的包注释掉,即把import a_dpk::*;注释掉

package a_dpk;  //创建一个名为a_dpk的包
    function print();
        $display("\t This is in a_dpk!");
    endfunction
endpackage

package b_dpk;  //创建一个名为b_dpk的包
    function print();
        $display("\t This is in b_dpk!");
    endfunction
endpackage

import b_dpk:: *; //导入$unit编译单元域

module test_tb;
    
   // import a_dpk:: *; //通配符导入

    //function print();
    //    $display("\t This is in module!");
    //endfunction

    initial begin
        $display("\t**********************");
        print;
        $display("\t***********End*********");
    end
endmodule

下面运行结果:

此时终于把导入$unit编译单元域的内容打印出来了

通过这个过程不难发现,在运行过程中,先从module内部找有没有声明,如果没有声明再从module内部import的包找,如果还是没有才会在module外部import的包中找

这也就是为什么 编译单元域在搜索规则中排第三

那么为什么编译单元域叫$unit呢?我们可以再把代码更改一下

package a_dpk;  //创建一个名为a_dpk的包
    function print();
        $display("\t This is in a_dpk!");
    endfunction
endpackage

//package b_dpk;  //创建一个名为b_dpk的包
//    function print();
//        $display("\t This is in b_dpk!");
//    endfunction
//endpackage

import b_dpk:: *; //导入$unit编译单元域

module test_tb;
    
   // import a_dpk:: *; //通配符导入

    //function print();
    //    $display("\t This is in module!");
    //endfunction

    initial begin
        $display("\t**********************");
        print;
        $display("\t***********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

运行结果如下


本文主要参考

  1. 《SystemVerilog硬件设计及建模》
  2. 人人都会用到,但是大部分人不清楚是什么的“神秘空间” (baidu.com)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值