Thanks for all the help and suggestions. Writing a separate testbench for the mult module helped arrive at a solution.
My issue was in the mult module. Since my inputs are 32 bits long, the mult output would be 32*2+1 = 65 bits long. My output port 'out' was only assigned to be 64 bits long which resulted in a sign issue when the answer was a negative number. Assigning it to be 65 bits long took care of my problem.
Looking at detecting Overflow and underflow in addition, Analysing a simple 4 bit example, sign extended to 5 bits.
Addition all +ve
3 : [0]0011
+ 3 : [0]0011
= 6 : [0]0110
With negative numbers
-3 : [1]1101
+ -3 : [1]1101
= -6 : [1]1010
Now causing an overflow : Result should be +8 but can not represent that in 4 bits.
+7 : [0]0111
+1 : [0]0001
+8 : [0]1000
Now cause underflow : Result should be -9 but can not represent that in 4 bits.
-8 : [1]1000
+ -1 : [1]1111
-9 : [1]0111
Therefore overflow and underflow are easy to detect if we sign extend the inputs by 1 bit
localparam WIDTH = 4;
localparam MSB = WIDTH-1;
logic [WIDTH-1:0] a;
logic [WIDTH-1:0] b;
logic [WIDTH-1:0] result;
logic extra;
logic overflow;
logic underflow;
always @* begin
{extra, result} = {a[MSB], a} + {b[MSB], b} ;
overflow = ({extra, result[MSB]} == 2’b01 );
underflow = ({extra, result[MSB]} == 2’b10 );
end
Regarding multiplication I do not understand why you can not have a 32 bit register. even if you reduce the final output to 16.
When performing the bit reduction you would need to check that the value is under the max and above the minimum negative number that you can support with the reduced width.
NB: In addition the result grows 1 bit bigger than largest input. The overflow/underflow occurs when truncating back to original width.
With multiplication the result is the width of both added together, 16bits * 16bits results in a 32 bit answer. Pretty sure you do not need 33 bits. If you do not keep the full width then it is pretty hard to tell if the result will overflow when truncated. It is quite common to design these things with a wide combinatorial result and only output so many bits through a flip-flop for the final output from the ALU.
I think that keeping a 32 bit output and comparing it to the max/min of a signed 16 bit number, will synthesise smaller than only using 16 bit multiplier and extra logic to detect the overflow condition.
Best Answer
There are a number of Verilog system functions can be used for synthesis as well as testbenches. Most of the synthesizable function perform some kind of arithmetic or logical conversion. SystemVerilog has replaced most of the $functions with casts or built-in methods.
In this example, the use of $signed makes no sense to me as the result of a comparison is always a 1-but unsigned value, and this result will be and'ed with another 1-bit expression. So the $signed is not doing anything. Casting a value to signed only makes sense if the value need to be extended to a larger width, or in a relational operation.
You can find the list of functions in the Language Reference Manual (LRM).