Signed arithmetics in Verilog: The only rule one needs to know

This post was written by eli on October 30, 2012
Posted Under: FPGA

The golden rule is: All operands must be signed.

Verilog, it seems, is strongly inclined towards unsigned numbers. Any of the following yield an unsigned value:

  • Any operation on two operands, unless both operands are signed.
  • Based numbers (e.g. 12′d10), unless the explicit “s” modifier is used)
  • Bit-select results
  • Part-select results
  • Concatenations

So the bottom line is to either use the $signed system function, or define signed wires and registers.

For example, to multiply a signed and unsigned register, yielding a signed value (of course), go something like this:

reg         [15:0] a; // Unsigned
reg signed  [15:0] b;
wire signed [16:0] signed_a;
wire signed [31:0] a_mult_b;

assign signed_a = a; // Convert to signed
assign a_mult_b = signed_a * b

Note that signed_a is one bit wider than “a”, so there’s room for the sign bit, which is always zero. If this wasn’t for this extra bit, a’s MSB would be treated as the sign bit in signed_a.

It may seem necessary to explicitly determine signed_a’s MSB with sometime like {1′b0, a} instead of just “a”, but the Verilog standard is pretty explicit about the signed vs. unsigned being determined by the expression only, and not by the left hand side. So “a” is treated as an unsigned value, and is hence extended by zero.


Reader Comments

Thank you, man. I thought I was crazy.

Written By Oggy on May 19th, 2014 @ 15:35


Written By Anonymous on March 31st, 2015 @ 18:52

thanks to you bro

Written By SHUBHAM on April 21st, 2016 @ 20:35

I also find that arrays of signed numbers do not work as expected. Verilog signed math is fraught with hazards.

Written By Pedro on October 27th, 2016 @ 20:06

Good point!

Written By Yan on March 7th, 2018 @ 03:27

Hey Eli,

thanks for your great blog, it helped me a lot so far! :)

Just recently I realized another issue with Verilog signed operations in Cadence:
I declared two 16-bit operands, one signed & one unsigned. Then I executed the following operation:

prod = $signed(op_a_signed * op_b_unsigned);

and it turned out that the result returned by the typecast $signed() was also a 16-bit value (LSBs) unlike the result reg (prod) which was declared as a 32-bit value.

So apparently the typecasts $signed() or $unsigned() return a value with bitness of the operands, not the bitness of the value the result is returned to.

Maybe this helps someone!

Written By Tim on July 23rd, 2019 @ 13:58

I avoid declaring signed type altogether. Much easier to just manage 2s complement sign explicitly. I have seen inconsistency in how signed type is handled between different simulators and compilers.

Written By Dave on November 15th, 2020 @ 21:26

Add a Comment

required, use real name
required, will not be published
optional, your blog address