Verilog:如何实例化模块

Verilog:如何实例化模块,verilog,system-verilog,Verilog,System Verilog,如果我有一个Verilog模块“top”和一个Verilog模块“subcomponent”,我如何在top中实例化subcomponent 顶部: 子组件: module subcomponent( input clk, input rst_n, input [9:0] data_rx, output [9:0] data_tx ); 注意 这是一个经常出现的一般性问题,它遵循着这个格式。鼓励添加答案和更新。本指南第23.3.2节一般介绍

如果我有一个Verilog模块“top”和一个Verilog模块“subcomponent”,我如何在top中实例化subcomponent

顶部:

子组件:

module subcomponent(
   input        clk,
   input        rst_n,
   input  [9:0] data_rx,
   output [9:0] data_tx
);
注意

这是一个经常出现的一般性问题,它遵循着这个格式。鼓励添加答案和更新。

本指南第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
本规范第23.3.2.1节对此进行了说明

这有一些缺点,特别是关于子组件代码的端口顺序。这里简单的重构可以破坏连接或改变行为。例如,如果其他人修复了一个bug,并出于某种原因对端口重新排序,请切换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
本规范第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]
);
上述代码的问题是,数据_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,有一些技巧可以避免键入少量字符。我相信它们会阻碍代码的可读性,并使查找bug变得更加困难

使用不带括号的
.port
连接到同名的导线/reg。这看起来很整洁,尤其是在大量时钟和复位的情况下,但在某些级别上,您可能会生成不同的时钟或复位,或者您实际上不希望连接到相同名称但经过修改的信号,这可能会导致肉眼看不到的接线错误

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
本规范第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]
);

这在的第23.3.2.4节中有描述。

请务必检查verilog模式,尤其是verilog auto。这是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*/
实现自动化。查看链接了解更多信息。

答案中没有提到的一件事(实际上问题中也没有提到)是如何使用参数实例化模块。我总是很难记住顺序和语法,因此:

#(.parameter_name(parameter_value),…)(.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));

我在标准中找不到的一条信息(我正在查看一个旧版本)是关于具有空端口列表的实例的:括号是否应该在
subcomponent subcomponent_instance_name()中或省略,如
子组件子组件\u实例\u名称?或者这应该是一个单独的问题?@FriendFX为什么会有一个端口列表为空的模块?早期版本的工具使用需要括号
()示例是芯片的Verilog网络列表,这些芯片上有块实例,但块实例上没有任何逻辑端口,例如去耦电容器或芯片的徽标,它们对功能没有贡献。通过反复试验,我发现旧的工具确实不能只接受
和要求
()。我正在寻找一个明确的参考为这个案件虽然。你的链接已被打破。你知道标准的另一个托管地吗?@gobernador更新了链接,我想你必须注册,但仍然可以免费使用。
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));