在 Verilog 模块之间传递参数

McK*_*igo 3 verilog parameter-passing vivado

我对 Verilog 还很陌生,正在学习入门知识。我有一些代码生成一个 8 位向上计数器(模块counter.v ),然后由顶部模块( top_module.v )调用。有一个模拟测试夹具(test_fixture.v)调用顶层模块进行测试。

我试图使用参数(参数COUNTER_WIDTH)定义计数器的宽度,但遇到困难。一位同事为我修复了代码,现在它确实可以工作,但我想了解一些事情,以便我能够了解实际发生的情况。

这是计数器模块的代码:

module counter
#(parameter COUNTER_WIDTH = 8)
(
    input wire CLK,
    input wire RST,
    input wire CE,
    output reg[COUNTER_WIDTH-1:0] out = {COUNTER_WIDTH{1'b0}}
    );


   always @(posedge CLK) begin
      if (RST == 1) begin
         out <= {COUNTER_WIDTH{1'b0}};
      end else begin
      if (CE == 1) begin
         out <= out + 1'b1;
      end
    end
  end
endmodule
Run Code Online (Sandbox Code Playgroud)

顶层模块:

module top_module
#(parameter COUNTER_WIDTH = 8)
(
    input wire CLK,
    input wire CE,
    input wire RST,
    output wire[COUNTER_WIDTH-1:0] out
    );

counter #(
    .COUNTER_WIDTH(COUNTER_WIDTH)
    )
counter_inst(
    .CLK(CLK),
    .RST(RST),
    .CE(CE),
    .out(out)
);

endmodule
Run Code Online (Sandbox Code Playgroud)

以及测试夹具:

module test_fixture();

parameter COUNTER_WIDTH = 8;

// inputs
reg CLK = 0;
reg RST = 0;
reg CE = 0;

// outputs
wire [COUNTER_WIDTH-1:0] Q;

// instance of top module to be tested
top_module #(
    .COUNTER_WIDTH(COUNTER_WIDTH)
) 

test_inst(
    .CLK(CLK),
    .RST(RST),
    .CE(CE),
    .out(Q)
);
endmodule
Run Code Online (Sandbox Code Playgroud)

我认为我对计数器模块很好,但对顶部模块/测试装置中发生的情况有疑问:

  • 看起来参数 COUNTER_WIDTH 在每个模块中声明(#(参数 COUNTER_WIDTH = 8)),然后“连接到”(如果这是正确的表达式)另一个模块中的参数声明(例如 #(.COUNTER_WIDTH(COUNTER_WIDTH) ) )
  • 这种理解正确吗?如果是这样,为什么我们必须在模块内声明参数并将其连接到另一个模块内的参数?

在此先感谢您的帮助!

Mat*_*lor 5

将参数视为一种特殊的常量输入,其值在编译时固定。最初,在 Verilog 中,参数是可以从模块外部覆盖的常量(使用现已弃用的defparam语句)。因此,Verilog 参数有两个作用:

  1. 局部常数
  2. 一种从外部定制模块的方法

然后,在 2001 版标准 (IEEE 1364-2001) 中,引入了您在下面使用的语法(所谓的“ANSI 样式”)。此外,IEEE 1364-2001 还引入了localparams(它满足这两个角色中的第一个角色,因为它们不能从外部覆盖),因此留下参数来处理这两个角色中的第二个角色(自定义)。使用 ANSI 样式语法,您可以在实例化模块时覆盖参数。您可以将参数与任何静态值关联,例如父模块的参数、 a 、 a或文字(代码中的硬编码值)。localparamgenvar

由于这种历史上的双重角色,在 Verilog 中,您必须为参数指定默认值,即使它没有意义。(SystemVerilog 中取消了此限制。)

给参数赋予不同的名称和默认值是否有助于您的理解?

                //    the default value
                //          |
module counter  //          V
#(parameter COUNTER_WIDTH = 1)
(
    input wire CLK,
    input wire RST,
    input wire CE,
    output reg[COUNTER_WIDTH-1:0] out = {COUNTER_WIDTH{1'b0}}
    );


   always @(posedge CLK) begin
      if (RST == 1) begin
         out <= {COUNTER_WIDTH{1'b0}};
      end else begin
      if (CE == 1) begin
         out <= out + 1'b1;
      end
    end
  end
endmodule
Run Code Online (Sandbox Code Playgroud)

然后,在顶部模块中,我们为参数指定一个不同的名称:

                   //    the default value
                   //          |
module top_module  //          V
#(parameter COUNTER_NUM_BITS = 2)
(
    input wire CLK,
    input wire CE,
    input wire RST,
    output wire[COUNTER_NUM_BITS-1:0] out
    );

counter #(
    .COUNTER_WIDTH(COUNTER_NUM_BITS)
    )
counter_inst(
    .CLK(CLK),
    .RST(RST),
    .CE(CE),
    .out(out)
);

endmodule
Run Code Online (Sandbox Code Playgroud)

再次在测试夹具中:

module test_fixture();

localparam COUNTER_SIZE = 8;   // This is not overridden from outside, so using
                               // a localparam would be better here.
                               // (A localparam being a kind of parameter that
                               //  cannot be overridden from outside. Normal 
                               //  languages would call it a constant.)    
// inputs
reg CLK = 0;
reg RST = 0;
reg CE = 0;

// outputs
wire [COUNTER_SIZE-1:0] Q;

// instance of top module to be tested
top_module #(
    .COUNTER_NUM_BITS(COUNTER_SIZE)
) 

test_inst(
    .CLK(CLK),
    .RST(RST),
    .CE(CE),
    .out(Q)
);
endmodule
Run Code Online (Sandbox Code Playgroud)