La règle d’or est la suivante : tous les opérandes doivent être signés.
On dirait bien que Verilog a un fort penchant pour les nombres non signés (unsigned). Chacun des cas suivants produit une valeur non signée :
- Toute opération portant sur deux opérandes, à moins que les deux opérandes soient signés.
- Les nombres écrits avec une base explicite (par exemple 12'd10), à moins d’utiliser le modificateur explicite « s ».
- Les résultats d’une sélection de bit (bit-select).
- Les résultats d’une sélection de partie (part-select).
- Les concaténations.
En bref, il faut soit utiliser la fonction système $signed, soit déclarer des fils (wire) signés et des registres signés.
Par exemple, pour multiplier un registre signé par un registre non signé (unsigned), tout en traitant le résultat comme une valeur signée (évidemment), on peut écrire quelque chose comme ceci :
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
Notez que signed_a fait un bit de plus que « a », ce qui laisse de la place pour le bit de signe, qui vaut toujours zéro. Sans ce bit supplémentaire, le MSB de a aurait été traité comme le bit de signe dans signed_a (MSB = most significant bit, soit bit de poids fort).
On pourrait croire, à tort, qu’il faut définir explicitement le MSB de signed_a avec quelque chose comme {1'b0, a} plutôt qu’avec « a » seul. Or, la norme Verilog est explicite : seule l’expression détermine si le nombre est signé ou non signé. La partie gauche de l’affectation (left-hand side) n’a donc aucune influence sur ce point. « a » est donc traité comme une valeur non signée et est étendu avec des zéros.