Don Rumata
Don Rumata

Reputation: 11

Module's parameter initialization troubles (updated)

I want to have universal function to initialize same module with different parameters. One of them is packed array with size depends on another parameter.

I've tried something like this:

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 

Vivado synthesis tool builds it without any errors, but Questa Sim doesn't allow to simulate this code. I'm getting following errors:

\*\* Error: External function '\<class-spec#1\>::GetRand' may not be used in a constant expression.

May be some one could advice some workaround for this trouble?

Same code with class without any parameters simulates with no problems. But function stops be universal...

Upvotes: 0

Views: 54

Answers (1)

Serge
Serge

Reputation: 12384

Unfortunately, in system verilog you can only use locally (or globally) defined functions in constant expressions like that. Because it depends on module parameters, it should be defined locally within the module as in the following example:

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


endmodule : myModule


module test_class#();

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

   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
   endfunction

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

endmodule : test_class

There are a couple of ways to make this function reusable.

  1. define it as a macro:

`define GetRand(p_COUNT, p_WIDTH) \
  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\
   endfunction

module test_class#();
  ...
  `GetRand(p_COUNT, p_WIDTH) // define it within the module by instantiating the macro
   myModule #(
              ...
              .p_INIT (GetRand(p_STEP))
          ) i_myModule ();

  1. you can define the function in a separate file and use `include to bring it into a module. However, I do not recommend using of this method.

Upvotes: 0

Related Questions