1
\$\begingroup\$

I am having trouble declaring Verilog parameters with division in Verilator tool. Here's the whole module:

`default_nettype none

module counter (
  input wire  i_clk,
  output wire o_counter_strobe
);
  
  // This does not work:
  // localparam CLOCK_FREQ = 25_000_000;
  // localparam CLOCK_COUNT = CLOCK_FREQ / 60;

  // Need to do it like this:
  localparam CLOCK_COUNT = 416_667; // 25MHz / 60Hz

  localparam WIDTH = $clog2(CLOCK_COUNT);
  reg [WIDTH-1:0] r_counter = 0;

  always @(posedge i_clk) begin
    if (r_counter == CLOCK_COUNT-1) begin
      r_counter <= 0;
    end else begin
      r_counter <= r_counter + 1;
    end
  end
  assign o_counter_strobe = (r_counter == CLOCK_COUNT-1);
endmodule

I need to declare the CLOCK_COUNT parameter like this:

  localparam CLOCK_COUNT = 416_667; // 25MHz / 60Hz

But I would really like to declare it like this:

  localparam CLOCK_FREQ = 25_000_000;
  localparam CLOCK_COUNT = CLOCK_FREQ / 60;

Then I could parameterize the clock frequency for different FPGA targets. Unfortunately, Verilator gives me this error when I try that:

%Warning-WIDTH: counter.v:19:19: Operator EQ expects 32 or 25 bits on the LHS, but LHS's VARREF 'r_counter' generates 19 bits.
                               : ... In instance fpga_sdl_top.counter
   19 |     if (r_counter == CLOCK_COUNT-1) begin
      |                   ^~
                fpgapu_sdl_top.v:48:1: ... note: In file included from fpgapu_sdl_top.v
                ... Use "/* verilator lint_off WIDTH */" and lint_on around source to disable this message.
%Warning-WIDTH: counter.v:25:40: Operator EQ expects 32 or 25 bits on the LHS, but LHS's VARREF 'r_counter' generates 19 bits.
                               : ... In instance fpga_sdl_top.counter
   25 |   assign o_counter_strobe = (r_counter == CLOCK_COUNT-1);
      |                                        ^~
                fpgapu_sdl_top.v:48:1: ... note: In file included from fpgapu_sdl_top.v
%Error: Exiting due to 2 warning(s)

Is there any way to make this work? I am okay with using SystemVerilog, if that has a better way.

\$\endgroup\$

3 Answers 3

3
\$\begingroup\$

So, after a bit of discussion and comparing all options, I think there's really two answers here:

  1. Ignore the warning.
  2. Cast the parameter to the proper bit width.

Ignore the Warning

Ignoring the warning can be done a number of ways:

  1. Add a comment:
  always @(posedge i_clk) begin
    /* verilator lint_off WIDTH */
    if (r_counter == CLOCK_COUNT-1) begin
      /* verilator lint_on WIDTH */
      r_counter <= 0;
    end else begin
      r_counter <= r_counter + 1;
    end
  end
  /* verilator lint_off WIDTH */
  assign o_counter_strobe = (r_counter == CLOCK_COUNT-1);
  /* verilator lint_on WIDTH */

I dislike that because it adds a lot of extra "noise" to the code and makes it fairly unreadable.

  1. Add a Verilator config file with the contents:
`verilator_config
lint_off -rule WIDTH -file "counter.v"

Or more specifically:

lint_off -rule WIDTH -file "counter.v" -match "*r_counter*"

This is okay, but it does ignore the error, so this technically does not "fix" the mismatch. It just silences Verilator. Given that most other tools do not warn, this is probably okay.

  1. Ignore the warning completely by passing -Wno-WIDTH to Verilator. This is not great because it could mask real width warnings elsewhere.

I would probably pick the Verilator config file, if I were to go this route, as it does not clutter the code and ignores the warning only in this specific case.

Cast the Parameter

