Verilog 带参数例化是 Verilog HDL 中的一种重要技术,它可以让用户在不同的情况下使用相同的代码,而不需要重新编写代码。它可以让用户在不同的情况下使用相同的代码,而不需要重新编写代码。
Verilog 带参数例化是一种抽象技术,它允许用户将多个实例化对象封装到一个单独的模块中,并使用参数来控制这些实例化对象之间的行为。这样做有助于减少代码量,并使得代码更加易读。
module my_module #(parameter WIDTH = 8) (input [WIDTH-1:0] A, B, output [WIDTH-1:0] C); assign C = A + B; endmodule
上面是一个带参数例化的 Verilog 模块,其中 WIDTH 是一个参数(也就是说,它是在实例化时提供的)。这意味着当你实例化这个模块时,你可以通过传递 WIDTH 的值来控制整个模块中所有信号的位宽。
my_module #(.WIDTH(16)) my_instance (A, B, C);
上面是一个带参数例化 Verilog 模块 my_module 的实例化语句。在这里,我们将 WIDTH 的值设置为 16 (.WIDTH(16) ) ,因此所有信号都将具有 16 位宽度。
当一个模块被另一个模块引用例化时,高层模块可以对低层模块的参数值进行改写。这样就允许在编译时将不同的参数传递给多个相同名字的模块,而不用单独为只有参数不同的多个模块再新建文件。
参数覆盖有 2 种方式:
defparam
可以用关键字 defparam
通过模块层次调用的方法,来改写低层次模块的参数值。
例如对一个单口地址线和数据线都是 4bit 宽度的 ram
模块的 MASK
参数进行改写:
//instantiation
defparam u_ram_4x4.MASK = 7 ;
ram_4x4 u_ram_4x4
(
.CLK (clk),
.A (a[4-1:0]),
.D (d),
.EN (en),
.WR (wr), //1 for write and 0 for read
.Q (q) );
ram_4x4
的模型如下:
module ram_4x4
(
input CLK ,
input [4-1:0] A ,
input [4-1:0] D ,
input EN ,
input WR , //1 for write and 0 for read
output reg [4-1:0] Q );
parameter MASK = 3 ;
reg [4-1:0] mem [0:(1<<4)-1] ;
always @(posedge CLK) begin
if (EN && WR) begin
mem[A] <= D & MASK;
end
else if (EN && !WR) begin
Q <= mem[A] & MASK;
end
end
endmodule
对此进行一个简单的仿真,testbench 编写如下:
`timescale 1ns/1ns
module test ;
parameter AW = 4 ;
parameter DW = 4 ;
reg clk ;
reg [AW:0] a ;
reg [DW-1:0] d ;
reg en ;
reg wr ;
wire [DW-1:0] q ;
//clock generating
always begin
#15 ; clk = 0 ;
#15 ; clk = 1 ;
end
initial begin
a = 10 ;
d = 2 ;
en = "b0 ;
wr = "b0 ;
repeat(10) begin
@(negedge clk) ;
en = 1"b1;
a = a + 1 ;
wr = 1"b1 ; //write command
d = d + 1 ;
end
a = 10 ;
repeat(10) begin
@(negedge clk) ;
a = a + 1 ;
wr = 1"b0 ; //read command
end
end // initial begin
//instantiation
defparam u_ram_4x4.MASK = 7 ;
ram_4x4 u_ram_4x4
(
.CLK (clk),
.A (a[AW-1:0]),
.D (d),
.EN (en),
.WR (wr), //1 for write and 0 for read
.Q (q)
);
//stop simulation
initial begin
forever begin
#100;
if ($time >= 1000) $finish ;
end
end
endmodule // test
仿真结果如下:
图中黄色部分,当地址第一次为 c 时写入数据 4, 当第二次地址为 c 时读出数据为 4;可知此时 ram
行为正确,且 MASK
不为 3。 因为 ram
的 Q 端 bit2 没有被屏蔽。
当第一次地址为 1 时写入数据为 9,第二次地址为 1 时读出的数据却是 1,因为此时 MASK
为 7,ram
的 Q 端信号 bit3 被屏蔽。由此可知,MASK
参数被正确改写。
第二种方法就是例化模块时,将新的参数值写入模块例化语句,以此来改写原有 module
的参数值。
例如对一个地址和数据位宽都可变的 ram
模块进行带参数的模块例化:
ram #(.AW(4), .DW(4))
u_ram
(
.CLK (clk),
.A (a[AW-1:0]),
.D (d),
.EN (en),
.WR (wr), //1 for write and 0 for read
.Q (q)
);
ram
模型如下:
module ram
#( parameter AW = 2 ,
parameter DW = 3 )
(
input CLK ,
input [AW-1:0] A ,
input [DW-1:0] D ,
input EN ,
input WR , //1 for write and 0 for read
output reg [DW-1:0] Q
);
reg [DW-1:0] mem [0:(1<<AW)-1] ;
always @(posedge CLK) begin
if (EN && WR) begin
mem[A] <= D ;
end
else if (EN && !WR) begin
Q <= mem[A] ;
end
end
endmodule
仿真时,只需在上一例的 testbench 中,将本次例化的模块 u_ram
覆盖掉 u_ram_4x4
, 或重新添加之即可。
仿真结果如下。由图可知,ram
模块的参数 AW
与 DW
均被改写为 4, 且 ram
行为正确。
u_ram
的例化可以描述为:ram #(4, 4) u_ram (......) ;
defparam
也可以改写模块在端口声明时声明的参数,利用带参数例化也可以改写模块实体中声明的参数。例如 u_ram
和 u_ram_4x4
的例化分别可以描述为:defparam u_ram.AW = 4 ;
defparam u_ram.DW = 4 ;
ram u_ram(......);
ram_4x4 #(.MASK(7)) u_ram_4x4(......);
u_ram
的声明还可以表示为(模块实体中参数可自行实验验证):defparam u_ram.AW = 4 ;
ram #(.DW(4)) u_ram (......); //也只有我这么无聊才会实验这种写法
ram
模块中加入 MASK
参数,模型如下:module ram
#( parameter AW = 2 ,
parameter DW = 3 )
(
input CLK ,
input [AW-1:0] A ,
input [DW-1:0] D ,
input EN ,
input WR , //1 for write and 0 for read
output reg [DW-1:0] Q );
parameter MASK = 3 ;
reg [DW-1:0] mem [0:(1<<AW)-1] ;
always @(posedge CLK) begin
if (EN && WR) begin
mem[A] <= D ;
end
else if (EN && !WR) begin
Q <= mem[A] ;
end
end
endmodule
此时再用 defparam
改写参数 MASK
值时,编译报 Error
:
//都采用defparam时会报Error
defparam u_ram.AW = 4 ;
defparam u_ram.DW = 4 ;
defparam u_ram.MASK = 7 ;
ram u_ram (......);
//模块实体中parameter用defparam改写也会报Error
defparam u_ram.MASK = 7 ;
ram #(.AW(4), .DW(4)) u_ram (......);
重点来了!!!如果你用带参数模块例化的方法去改写参数 MASK
的值,编译不会报错,MASK
也将被成功改写!
ram #(.AW(4), .DW(4), .MASK(7)) u_ram (......);
可能的解释为,在编译器看来,如果有模块在端口声明时的参数,那么实体中的参数将视为 localparam
类型,使用 defparam
将不能改写模块实体中声明的参数。
也可能和编译器有关系,大家也可以在其他编译器上实验。
defparam
的方法。除了上述缺点外,defparam
一般也不可综合。
#
表示)。这样的代码格式不仅有很好的可读性,而且方便调试。点击这里下载源码
其实,介绍到这里,大家完全可以用前面学习到的 Verilog 语言知识,去搭建硬件电路的小茅草屋。对,是小茅草屋。因为硬件语言对应实际硬件电路的这种特殊性,在用 Verilog 建立各种模型时必须考虑实际生成的电路是什么样子的,是否符合实际要求。有时候 rtl
仿真能通过,但是最后生成的实际电路可能会工作异常。
所以,要为你的小茅草屋添砖盖瓦,还需要再学习下进阶部分。当然,进阶部分也只能让你的小茅草屋变成硬朗的砖瓦房,能抵挡风雪交加,可能遇到地震还是会垮塌。
如果你想巩固下你的砖瓦房,去建一套别墅,那你需要再学习下 Verilog 高级篇知识,例如 PLI
(编程语言接口)、UDP
(用户自定义原语),时序约束和时序分析等,还需要多参与项目工程积累经验,特别注意一些设计技巧,例如低功耗设计、异步设计等。当然学会用 SystemVerilog
去全面验证,又会让你的建筑增加一层防护盾。
但是如果你想把数字电路、Verilog 所有的知识学完,去筑一套防炮弹的总统府,那真的是爱莫能助。因为,学海无涯,回头没岸哪。
限于篇幅,这里只介绍下进阶篇。有机会,高级篇,技巧篇,也一并补上。
RequestTaskwx.request(Object object)发起 HTTPS 网络请求。使用前请注意阅读相关说明。参数Object object属性类型默认值必填说...
wx.getImageInfo(Object object)获取图片信息。网络图片需先配置download域名才能生效。参数Object object属性类型默认值必填说...
本节主要对有符号数的十进制与二进制表示以及一些数值变换进行简单的总结。定义一个宽度为DW的二进制补码格式的数据dbin,其表示...
HC-SR04超声波传感器使用声纳来确定物体的距离,就像蝙蝠一样。它提供了非常好的非接触范围检测,准确度高,读数稳定,易于使用...