举报投诉联系我们 手机版 热门标签 名动网
您的位置:名动网 > verilog 带参数例化 Verilog 带参数例化

verilog 带参数例化 Verilog 带参数例化

2023-03-13 23:20 Verilog手册

verilog 带参数例化 Verilog 带参数例化

verilog 带参数例化

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 位宽度。

Verilog 带参数例化

关键词: defparam,参数,例化,ram

当一个模块被另一个模块引用例化时,高层模块可以对低层模块的参数值进行改写。这样就允许在编译时将不同的参数传递给多个相同名字的模块,而不用单独为只有参数不同的多个模块再新建文件。

参数覆盖有 2 种方式:

  1. 使用关键字 ​defparam
  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​ 行为正确。


区别与建议

  1. 和模块端口实例化一样,带参数例化时,也可以不指定原有参数名字,按顺序进行参数例化,例如 ​u_ram​ 的例化可以描述为:
  2. ram #(4, 4)   u_ram (......) ;
  3. 当然,利用 ​defparam​ 也可以改写模块在端口声明时声明的参数,利用带参数例化也可以改写模块实体中声明的参数。例如 ​u_ram​ 和 ​u_ram_4x4​ 的例化分别可以描述为:
  4. defparam     u_ram.AW = 4 ;
    defparam     u_ram.DW = 4 ;
    ram   u_ram(......);
    ram_4x4  #(.MASK(7))    u_ram_4x4(......);
  5. 那能不能混合使用这两种模块参数改写的方式呢?当然能!前提是所有参数都是模块在端口声明时声明的参数或参数都是模块实体中声明的参数,例如 ​u_ram​ 的声明还可以表示为(模块实体中参数可自行实验验证):
  6. defparam     u_ram.AW = 4 ;
    ram #(.DW(4)) u_ram (......);  //也只有我这么无聊才会实验这种写法
  7. 那如果一个模块中既有在模块在端口声明时声明的参数,又有在模块实体中声明的参数,那这两种参数还能同时改写么?例如在 ​ram​ 模块中加入 ​MASK​ 参数,模型如下:
  8. 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​ 将不能改写模块实体中声明的参数。

    也可能和编译器有关系,大家也可以在其他编译器上实验。

  9. 建议,对已有模块进行例化并将其相关参数进行改写时,不要采用 ​defparam​ 的方法。除了上述缺点外,​defparam​ 一般也不可综合。
  10. 而且建议,模块在编写时,如果预知将被例化且有需要改写的参数,都将这些参数写入到模块端口声明之前的地方(用关键字井号 ​​表示)。这样的代码格式不仅有很好的可读性,而且方便调试。

点击这里下载源码

阶段总结

其实,介绍到这里,大家完全可以用前面学习到的 Verilog 语言知识,去搭建硬件电路的小茅草屋。对,是小茅草屋。因为硬件语言对应实际硬件电路的这种特殊性,在用 Verilog 建立各种模型时必须考虑实际生成的电路是什么样子的,是否符合实际要求。有时候 ​rtl​ 仿真能通过,但是最后生成的实际电路可能会工作异常。

所以,要为你的小茅草屋添砖盖瓦,还需要再学习下进阶部分。当然,进阶部分也只能让你的小茅草屋变成硬朗的砖瓦房,能抵挡风雪交加,可能遇到地震还是会垮塌。

如果你想巩固下你的砖瓦房,去建一套别墅,那你需要再学习下 Verilog 高级篇知识,例如 ​PLI​(编程语言接口)、​UDP​(用户自定义原语),时序约束和时序分析等,还需要多参与项目工程积累经验,特别注意一些设计技巧,例如低功耗设计、异步设计等。当然学会用 ​SystemVerilog​ 去全面验证,又会让你的建筑增加一层防护盾。

但是如果你想把数字电路、Verilog 所有的知识学完,去筑一套防炮弹的总统府,那真的是爱莫能助。因为,学海无涯,回头没岸哪。

限于篇幅,这里只介绍下进阶篇。有机会,高级篇,技巧篇,也一并补上。


阅读全文
以上是名动网为你收集整理的verilog 带参数例化 Verilog 带参数例化全部内容。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。
相关文章
© 2024 名动网 mdwl.vip 版权所有 联系我们