掌握 Verilog 模块实例化:层次化设计最佳实践
如前文所述,复杂设计通过层次化集成多个模块构建。模块可在其他模块内实例化,这些实例的端口可与父模块内其他信号连接。
端口连接可通过有序列表或按名称进行。
按有序列表连接端口
模块实例化中端口表达式列表与父模块内信号连接的一种方法是有序列表。
mydesign 模块在另一模块 tb_top 中实例化为 d0。端口按特定顺序连接,顺序由模块声明中端口列表位置决定。例如 tb_top 中的 b 连接到设计的 y,仅因两者在端口列表中均处于第二位置。
module mydesign (
input x, y, z, // x at position 1, y at 2, z at 3
output o // o at position 4
);
endmodule
module tb_top;
wire [1:0] a;
wire b, c;
mydesign d0 (a[0], b, a[1], c); // a[0] at pos1 -> x; b at pos2 -> y; a[1] at pos3 -> z; c at pos4 -> o
endmodule
必须知道设计模块端口顺序才能正确连接。
这非常不便,因为新增端口会改变顺序,且端口数量大时极易出错。
按名称连接端口
更好的方式是显式使用端口名称链接两侧端口。
点 . 表示其后的端口名属于被实例化模块。要连接的信号名放在括号 ( ) 中。
module design_top;
wire [1:0] a;
wire b, c;
mydesign d0 (
.x (a[0]), // mydesign 的 "x" 连接到 design_top 的 "a[0]"
.y (b), // mydesign 的 "y" 连接到 design_top 的 "b"
.z (a[1]),
.o (c)
);
endmodule
建议每行写一个端口连接,这样编译错误能准确指示出错行号,比所有连接写在同一行更易调试。
因按名称连接,出现顺序无关紧要。不允许多个模块实例端口连接重复。
module design_top;
mydesign d0 (
.x (a[0]),
.z (a[1]), // z 在第二位置没问题,因显式连接
.y (a[1]),
.x (b), // illegal - x already connected to a[0]
.o (c)
);
endmodule
未连接/悬空端口
实例化模块中未连接任何线网的端口将呈高阻抗值。
module design_top;
mydesign d0 (
// x 是输入且未连接,故 a[0] 将为 Z
.y (a[1]),
.z (a[1]),
.o () // o 在 mydesign 有有效值,但未连接到 design_top 的 c,故 c 为 Z
);
endmodule
示例
以前面的移位寄存器为例,留些端口未连接。
module shift_reg (
input d,
input clk,
input rstn,
output q
);
wire [2:0] q_net;
dff u0 (.d(d), .clk(clk), .rstn(rstn), .q(q_net[0]));
dff u1 (.d(q_net[0]), .clk(clk), .rstn(rstn), .q()); // 输出 q 悬空
dff u2 (.d(q_net[1]), .clk(clk), .rstn(rstn), .q()); // 输出 q 悬空
dff u3 (.d(q_net[2]), .clk(clk), .rstn(rstn), .q(q));
endmodule
注意综合后的 RTL 原理图中,实例 u1 和 u2 的输出未连接。由于 u2 和 u3 的输入 d 现连接到无驱动线网,它们被接地。
仿真中,此类未连接端口通常显示为高阻抗 ('hZ),波形中呈垂直居中的橙线。
所有端口声明隐式为 wire,故端口方向足够。但需存储值的 output 端口应声明为 reg 类型,仅可在 always 和 initial 等过程块中使用。
input 或 inout 端口不能声明为 reg,因为它们持续从外部驱动,不应存储值,而应尽快反映外部信号变化。连接不同向量尺寸的端口合法,但较低向量尺寸生效,另一端口较高宽度的剩余位被忽略。
// Case #1 : Inputs are by default implicitly declared as type "wire"
module des0_1 (input wire clk ...); // wire need not be specified here
module des0_2 (input clk, ...); // By default clk is of type wire
// Case #2 : Inputs cannot be of type reg
module des1 (input reg clk, ...); // Illegal: inputs cannot be of type reg
// Case #3: Take two modules here with varying port widths
module des2 (output [3:0] data, ...); // 4-bit vector output
module des3 (input [7:0] data, ...); // 8-bit vector input
module top ( ... );
wire [7:0] net;
des2 u0 ( .data(net) ... ); // Upper 4-bits of net are undriven
des3 u1 ( .data(net) ... );
endmodule
// Case #4 : Outputs cannot be connected to reg in parent module
module top_0 ( ... );
reg [3:0] data_reg;
des2 ( .data(data) ...); // Illegal: data output port is connected to a reg type signal "data_reg"
endmodule
Verilog
- Verilog 入门:数字电路设计完整教程
- 掌握 Verilog 拼接操作:实用指南
- Verilog 核心要点速览
- 深入理解 Verilog 赋值语句:类型、语法与最佳实践
- Verilog阻塞与非阻塞赋值:关键区别详解
- 掌握Verilog函数:高效RTL设计的复用利器
- 理解Verilog任务:函数vs任务、语法与用法全解析
- 掌握 Verilog 层次引用作用域:避免模块与块中的名称冲突
- Verilog 时钟发生器设计与实现指南
- 掌握 Verilog 数学函数:高效硬件设计的利器
- 掌握 Verilog $timeformat:精度控制与显示格式化
- 理解与打印 Verilog 任意作用域下的 Timescale