Casting the parameter could be done a number of ways. The Verilator docs suggest using syntax like WIDTH'(CLOCK_COUNT):

  always @(posedge i_clk) begin
    if (r_counter == WIDTH'(CLOCK_COUNT)-1) begin
      r_counter <= 0;
    end else begin
      r_counter <= r_counter + 1;
    end
  end
  assign o_counter_strobe = (r_counter == WIDTH'(CLOCK_COUNT)-1);

However this syntax is SystemVerilog, and it turns out the synthesis tool I am using (Lattice iCEcube with Synplify Pro) does not like this syntax.

The equivalent Verilog syntax is CLOCK_COUNT[WIDTH-1:0]-1:

  always @(posedge i_clk) begin
    if (r_counter == CLOCK_COUNT[WIDTH-1:0]-1) begin
      r_counter <= 0;
    end else begin
      r_counter <= r_counter + 1;
    end
  end
  assign o_counter_strobe = (r_counter == WIDTH[WIDTH-1:0]-1);

And while this does work, this again, adds a lot of extra "noise" to the code making it more unreadable.

I think the best solution I found is to make the parameter itself the proper bit width:

  localparam CLOCK_FREQ = 25_000_000;
  localparam CLOCK_COUNT_ = CLOCK_FREQ / 60;
  localparam WIDTH = $clog2(CLOCK_COUNT_);
  localparam CLOCK_COUNT = CLOCK_COUNT_[WIDTH-1:0];

This does require an extra parameter, which I've named CLOCK_COUNT_, but it no longer requires a cast when using the parameter. This produces no warnings:

  always @(posedge i_clk) begin
    if (r_counter == CLOCK_COUNT-1) begin
      r_counter <= 0;
    end else begin
      r_counter <= r_counter + 1;
    end
  end
  assign o_counter_strobe = (r_counter == CLOCK_COUNT-1);

Conclusion

I'm leaning towards casting the parameter itself, even if it does require an intermediate parameter. But my second choice is ignoring the warning via a config file.

It's still a bit of a mystery to me why Verilator cannot figure out the proper bit width on its own with the division, but it's probably a quirk of Verilog.

Update: The author of Verilator said:

If you look at the error Verilator says it's "32 or 19 bits" - 32 because that's the IEEE size, and 19 because that's what's needed for the number. Verilator will accept using it in an expression of either size to avoid the WIDTH.

The second is 32 or 25 because 32 is the IEEE size, and 25 is the size using the 25_000_000 in the IEEE rules for division which say the result is the same width (25).

If you don't have SystemVerilog, seems to me a reasonable case for disabling WIDTH on the using lines.

\$\endgroup\$
2
\$\begingroup\$
 localparam CLOCK_FREQ = 25_000_000;
 localparam CLOCK_COUNT = CLOCK_FREQ / 60;

Here, you have not explicitly specified the data types of the two localparam, so the compiler derives it from expressions on the RHS. So CLOCK_FREQ and CLOCK_COUNT are of int type or 32-bit type.

Now, one of the rules set in your linting tool seems to complain that you are comparing a 32-bit value expression (CLOCK_COUNT-1) with r_counter which is of width 19. You can safely ignore this warning though as it is not critical from the perspective of functionality (there must be some option in Verilator to disable this Lint check?)

UPDATE:

  1. You can use in Verilog, /* verilator lint_off WIDTH */ and /* verilator lint_on WIDTH */ attributes to disable/enable lint checks for width mismatches on specific lines.
  2. If SV, use explicit type casting: X'(CLOCK_COUNT) where X is the width to which CLOCK_COUNT has to be casted to.
\$\endgroup\$
9
  • \$\begingroup\$ Doesn't seem to like that, either: %Warning-WIDTH: counter.v:14:46: Operator VAR 'CLOCK_COUNT_INTERNAL' expects 19 bits on the Initial value, but Initial value's VARREF 'CLOCK_COUNT' generates 32 or 25 bits. \$\endgroup\$ Commented Sep 13, 2021 at 14:13
  • \$\begingroup\$ @Dave Dribin Try: /* verilator lint_off WIDTH */ <your statement > /* verilator lint_on WIDTH */ \$\endgroup\$
    – Mitu Raj
    Commented Sep 13, 2021 at 14:47
  • \$\begingroup\$ Thanks Mitu. I'm looking for a way that doesn't use those Verilator specific comments. I think using SystemVerilog bit width casting will work. \$\endgroup\$ Commented Sep 14, 2021 at 3:47
  • \$\begingroup\$ Yea, SV has type-casting constructs like int'(..), <bitsize>'(..) even though nobody uses it, as an SV compiler implicitly does that. \$\endgroup\$
    – Mitu Raj
    Commented Sep 14, 2021 at 4:26
  • \$\begingroup\$ The trick with casting is figuring out how to use WIDTH for the cast. Also, you say nobody uses it, do people just ignore Verilog warnings? I would think that seeing those Verilator comments would be more annoying than using a type cast. \$\endgroup\$ Commented Sep 14, 2021 at 13:11
1
\$\begingroup\$

Verilator has an extremely pedantic linter. Those are linter warnings, so you can configure verilator to disable that warning completely or to disable it on those specific lines. The output tells you what to do here. Another option is to force the width on both sides to match to satiate the linter.

\$\endgroup\$
3
  • \$\begingroup\$ Yeah, I could disable it on those lines, but that means disabling the warning wherever CLOCK_COUNT is used. I was hoping there was a way to annotate the parameter declaration, somehow. How do you force a width on both sides? The width could change depending on CLOCK_FREQ. \$\endgroup\$ Commented Sep 13, 2021 at 14:04
  • \$\begingroup\$ Hrm, this seems to work: if (r_counter == WIDTH'(CLOCK_COUNT-1)) begin. \$\endgroup\$ Commented Sep 13, 2021 at 14:15
  • \$\begingroup\$ But, that appears to be SystemVerilog casting syntax. Lattice iCEcube2 (Synplify) requires renaming the file to .sv to synthesize it. Maybe this is the best option, though. \$\endgroup\$ Commented Sep 13, 2021 at 20:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.