# Is it a bad idea to use unsized integer constants in equality expressions in Verilog?

I noticed the following in the Verilog 2005 standard (5.1.8 Equality operators):

If the operands are of unequal bit lengths and if one or both operands are unsigned, the smaller operand shall be zero-extended to the size of the larger operand. If both operands are signed, the smaller operand shall be sign-extended to the size of the larger operand.

and then in (3.5.1 Integer constants):

Simple decimal numbers without the size and the base format shall be treated as signed integers

I've actually had a bit of trouble finding the bit size of integers in verilog, although this website says:

integer is typically a 32 bit twos complement integer.

So, given that and the fact that the equality operation is performed bitwise (also stated in 5.1.8), does using an unsized integer cost more in the sense that the bit comparison is forced to use all 32 bits?

Take the following example as an illustration:

default_nettype none

timescale 1ns/1ps
module top;

reg [3:0] ctr = 0;
reg       clk = 0;

reg some_reg = 0;

always #1 clk = !clk;

initial begin
$dumpfile("top.vcd");$dumpvars(0, top);
#20 $finish; end always @(posedge clk) begin ctr <= ctr + 1; // if (ctr == 4'd5) // cheaper? if (ctr == 5) some_reg <= 1; end endmodule  What are the implications for comparisons with parameters? ie default_nettype none timescale 1ns/1ps module top; localparam SIZE = 5; reg [3:0] ctr = 0; reg clk = 0; reg some_reg = 0; always #1 clk = !clk; initial begin$dumpfile("top.vcd");
$dumpvars(0, top); #20$finish;
end

always @(posedge clk) begin
ctr     <= ctr + 1;
if (ctr == SIZE)
some_reg <= 1;
end

endmodule


Should we specify the bit widths of parameters then? i.e.

default_nettype none

timescale 1ns/1ps
module top;

localparam [3:0] SIZE = 5;

reg [3:0] ctr = 0;
reg       clk = 0;

reg some_reg = 0;

always #1 clk = !clk;

initial begin
$dumpfile("top.vcd");$dumpvars(0, top);
#20 \$finish;
end

always @(posedge clk) begin
ctr     <= ctr + 1;
if (ctr == SIZE)
some_reg <= 1;
end

endmodule


Are there any good guidelines about when its safe to use unsized integer constants in expressions? For instance, I use them on the rhs of expressions above since the standard states (5.6 Assignments and truncation):

If the width of the right-hand expression is larger than the width of the left-hand side in an assignment, the MSBs of the right-hand expression will always be discarded to match the size of the left-hand side.

I assumed this means that ctr <= ctr + 1 adds ctr to 1'b1 or 4'd1. Or, does it add ctr to 32'd1 and then discard the top 28 bits?

• Thumb rule: always constraint integer sizes, not all synthesisers are smart..... Sep 20, 2019 at 9:10

It is always a good idea to specify the bit width where possible. (I know this may be a lot of work if you have a 4-bit counter which you used 20 times and then needed to go to 5 bits).

However what counts (to me!) is that in the end these all produce the same amount of gates:

localparam [3:0] SIZEA = 5;
localparam SIZEB = 5;
reg [3:0] ctr;
ctr <= ctr + 1;
ctr <= ctr + 4'd1;
ctr <= ctr + SIZEA;
ctr <= ctr + SIZEB;


Are there any good guidelines about when its safe to use unsized integer constants in expressions?

The basic rule I use for myself is:

"Know what you are doing but realize others might not be as proficient in Verilog coding."

Additional: many companies have a coding guidelines. Some where I have worked for insisted that all code pass some lint tool. The latter meant that almost every addition had to be written like:

wire [3:0] a,b,c;
assign c = {a + b};


Do you know why?

Because the standard addition in Verilog produces a carry. Thus a + b is 5 bits wide. But c is only 4 bits and the linting tool complains about that. By using {...} we force the addition to the strict size of 4 bits.

Last but not least: Be extra vigilant when using constants in concatenations.

These produce the same result:

wire [7:0] a,b;
assign a = {1,b[3:0]};
assign a = {4'h1,b[3:0]};


But these do not:

wire [7:0] a,b;
assign a = {b[3:0],1};
assign a = {b[3:0],4'h1};


Why?

{1,b[3:0]} equals {32'b00000000_00000000_00000000_000000001, b[3:0]}
The top bits are dropped in the assignment so the net effect is a = {4'b0001, b[3:0]}

This is in contrast to {b[3:0],1} which equals {b[3:0],32'b00000000_00000000_00000000_000000001} Again the top bits are dropped in the assignment so the net effect is: a = {8'b00000001}

• "These produce the same result:... But these do not:" why? Sep 20, 2019 at 5:51
• The standard says it's illegal to use an unsized literal in a concatenation. Sep 20, 2019 at 19:35
• @dave_59 Be that as it may, most simulators and synthesis tools do accept it and default the size to 32 bits. Albeit they may give a warning. Sep 20, 2019 at 20:14

The SystemVerilog LRM standardized the size of all integers to 32-bits. All tools that support both Verilog and SystemVerilog use this size. But it's better to be explicit when dealing with synthesis tools so you don't get extra hardware synthesized. The only place it would not matter would be with constants and for-loop variables. For-loops get unrolled into constants and constants get optimized away in hardware.

• constants being parameters like parameter or localparam? Does this mean I don't need to specify bit widths for parameters? Sep 20, 2019 at 20:06
• You just have to realize how the width of operands get propagated to the other operas in the same expression. For example var16*param32>>4. There would be a difference if param was 16 or 32 bits Sep 20, 2019 at 20:19