You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带参数类静态函数初始化Verilog模块参数的仿真兼容问题

问题背景

需要实现通用函数,用于对同一模块使用不同参数进行初始化,其中一个模块参数为依赖其他参数的打包数组。尝试编写的代码可通过Vivado综合工具编译,但在Questa Sim仿真时触发错误。

尝试代码
package my_pkg;

    class helper #(
        parameter p_WIDTH = 16,
        parameter p_COUNT = 2
    );

    typedef logic [p_COUNT - 1 : 0] [p_WIDTH - 1 : 0] ret_t;

    static function ret_t GetRand(int value);
        automatic ret_t result;

        for (int i = 0; i < p_COUNT; ++i) begin
            result[i] = value * i;
        end
    endfunction

    endclass : helper

endpackage : my_pkg

module myModule #(
    parameter                                     p_WIDTH = 16,
    parameter                                     p_COUNT = 2,
    parameter [p_COUNT - 1 : 0] [p_WIDTH - 1 : 0] p_INIT  = '{default: 0}
);

    // other module i can't edit
    otherModule #(
        ......
        .p_INIT(p_INIT),
        ......
    ) (
      .....
    )

endmodule : myModule

module test_class
    import my_pkg::*;
#();

    localparam p_WIDTH = 16;
    localparam p_COUNT = 2;
    localparam p_STEP  = 6;

    typedef helper#(.p_WIDTH(p_WIDTH), .p_COUNT(p_COUNT)) helper_t;

    myModule #(
        .p_WIDTH(p_WIDTH                  ),
        .p_COUNT(p_COUNT                  ),
        .p_INIT (helper_t::GetRand(p_STEP))
    ) i_myModule (
        .....
    );

endmodule : test_class 
仿真报错信息
** Error: External function '<class-spec#1>::GetRand' may not be used in a constant expression.

注:使用无参数类时仿真可正常运行,但函数会失去通用性。

解决办法

方案1:用包内参数化函数替代参数化类

将类中的静态函数迁移到包的参数化函数中,SystemVerilog标准对包内函数在常量表达式中的支持更广泛,能兼容多数仿真器:

package my_pkg;
    // 带默认参数的通用函数
    function automatic [p_COUNT-1:0][p_WIDTH-1:0] GetRand(
        int value, 
        int p_WIDTH=16, 
        int p_COUNT=2
    );
        automatic [p_COUNT-1:0][p_WIDTH-1:0] result;
        for (int i=0; i<p_COUNT; i++) begin
            result[i] = value * i;
        end
        return result;
    endfunction
endpackage : my_pkg

在test_class中调用时传递参数:

myModule #(
    .p_WIDTH(p_WIDTH),
    .p_COUNT(p_COUNT),
    .p_INIT (my_pkg::GetRand(p_STEP, p_WIDTH, p_COUNT))
) i_myModule (
    // ...端口连接
);

方案2:通过局部参数中转计算结果

部分仿真器对直接在模块参数中调用类静态函数限制严格,但允许在局部参数中计算后传递。修改test_class:

module test_class
    import my_pkg::*;
#();

    localparam p_WIDTH = 16;
    localparam p_COUNT = 2;
    localparam p_STEP  = 6;

    typedef helper#(.p_WIDTH(p_WIDTH), .p_COUNT(p_COUNT)) helper_t;
    // 先计算出初始化值存到局部参数
    localparam [p_COUNT-1:0][p_WIDTH-1:0] INIT_VAL = helper_t::GetRand(p_STEP);

    myModule #(
        .p_WIDTH(p_WIDTH),
        .p_COUNT(p_COUNT),
        .p_INIT (INIT_VAL)
    ) i_myModule (
        // ...端口连接
    );

endmodule : test_class

方案3:使用参数化包替代参数化类

利用SystemVerilog的参数化包特性,将辅助逻辑封装到参数化包中,其函数在常量表达式中的兼容性优于参数化类:

package my_pkg #(
    parameter p_WIDTH = 16,
    parameter p_COUNT = 2
);
    typedef logic [p_COUNT-1:0][p_WIDTH-1:0] ret_t;
    function automatic ret_t GetRand(int value);
        automatic ret_t result;
        for (int i=0; i<p_COUNT; i++) begin
            result[i] = value * i;
        end
        return result;
    endfunction
endpackage : my_pkg

在test_class中导入参数化包并使用:

module test_class#();
    localparam p_WIDTH = 16;
    localparam p_COUNT = 2;
    localparam p_STEP  = 6;

    // 导入指定参数化版本的包
    import my_pkg#(.p_WIDTH(p_WIDTH), .p_COUNT(p_COUNT))::*;
    localparam ret_t INIT_VAL = GetRand(p_STEP);

    myModule #(
        .p_WIDTH(p_WIDTH),
        .p_COUNT(p_COUNT),
        .p_INIT (INIT_VAL)
    ) i_myModule (
        // ...端口连接
    );
endmodule : test_class

内容的提问来源于stack exchange,提问作者Don Rumata

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 22:15:33