【问题标题】:Passing parameters between Verilog modules在 Verilog 模块之间传递参数
【发布时间】:2020-03-18 09:37:51
【问题描述】:

我对 Verilog 还很陌生,并且正在学习其中的技巧。我有一些代码生成一个 8 位向上计数器(模块 counter.v),然后由顶级模块(top_module.v)调用。有一个模拟测试夹具(test_fixture.v)调用顶层模块进行测试。

我试图使用参数 (parameter 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

顶层模块:

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

还有测试夹具:

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

我认为我对计数器模块没问题,但对顶级模块/测试夹具中发生的事情有疑问:

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

提前感谢您的帮助!

【问题讨论】:

    标签: parameter-passing verilog vivado


    【解决方案1】:

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

    1. 一个局部常量
    2. 一种从外部自定义模块的方法

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

    因为这个历史上的双重角色,在 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
    

    然后,在顶层模块中,让我们给参数一个不同的名字:

                       //    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
    

    再次在测试夹具中:

    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
    

    【讨论】:

    • 谢谢,这澄清了我的理解。我是否正确地说在每个模块中都声明了参数(带有值)以确保模块具有 some 值可以使用,但是这些随后通过将参数连接到另一个参数来覆盖另一个模块的实例?
    • 只是在花时间玩代码后跟进我自己的评论...参数值向下传递(从 test_fixture,到 top_module,到 counter),而不是向上,这是有道理的。因此,如果在 test_fixture 中将计数器大小设置为 32,并在其他地方设置为 8,那么您最终会得到 32 位输出。如果将 counter 子模块中的计数器大小设置为 32,在其他地方设置为 8,则最终会得到 8 位输出。再次感谢您的帮助,马修。
    • @McKendrigo 我已经编辑了我的答案以添加更多关于参数的信息,例如为什么它们必须具有默认值以及你可以将它们“连接”到什么(我会说“关联”它们) .
    【解决方案2】:

    为什么我们必须在一个模块中声明参数并将其连接到另一个模块中的参数?

    因为,当您实例化模块时,您不必给参数一个值。 在这种情况下,这些工具需要至少有一些价值才能使用。

    那么你在哪里做:

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

    你也可以使用:

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

    在这种情况下使用默认值。

    【讨论】:

    • 感谢您的回复,我可以仔细检查我的理解是否正确吗?在我的原始帖子以及上面显示的第一个代码块中,在 top_module 中首先声明了参数 COUNTER_WIDTH (#(parameter COUNTER_WIDTH = 8),然后将其连接到计数器中的同名参数。 v 模块,它将值覆盖为 counter.v 模块中的任何值。在第二个示例中,因为它没有连接到 counter.v 中的参数声明,所以它保留“默认值”,这就是之前声明过。对吗?
    • 是的,在我工作的公司中,默认值将由设计师设置为最“明智的”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-15
    相关资源
    最近更新 更多