如果我有一个Verilog模块'top'和一个verilog模块'子组件',我如何在顶部实例化子组件?
顶
module top(
input clk,
input rst_n,
input enable,
input [9:0] data_rx_1,
input [9:0] data_rx_2,
output [9:0] data_tx_2
);
副成分:
module subcomponent(
input clk,
input rst_n,
input [9:0] data_rx,
output [9:0] data_tx
);
注意
这是一个通用的问题,不断出现,它遵循self-answer格式。鼓励添加答案和更新。
答案 0 :(得分:38)
SystemVerilog IEEE Std 1800-2012的第23.3.2节通常涵盖了这一点。
最简单的方法是在top的主要部分实例化,创建一个命名实例并按顺序连接端口:
module top(
input clk,
input rst_n,
input enable,
input [9:0] data_rx_1,
input [9:0] data_rx_2,
output [9:0] data_tx_2
);
subcomponent subcomponent_instance_name (
clk, rst_n, data_rx_1, data_tx );
endmodule
SystemVerilog IEEE Std 1800-2012的第23.3.2.1节对此进行了描述。
这有一些缺点,特别是关于子组件代码的端口顺序。这里简单的重构可以打破连接或改变行为。例如,如果其他人修复了一个错误并因某种原因重新排序了端口,请切换clk并重置顺序。您的编译器不会出现连接问题,但无法按预期工作。
module subcomponent(
input rst_n,
input clk,
...
因此建议使用命名端口进行连接,这也有助于跟踪代码中线路的连接。
module top(
input clk,
input rst_n,
input enable,
input [9:0] data_rx_1,
input [9:0] data_rx_2,
output [9:0] data_tx_2
);
subcomponent subcomponent_instance_name (
.clk(clk), .rst_n(rst_n), .data_rx(data_rx_1), .data_tx(data_tx) );
endmodule
SystemVerilog IEEE Std 1800-2012的第23.3.2.2节对此进行了描述。
为每个端口提供自己的行并正确缩进会增加可读性和代码质量。
subcomponent subcomponent_instance_name (
.clk ( clk ), // input
.rst_n ( rst_n ), // input
.data_rx ( data_rx_1 ), // input [9:0]
.data_tx ( data_tx ) // output [9:0]
);
到目前为止,所有已建立的连接都重复使用输入和输出到子模块,并且没有创建连接线。如果我们要将输出从一个组件转移到另一个组件会发生什么:
clk_gen(
.clk ( clk_sub ), // output
.en ( enable ) // input
subcomponent subcomponent_instance_name (
.clk ( clk_sub ), // input
.rst_n ( rst_n ), // input
.data_rx ( data_rx_1 ), // input [9:0]
.data_tx ( data_tx ) // output [9:0]
);
这个名义上用作clk_sub的电线是自动创建的,依赖于此存在危险。它默认只会创建1位线。这是一个问题的例子是数据:
请注意,第二个组件的实例名称已更改
subcomponent subcomponent_instance_name (
.clk ( clk_sub ), // input
.rst_n ( rst_n ), // input
.data_rx ( data_rx_1 ), // input [9:0]
.data_tx ( data_temp ) // output [9:0]
);
subcomponent subcomponent_instance_name2 (
.clk ( clk_sub ), // input
.rst_n ( rst_n ), // input
.data_rx ( data_temp ), // input [9:0]
.data_tx ( data_tx ) // output [9:0]
);
上面代码的问题是data_temp只有1位宽,会有关于端口宽度不匹配的编译警告。需要创建连接线并指定宽度。我建议明确写出所有连接线。
wire [9:0] data_temp
subcomponent subcomponent_instance_name (
.clk ( clk_sub ), // input
.rst_n ( rst_n ), // input
.data_rx ( data_rx_1 ), // input [9:0]
.data_tx ( data_temp ) // output [9:0]
);
subcomponent subcomponent_instance_name2 (
.clk ( clk_sub ), // input
.rst_n ( rst_n ), // input
.data_rx ( data_temp ), // input [9:0]
.data_tx ( data_tx ) // output [9:0]
);
转到SystemVerilog,有一些技巧可以节省输入少数字符。我相信它们会妨碍代码的可读性,并且会使查找错误变得更加困难。
使用不带括号的.port
连接到同名的电汇/注册表。这看起来很整洁,尤其是在大量clk和复位时,但在某些级别,您可能会生成不同的时钟或复位,或者您实际上不希望连接到相同名称但已修改的信号,这可能会导致布线错误对眼睛不明显。
module top(
input clk,
input rst_n,
input enable,
input [9:0] data_rx_1,
input [9:0] data_rx_2,
output [9:0] data_tx_2
);
subcomponent subcomponent_instance_name (
.clk, // input **Auto connect**
.rst_n, // input **Auto connect**
.data_rx ( data_rx_1 ), // input [9:0]
.data_tx ( data_tx ) // output [9:0]
);
endmodule
SystemVerilog IEEE Std 1800-2012的第23.3.2.3节对此进行了描述。
我认为另一个比上面那个更糟糕的技巧是.*
,它将未提及的端口连接到同一线路的信号。我认为这在生产代码中非常危险。如果新端口名称在实例化级别中具有计数器部分,则新端口已添加且丢失或者可能意外连接时,它们并不明显,它们会自动连接并且不会生成警告。
subcomponent subcomponent_instance_name (
.*, // **Auto connect**
.data_rx ( data_rx_1 ), // input [9:0]
.data_tx ( data_tx ) // output [9:0]
);
SystemVerilog IEEE Std 1800-2012的第23.3.2.4节对此进行了描述。
答案 1 :(得分:3)
请务必查看verilog-mode,特别是verilog-auto。 http://www.veripool.org/wiki/verilog-mode/这是emacs的verilog模式,但例如vi(m?)存在插件。
可以使用AUTOINST自动化实例化。评论随M-x verilog-auto
一起扩展,之后可以手动编辑。
subcomponent subcomponent_instance_name(/*AUTOINST*/);
扩展
subcomponent subcomponent_instance_name (/*AUTOINST*/
//Inputs
.clk, (clk)
.rst_n, (rst_n)
.data_rx (data_rx_1[9:0]),
//Outputs
.data_tx (data_tx[9:0])
);
隐式线可以使用/*AUTOWIRE*/
自动化。查看链接以获取更多信息。
答案 2 :(得分:1)
答案中没有提到的一件事(实际上也没有在问题中提出)是如何使用参数实例化模块。我总是很难记住顺序和语法,所以这里是:
<component_name> #(.parameter_name( parameter_value ), ... ) <instance_name>(.port_name( wire/reg_name ), ...)
因此,如果您有一个带有参数 N 的模块 fooBar,并且您想创建一个具有不同参数值的 fooBarInstance:
module fooBar #(parameter N = 8) (input [N-1:0] foo, output[N-1:0] bar)
endmodule
// Instantiate fooBar with N=12
fooBar #(.N(12)) fooBarInstance(.foo(fooWire), .bar(barReg));