La regla de oro es: todos los operandos deben tener signo.
Parece que Verilog tiene una clara preferencia por los números sin signo. Cualquiera de los siguientes casos produce un valor sin signo:
- Cualquier operación con dos operandos, a menos que ambos operandos tengan signo.
- Números con una base explícita (p. ej., 12'd10), salvo que se utilice el modificador explícito «s».
- Resultados de la selección de un bit (bit-select).
- Resultados de la selección de un rango de bits (part-select).
- Concatenaciones.
Así que, en resumen, la solución pasa por usar la función de sistema $signed o declarar wires con signo y registros con signo.
Por ejemplo, para multiplicar un registro con signo por uno sin signo, tratando (por supuesto) el resultado como un valor con signo, puedes hacer algo como esto:
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
Observa que signed_a es un bit más ancho que «a», de modo que hay sitio para el bit de signo, que siempre es cero. De no ser por este bit adicional, el MSb de «a» se habría tratado como el bit de signo en signed_a (MSb = bit más significativo).
Podría parecer erróneamente necesario determinar de forma explícita el MSb de signed_a con algo como {1'b0, a} en lugar de simplemente «a». Sin embargo, el estándar de Verilog es explícito: solo la expresión determina si el número es con signo o sin signo. Por tanto, el lado izquierdo de la asignación no influye en este asunto. Así, «a» se trata como un valor sin signo y, en consecuencia, se extiende con ceros.