在Verilog中,parameter
和 localparam
都用于定义常量,但是它们之间有一些重要的区
-
作用范围:
-
parameter:可以在模块外部被修改或重定义。它可以被作为模块的参数传递给其他模块,因此具有较广泛的作用范围,适用于设计中需要在多个地方使用的常量。
-
localparam:只能在定义它的模块内使用,不能被外部修改。
localparam
常用于定义模块内部的常量,这些常量不希望被外部更改。
-
-
可修改性:
-
parameter:是可以在实例化模块时修改的常量。换句话说,当模块被实例化时,可以通过提供不同的参数值来改变其行为。
-
localparam:不能在模块实例化时被修改,它的值只能在模块内部定义,并且始终保持固定。
-
-
默认值:
-
parameter:在模块定义时可以为其指定默认值,但该值可以在模块实例化时被修改。
-
localparam:它的值只能在模块定义时设置,无法在模块实例化时修改。
-
-
典型使用场景:
-
parameter:用于那些可能会根据不同模块实例化的需求而变化的常量,例如控制信号宽度、数据位数等。
-
localparam:用于模块内部需要使用的常量,这些常量不需要暴露给模块的外部使用。比如在实现复杂功能时,常用来定义一些中间计算值。
-
语法示例:
module example (
input wire clk,
input wire rst
);
// parameter可以在实例化时修改
parameter WIDTH = 8;
// localparam不可以在外部修改
localparam DELAY = 5;
reg [WIDTH-1:0] data;
reg [DELAY-1:0] delay_reg;
always @(posedge clk or posedge rst) begin
if (rst) begin
data <= 0;
delay_reg <= 0;
end else begin
data <= data + 1;
delay_reg <= delay_reg + 1;
end
end
endmodule
在上面的例子中,WIDTH
是一个 parameter
,可以在实例化模块时被修改,而 DELAY
是一个 localparam
,它只能在模块内部使用,无法在实例化时被改变。
例化时修改parameter的值
在Verilog中,如果你想在实例化时修改 parameter
的值,你可以在实例化模块时指定一个新的值。localparam
是不能在实例化时修改的,所以只能修改 parameter
。
module adder #(parameter WIDTH = 8) (
input wire [WIDTH-1:0] a, // 输入信号a,位宽由WIDTH决定
input wire [WIDTH-1:0] b, // 输入信号b,位宽由WIDTH决定
output wire [WIDTH-1:0] sum // 输出结果,位宽由WIDTH决定
);
assign sum = a + b; // 进行加法操作
endmodule
例化后
module top;
reg [15:0] a, b; // 定义两个16位输入信号
wire [15:0] sum; // 定义16位输出信号
// 实例化adder模块,修改WIDTH为16
adder #(16) u_adder (
.a(a),
.b(b),
.sum(sum)
);
initial begin
// 给输入信号赋值
a = 16'hAAAA;
b = 16'h5555;
// 输出结果
#10; // 延时,模拟一段时间后输出
$display("a = %h, b = %h, sum = %h", a, b, sum);
end
endmodule
在上面的代码中:
我们实例化了 adder
模块,并在实例化时通过 #(16)
修改了 WIDTH
为 16。这意味着 a
、b
和 sum
信号的位宽都会是 16 位,而不是默认的 8 位。在 initial
块中,给 a
和 b
信号赋值,进行加法操作,最后输出结果。
修改多个parameter
底层代码:
module multiplier_adder #(parameter WIDTH_A = 8, // 输入A的位宽
parameter WIDTH_B = 8, // 输入B的位宽
parameter OP_MODE = 0 // 操作模式:0 表示加法,1 表示乘法
) (
input wire [WIDTH_A-1:0] a,
input wire [WIDTH_B-1:0] b,
output wire [WIDTH_A-1:0] sum, // 输出加法结果
output wire [WIDTH_A-1:0] product // 输出乘法结果
);
// 加法操作
assign sum = a + b;
// 乘法操作
assign product = (OP_MODE == 1) ? (a * b) : 0; // 只有在OP_MODE为1时进行乘法计算
endmodule
实例化模块并修改多个 parameter
的值
接下来,我们将修改 WIDTH_A
、WIDTH_B
和 OP_MODE
的值来定制模块的行为(按顺序的方式进行修改)。
module top;
reg [15:0] a, b;
wire [15:0] sum, product;
// 实例化multiplier_adder模块,修改WIDTH_A为16,WIDTH_B为8,OP_MODE为1(乘法模式)
multiplier_adder #(16, 8, 1) u_multiplier_adder (
.a(a),
.b(b),
.sum(sum),
.product(product)
);
initial begin
a = 16'hAAAA; // a的值为16位的十六进制数
b = 8'h55; // b的值为8位的十六进制数
#10;
$display("a = %h, b = %h, sum = %h, product = %h", a, b, sum, product);
end
endmodule
命名参数实例化
module top;
reg [15:0] a, b; // 定义16位输入信号
wire [15:0] sum, product;
multiplier_adder #(
.WIDTH_A(16), // 指定WIDTH_A为16
.OP_MODE(1), // 指定OP_MODE为1(乘法)
.WIDTH_B(8) // 指定WIDTH_B为8
) u_multiplier_adder (
.a(a),
.b(b),
.sum(sum),
.product(product)
);
initial begin
a = 16'hAAAA;
b = 8'h55;
#10;
$display("a = %h, b = %h, sum = %h, product = %h", a, b, sum, product);
end
endmodule
总结:
顺序修改:通常情况下,parameter
的值是按顺序修改的,传递的顺序必须和模块定义中的 parameter
顺序一致。
命名参数:为了不按照顺序修改参数,可以使用命名参数(parameter_name(value)
)的方式指定每个 parameter
的值,这样可以确保修改的准确性,并且使代码更具可读性。