1

I don't get it. For some reason the < operator returns 0 even though a is clearly less than 0. The function only works if I use a[15] to check for the sign bit.

N = 16;
wire signed [15:0] a;
assign a = -100;

function [N-1:0] abs (input signed [N-1:0] a);
    abs = (a < {N{1'b0}}) ? -a : a;
endfunction
geft
  • 615
  • 1
  • 6
  • 18
  • I think you want to change your 'a' variable to a 'signed reg'. See this post for a similar question: http://stackoverflow.com/questions/12399991/how-does-verilog-behave-with-negative-numbers – Ciano Jun 22 '14 at 16:15
  • My bad! In my actual code, it is already signed. – geft Jun 22 '14 at 16:17
  • @Ciano I'm not sure I'm getting it, but is it because `{N{1'b0}}` is deemed unsigned and therefore `a` becomes unsigned with the sign bit negated? – geft Jun 22 '14 at 16:21

3 Answers3

4

The issue is {N{1'b0}} is an unsigned value. When verilog compares an unsigned and signed value it will treat both values as unsigned.

The following quote exists in IEE1364-2005 (Verilog) § 5.1.7 and IEEE1800-2012 (SystemVerilog) & section 11.4.4. A near identical quote is in IEEE1364-2001 (Verilog) § 4.1.7:

When one or both operands of a relational expression are unsigned, the expression shall be interpreted as a comparison between unsigned values. If the operands are of unequal bit lengths, the smaller operand shall be zero-extended to the size of the larger operand.

When both operands are signed, the expression shall be interpreted as a comparison between signed values. If the operands are of unequal bit lengths, the smaller operand shall be sign-extended to the size of the larger operand.

You need to cast the unsinged as signed, ie $signed({N{1'b0}}). Alternatively, you could look at the MSB of a to know if it is negative.

parameter N = 16;

wire signed [N-1:0] a;
assign a = -100;

function [N-1:0] abs_old (input signed [N-1:0] a);
  abs_old = (a < {N{1'b0}}) ? -a : a; // unsigned compare
endfunction
  function [N-1:0] abs_new (input signed [N-1:0] a);
  abs_new = (a < $signed({N{1'b0}})) ? -a : a; // signed compare
endfunction
  function [N-1:0] abs_msb (input signed [N-1:0] a);
  abs_msb = (a[N-1]) ? -a : a; // MSB check
endfunction
initial begin
  $strobe("a:%0d abs(old):%0d", a, abs_old(a)); // a:-100 abs(old):65436
  $strobe("a:%0d abs(new):%0d", a, abs_new(a)); // a:-100 abs(new):100
  $strobe("a:%0d abs(msb):%0d", a, abs_msb(a)); // a:-100 abs(msb):100
end
Greg
  • 18,111
  • 5
  • 46
  • 68
  • 1
    I would vote for just `a < 0` which should work fine and the intent of the code is very clear. A lint tool will yell at you since there is a size mismatch between the `a` and the `0` but synthesis should figure it out and know to just look at the MSB. – nguthrie Jun 23 '14 at 13:43
2

Try this:

N = 16;
reg signed [15:0] a;
assign a = -100;

function [N-1:0] abs (input signed [N-1:0] a);
    abs = (a < 0) ? -a : a;
endfunction

I changed your variable 'a' into a reg, and when doing the compare in your function I compare against '0' as opposed to a vector and things work as you expect.

Ciano
  • 554
  • 5
  • 16
  • 2
    I usually use VHDL, and more recently Systemverilog where 'reg' and 'wire' have been replaced by 'logic'. In verilog it appears to be acceptable to use 'reg' for variables and 'wire' for interconnect. However, I don't think the wire/reg declaration is the issue, I just did that for my testbench. The thing that made the difference was changing the comparison to 'a < 0' instead of comparing to a vector. Even using a 'reg' declaration, the comparison will fail if I'm comparing 'a' to vector of 0's. Not sure why that is. Maybe someone better versed in the verilog internals can chime in? – Ciano Jun 22 '14 at 17:11
0

An alternative:

N = 16;
reg signed [N-1:0] a;
assign a = -100;

assign abs = (a[N-1]) ? -a : a;

You can make the sign decision based on the most significant bit; is it set or not. Simple enough that a function is probably overkill. I leveraged your parameter N as well... that allows fewer code changes over time.

JerryJamesO
  • 11
  • 1
  • 4