MikePopoloski / slang

SystemVerilog compiler and language services
MIT License
618 stars 137 forks source link

Inconsistent Elaboration of Generate Blocks in Slang and Other Simulators #1028

Closed hankhsu1996 closed 4 months ago

hankhsu1996 commented 5 months ago

Describe the bug

There is a difference in the elaboration of generate blocks between VCS and Slang. VCS can elaborate the following code snippet without issues, but Slang cannot. The code has been simplified, so it may not seem practical, but this is to illustrate the issue.

To Reproduce

Consider the following code snippet:

typedef struct packed {
  logic [7:0] r;
  logic [7:0] g;
  logic [7:0] b;
} RGB;

typedef struct packed {
  logic [7:0] c;
  logic [7:0] m;
  logic [7:0] y;
  logic [7:0] k;
} CMYK;

typedef logic [7:0] GrayScale;

virtual class ColorFunctions #(
  parameter type T_RGB  = RGB,
  parameter type T_CMYK = CMYK
);
  static function GrayScale rgb_to_grayscale(input T_RGB color);
    return (color.r * 3 + color.g * 6 + color.b * 1) / 10;
  endfunction

  static function GrayScale cmyk_to_grayscale(input T_CMYK color);
    return ((8'hFF - color.c) + (8'hFF - color.m) + (8'hFF - color.y)) / 3 * (8'hFF - color.k) / 8'hFF;
  endfunction
endclass

module GrayScaleModule #(
  parameter COLOR_SPACE = "RGB",
  type T = RGB
) (
  input T color,
  output GrayScale grayscale
);

  if (COLOR_SPACE == "RGB") begin
    assign grayscale = ColorFunctions#(.T_RGB(T))::rgb_to_grayscale(color);
  end
  else if (COLOR_SPACE == "CMYK") begin
    assign grayscale = ColorFunctions#(.T_CMYK(T))::cmyk_to_grayscale(color);
  end

endmodule

module Top (
  input RGB rgb,
  input CMYK cmyk,
  output GrayScale grayscale_rgb,
  output GrayScale grayscale_cmyk
);

  GrayScaleModule #(
    .COLOR_SPACE("RGB"),
    .T(RGB)
  ) rgb_module (
    .color(rgb),
    .grayscale(grayscale_rgb)
  );

  GrayScaleModule #(
    .COLOR_SPACE("CMYK"),
    .T(CMYK)
  ) cmyk_module (
    .color(cmyk),
    .grayscale(grayscale_cmyk)
  );

endmodule

The critical part is the conditional generate block. Due to certain constraints, I need to use the parameter assignment .T_RGB(T) / .T_CMYK(T) because the RGB definition might need to be changed in reality, such as changing each field to 10 bits while keeping the field names the same.

In VCS, this code can be elaborated without errors. However, Slang reports:

test.sv:22:19: error: no member named 'r' in 'T_RGB' (aka 'CMYK')
    return (color.r * 3 + color.g * 6 + color.b * 1) / 10;
            ~~~~~~^

It seems that regardless of the generate block condition, both ColorFunctions are elaborated, causing the type T with CMYK fields to be provided to the T_RGB parameter. I would expect Slang not to elaborate ColorFunctions in the false condition branch.

There are some ways to fix this issue in RTL. For example, in GrayScaleModule, provide two types, one for RGB and one for CMYK, but it would complicate the I/O. Alternatively, we can split the virtual class into two to avoid mixing different type parameters. However, I want to bring up this issue as it seems to be a major difference in different tools.

On a higher level, I believe this syntax oddity is due to the lack of OOP support in SystemVerilog. Because we cannot implement functions for structs like in C, we need to use this virtual class with static functions, as recommended in LRM. The LRM does not specify whether to always elaborate 'static' functions regardless of the generate block condition, leaving the decision to the tools, but I might be missing something.

Additional context

I've run the code with Synopsys VCS, Cadence Xcelium, and Siemens Questa. All tools can elaborate the code snippet I provided.

MikePopoloski commented 4 months ago

Fixed in 9a29dbb248f0d9e4f84068c13f1e22e544f866fb