当前位置: 首页 > news >正文

5.3 Verilog 带参数例化

5.3 Verilog 带参数例化

分类 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 = ~clk ; // 修改

    end
 
    initial begin

        clk = 0 ;
        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 的例化可以描述为:

ram #(4, 4)   u_ram (......) ;

(2) 当然,利用 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(......);

(3) 那能不能混合使用这两种模块参数改写的方式呢?当然能!前提是所有参数都是模块在端口声明时声明的参数或参数都是模块实体中声明的参数,例如 u_ram 的声明还可以表示为(模块实体中参数可自行实验验证):

实例

defparam     u_ram.AW = 4 ;
ram #(.DW(4)) u_ram (......);  //也只有我这么无聊才会实验这种写法

(4) 那如果一个模块中既有在模块在端口声明时声明的参数,又有在模块实体中声明的参数,那这两种参数还能同时改写么?例如在 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 将不能改写模块实体中声明的参数。

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

(5)建议,对已有模块进行例化并将其相关参数进行改写时,不要采用 defparam 的方法。除了上述缺点外,defparam 一般也不可综合。

(6)而且建议,模块在编写时,如果预知将被例化且有需要改写的参数,都将这些参数写入到模块端口声明之前的地方(用关键字井号 # 表示)。这样的代码格式不仅有很好的可读性,而且方便调试。

相关文章:

  • mybatis xml多表查询,子查询,连接查询,动态sql
  • Git学习笔记(第1章):Git概述
  • RDMA Scatter Gather List详解
  • Go 日期时间包装器:15条更便捷的时间处理
  • (学习日记)2024.01.19
  • C++:史上最坑小游戏
  • 【RabbitMQ】快速入门及基本使用
  • C#,字符串匹配(模式搜索)有限自动机(Finite Automata)算法的源代码
  • 配置中心原理和选型
  • Python文件自动化处理
  • vue 解决el-table 表体数据发生变化时,未重新渲染问题
  • 代码随想录算法训练53 | 动态规划part14
  • 带你学C语言-指针(4)
  • cetos7搭建部署k8s 版本1.28
  • Docker进阶篇-安装MySQL主从复制
  • 【译】理解JavaScript:new 关键字
  • 2017-09-12 前端日报
  • Android Studio:GIT提交项目到远程仓库
  • angular学习第一篇-----环境搭建
  • echarts的各种常用效果展示
  • Javascript基础之Array数组API
  • java正则表式的使用
  • JS基础之数据类型、对象、原型、原型链、继承
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • Linux编程学习笔记 | Linux IO学习[1] - 文件IO
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • Sublime Text 2/3 绑定Eclipse快捷键
  • Vue官网教程学习过程中值得记录的一些事情
  • 大型网站性能监测、分析与优化常见问题QA
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 解析 Webpack中import、require、按需加载的执行过程
  • 聚簇索引和非聚簇索引
  • 力扣(LeetCode)965
  • 前端面试总结(at, md)
  • 异常机制详解
  • 原生Ajax
  • 【干货分享】dos命令大全
  • Python 之网络式编程
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • #13 yum、编译安装与sed命令的使用
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (solr系列:一)使用tomcat部署solr服务
  • (差分)胡桃爱原石
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (七)理解angular中的module和injector,即依赖注入
  • .NET Core Web APi类库如何内嵌运行?
  • .net开发时的诡异问题,button的onclick事件无效
  • .Net转Java自学之路—基础巩固篇十三(集合)
  • :not(:first-child)和:not(:last-child)的用法
  • @angular/cli项目构建--Dynamic.Form
  • @Pointcut 使用
  • @SentinelResource详解