geft
geft

Reputation: 625

Why does the following Verilog absolute function fail?

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

Upvotes: 1

Views: 9122

Answers (3)

JerryJamesO
JerryJamesO

Reputation: 11

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.

Upvotes: 0

Greg
Greg

Reputation: 19112

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

Upvotes: 4

Ciano
Ciano

Reputation: 554

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.

Upvotes: 2

Related Questions