Reputation: 625
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
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
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
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