Verilog – Is Using Unsized Integer Constants in Equality Expressions a Bad Idea?

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?

Best Answer

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}