相关阅读
Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm=1001.2014.3001.5482
参数(parameter)一般用于定义常数,常用于进行可配置的参数化设计中,本文将对参数的使用进行详细介绍。
首先来看看参数的BNF范式(语法),有关BNF范式相关内容,可以参考之前的文章。
图1 参数的定义
参数可分为两大类:局部参数和普通参数,分别由关键词local_parameter和parameter声明。它们俩的区别在于,局部参数不允许使用defparam语句或在模块实例化时(在elaboration阶段)进行参数覆盖,普通参数在满足一定条件时允许参数覆盖。需要注意的是参数代表常量,在运行时修改它们的值是非法的。
list_of_param_assignments是一个逗号分隔的赋值列表(这允许在一条语句中定义多个参数),其中赋值的右侧应该是常量表达式,只包含常数和之前已定义的参数。
localparam的声明位置
local_parameter可以在下面这些位置声明:
1、模块中
module example_module (
input wire clk,
input wire rst,
output wire [3:0] out
);
localparam integer WIDTH = 4; // 在模块中声明localparam
localparam [WIDTH-1:0] MAX_VALUE = 15;
reg [WIDTH-1:0] counter;
always @(posedge clk or posedge rst) begin
if (rst) begin
counter <= 0;
end else if (counter < MAX_VALUE) begin
counter <= counter + 1;
end else begin
counter <= 0;
end
end
assign out = counter;
endmodule
2、命名块中
module example_module (
input wire clk,
input wire rst,
output wire [3:0] out
);
reg [3:0] counter;
always @(posedge clk or posedge rst) begin
if (rst) begin
counter <= 0;
end else begin
// 命名块
begin: COUNTER_LOGIC
localparam integer MAX_COUNT = 10; // 在命名块中声明localparam
if (counter < MAX_COUNT) begin
counter <= counter + 1;
end else begin
counter <= 0;
end
end
end
end
assign out = counter;
endmodule
3、任务中
module example_module;
reg [3:0] value;
task example_task;
localparam integer MAX_VALUE = 10; // 在任务内部声明localparam
input reg [3:0] in_value;
output reg [3:0] out_value;
begin
if (in_value < MAX_VALUE) begin
out_value = in_value + 1;
end else begin
out_value = 0;
end
end
endtask
initial begin
value = 5;
example_task(value, value);
end
endmodule
4、函数中
module example_module;
reg [3:0] input_value;
reg [3:0] output_value;
function [3:0] example_function;
localparam integer MAX_VALUE = 8; // 在函数内部声明localparam
input [3:0] in_value;
begin
if (in_value < MAX_VALUE) begin
example_function = in_value + 1;
end else begin
example_function = 0;
end
end
endfunction
initial begin
input_value = 5;
output_value = example_function(input_value);
$display("Output Value: %d", output_value);
end
endmodule
5、在生成块中
module example_module (
output reg [7:0] out
);
generate
if (1) begin: GEN_BLOCK_LOW
localparam integer WIDTH = 8; // 在生成块中声明localparam
always @(*) begin
out = WIDTH;
end
end else begin: GEN_BLOCK_HIGH
localparam integer WIDTH = 16; // 在生成块中声明另一个localparam
always @(*) begin
out = WIDTH;
end
end
endgenerate
endmodule
parameter的声明位置
parameter可以在下面这些位置声明:
1、模块参数列表中
module example_module #(
parameter integer WIDTH = 8, // 在模块参数声明列表中声明parameter
parameter integer DEPTH = 16
)(
input wire [WIDTH-1:0] data_in,
output reg [WIDTH-1:0] data_out
);
reg [WIDTH-1:0] memory [0:DEPTH-1];
always @(*) begin
data_out = memory[data_in % DEPTH];
end
endmodule
2、模块中
module example_module (
input wire [3:0] select,
output reg [7:0] out
);
parameter integer WIDTH = 8; // 在模块内部声明parameter
parameter integer OFFSET = 5;
always @(*) begin
out = (select * WIDTH) + OFFSET;
end
endmodule
3、命名块中
module example_module (
input wire [3:0] select,
output reg [7:0] out
);
always @(*) begin
// 命名块
begin : MY_BLOCK
parameter integer MULTIPLIER = 4; // 在命名块中声明 parameter
parameter integer OFFSET = 2;
out = (select * MULTIPLIER) + OFFSET;
end
end
endmodule
4、任务中
module example_module;
reg [3:0] value;
task example_task;
parameter integer MAX_VALUE = 10; // 在任务内部声明parameter
input reg [3:0] current_value;
output reg [3:0] next_value;
begin
if (current_value < MAX_VALUE) begin
next_value = current_value + 1;
end else begin
next_value = 0;
end
end
endtask
initial begin
value = 5;
example_task(value, value);
$display("Value after increment: %d", value);
end
endmodule
5、函数中
module example_module;
reg [3:0] input_value;
reg [3:0] output_value;
function [3:0] example_function;
input [3:0] in_value;
parameter integer MAX_VALUE = 8; // 在函数内部声明的parameter
begin
if (in_value < MAX_VALUE) begin
example_function = in_value + 1;
end else begin
example_function = 0;
end
end
endfunction
initial begin
input_value = 5;
output_value = example_function(input_value);
$display("Output Value: %d", output_value);
end
endmodule
符号、位宽和类型
参数声明时可以指定可选的有符号、位宽和类型,但如果指定了符号和位宽,则不能指定类型,也就是说有以下搭配:
- 不指定有符号、位宽和类型:参数为无符号的,参数的位宽由所有参数覆盖完成后,最后赋给参数的结果相同。
- 指定有符号,不指定位宽和类型:参数为有符号的,参数的位宽由所有参数覆盖完成后,最后赋给参数的结果相同。
- 指定位宽,不指定有符号和类型:参数为无符号的,参数的位宽由指定位宽决定。
- 指定有符号和位宽,不指定类型:参数为有符号的,参数的位宽由指定位宽决定。
- 指定类型,不指定有符号和位宽:参数是否有符号和位宽取决于具体的类型(例如对于time类型是无符号的且位宽至少64位,而对于integer类型,是有符号的且位宽至少32位)。
参数的覆盖
模块参数列表和模块中定义的参数在满足一定条件时,可以进行参数覆盖(本文只涉及在模块实例化时的参数覆盖)。
模块参数列表中定义的参数可以像端口连接类似,使用位置参数覆盖或命名端口覆盖,如例1所示。
// 例1
module top_module;
reg input_data;
wire output_data;
// 位置端口覆盖PARAM1和PARAM3,保留PARAM2的默认值
my_module #(12, ,64)
) instance1 (
.data_in(input_data[11:0]),
.data_out(output_data)
);
// 命名端口覆盖PARAM1和PARAM3,保留PARAM2的默认值
my_module #(
.PARAM1(12), // 覆盖PARAM1为12
.PARAM3(64) // 覆盖PARAM3为64
) instance1 (
.data_in(input_data),
.data_out(output_data)
);
endmodule
module my_module #(
parameter PARAM1 = 8,
parameter PARAM2 = 16,
parameter PARAM3 = 32
)(
input data_in,
output reg data_out
);
initial $display("%d, %d, %d",PARAM1, PARAM2, PARAM3);
endmodule
模块中定义的参数,在模块列表中没有定义参数时,可以使用位置参数覆盖或命名端口覆盖,在进行位置参数覆盖时顺序与参数定义的顺序相同,但需要注意的是,如果想不覆盖某个参数,则需要将该参数的值重新写一遍,而不像位置端口连接,如果想表示某个端口没连接,直接留空即可,如例2所示。
// 例2
module top_module;
reg input_data;
wire output_data;
// 位置端口覆盖PARAM1和PARAM3,保留PARAM2的默认值
my_module #(12, 16, 64)
) instance1 (
.data_in(input_data),
.data_out(output_data)
);
// 命名端口覆盖PARAM1和PARAM3,保留PARAM2的默认值
my_module #(
.PARAM1(12), // 覆盖PARAM1为12
.PARAM3(64) // 覆盖PARAM3为64
) instance1 (
.data_in(input_data),
.data_out(output_data)
);
endmodule
module my_module ( // 此处不能定义参数,否则模块中定义的参数无法覆盖
input data_in,
output reg data_out
);
parameter PARAM1 = 8;
parameter PARAM2 = 16;
parameter PARAM3 = 32;
initial $display("%d, %d, %d",PARAM1, PARAM2, PARAM3);
endmodule
参数覆盖的过程,就和普通的赋值一样,右侧表达式中操作数的位宽可能会受到左侧参数位宽(如有)的影响,而右侧表达式的符号不受左侧参数的符号影响,详情见以下两文。
Verilog基础:表达式位宽的确定(位宽拓展)-CSDN博客文章浏览阅读1.1w次,点赞137次,收藏181次。我们可以首先找到被加入上下文环境的操作数,a和b作为+操作符的操作数,是上下文决定的,(a+b)作为整体位与移位操作符的左边也是上下文决定的,(a+b)>>1作为整体是=操作符的右操作数,也是上下文决定的,因此answer(因为=),a,b(因为三层上下文嵌套)都被加入上下文环境中,这三个变量的位宽都是一样的,所以他们三个在运算前不会有拓展。因此首先执行a+b,根据规则,结果为16位,进位被丢失,然后再右移一位,最高位补0,最后赋值给同为16位的answer。很多时候方法很简单。改成a+b+17'b0;_verilog动态位宽https://chenzhang.blog.csdn.net/article/details/128772558?spm=1001.2014.3001.5502Verilog基础:表达式符号的确定-CSDN博客文章浏览阅读846次,点赞15次,收藏39次。文章介绍了Verilog中处理表达式符号的重要性和规则,包括$signed和$unsigned函数的作用。强调了位选、域选、拼接操作的结果通常为无符号数,以及在加法操作中,如果上下文操作数包含无符号数,则结果也会是无符号的,可能导致预期外的结果。解决这个问题的方法是确保所有操作数的符号一致。https://blog.csdn.net/weixin_45791458/article/details/128840843?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522677A413D-10AF-4A54-82E7-5B5203BB4177%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=677A413D-10AF-4A54-82E7-5B5203BB4177&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-5-128840843-null-null.nonecase&utm_term=%E8%A1%A8%E8%BE%BE%E5%BC%8F&spm=1018.2226.3001.4450