Assign variable out of genvar in generate

hello, I have a property that looks like this:

  property p_port_orientation_chk (logic disable_cond, logic rst, logic cond, logic valid, int min_dly=0, max_dly=0, int reg_no, logic port);

I want to use a generate statement to loop through an array of values so that I can check multiple ports without the need to explicitly instantiate N instances. So I’m looking at something like the following:

  genvar i;
  generate
  for (i=0; i<num_ports / 2; i++) begin
      portno = expected_port_orientation ? i + num_ports / 2 : i;
      ap_p_rt_port_orientation_check_upp_i : assert property (p_port_orientation_chk(disable_chk[portno], rst_n, cond[portno], valid[portno], 0, 1, portno, UPP));
      else
          `uvm_error($sformatf("ap_p_rt_port_orientation_check_upp_%s", i),$sformatf("%0t : Expected 'b%b Actual 'h%0h", $realtime, UPP, int_var[portno].port_orientation))
  end
  endgenerate

I’m using the variable portno to facilitate passing parameters to the parametrized property, but the code doesn’t compile and the failure is:

expecting a left parenthesis ('(') [12.1.2][7.1(IEEE)].

pointing to the portno variable (which is an int).

I could clearly substitute the portno value with the expression but it looks tedious and ugly. Is there any other way I could achieve that?

The problem is the code inside the generate loop gets unrolled and expanded into the module where the generate block exists. You wind up with multiple procedural assignments to portno where a procedural statement is not allowed. A generate block is not procedural.

What you can do is declare a parameter inside the loop.

for (genvar i=0; i<num_ports / 2; i++) begin : each_port
      parameter int portno = expected_port_orientation ? i + num_ports / 2 : i;
      ap_p_rt_port_orientation_check_upp_i : assert property (p_port_orientation_chk(disable_chk[portno], rst_n, cond[portno], valid[portno], 0, 1, portno, UPP));
      else
          `uvm_error($sformatf("ap_p_rt_port_orientation_check_upp_%s", i),$sformatf("%0t : Expected 'b%b Actual 'h%0h", $realtime, UPP, int_var[portno].port_orientation))
  end

Hello Dave, thanks for the suggestion.

For some reason the code suggested reports the following error:

Illegal operand for constant expression [4(IEEE)].

I’m not sure why, since I do not see why the constant expression is having an illegal operand, both sides of the conditional are equally defined.

I’ve come up with a similar solution, using defines:

for (genvar i=0; i<num_ports / 2; i++) begin : each_port
      `define portno expected_port_orientation ? i + num_ports / 2 : i;
      ap_p_rt_port_orientation_check_upp_i : assert property (p_port_orientation_chk(disable_chk[`portno], rst_n, cond[`portno], valid[`portno], 0, 1, `portno, UPP));
      else
          `uvm_error($sformatf("ap_p_rt_port_orientation_check_upp_%s", i),$sformatf("%0t : Expected 'b%b Actual 'h%0h", $realtime, UPP, int_var[`portno].port_orientation))
  end

It seems to work correctly, although I’d like to understand why the parameter did not work.

It would really help to provide a complete example, at least declarations for all variables used in the example.

I’m guessing expected_port_orientation is a variable that you either set at time 0, or it changes over time. So portno has to be defined as a variable. And just like the parameter, each iteration of the loop creates a local declaration that needs an assignment.

for (genvar i=0; i<num_ports / 2; i++) begin : each_port
      int portno;
      assign portno = expected_port_orientation ? i + num_ports / 2 : i;
      ap_p_rt_port_orientation_check_upp_i : assert property (p_port_orientation_chk(disable_chk[portno], rst_n, cond[portno], valid[portno], 0, 1, portno, UPP));
      else
          `uvm_error($sformatf("ap_p_rt_port_orientation_check_upp_%s", i),$sformatf("%0t : Expected 'b%b Actual 'h%0h", $realtime, UPP, int_var[portno].port_orientation))
  end

BTW, declaring a `define macro inside a loop gives you the false impression that it evaluates each iteration, which is not the case.

Hi Dave, sorry for missing a complete example, I’ve created an MRE here: (1) - EDA Playground

Pasting the content here for convenience:

localparam NUM_PORTS = 8;

typedef struct packed {
  logic       port_orientation;
} int_var_t;

module genvarloop(
  input rst_n,
  input int_var_t int_var[NUM_PORTS-1:0],
  input expected_port_orientation
);
  
  logic [NUM_PORTS-1:0]  disable_chk;
  logic [NUM_PORTS-1:0]  cond;
  logic [NUM_PORTS-1:0]  valid;

  typedef enum {
    UPP = 0,
    DPP = 1
  } port_typ_e;
  
  property p_port_orientation_chk (logic disable_cond, logic rst_n, logic cond, logic valid, int min_dly=0, max_dly=0, int reg_no, port_typ_e port);
    disable iff(disable_cond === 1'b1)
    @(posedge valid) cond == 1'b1 |-> ##[min_dly:max_dly] (int_var[reg_no].port_orientation == port);
  endproperty: p_port_orientation_chk

  
  for (genvar i=0; i < NUM_PORTS / 2; i++) begin : ap_p_rt_port_orientation_i
    int portno;  
    assign portno = expected_port_orientation ? i + NUM_PORTS / 2 : i;
    
    ap_p_rt_port_orientation_check_i : assert property (p_port_orientation_chk(disable_chk[portno], rst_n, cond[portno], valid[portno], 0, 1, portno, UPP))
    else
	    $error($sformatf("ap_p_rt_port_orientation_check_%s", i),$sformatf("%0t : Expected 'b%b Actual 'h%0h", $realtime, UPP, int_var[portno].port_orientation));
  end

endmodule: genvarloop

I’ve finally adopted your latest solution and it worked. As for the define approach, indeed the macro expansion is going to happen at compilation time , while the generate is going to be unfolded during elaboration AFAIK.

I did not know you could create a variable internal to the generate, I had the impression memory allocation is done at compile time and not at elaboration time, but clearly my understanding was flawed.