12.2 Overriding module parameter values
覆写模块参数值
定义参数有两种不同的方法。第一个是module_parameter_port_list(见12.1),第二个是作为module_item(见4.10)。模块声明可以包含其中一种或两种类型的参数定义,也可以不包含参数定义。
模块参数可以有类型规范和范围规范。参数覆盖对参数类型和范围的影响应遵循以下规则:
—没有类型或范围说明的参数声明应默认为分配给参数的最终覆盖值的类型和范围。
—具有范围规范但没有类型规范的参数应该是参数声明的范围,并且应该是无符号的。应将覆盖值转换为参数的类型和范围。
—具有类型规范但没有范围规范的参数应具有指定的类型。应将覆盖值转换为参数的类型。有符号的参数应默认为分配给该参数的最终覆盖值的范围。
—具有符号的类型规范和范围规范的参数应该是有符号的,并且应该是其声明的范围。应将覆盖值转换为参数的类型和范围。
例如:
module generic_fifo
#(parameter MSB=3, LSB=0, DEPTH=4) //这些公共参数可以被覆盖
(input [MSB:LSB] in,
input clk, read, write, reset,
output [MSB:LSB] out,
output full, empty );
localparam FIFO_MSB = DEPTH*MSB;
localparam FIFO_LSB = LSB;
// 这些参数是局部的,不能被覆盖。他们通过改变以上公共参数收到影响,模块正常工作
reg [FIFO_MSB:FIFO_LSB] fifo;
reg [LOG2(DEPTH):0] depth;
always @(posedge clk or reset) begin
casex ({read,write,reset})
// implementation of fifo
endcase
end
endmodule
有两种改变非局部参数值的方法:defparam语句,它允许使用参数的层次名称赋值,以及模块实例参数值赋值,它允许在模块实例化期间按行赋值。如果defparam赋值与模块实例参数冲突,则模块中的参数将采用defparam指定的值。模块实例参数值赋值有两种形式,按顺序列表或按名称赋值。接下来的两个子句描述了这两种方法。
有两种参数声明。第一类参数声明有类型和/或范围限定,而第二类没有。当覆盖未类型和未范围参数的值时,参数采用覆盖的大小和类型。
当覆盖类型化和/或范围形参时,新值将转换为目标的类型和大小,并分配给该形参。
例如:
module foo(a,b);
real r1,r2;
parameter [2:0] A = 3'h2;
parameter B = 3'h2;
initial begin
r1 = A;
r2 = B;
$display("r1 is %f r2 is %f",r1,r2);
end
endmodule // foo
module bar;
wire a,b;
defparam f1.A = 3.1415;
defparam f1.B = 3.1415;
foo f1(a,b);
endmodule // bar
参数A是类型化和/或范围参数;因此,在重新定义参数值时,参数保留其原始类型和符号。因此,f1的defparam.A的值为3.1415,通过将浮点数3.1415转换为定点数3来执行,然后将3的低3位赋给A。
参数B不是类型化和/或范围参数;因此,当它的值被重新定义时,参数类型和范围采用新值的类型和范围。因此,f1的defparam.B的值3.1415将B的当前值3’h2替换为浮点数3.1415。
12.2.1 defparam statement
defparam语句
使用defparam语句,可以使用参数的分层名称在整个设计过程中的任何模块实例中更改参数值。层次结构的名称请参见12.5。
但是,在一个生成块实例(见12.4)或一个实例数组(见7.1和12.1.2)的层次结构中,defparam语句不能改变该层次结构之外的参数值。
每个生成块的实例化被认为是一个独立的层次结构作用域。因此,该规则意味着generate块中的defparam语句不能针对同一个generate块的另一个实例化中的参数,即使另一个实例化是由相同的循环generate构造创建的。例如,以下代码是不允许的:
genvar i;
generate
for (i = 0; i < 8; i = i + 1) begin : somename
flop my_flop(in[i], in1[i], out1[i]);
defparam somename[i+1].my_flop.xyz = i ;
end
endgenerate
类似地,实例数组的一个实例中的defparam语句不能修改数组的另一个实例中的参数。
defparam赋值右边的表达式应该是只涉及数字和参数引用的常量表达式。引用的参数(在defparam的右边)应该和defparam语句在同一个模块中声明。
defparam语句对于将所有参数值重写赋值分组到一个模块中特别有用。
在单个参数有多个defparam的情况下,该参数接受源文本中遇到的最后一个defparam语句的值。当在多个源文件中遇到defparam时,例如,通过库搜索找到,参数的defparam值是未定义的。
例如:
module top;
reg clk;
reg [0:4] in1;
reg [0:9] in2;
wire [0:4] o1;
wire [0:9] o2;
vdff m1 (o1, in1, clk);
vdff m2 (o2, in2, clk);
endmodule
module vdff (out, in, clk);
parameter size = 1, delay = 1;
input [0:size-1] in;
input clk;
output [0:size-1] out;
reg [0:size-1] out;
always @(posedge clk)
# delay out = in;
endmodule
module annotate;
defparam
top.m1.size = 5,
top.m1.delay = 10,
top.m2.size = 10,
top.m2.delay = 20;
endmodule
模块注释有defparam语句,它覆盖顶层模块顶部实例m1和m2的大小和延迟参数值。模块top和annotate都被认为是顶级模块。
12.2.2 Module instance parameter value assignment
模块实例参数值赋值
在模块实例中为参数赋值的另一种方法是使用模块实例参数值赋值的两种形式之一。它们是按顺序列表分配和按名称分配。两种模块实例参数赋值方式不得混合使用;对特定模块实例的参数赋值应该完全按顺序或完全按名称赋值。
按顺序列表分配模块实例参数值在外观上类似于给gate实例分配延迟值,按名称分配类似于按名称连接模块端口。它向模块定义中指定的任何参数提供特定模块实例的值。
在命名块、任务或函数中声明的参数只能使用defparam语句直接重新定义。但是,如果参数值依赖于第二个参数,那么重新定义第二个参数将更新第一个参数的值(参见12.2.3)。
12.2.2.1 Parameter value assignment by ordered list
参数值按有序列表分配
通过有序列表进行模块实例参数值赋值的顺序应遵循模块内参数声明的顺序。使用此方法时,没有必要为模块中的所有参数赋值。但是,跳过参数是不可能的。因此,要给模块中声明的参数的子集赋值,组成这个子集的参数的声明应该在其余参数的声明之前。另一种方法是给所有的形参赋值,但是对那些不需要新值的形参使用默认值(在模块定义中的形参声明中赋值的相同值)。
例如:
考虑以下示例,其中模块实例mod_a、mod_c和mod_d中的参数在实例化过程中被更改:
module tb1;
wire [9:0] out_a, out_d;
wire [4:0] out_b, out_c;
reg [9:0] in_a, in_d;
reg [4:0] in_b, in_c;
reg clk;
// testbench clock & stimulus generation code ...
// 通过有序列表的四个vdff实例的参数值赋值
// mod_a 有新的参数值 size=10 and delay=15
vdff #(10,15) mod_a (.out(out_a), .in(in_a), .clk(clk));
// mod_b 有默认参数 (size=5, delay=1)
vdff mod_b (.out(out_b), .in(in_b), .clk(clk));
// mod_c 有一个默认值 size=5 and 一个新的值 delay=12,为了改变delay的值,还需要指定size的(默认)值。
vdff #(5,12) mod_c (.out(out_c), .in(in_c), .clk(clk));
// mod_d 有一个新的参数值 size=10。delay 保持它的默认值
vdff #(10) mod_d (.out(out_d), .in(in_d), .clk(clk));
endmodule
module vdff (out, in, clk);
parameter size=5, delay=1;
output [size-1:0] out;
input [size-1:0] in;
input clk;
reg [size-1:0] out;
always @(posedge clk)
#delay out = in;
endmodule
局部参数不能被重写;因此,在参数值赋值时,它们不被认为是有序列表的一部分。在下面的示例中,addr_width将被赋值为12,data_width将被赋值为16。Mem_size不会因为有序列表而显式赋值,而是因为它的声明表达式而赋值4096。
module my_mem (addr, data);
parameter addr_width = 16;
localparam mem_size = 1 << addr_width;
parameter data_width = 8;
...
endmodule
module top;
...
my_mem #(12, 16) m(addr,data);
endmodule
12.2.2.2 Parameter value assignment by name
参数值按名称赋值
参数名称赋值由参数名称及其新值的显式链接组成。参数的名称应该是在实例化模块中指定的名称。使用此方法时,没有必要为模块中的所有参数赋值。只有被赋予新值的参数才需要指定。参数表达式是可选的,因此实例化模块可以记录参数的存在,而不需要为它赋值任何东西。括号是必需的,在这种情况下,参数保留其默认值。一旦给一个参数赋值,就不能再给这个参数名赋值。
考虑下面的例子,在实例化过程中,mod_a的两个参数以及mod_c和mod_d的一个参数都被改变了:
module tb2;
wire [9:0] out_a, out_d;
wire [4:0] out_b, out_c;
reg [9:0] in_a, in_d;
reg [4:0] in_b, in_c;
reg clk;
// testbench clock & stimulus generation code ...
// 通过名字进行参数赋值的四个vdff实例
// mod_a 有新的参数值 size=10 and delay=15
vdff #(.size(10),.delay(15)) mod_a (.out(out_a),.in(in_a),.clk(clk));
// mod_b 有默认参数值 (size=5, delay=1)
vdff mod_b (.out(out_b),.in(in_b),.clk(clk));
// mod_c 有一个默认参数值 size=5 a和 新的默认值 delay=12
vdff #(.delay(12)) mod_c (.out(out_c),.in(in_c),.clk(clk));
// mod_d 有一个新的参数值 size=10.delay保持默认值
vdff #(.delay( ),.size(10) ) mod_d (.out(out_d),.in(in_d),.clk(clk));
endmodule
module vdff (out, in, clk);
parameter size=5, delay=1;
output [size-1:0] out;
input [size-1:0] in;
input clk;
reg [size-1:0] out;
always @(posedge clk)
#delay out = in;
endmodule
在同一个顶层模块中使用不同类型的参数重定义实例化模块应该是合法的。考虑下面的例子,其中mod_a的参数通过参数重定义排序列表来改变,而mod_c的第二个参数在实例化过程中通过参数重定义名称来改变:
module tb3;
// 实例与位置参数的合法混合,另一个具有命名参数的实例
vdff #(10, 15) mod_a (.out(out_a), .in(in_a), .clk(clk));
vdff mod_b (.out(out_b), .in(in_b), .clk(clk));
vdff #(.delay(12)) mod_c (.out(out_c), .in(in_c), .clk(clk));
endmodule
如下面mod_a的实例化所示,使用混合的按顺序和按名称的参数重定义来实例化任何模块都是非法的:
// 非法混合参数赋值的mod_a实例
vdff #(10, .delay(15)) mod_a (.out(out_a), .in(in_a), .clk(clk));
12.2.3 Parameter dependence
参数依赖性
参数(例如,memory_size)可以用包含另一个参数的表达式来定义(例如,word_size)。然而,覆盖参数,无论是通过defparam语句还是在模块实例化语句中,都会有效地用new表达式替换参数定义。因为memory_size取决于word_size的值,所以修改word_size会改变memory_size的值。例如,在下面的参数声明中,word_size的更新,无论是通过defparam语句还是在定义这些参数的模块的实例化语句中,都会自动更新memory_size。如果memory_size由于defparam或实例化语句而更新,那么它将采用该值,而不管word_size的值是多少。
parameter
word_size = 32,
memory_size = word_size * 4096;