user2382841
user2382841

Reputation: 51

System Verilog parameters in generate block

I'd like to set a parameter based on a parameter which is set when the module is instantiated. I have the following.

module foo #(WORDS = 8);

parameter P00 = 33;
logic [7:0] tmp;

generate
  case (WORDS)
    4: begin : A
         assign tmp = 8'haa;
         parameter P00 = 4;
       end
    8: begin : B
         assign tmp = 8'hbb;
         parameter P00 = 8;
       end
   16: begin : C
         assign tmp = 8'hcc;
         parameter P00 = 16;
       end
   default: begin : D
              assign tmp = 8'hdd;
              parameter P00 = 8;
            end
  endcase
endgenerate

initial begin
  $display ("WORDS = %d", WORDS);
  $display ("tmp   = %h", tmp);
  $display ("P00   = %d", P00);
end

endmodule

I expected to get an error for redefining P00 but it compiled and ran and displayed the following instead.

WORDS =       8
tmp    = bb
P00    = 33

If I comment the "parameter P00 = 33" assignment, I get a "Identifier P00 has not been declared yet." error.

It seems that the generate block is being ignored. What is wrong here?

Upvotes: 2

Views: 23297

Answers (3)

Greg
Greg

Reputation: 19104

Placing a parameter definition inside a generate block generates a new local parameter relative to the hierarchical scope within the generate block. defparam is the usually the way to override a parameter value. However the IEEE std 1800-2012 explicitly states a defparam cannot effect its parent scope in §23.10.1:

a defparam statement in a hierarchy in or under a generate block instance (see Clause 27) or an array of instances (see 28.3.5 and 23.3.2) shall not change a parameter value outside that hierarchy.

For complex derived parameter assignments you can use functions. For example:

parameter P01 = FUNC01(WORDS,P00);
function byte FUNC01(input byte w,p);
/* ... */
endfunction

This is also legal: module foo #(parameter WORDS, P00=FUNC00(WORDS));

A challenge could be that each parameter may need its own function. Using a parameter with a struct data type is a potential work around to group the assignments into a single function. This approach needs to be evaluated by your simulator, synthesizer and other tools. Example:

typedef struct packed {
  int sub00;
  byte sub01;
  /* ... */
 bit [13:0] subNN
} param_t;
paramter param_t P = FUNC_P(/* inputs */);

function param_t FUNC_P(/* inputs */);
  param_t rtn;
  /* assign all rtn.sub* */
  return rtn;
endfunction

logic [P.sub01-1:0] tmpvar;

As Morgan has stated, you could define most of the parameters as logic and use a combination block. However I would strongly insist on using an always_comb block instead of a always @* to guarantee the values are calculation. As stated in the LRM §9.2.2.2.2:

always_comb automatically executes once at time zero, whereas always @* waits until a change occurs on a signal in the inferred sensitivity list.

Upvotes: 1

Lino
Lino

Reputation: 3

This works (generally say you need to make all 4 generate blocks name as the same):

module foo #(WORDS = 8);

parameter P00 = 33;
logic [7:0] tmp;

generate
  case (WORDS)
    4: begin : B
         assign tmp = 8'haa;
         parameter P00 = 4;
       end
    8: begin : B
         assign tmp = 8'hbb;
         parameter P00 = 8;
       end
   16: begin : B
         assign tmp = 8'hcc;
         parameter P00 = 16;
       end
   default: begin : B
              assign tmp = 8'hdd;
              parameter P00 = 8;
            end
  endcase
endgenerate

initial begin
  $display ("WORDS = %d", WORDS);
  $display ("tmp   = %h", tmp);
  $display ("P00   = %d", B.P00);
end

endmodule

Upvotes: 0

Morgan
Morgan

Reputation: 20514

There has been quite a few questions on here recently using generates and assigns inappropriately not sure if a new tutorial has been written which is not teaching these things correctly.

Parameters or Localparams should not be defined more than once, and they are constants so can not have the value changed. I think you are also missing the parameter keyword from module foo.

module foo #(
  parameter WORDS = 8
);

localparam P00 = WORD;

It is common to use as scaling factors:

module foo #(
  parameter WIDTH = 8
  parameter MAX_VALUE = 2**WIDTH
);

What you have defined looks like you should just be using a logic not parameter to hold the value;

I would rewrite the whole thing as:

module foo #(WORDS = 8);

logic [31:0] P00 = 33;
logic [7:0]  tmp;

always @* begin
  case (WORDS)
    4: begin : A
         tmp = 8'haa;
         P00 = 4;
       end
    8: begin : B
         tmp = 8'hbb;
         P00 = 8;
       end
   16: begin : C
         tmp = 8'hcc;
         P00 = 16;
       end
   default: begin : D
            tmp = 8'hdd;
            P00 = 8;
      end
  endcase
end

The use of generate is unnecessary for what you are trying to achieve here.

Upvotes: 1

Related Questions