Menu

Concaténation et réplication en Verilog : les opérateurs {}

Comment coller des signaux ensemble avec {} et copier un motif N fois avec {N{...}} - les opérateurs Verilog indispensables pour construire des bus plus larges à partir de morceaux.

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

Deux petits opérateurs qui gagnent leur place

Verilog a deux formes {} :

  1. Concaténation - {a, b, c} - colle plusieurs signaux en un seul plus large.
  2. Réplication - {N{pattern}} - copie un motif N fois.

Tu utiliseras les deux constamment. Chaque fois qu'un bus doit être divisé, joint, paddé ou sign-étendu, l'un de ces deux opérateurs est la réponse.

Concaténation : {a, b, c}

Le cas le plus simple : empiler deux signaux en un :

Lecture gauche à droite : a est la moitié haute, b est la moitié basse. Le premier opérande à l'intérieur de {} est le plus significatif. La largeur du résultat est la somme des largeurs d'opérandes.

Tu peux concaténer n'importe quel nombre d'opérandes :

wire [31:0] word = {byte3, byte2, byte1, byte0};

Et les opérandes peuvent avoir des largeurs différentes :

wire [11:0] frame = {start_bit, data_byte, parity, stop_bit};
//                    1 bit      8 bits     1 bit   2 bits  = 12

Une règle courante : toutes les opérandes d'une concaténation doivent avoir une largeur explicite. Les littéraux non dimensionnés (1 au lieu de 1'b1) causent des erreurs parce que le parser ne sait pas combien de bits allouer. Écris toujours 1'b0, 1'b1, 4'd0, jamais 0 ou 1 nus à l'intérieur de {}.

Concaténation à gauche

La concaténation à gauche d'une assignation divise un signal large en ses parties :

C'est le motif additionneur-avec-carry standard. {carry_out, sum_bits} est une destination 9 bits unique à gauche de l'assignation, et les bits sont distribués : bit du haut vers carry_out, les 8 bas vers sum_bits.

La concaténation à gauche marche dans assign et dans les blocs procéduraux :

always @(posedge clk) begin
    {high_byte, low_byte} <= incoming_word;
end

Réplication : {N{pattern}}

L'opérateur de réplication copie un motif un nombre fixé de fois. La forme est {N{pattern}} - un compteur, puis le motif dans sa propre paire d'accolades :

N doit être une constante - le compilateur a besoin de connaître la largeur du résultat à l'élaboration. Le motif peut être un littéral, un paramètre, ou n'importe quelle expression dont la largeur est connue.

N'oublie pas les accolades internes. {8 1'b1} est une erreur de syntaxe ; {8{1'b1}} est correct. Les {} externes sont l'opérateur ; les {...} internes entourent le motif à répliquer.

Combiner les deux : extension de signe et zero-extension

Les deux opérateurs se composent naturellement pour construire des valeurs plus larges :

Le { {8{a_negative[7]}}, a_negative } est le motif standard d'extension de signe. Lis-le ainsi : réplique le bit de signe huit fois, puis ajoute les 8 bits originaux. Résultat net : représentation complément à deux 16 bits du même nombre.

Pour l'élargissement non signé, tu peux compter sur l'assignation - Verilog zero-étend automatiquement quand la destination est plus large :

wire [15:0] zext_auto = a_unsigned;   // marche, les 8 bits hauts sont 0

Mais l'extension signée n'arrive jamais implicitement sauf si les deux opérande et destination sont déclarées signed. L'idiome de réplication explicite est plus sûr.

Motifs utiles

Construire un masque de N uns

parameter N = 5;
wire [31:0] mask = { {32-N{1'b0}}, {N{1'b1}} };
// par ex. avec N=5, mask = 32'h0000_001F

Inverser un vecteur

Verilog n'a pas de reverse intégré, mais tu peux en écrire un inline avec la concaténation :

wire [3:0] forward  = 4'b1010;
wire [3:0] reversed = {forward[0], forward[1], forward[2], forward[3]};
// reversed = 4'b0101

Pour des vecteurs plus larges, une boucle generate ou une function est plus propre.

Empaqueter des flags dans un octet de statut

wire [7:0] status = {
    error_flag,    // [7]
    overflow,      // [6]
    underflow,     // [5]
    ready,         // [4]
    busy,          // [3]
    1'b0,          // [2] réservé
    1'b0,          // [1] réservé
    valid          // [0]
};

Les slots réservés 1 bit ont des largeurs explicites - jamais de 0 nu à l'intérieur de {}.

Erreurs courantes

Littéraux non dimensionnés à l'intérieur de {}. {a, 0} est une erreur de syntaxe ou un zéro 32 bits de large. Dimensionne toujours : {a, 1'b0}.

Oublier les accolades internes dans la réplication. {8 1'b1} ne parse pas ; {8{1'b1}} oui.

Confondre l'ordre. {a, b} met a côté haut et b côté bas. Inverse ton hypothèse et tu auras un ordre d'octets inversé quelque part.

Répliquer zéro fois. {0{...}} est illégal en Verilog standard. SystemVerilog l'autorise (et produit un résultat de largeur zéro). Verilog classique le rejettera.

La suite

Tu as maintenant vu chaque opérateur Verilog. Le prochain chapitre change de vitesse et plonge dans la structure - comment les modules se connectent entre eux, les règles des ports, et la syntaxe pour instancier des sous-modules pour construire des designs plus larges.

Questions fréquentes

Comment concaténer des signaux en Verilog ?

Utilise les accolades : {a, b, c} produit un seul vecteur large en collant les opérandes de gauche à droite. Le MSB de a devient le MSB du résultat ; le LSB de c devient le LSB. La largeur du résultat est la somme des largeurs d'opérandes. La concaténation marche à la fois à droite d'une assignation (pour construire une valeur) et à gauche (pour diviser une valeur).

Que veut dire {N{pattern}} en Verilog ?

C'est l'opérateur de réplication : il produit N copies de pattern concaténées ensemble. {8{1'b1}} est une valeur all-ones 8 bits (8'hFF). {4{2'b10}} est la valeur 8 bits 8'b10101010. La réplication est ainsi qu'on fait du zero-extend, du sign-extend, ou qu'on construit n'importe quel motif de bits répétitif sans l'écrire à la main.

Comment sign-étendre un signal en Verilog ?

Utilise la réplication pour répéter le bit de signe : { {24{a[7]}}, a } étend un a 8 bits à 32 bits en répliquant le bit 7 (bit de signe) 24 fois puis en ajoutant l'original. Pour le zero-extension non signé, réplique 1'b0 ou laisse simplement l'assignation le faire implicitement quand la destination est plus large.

Peut-on utiliser la concaténation à gauche d'une assignation ?

Oui - c'est comme ça qu'on assigne plusieurs cibles depuis une source large. {carry, sum} = a + b; met le résultat de l'addition dans carry (le bit haut) et sum (le reste) en une seule instruction. Chaque cible garde sa largeur ; le parser distribue les bits du côté droit en conséquence.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER