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.
3 Answers 3
So, after a bit of discussion and comparing all options, I think there's really two answers here:
- Ignore the warning.
- Cast the parameter to the proper bit width.
Ignore the Warning
Ignoring the warning can be done a number of ways:
- 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.
- 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.
- 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.
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:
- 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. - If SV, use explicit type casting:
X'(CLOCK_COUNT)
whereX
is the width to whichCLOCK_COUNT
has to be casted to.
-
\$\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\$Dave Dribin– Dave Dribin2021年09月13日 14:13:07 +00:00Commented Sep 13, 2021 at 14:13 -
\$\begingroup\$ @Dave Dribin Try:
/* verilator lint_off WIDTH */
<your statement >/* verilator lint_on WIDTH */
\$\endgroup\$Mitu Raj– Mitu Raj2021年09月13日 14:47:50 +00:00Commented 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\$Dave Dribin– Dave Dribin2021年09月14日 03:47:16 +00:00Commented 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– Mitu Raj2021年09月14日 04:26:32 +00:00Commented 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\$Dave Dribin– Dave Dribin2021年09月14日 13:11:21 +00:00Commented Sep 14, 2021 at 13:11
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.
-
\$\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 onCLOCK_FREQ
. \$\endgroup\$Dave Dribin– Dave Dribin2021年09月13日 14:04:41 +00:00Commented Sep 13, 2021 at 14:04 -
\$\begingroup\$ Hrm, this seems to work:
if (r_counter == WIDTH'(CLOCK_COUNT-1)) begin
. \$\endgroup\$Dave Dribin– Dave Dribin2021年09月13日 14:15:27 +00:00Commented 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\$Dave Dribin– Dave Dribin2021年09月13日 20:35:36 +00:00Commented Sep 13, 2021 at 20:35
Explore related questions
See similar questions with these tags.