Menu

Valeurs X et Z en Verilog : signaux inconnus et haute impédance expliqués

Les signaux Verilog ont quatre valeurs possibles, pas deux. Voici ce que x (inconnu) et z (haute impédance) signifient réellement en simulation, et comment les debugger.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Logique 2 états vs 4 états

En logiciel, un bit est 0 ou 1. En Verilog, un bit peut prendre quatre valeurs :

  • 0 - le wire est piloté à bas.
  • 1 - le wire est piloté à haut.
  • x - la valeur du wire est inconnue. Le simulateur ne peut pas dire laquelle.
  • z - le wire est en haute impédance. Rien ne le pilote.

Ce modèle à 4 états existe parce que le matériel a le même problème. Un vrai wire peut être tiré bas, tiré haut, indéfini (piloté par deux sources qui se battent) ou flottant (aucun pilote). Le simulateur doit modéliser les quatre pour être utile.

Comment x apparaît

Lance ça et regarde la sortie :

a est déclaré mais jamais écrit, donc il reste à x. Ajouter x + 5 produit x - toute arithmétique avec un inconnu produit un inconnu. La sortie affiche autant de x que de bits.

Sources courantes de x dans tes designs :

  • Un reg déclaré mais jamais reset (la plupart du Verilog synthétisable utilise un reset explicite pour les nettoyer).
  • Un case sans default, atteint par une valeur d'entrée qu'aucun cas n'a matché.
  • Un wire qui a perdu son unique pilote après un refactor.
  • Une chaîne if/else où une branche n'assigne pas un signal que l'autre assigne (latche x si non couvert).
  • Lire au-delà de la fin d'un vecteur ou d'un tableau.

La propagation de X : un petit bout de x gâche tout

La cruauté de x, c'est qu'il se propage. Un bit x dans une opérande rend tout le résultat x :

Remarque que 0 & x est 0 (AND avec 0 est toujours 0) et 1 | x est 1 (OR avec 1 est toujours 1). Le simulateur est pessimiste bit-par-bit mais respecte quand même les identités. L'arithmétique et la comparaison ne sont pas aussi généreuses.

C'est pourquoi un seul registre non initialisé peut faire qu'un bus de sortie entier devienne xxxx. Trace en arrière depuis n'importe quel x et tu trouveras la source.

Comment z apparaît

z est la valeur d'un wire que personne ne pilote :

Deux motifs dans ce snippet :

  • floating est juste déclaré et jamais piloté. Il vaut z par défaut.
  • data_out est un tri-state délibéré. Quand enable est bas, la sortie relâche explicitement à z. C'est ainsi qu'un pilote de bus « lâche » pour qu'un autre pilote prenne le relais.

Sur la logique interne, z est presque toujours faux. Sur une broche bidirectionnelle ou un bus partagé, z est exactement juste.

Comparer avec == vs ===

L'opérateur d'égalité standard == retourne x quand l'une des opérandes a un bit x ou z :

=== (et son partenaire !==) fait une comparaison stricte bit-à-bit y compris x et z. Utilise-le quand tu dois tester pour ou contre x/z dans un testbench. === n'est pas synthétisable, mais à l'intérieur d'un bloc initial dans un testbench ça n'a pas d'importance.

La fonction système $isunknown(expr) est la façon la plus propre de demander « cette expression a-t-elle des bits x ou z ? » - retourne 1 si oui, 0 si non.

Utiliser x comme don't-care intentionnel

Un motif controversé mais légitime : 'x dans un cas default d'une machine à états dit au synthétiseur « cet état est inatteignable, optimise librement » :

case (state)
    IDLE:    next_state = go ? RUNNING : IDLE;
    RUNNING: next_state = done ? IDLE : RUNNING;
    default: next_state = 'x;   // inatteignable
endcase

Le synthétiseur peut utiliser le x pour fusionner des états et réduire le nombre de portes. En simulation, si ton raisonnement était faux et que le default est atteint, tu verras x se propager depuis next_state et le bug devient visible immédiatement.

Utilise ça seulement quand tu as bien réfléchi à si le default est vraiment inatteignable. Si tu n'as pas réfléchi, mets le default à un état sûr à la place.

Recette de debug courante

Tu fixes une forme d'onde pleine de x. La recette :

  1. Trouve le x le plus précoce. Remonte la forme d'onde dans le temps. Le signal le plus précoce à devenir x est le plus proche de la source.
  2. Trouve son pilote. Ouvre le source. Qu'est-ce qui assigne ce signal ? Un assign ? Un bloc always ?
  3. Vérifie les entrées du pilote. Si la RHS du pilote a un x, la propagation fait son boulot - le bug est en amont.
  4. Si le pilote a des entrées propres mais produit x, le pilote est incomplet. Un case sans default, un if sans else, un registre sans reset.

La plupart des bugs en x-storm se résument à : reset manquant, default manquant, ou un sous-module non connecté.

La suite

Tu as maintenant l'histoire complète des types de données : wire/reg, vecteurs, paramètres, littéraux numériques et le modèle de logique 4 états. Le prochain chapitre commence à utiliser tout ça pour construire des expressions - opérateurs de tous types, y compris les opérateurs au niveau bit qui n'auraient pas de sens en logiciel.

Questions fréquentes

Que veut dire x en Verilog ?

x est la valeur inconnue. Un signal qui est x pourrait être 0 ou 1 - le simulateur ne peut pas le dire. Il apparaît quand un signal n'a pas été piloté, quand deux pilotes sont en contention, quand un registre est lu avant reset, ou partout où un comportement indéfini se propagerait silencieusement. Traite x comme un signal de bug ; ça ne veut presque jamais dire ce que tu veux.

Que veut dire z en Verilog ?

z est la valeur haute impédance - le wire n'est piloté par rien. C'est l'état légitime des sorties tri-state (bus de données, broches bidirectionnelles), mais sur des signaux internes z est généralement une erreur qui veut dire « rien n'est connecté ici ». Les synthétiseurs rejettent la plupart des motifs z en dehors des output enables tri-state explicites.

Pourquoi ma sortie Verilog est-elle xxxx ?

Presque toujours parce qu'un signal n'est piloté par rien, ou parce qu'une opération a propagé x depuis un autre signal. Remonte en arrière : quel signal est x, qu'est-ce qui l'alimente, le pilote est-il actif ? Les coupables courants sont les cas par défaut manquants dans les case, les registres qui ne sont pas reset, et les wires qui ont perdu leur pilote après un refactor.

Comment vérifier x ou z en Verilog ?

Utilise l'opérateur ===, qui compare bit-exactement y compris x et z. a === 1'bx est vrai quand a est effectivement x. Le == normal retourne x quand l'une des opérandes a un bit x, ce qui veut dire que a == 1'bx n'est jamais la réponse que tu veux. Il y a aussi $isunknown(a), qui est un booléen propre.

Peut-on assigner x comme valeur par défaut en Verilog ?

Oui, et c'est une technique délibérée dans les case : default: out = 'x; dit au synthétiseur « je promets que ce cas n'arrive jamais, optimise librement ». Le coût est que s'il arrive en simulation, x se propage et tu vois le bug. Utilise-le quand tu as déjà prouvé que le default est inatteignable, pas comme moyen d'éviter d'écrire le cas.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER