The golden rule is: All operands must be signed.
It seems like Verilog is strongly inclined towards unsigned numbers. Any of the following yield an unsigned value:
- Any operation on two operands, unless both operands are signed.
- Numbers given with an explicit base (e.g. 12′d10), unless the explicit "s" modifier is used)
- Results of bit-select
- Results of part-select
- Concatenations
So the bottom line is to either use the $signed system function, or define signed wires and signed registers.
For example, to multiply a signed register with an unsigned register, treating the result as a signed value (of course), do 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 place for the sign bit, which is always zero. Had it not been for this extra bit, a’s MSb would have been treated as the sign bit in signed_a (MSb = most significant bit).
It may mistakenly seem necessary to explicitly determine signed_a’s MSb with something like {1′b0, a} instead of just "a". However, the Verilog standard is explicit about that only the expression determines if the number is signed or unsigned. The left hand side has therefore no influence on this matter. So "a" is treated as an unsigned value, and is hence extended with zero.