Ce que « opérateur » veut dire en Verilog
Les opérateurs prennent des expressions portant des signaux et produisent de nouvelles expressions portant des signaux. a + b est du matériel - le synthétiseur le transforme en additionneur. a == b est du matériel - il construit un comparateur. Rien de tout cela n'est « du code qui tourne » ; c'est tout « des circuits que l'opérateur décrit ».
Le menu complet des opérateurs est assez petit pour être mémorisé. Ce document couvre les opérateurs arithmétiques, de comparaison, logiques et conditionnel. Bitwise et réduction couvre les opérateurs au niveau bit qui n'ont pas d'analogue logiciel direct.
Arithmétique : + - * / %
Trois choses à savoir :
- La division est entière.
7/2est3, pas3.5. Verilog n'a pas de type float (enfin,real, mais c'est simulation uniquement). - Division et modulo sont coûteux en matériel. Un
/4est OK (le synthétiseur le transforme en shift à droite), mais un/npour unnvariable au runtime construit un diviseur multi-cycles que tu ne veux presque jamais. - L'overflow wrappe. Additionner deux valeurs non signées N bits qui dépassent N bits tronque à N bits. Si tu veux capturer la retenue, déclare le résultat un bit plus large :
wire [7:0] a, b;
wire [8:0] sum = a + b; // somme 9 bits capture le carry out
Comparaison : == != < > <= >=
Les six opérateurs de comparaison retournent un résultat 1 bit : 1 si vrai, 0 si faux, x si l'une des opérandes a un bit x ou z. Pour gérer le cas x/z, utilise === et !== (les opérateurs d'égalité case) couverts dans Valeurs X et Z.
<= ici est l'opérateur de comparaison (inférieur ou égal). Les mêmes caractères veulent aussi dire assignation non-blocking dans un bloc procédural (q <= d). Le contexte les distingue : à l'intérieur d'une expression c'est une comparaison ; à gauche d'une valeur en cours d'assignation, c'est l'assignation. Le chevauchement est déroutant mais sans ambiguïté pour le parser.
Logique : && || !
Les opérateurs logiques traitent leurs opérandes comme des booléens (tout ce qui est non-zéro est vrai, zéro est faux) et produisent un résultat 1 bit :
La distinction cruciale à intégrer : && logique n'est pas & bitwise.
4'b1100 && 4'b0011 // 1 (les deux sont non-zéro, AND logique est vrai)
4'b1100 & 4'b0011 // 4'b0000 (aucune position de bit n'est 1 dans les deux)
Pareil pour || vs |. Utilise le logique dans les conditions if et les prédicats ?: ; utilise le bitwise quand tu veux que chaque position de bit fasse AND/OR indépendamment.
&& et || sont court-circuit au sens C : la partie droite n'est pas évaluée si le résultat est déjà déterminé par la gauche. Ça compte pour les testbenches qui vérifient des pointeurs ou des appels de fonction ; ça compte rarement pour le RTL synthétisable où tout arrive en parallèle.
L'opérateur conditionnel : ?:
Le ternaire est ton meilleur ami pour construire des muxes et des expressions conditionnelles :
Le motif cond ? a : b est un multiplexeur 2 vers 1 à sélection 1 bit en matériel. Les chaîner est OK pour des sélections 3 et 4 voies, mais au-delà une instruction case (couverte dans Instruction case) est dramatiquement plus lisible.
L'opérateur ?: est aussi la façon standard de donner à un registre une valeur par défaut que tu ne surcharges que parfois :
next_value = update ? new_data : next_value;
Règles de largeur : le piège du débutant
Les règles de largeur de Verilog pour les opérateurs arithmétiques sont notoirement surprenantes. La version courte :
- La largeur du résultat est le maximum des largeurs des entrées.
- Si le résultat est assigné à quelque chose de plus large, il est zero-étendu (ou sign-étendu si signé).
- La largeur des calculs intermédiaires est aussi la largeur du résultat, ce qui veut dire que l'overflow arrive dans l'intermédiaire.
La première multiplication déborde parce que les largeurs des opérandes sont toutes deux 8 bits, la largeur du résultat est donc 8 bits, et 40000 ne tient pas. La seconde marche parce qu'on a explicitement élargi a avant de multiplier.
Les techniques de correction :
- Utiliser l'opérateur de concaténation
{8'b0, a}pour zero-étendre. - Utiliser
$unsigned(a)ou$signed(a)pour influencer l'interprétation. - Caster les expressions intermédiaires à des largeurs plus grandes en cas de doute.
Précédence des opérateurs
Tu ne mémoriseras pas le tableau, mais tu t'appuieras sur quelques règles :
*,/,%lient plus fort que+,-.- La comparaison lie plus fort que les logiques (
&&,||). - L'opérateur conditionnel
?:a une précédence très basse - il faut généralement des parenthèses autour de ses branches dans les expressions plus grosses. - En cas de doute, parenthèse. Le simulateur ne facture pas au caractère.
out = (mode == 2'd0) ? a + b : a - b; // plus clair avec les parens
La suite
Tu as couvert chaque opérateur qui ressemble à son cousin logiciel. Le prochain document - Bitwise et réduction - gère les opérateurs qui n'ont pas d'analogues logiciels directs : AND/OR/XOR bitwise par bit, plus les formes réduction qui collapsent un vecteur entier à un seul bit.
Questions fréquentes
Quels opérateurs Verilog supporte-t-il ?
Verilog a quatre grandes familles d'opérateurs : arithmétique (+, -, *, /, %), comparaison (==, !=, <, >, <=, >=), logique (&&, ||, !) et bitwise/reduction (&, |, ^, ~, ~&, ~|, ~^). Plus les opérateurs de shift (<<, >>, <<<, >>>), la concaténation {}, la réplication {N{...}} et le conditionnel ?:.
Quelle est la différence entre && et & en Verilog ?
&& est le AND logique - il traite chaque opérande comme un booléen (zéro ou non-zéro) et retourne un résultat 1 bit. & est le AND bitwise - il apparie les bits position par position. 4'b1100 && 4'b0011 est 1 (les deux sont non-zéro) ; 4'b1100 & 4'b0011 est 4'b0000. Utilise && dans les conditions, & pour les opérations au niveau bit.
Comment fonctionne la division en Verilog ?
Division entière - la partie fractionnaire est jetée. 7 / 2 est 3, pas 3.5. L'opérateur % donne le reste : 7 % 2 est 1. La division par zéro produit x (inconnu) en simulation. Les deux opérateurs sont techniquement synthétisables mais produisent du matériel très gros et lent sur les FPGA et ASIC - évite-les dans les chemins synthétisables sauf si le diviseur est une puissance de 2.
Qu'est-ce que l'opérateur conditionnel en Verilog ?
?: est l'opérateur ternaire ou conditionnel, copié de C. cond ? a : b s'évalue à a si cond est vrai (non-zéro) et b sinon. C'est la construction la plus utilisée pour la logique de type multiplexeur : out = sel ? in1 : in0 construit un mux 2 vers 1. Chaîner des ?: construit des mux plus larges, bien que case soit souvent plus clair au-delà de deux entrées.