【路科V0】systemVerilog基础10——包的使用

包的定义

        如果两个模块(例如A、B模块)重名,就会有命名冲突的问题,尽管对于这两个同名模块,编译的时候不会出现错误 ,但是下一个编译的模块会覆盖上一个编译的模块。

 在大型的验证项目中,很容易出现模块重名的情况。怎么处理?

  • 对于重名的硬件模块:将它们置入到不同编译的库中。
  • 对于重名的软件类、方法等:将它们置入到不同的包中。

包(package)的使用情况

 1、我们可能使用不同的验证IP,我们也无法预测这些类名是否可能重名。 通过包(package)可以将关联的类和方法并入到同一个逻辑集合中。

 2、为了使得可以在多个模块(硬件)或者类(软件)之间共享用户定义的类型SV添加了包(package) 。 (该点与库的概念类似,但是我们无法在代码中容易的指出调用哪个库的类型。但是SV很容易支持调用哪个包的类型)

包的定义语法:

package
. . .
endpackage

        用户自定义的类型譬如类、方法、变量、结构体、枚举类等都可以在包中定义。 

示例:

        这个包在编译时, 会将其中所有的类型一同编译到definition包中,而这个包最终被编译到某一个库中。

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

导出包的内容

module、interface、 class等可以使用包中定义或者声明的内容。

(1)导出包的方法 :可以通过域的索引符::号直接引用。

        直接通过索引,使用definition包中的  参数、结构体类型。

definitions::parameter
definitions::instruction_t inst

        这种使用方式,在代码较多时,较为繁琐,一般在使用直接引用方式时,是为了特意强调某些类型或者某些变量来自于某一个包。避免了重名,以及方便代码阅读  

(2)指定索引一些需要的包中定义的类型到指定的域中。

        先将类型导出到指定的类中,再加以使用。

        使用import将需要的类型从包中导出到指定的域,以供代码中的类型识别和使用

module M;
    import definitions::instruction_t;
    instruction_t inst;
endmodule

 (3)通过通配符*来将包中所有的类别导入到指定的域中。(该方法使用简便、易用)

        如果包中定义的类型比较多,且需要导出的类型比较多时,通过通配符* ,将包中所有类型导出到指定的域中。

module M;
    import definitions::*;
    instruction_t inst;
endmodule

举例 

(1)可以通过域的索引符::号直接引用。

module ALU(input definitions::instruction_t lw,
    input logic clock,
    output logic [31:0] result
    );
  always_ff @(posedge clock) begin
     case (lw.opcode)
        definitions::ADD : result = lW.a + lW.b;
        definitions::SUB : result = lW.a - lW.b;
        definitions::MUL : result =
        definitions::multiplier(lW.a,lW.b);
     endcase
    end
endmodule

(2)可以指定索引一些需要的包中定义的类型到指定的域中。

从包中导入了枚举类型ADD  SUB   MUL,在下面就使用这些变量。 

module ALU(...);
    import definitions::ADD;
    import definitions::SUB;
    import definitions: :MUL;
    import definitions::multiplier ;
    always_comb begin
        case (lW.opcode)
            ADD : result = lW.a + lW.b;
            SUB : result = lW.a - lW.b;
            MUL : result = multiplier(lW.a,lW.b);
       endcase
endmodule

(3)通过通配符*来将包中所有的类别导入到指定域中。

        导入所有类型,只要在 ALU模块中使用任何包中的类型,都可以识别

module ALU ( ... ) ;
    import definitions::*; //通配符导入always_comb 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

包的应用

       在验证MCDT的环境中,包含两个来自channel和arbiter的模块验证包chnl_pkg和arb_pkg。

它们分别定义了同名的 stimulator、 monitor  、 chker 、 env类型。

       然而这两个package中同名的类,它们的内容是不相同的,实现的也是不同的功能。

  channel和arbiter的验证工程师package定义是这样的:

package chnl_pkg;
    `include "stimulator.sv"
    `include "monitor.sv"
    `include "chker.sv"
    `include "env.sv"
endpackage

package arb_pkg ;
    `include "stimulator.sv"
    `include "monitor.sv"
    `include "chker.sv"
    `include "env.sv"
endpackage

         由于我们将这些重名的类归属到不同的package中编译,这样如果要使用不同package中的同名类,只需要注明要使用哪一个package中的。

module mcdf_tb;
    chnl_pkg::monitor mon1 = new() ;
     arb_pkg::monitor mon2 = new() ;
endmodule
//在mcdf_tb模块中,monitor分别来自 chnl_pkg和arb_pkg
//尽管它们同名,但是借助于将它们集合在不同的包内,这避免了类型名冲突的问题

包的命名规则

          尽管之前的方法可以解决类型名冲突的问题,但是由于类名本身有冲突,使得在引用类的时候,不得不通过直接索引的方式。这在从两个包中 导出多个类型的时候,不方便。

       在实际代码中建议,包中的类型名称带有包名的前缀 。        

#更新前的代码
package chnl_pkg;
    `include "stimulator.sv"
    `include "monitor.sv"
    `include "chker.sv"
    `include "env.sv"
endpackage

package arb_pkg ;
    `include "stimulator.sv"
    `include "monitor.sv"
    `include "chker.sv"
    `include "env.sv"
endpackage

#包中的类型名称带有包名的前缀
#更新后的代码

package chnl _pkg;
    'include "chnl_stm.sv"
    'include "chnl_mon.sv"
    'include "chnl_chk.sv"
    'include "chnl_env.sv"
endpackage

package arb_pkg;
    'include "arb_stm.sv"
    'include "arb_mon.sv"
    'include "arb_chk.sv"
    'include "arb_env.sv"
endpackage

#引用两个monitor时,就不用担心类名冲突的问题
#通过通配符* ,将包中所有类型导出到指定的域中。使用更简便
module mcdf_tb;
    import chnl_pkg::* ;
    import arb_pkg:: *;
    chnl_mon mon1= new () ;
    arb_mon mon2 = new () ;
endmodule

包与库的区分

         从这个简单的例子来看,package这个容器可以对类型做一个隔离的作用。

        package的意义在于将软件(类、类型、方法等)封装在不同的域中,以此来与全局的域进行隔离。

        库是编译的产物,硬件(module、interface、program)都会编译到库中,如果不指定编译库的话,会被编译进入默认的库中。

        库可以容纳硬件类型,也可以容纳软件类型,例如类、方法和包。

        包只能容纳软件类型例如类、方法和参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值