Menu

Instruction case en Verilog : décodage multi-voies bien fait

Comment case fonctionne pour un décodage multi-voies propre, le default qu'il ne faut jamais oublier, et les différences entre case, casex et casez.

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

Le branchement multi-voies

case est la construction de dispatch plat de Verilog. Tu lui donnes une expression ; il choisit la branche qui matche :

case (expression)
    pattern_1: statement_1;
    pattern_2: statement_2;
    pattern_3: begin
        statement_3a;
        statement_3b;
    end
    default: default_statement;
endcase

Structurellement similaire au switch de C, mais :

  • Pas de break - chaque branche est implicitement terminée par la suivante.
  • Les patterns peuvent être des vecteurs, pas seulement des entiers.
  • Le synthétiseur transforme l'ensemble en un mux plat (ou un décodeur one-hot quand les patterns le permettent).

Un exemple : mux 4 vers 1

Le corps case a quatre patterns explicites plus un default. Le synthétiseur voit une entrée 2 bits mappée à l'une de quatre valeurs et émet un multiplexeur 4 vers 1. Propre, plat, rapide.

Pourquoi tu as besoin de default

Pour un case combinatoire, omettre default est le même piège que if sans else : toute valeur d'entrée non matchée laisse out non assigné, et le synthétiseur infère un latch.

Pour un sel 2 bits, les patterns ci-dessus couvrent les quatre valeurs possibles - donc en théorie default est redondant. En pratique :

  1. Le synthétiseur ne prouve pas toujours que les cases sont exhaustifs.
  2. Le sélecteur peut être x ou z en simulation, ce qui ne matche aucun case explicite.
  3. Ajouter un nouveau case plus tard peut laisser le comportement par défaut non spécifié.

Écris toujours default. Pour les machines à états et la logique mux où tu sais que le default est inatteignable, assigner 'x :

default: out = 8'bx;

…dit au synthétiseur « c'est un don't-care, optimise librement » et fait surface un x rouge vif en simulation si le case inatteignable est quand même atteint. C'est le meilleur des deux mondes.

Une machine à états dans case

L'usage classique de case est la logique de transition d'état d'une machine à états finis :

Le bloc case (state) est la logique de transition d'état. Chaque branche décide de l'état suivant et de combien de temps y rester. default est inatteignable ici (on couvre RED/GREEN/YELLOW exhaustivement dans l'espace 2 bits), mais il est là comme filet de sécurité - si state devient 2'd3 pour quelque raison, la FSM se reset proprement à RED au lieu de latcher.

Machines à états finis va plus loin sur ce motif.

Plusieurs patterns par branche

Tu peux lister plusieurs patterns partageant une seule instruction, séparés par des virgules :

case (opcode)
    4'h0, 4'h1, 4'h2: result = a + b;
    4'h3, 4'h4:       result = a - b;
    4'h8:             result = a & b;
    default:          result = 8'd0;
endcase

Ce sont deux opcodes voulant dire « soustraire », trois voulant dire « additionner ». Le synthétiseur ORifie les patterns ensemble pour le comparateur.

casez et casex : matching don't-care

Parfois tu veux matcher un pattern avec certains bits non spécifiés - « n'importe quel opcode qui commence par 010 » :

casez (opcode)
    8'b010?_????: instruction = ALU_OP;
    8'b110?_????: instruction = LOAD_OP;
    8'b1110_????: instruction = JUMP_OP;
    default:      instruction = UNKNOWN;
endcasez

casez traite ? (et z) dans le pattern comme des don't-cares. Chaque ? matche 0 ou 1. Utile pour décoder les formats d'instructions où certaines positions de bits ne sont pas utilisées pour certaines classes d'opcodes.

casex étend ça pour traiter x aussi comme un don't-care. casex est dangereux parce que les signaux non initialisés (qui sont x en simulation) matchent chaque case, produisant un comportement surprenant. La plupart des guides de style modernes recommandent casez et interdisent casex.

En SystemVerilog tu obtiens aussi case inside, qui est la version la plus propre - elle accepte les plages et les listes - mais le Verilog classique s'arrête à casez.

case vs chaîne if/else if

Les deux constructions peuvent exprimer des décisions multi-voies, mais elles se synthétisent en matériel différent :

  • case est un dispatch plat. Le synthétiseur peut produire un décodeur one-hot, un arbre mux équilibré, ou d'autres structures plates. Temps constant pour évaluer.
  • if/else if est une chaîne de priorité. Le synthétiseur produit une cascade où chaque niveau ajoute du délai. Logarithmiquement plus lent.

Fonctionnellement ils se chevauchent. Stylistiquement : utilise case chaque fois que les conditions portent sur la valeur d'une seule expression. Utilise if/else if quand il y a une vraie priorité ou quand les conditions impliquent des signaux différents.

// Meilleur en case :
if      (sel == 2'd0) out = a;
else if (sel == 2'd1) out = b;
else if (sel == 2'd2) out = c;
else                  out = d;

// Meilleur en if/else if :
if      (urgent_event)  next_state = HANDLE_URGENT;
else if (timer_expired) next_state = TIMEOUT;
else if (data_ready)    next_state = PROCESS;
else                    next_state = state;

Les trois premières conditions portent toutes sur « qu'est-ce que sel ? » - un case se lit plus naturellement et se synthétise plus plat. Les trois secondes sont des événements indépendants avec une priorité évidente - if/else if est le meilleur choix.

La suite

Le dernier document de ce chapitre - Boucles for - couvre le for de Verilog et la chose surprenante qui arrive quand tu l'utilises dans du code synthétisable. Ensuite on entre dans la logique séquentielle et les FSM pour de bon.

Questions fréquentes

Qu'est-ce que l'instruction case en Verilog ?

case (expr) ... endcase est la construction de branchement multi-voies de Verilog. Elle évalue l'expression une fois et dispatche vers la branche qui matche. C'est le choix idiomatique pour les machines à états, les décodeurs d'opcodes, les sélecteurs de mux, et tout ce qui choisit parmi plusieurs options mutuellement exclusives.

Quelle est la différence entre case, casex et casez en Verilog ?

case matche bit-exactement, y compris les valeurs x et z. casez traite z (et ?) dans les items case comme des don't-cares. casex traite à la fois x et z comme des don't-cares. Le matching don't-care est utile pour les motifs d'opcodes où certaines positions de bits sont sans importance, mais casex est dangereux en simulation parce que les signaux non initialisés (x) peuvent accidentellement matcher chaque case.

Pourquoi a-t-on besoin de default dans une instruction case Verilog ?

Sans default, l'outil de synthèse voit la possibilité qu'aucun case ne matche, décide que le signal de sortie doit garder sa valeur précédente, et infère un latch non voulu. La branche default gère chaque valeur non matchée - typiquement en mettant la sortie à une valeur sûre ou en marquant le case inatteignable avec une assignation x. Inclus-le toujours.

Quand utiliser case vs if-else en Verilog ?

Utilise case quand les conditions sont mutuellement exclusives et dispatchent sur la valeur d'une seule expression - machines à états, décodeurs d'opcodes, sélecteurs de mux. Utilise if/else quand il y a un véritable ordre de priorité ou que les conditions impliquent des signaux différents. case se synthétise en matériel plus plat et plus rapide qu'une longue chaîne else if.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER