目录

  1. 带参数模块的概述
  2. Verilog 模块参数的定义与使用
  3. 带参数的模块例化语法
  4. 带参数模块例化实例
  5. 参考资料

带参数模块的概述

在 Verilog 中,参数(Parameter)是模块内部可配置的常量值。参数通常用于指定模块的配置,如位宽、延迟、计数范围等。通过带参数的模块,我们可以灵活地调整模块的行为,而无需修改模块的代码。

模块参数的主要优点是:

  1. 增加模块的灵活性:通过改变参数值,可以生成不同配置的模块实例,而无需复制模块代码。
  2. 可读性和维护性:参数使代码更加清晰和易于维护,避免了硬编码常量。
  3. 提高可重用性:可以在多个实例之间共享相同的模块定义,只需修改参数来适应不同的需求。

Verilog 模块参数的定义与使用

在 Verilog 中,参数的定义通常放在模块定义的开头。通过关键字 parameter 来声明。

基本语法:

module <module_name> #(parameter <param_name> = <default_value>) (
    input [<size>:0] input_name,
    output [<size>:0] output_name
);
    // 内部逻辑
endmodule

  • <module_name>:模块名称。
  • parameter <param_name> = <default_value>:定义一个名为 param_name 的参数,并给它一个默认值 default_value
  • 通过 #(parameter) 来设置模块的参数。

示例:

module adder #(parameter WIDTH = 8) (
    input [WIDTH-1:0] a, b,
    output [WIDTH-1:0] sum
);
    assign sum = a + b;
endmodule

在上面的例子中,adder 模块有一个参数 WIDTH,它定义了输入和输出信号的位宽。WIDTH 的默认值为 8。


带参数的模块例化语法

在 Verilog 中,带参数的模块可以通过实例化时指定不同的参数值来实现灵活的配置。这种方式使得模块在不同的实例中可以有不同的行为或属性。

语法:

<module_name> #( <parameter_value> ) <instance_name> (
    .<port_name1>(<signal_name1>),
    .<port_name2>(<signal_name2>)
);

  • <module_name>:模块的名称。
  • <parameter_value>:实例化时指定的参数值。
  • <instance_name>:实例化的名称(实例名)。
  • <port_name>:模块的端口名称。
  • <signal_name>:连接到该端口的信号。

示例:

adder #(16) u1 (
    .a(a),
    .b(b),
    .sum(sum)
);

在这个例子中,adder 模块的 WIDTH 参数被设置为 16,即 absum 的位宽是 16。


带参数模块例化实例

示例 1:简单的带参数模块例化

在这个示例中,我们将创建一个带参数的加法器模块,并在顶层模块中实例化多个不同参数配置的加法器。

module top_module(
    input [7:0] a, b,    // 8 位输入
    input [15:0] c, d,   // 16 位输入
    output [7:0] sum_8,  // 8 位输出
    output [15:0] sum_16 // 16 位输出
);
    // 实例化一个 8 位加法器
    adder #(8) u1 (
        .a(a),
        .b(b),
        .sum(sum_8)
    );
    
    // 实例化一个 16 位加法器
    adder #(16) u2 (
        .a(c),
        .b(d),
        .sum(sum_16)
    );
endmodule

module adder #(parameter WIDTH = 8) (
    input [WIDTH-1:0] a, b,  // 动态位宽输入
    output [WIDTH-1:0] sum   // 动态位宽输出
);
    assign sum = a + b;  // 执行加法操作
endmodule

在这个例子中:

  • adder 模块有一个参数 WIDTH,用于定义输入和输出信号的位宽。
  • top_module 中,实例化了两个加法器:一个是 8 位宽的 adder,另一个是 16 位宽的 adder

示例 2:带参数的模块实例化与计算

假设我们有一个带参数的模块 multiplier,它的功能是进行乘法操作,参数指定了输入的位宽。

module top_module(
    input [7:0] a, b,
    input [15:0] c, d,
    output [15:0] product_8, 
    output [31:0] product_16
);
    // 8 位乘法器实例
    multiplier #(8) u1 (
        .a(a),
        .b(b),
        .product(product_8)
    );
    
    // 16 位乘法器实例
    multiplier #(16) u2 (
        .a(c),
        .b(d),
        .product(product_16)
    );
endmodule

module multiplier #(parameter WIDTH = 8) (
    input [WIDTH-1:0] a, b,
    output [2*WIDTH-1:0] product  // 乘积位宽是输入位宽的两倍
);
    assign product = a * b;  // 执行乘法操作
endmodule

在这个例子中:

  • multiplier 模块的参数 WIDTH 用于确定输入信号的位宽,输出信号的位宽是输入位宽的两倍。
  • top_module 中,实例化了两个乘法器:一个是 8 位宽的 multiplier,另一个是 16 位宽的 multiplier

示例 3:参数化计数器

我们可以使用参数化模块来创建不同位宽的计数器。

module top_module(
    input clk,
    input reset,
    output [7:0] count_8,  // 8 位计数器输出
    output [15:0] count_16  // 16 位计数器输出
);
    // 实例化 8 位计数器
    counter #(8) u1 (
        .clk(clk),
        .reset(reset),
        .count(count_8)
    );
    
    // 实例化 16 位计数器
    counter #(16) u2 (
        .clk(clk),
        .reset(reset),
        .count(count_16)
    );
endmodule

module counter #(parameter WIDTH = 8) (
    input clk, reset,
    output [WIDTH-1:0] count
);
    reg [WIDTH-1:0] count_reg;
    
    always @(posedge clk or posedge reset) begin
        if (reset)
            count_reg <= 0;
        else
            count_reg <= count_reg + 1;
    end
    
    assign count = count_reg;
endmodule

在这个例子中:

  • counter 模块的参数 WIDTH 用于确定计数器的位宽。
  • top_module 中,实例化了两个计数器,一个是 8 位宽的计数器,另一个是 16 位宽的计数器。

参考资料