平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“Verilog语言关键字模块例化实例讲解”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
关键字:例化,generate,全加器,层次访问
落到代码里,在一个模块中引用另一个模块,对其端口进行相关连接,叫做模块例化。模块例化建立了描述的层次。信号端口能够借助位置或名称关联,端口连接也必须遵循一些规则。
命名端口连接
落到代码里,这种方法将需例化的模块端口与外部信号按照其名字进行连接,端口顺序随意,能够与引用 module 的声明端口顺序不一致,只要保证端口名字与外部信号匹配即可。
下面是例化一次 1bit 全加器的例子:
full_adder1 u_adder0(
.Ai (a[0]),
.Bi (b[0]),
.Ci (c==1'b1 ? 1'b0 : 1'b1),
.So (so_bit0),
.Co (co_temp[0]));
从实现思路看,若某些输出端口同时不需在外部连接,例化时 能够悬空不连接,甚至删除。一般来说,input 端口在例化时不能删除,否则编译报错,output 端口在例化时能够删除。比如:
//output 端口 Co 悬空
full_adder1 u_adder0(
.Ai (a[0]),
.Bi (b[0]),
.Ci (c==1'b1 ? 1'b0 : 1'b1),
.So (so_bit0),
.Co ());
//output 端口 Co 删除
full_adder1 u_adder0(
.Ai (a[0]),
.Bi (b[0]),
.Ci (c==1'b1 ? 1'b0 : 1'b1),
.So (so_bit0));
顺序端口连接
理解这一步时,这种方法将需例化的模块端口按照模块声明时端口的顺序与外部信号进行匹配连接,位置要严格保持一致。比如例化一次 1bit 全加器的代码能够改为:
full_adder1 u_adder1(
a[1], b[1], co_temp[0], so_bit1, co_temp[1]);
实际处理时,虽然代码从书写上可能会占用相对较少的空间,但代码可读性降低,也不易于调试。有时候在大型的设计中可能会有很多个端口,端口信号的顺序时不时的可能也会有所改动,此时再借助顺序端口连接进行模块例化,显然是不便于的。所以平时,建议采用命名端口方式对模块进行例化。
端口连接规则
输入端口
结合项目来看,模块例化时,从模块外部来讲, input 端口能够连接 wire 或 reg 型变量。这与模块声明是不同的,从模块内部来讲,input 端口必须是 wire 型变量。
输出端口
在这个场景下,模块例化时,从模块外部来讲,output 端口必须连接 wire 型变量。这与模块声明是不同的,从模块内部来讲,output 端口能够是 wire 或 reg 型变量。
输入输出端口
从实现思路看,模块例化时,从模块外部来讲,inout 端口必须连接 wire 型变量。这与模块声明是相同的。
悬空端口
实际处理时,模块例化时,如果某些信号不需与外部信号进行连接交互,我们能够将其悬空,即端口例化处保留空白即可,上述例子中有提及。
output 端口正常悬空时,我们甚至能够在例化时将其删除。
理解这一步时,input 端口正常悬空时,悬空信号的逻辑功能表现为高阻状态(逻辑值为 z)。但是,例化时一般不能将悬空的 input 端口删除,否则编译会报错,比如:
//下述代码编译会报Warning
full_adder4 u_adder4(
.a (a),
.b (b),
.c (),
.so (so),
.co (co));
//如果模块full_adder4有input端口c,则下述代码编译是会报Error
full_adder4 u_adder4(
.a (a),
.b (b),
.so (so),
.co (co));
理解这一步时,一般来说,建议 input 端口不要做悬空处理,无其他外部连接时赋值其常量,比如:
full_adder4 u_adder4(
.a (a),
.b (b),
.c (1'b0),
.so (so),
.co (co));
位宽匹配
实际处理时,例化端口与连续信号位宽不匹配时,端口会借助无符号数的右对齐或截断方式进行匹配。
理解这一步时,假如在模块 full_adder4 中,端口 a 和端口 b 的位宽都为 4bit,则下面代码的例化结果会导致:u_adder4.a = {2'bzz, a[1:0]}, u_adder4.b = b[3:0]。
full_adder4 u_adder4(
.a (a[1:0]), //input a[3:0]
.b (b[5:0]), //input b[3:0]
.c (1'b0),
.so (so),
.co (co));
端口连续信号类型
落到代码里,连接端口的信号类型能够是,1)标识符,2)位选择,3)部分选择,4)上述类型的合同时,5)用来输入端口的表达式。
从实现思路看,当然,信号名字能够与端口名字一样,但他们的意义是不一样的,分别代表的是 2 个模块内的信号。
用 generate 进行模块例化
落到代码里,例化多个相同的模块时,一个一个的手动例化会比较繁琐。用 generate 语句进行多个模块的重复例化,可大大简化程序的编写过程。
重复例化 4 个 1bit 全加器组成一个 4bit 全加器的代码如下所示:
module full_adder4(
input [3:0] a , //adder1
input [3:0] b , //adder2
input c , //input carry bit
output [3:0] so , //adding result
output co //output carry bit
);
wire [3:0] co_temp ;
//第一个例化模块一般格式有所差异,需要单独例化
full_adder1 u_adder0(
.Ai (a[0]),
.Bi (b[0]),
.Ci (c==1'b1 ? 1'b1 : 1'b0),
.So (so[0]),
.Co (co_temp[0]));
genvar i ;
generate
for(i=1; i<=3; i=i+1) begin: adder_gen
full_adder1 u_adder(
.Ai (a[i]),
.Bi (b[i]),
.Ci (co_temp[i-1]), //上一个全加器的溢位是下一个的进位
.So (so[i]),
.Co (co_temp[i]));
end
endgenerate
assign co = co_temp[3] ;
endmodule
`timescale 1ns/1ns
module test ;
reg [3:0] a ;
reg [3:0] b ;
//reg c ;
wire [3:0] so ;
wire co ;
//简单驱动
initial begin
a = 4'd5 ;
b = 4'd2 ;
#10 ;
a = 4'd10 ;
b = 4'd8 ;
end
full_adder4 u_adder4(
.a (a),
.b (b),
.c (1'b0), //端口可以连接常量
.so (so),
.co (co));
initial begin
forever begin
#100;
if ($time >= 1000) $finish ;
end
end
endmodule // test
仿真结果如下所示,可知 4bit 全加器工作正常:

层次访问
从实现思路看,每一个例化模块的名字,每个模块的信号变量等,都采用一个特定的标识符进行定义。在整个层次设计中,每个标识符都具有唯一的位置与名字。
结合项目来看,Verilog 中,借助采用一连串的 . 符号对各个模块的标识符进行层次分隔连接,就能够在任何地方借助指定完整的层次名对整个设计中的标识符进行访问。
层次访问多见于仿真中。
从实现思路看,比如,有以下层次设计,则叶单元、子模块和顶层模块间的信号就能够相互访问。
//u_n1模块中访问u_n3模块信号:
a = top.u_m2.u_n3.c ;
//u_n1模块中访问top模块信号
if (top.p == 'b0) a = 1'b1 ;
//top模块中访问u_n4模块信号
assign p = top.u_m2.u_n4.d ;

实际处理时,以上就是Verilog语言关键字模块例化实例讲解的详细内容,更多关于Verilog语言关键字模块例化的资料请关注脚本之家其它相关文章!
- Verilog语言数据类型基础教程
- Verilog语言表达式基本采用
- Verilog 8 种编译指令采用详解
- Verilog关键词的多分支语句实例详解
- Verilog关键词的条件语句实例详解
- Verilog语言的循环语句示例详解
- Verilog 设计方法及流程详解