数字硬件建模SystemVerilog(十一)-SystemVerilog 包
最初的Verilog语言没有一个可用于多个模块的定义。每个模块都必须有任务、函数、常量和其他共享定义的冗余副本。传统的Verilog编码风格是将共享定义放在一个单独的文件中,然后可以使用“include”编译指令将其包含在其他文件中。该指令指示编译器复制包含文件的内容,并将这些内容粘贴到“include”指令的位置。虽然使用文件包含有助于减少代码冗余,但对于代码重用和维护来说,它是一种笨拙的编码方式。
SystemVerilog将包(packages)添加到最初的Verilog HDL中。包是可以保存共享定义的声明空间。多个模块和接口可以直接引用这些共享定义,或者通过定义导入特定的包项,或者通过导入整个包来引用这些共享定义。包解决了必须在多个模块中复制定义的问题,以及使用xxx将定义复制到多个模块中的尴尬。
包声明
SystemVerilog包是在关键字package和endpackage之间定义的。包是一个独立的声明空间。包的概念继承于VHDL。它不嵌入Verilog模块中。包中的定义和声明称为包项。包可以包含的可综合定义包括:
包还可以包含不可综合的验证定义,例如:类(classe)。本文没有介绍验证结构体。
`begin_keywords"1800-2012"//useSystemVerilog-2012keywords
packagedefinitions_pkg;
timeunit1ns/1ns;
parameterVERSION="1.1";
`ifdef_64bit
typedeflogic[63:0]word_t;
`elsif_32bit
typedeflogic[31:0]word_t;
`else//defaultis16bit
typedeflogic[15:0]word_t;
`endif
typedefenumlogic[1:0]{ADD,SUB,MULT}opcodes_t;
typedefstruct{
word_ta,b;
opcodes_topcode_e;
}instruction_t;
functionautomaticword_tmultiplier(inputword_ta,b);
//codeforacustomn-bitmultiplier
endfunction
endpackage:definitions_pkg
`end_keywords
本文后面将讨论示例4-1中所示的枚举enum和结构体struct构造。示例中的word_t用户自定义类型定义位于’ifdef条件编译指令中,该指令将word_t定义为16位向量、32位向量或64位向量。“ifdef构造允许工程师选择调用编译器时要编译的代码。所有使用word_t用户自定义类型的设计模块将使用编译包时选择的word大小。
最佳实践指南4-1 |
---|
对包常量仅使用localparam或const定义。不要在包中使用parameter定义 |
包中定义的parameter与模块中定义的parameter不同。可以为模块的每个实例重新定义模块级parameter常量。无法对包中parameter重新定义,因为它不是模块实例的一部分。在包中,parameter与localparam相同。
使用包项
包中的定义和声明称为包项。模块和接口可以通过四种方式引用包项:
- 通配符导入所有包项
- 显式地导入特定包项
- 明确导入特定包和包项
- 将包项导入$unit声明空间
本节将讨论引用包项目的前三种方法。后面章节将讨论导入$unit声明空间中。
包项目的通配符导入(wildcard import)
模块引用包项的最简单也是最常见的方法是使用通配符导入语句从包中导入所有项。例如:
包名称后面的(::)双冒号是作用域解析运算符。它指示编译器在另一个位置(范围)查找信息——本例中是definitions_pkg包。
星号(*)是通配符标记。通配符有效地将导入的包添加到SystemVeriog使用的搜索路径中。
当SystemVerilog编译器遇到标识符(名称)时,它将首先在本地范围内搜索该标识符的定义。本地范围内可以是任务、函数、begin-end块、模块、接口或包。如果在本地范围中找不到标识符定义;接下来,编译器将搜索下一个作用域级别,直到到达模块、接口或包边界。如果未找到标识符定义,则搜索任何通配符导入的包;最后,工具将在$unit声明空间中搜索。通配符导入搜索规则的完整语义规则比这个描述更复杂,并在IEEE 1800 SystemVerilog标准中定义。
示例4-2演示了如何使用通配符导入语句。
`begin_keywords"1800-2012"//useSystemVerilog-2012keywords
modulealu
(inputdefinitions_pkg::instruction_tiw,
inputlogicclk,
outputdefinitions_pkg::word_tresult
);
always_ff@(posedgeclk)begin
case(iw.opcode)
definitions_pkg::ADD:result=iw.a+iw.b;
definitions_pkg::SUB:result=iw.a-iw.b;
definitions_pkg:result=definitions_pkg::
multiplier(iw.a,iw.b);
endcase
end
endmodule:alu
`end_keywords
在本例中,通配符导入的作用是将definitions_pkg包添加到模块的标识符搜索路径。端口列表可以引用instruction_t用户自定义类型,编译器将在包中找到该定义。同样,case语句可以引用opcode使用的枚举数据类型标签,这些标签的定义也将在包中找到。
但是, 当包中的一项或多项需要在模块中多次引用时,每次显式地引用包的名称则太过麻烦了, 我们希望将包中包项导入到设计块中(使用通配符全部导入)。
显式地导入特定包项
SystemVerilog还允许将特定的包项导入模块,而无需将整个包添加到该模块的标识符搜索路径中。
显式地导入特定包项的一般语法为:
importpackage_name::item_name;
例4-3,使用显式地导入将特定的包项带入模块。显式导入比使用通配符导入更简单,也使模块更具自文档性,很容易看出从包中使用了哪些包项。
`begin_keywords"1800-2012"//useSystemVerilog-2012keywords
modulealu
importdefinitions_pkg::instruction_t,
definitions_pkg::word_t;
(inputinstruction_tiw,
inputlogicclk,
outputword_tresult
);
importdefinitions_pkg::ADD;
importdefinitions_pkg::SUB;
importdefinitions_pkg::MULT;
importdefinitions_pkg::multiplier;
always_combbegin
case(iw.opcode)
ADD:result=iw.a+iw.b;
SUB:result=iw.a-iw.b;
MULT:result=multiplier(iw.a,iw.b);
endcase
end
endmodule:alu
`end_keywords
笔记 |
---|
枚举数据类型定义的显式导入不会导入该定义中使用的标签,所以对于枚举数据类型定义的标签也必须显式导入。后面会详细介绍 |
声明包导入的位置
声明包导入的位置语句,无论是通配符导入还是特定包项导入:
- 在模块端口列表之前——包项可以在端口定义和模块内使用。
- 在模块端口列表之后-包项可以在模块内使用,但不能在端口定义内使用。
- 模块定义之外——将包项导入伪全局unit声明空间及其危害。
笔记 |
---|
在SystemVerilog-2009标准中添加了模块端口列表之前声明包导入语句。在SystemVerilog-2005中,声明包导入语句只能出现在端口列表之后,或者出现在unit声明空间中。 |
使用作用域解析运算符直接导入包
作用域解析运算符(::)可用于通过指定包名称和包中的特定项直接引用包项。
示例4-4使用作用域解析运算符引用了前面示例4-1中所示的包中定义的几个包项:
`begin_keywords"1800-2012"//useSystemVerilog-2012keywords
modulealu
importdefinitions_pkg::*;//wildcardimport
(inputinstruction_tiw,
inputlogicclk,
outputword_tresult
);
always_combbegin
case(iw.opcode)
ADD:result=iw.a+iw.b;
SUB:result=iw.a-iw.b;
MULT:result=multiplier(iw.a,iw.b);
endcase
end
endmodule:alu
`end_keywords
显式引用包项有助于记录设计源代码。在示例4-4中,包名的使用使得找到instruction_t,word_t ADD, SUB, MULT 和 multiplier定义的地方变得很明显。但是,对于包项的每次使用,显式引用包名称比较冗长。声明包导入语句的一种更常见的方法是导入整个包,如前文所述。只有当多个包中有同名的定义,并且需要指明要从哪个包导入包项时,才需要本节中显式引用包。
多个包导入
较大的设计项目通常会使用几个包。一个模块或接口可以根据需要从任意多个包中导入。
导入多个包时,注意避免名称冲突。使用通配符导入时尤其如此。在下面的代码片段中,包cpu_pkg和gpu_pkg都有一个名为instruction_t的标识符(包项)。
在这个代码片段中:两个包中都定义了一个instruetion_t标识符,并且两个包都是通配符(::)导入的,变量instruction被声明为instruction_t类型。当仿真器、综合编译器或其他软件工具搜索instruction_t的定义时,它会在两个通配符导入的包中找到一个定义,并且不知道使用哪个定义。多个定义将导致编译或细化(Elaboration:是将设计组件绑定在一起的过程。Elaboration包括创建实例化,计算参数值,解析分层名称和连接nets。通常在引用compilation 和 elaboration阶段时,它们不会被区分,但通常直接被称为compilation。换句话说,“编译时错误”可能指的是run-time阶段之前的任何时间的错误。)错误。
当存在多个定义时,源代码必须显式引用或显式导入要在该模型中使用的定义,例如:
显式包引用优先于本地定义或包的显式导入,后者优先于通配符导入。前面代码段中的显式导入解决了processor模块中使用instruction_t定义时的模糊性。
包链
一个包可以显式地从另一个包导入定义,也可以通配符导入另一个包。但是,导入的项目在该包外不会自动可见。考虑下面的例子:
为了让模块alu使用来自两个包的定义,两个包都需要导入到模块alu。SystemVerilog能够链接包,因此模块只需导入链中的最后一个包,即前面代码段中的alu_types_pkg。包链是通过包组合导入和导出语句来完成的。
export语句可以显式导出特定项,或使用通配符导出从另一个包导入所有项。请注意,使用通配符导出时,仅导出包中实际使用的定义。在前面的片段中;base_types_pkg中word32_t的定义未在alu_types_pkg中使用,因此未链接,并且在alu模块中不可用。下面的显式导出可以添加到上面的alu_types_pkg示例中,链接到word32_t,这样它就可以在alu模块中使用。
笔记 |
---|
在写这本文的时候,一些仿真器和综合编译器还不支持包链。包链的export声明是SystemVerilog-2009标准的一部分。SystemVerilog-2005标准没有定义进行包链的方法。 |
包的编译顺序
SystemVerilog要求在引用包定义之前对其进行编译。这意味着编译包和模块时存在文件顺序依赖关系——必须先编译包。这也意味着引用包项的模块不能独立编译。如果工具支持单独的文件编译,则包必须与模块一起编译,或者已经预编译。
确保在引用包或包项的任何文件之前编译包的一种方法是控制编译命令中列出文件的顺序。
文件编译顺序通常由Linux“make”文件控制。也可以使用Verilog命令文件(使用-f调用选项读取)和脚本或批处理文件。
确保在引用包或包项的任何文件之前编译包的另一种方法是使用’include 编译指令指示编译器读取包含包的文件,’include指令放在包含对包项引用的每个设计或测试台文件的开头。
使用“include指令”时需要注意一点,SystemVerilog不允许在同一编译中多次包含同一个包。可以通过放置’ifdef(“if defined-定义”)或’ifndef(“if not defined-未定义”)来实现围绕包定义的条件编译指令,以便编译器跳过已编译的包。条件编译指令允许SystemVerilog源代码根据宏名是否已使用’define指令定义进行选择性编译。
下面的示例使用’ifndef条件编译指令围绕包。当包含包的文件被编译器读入时,“未定义”测试将为真,包将被编译。编译的代码行包含一个’define指令,该指令设置了使用的宏名称。如果在编译器的同一调用过程中再次读取此文件,“未定义”测试将为false,并且将跳过’ifndef和endif之间的代码。
综合条件
为了能够进行综合,包中定义的任务和函数必须声明为自动的(automatic),并且不能包含静态变量。此规则的原因是,综合将在引用包任务或函数的每个模块或接口中复制任务或函数。如果任务或函数在仿真中有静态存储,那么该存储将由任务或函数的所有引用共享,这是一种与复制不同的行为。通过将任务或函数声明为自动的,每次调用它时都会分配新的存储,使其行为与任务或函数的唯一副本相同。这确保了对包任务或函数的预综合引用的仿真行为与后综合行为相同。
出于仿真的原因,综合不支持包中的变量声明。在仿真中,一个包变量由导入该变量的所有模块共享。一个模块可以写入变量,另一个模块将看到新值。这种不通过模块端口传递值的模块间通信是不可综合的。
原文标题:SystemVerilog(十一)-SystemVerilog 包
文章出处:【微信公众号:OpenFPGA】欢迎添加关注!文章转载请注明出处。
-
模块
+关注
关注
7文章
2692浏览量
47426 -
Verilog
+关注
关注
28文章
1351浏览量
110067 -
函数
+关注
关注
3文章
4326浏览量
62558
原文标题:SystemVerilog(十一)-SystemVerilog 包
文章出处:【微信号:Open_FPGA,微信公众号:OpenFPGA】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论