Menu

Vecteurs et tableaux Verilog : signaux multi-bits expliqués

Comment déclarer des signaux multi-bits avec [7:0], les découper, les combiner, et la différence entre un vecteur packé et un tableau mémoire.

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

Un wire vs plusieurs

Jusqu'ici chaque signal qu'on a vu était sur un bit. Les vrais designs ne ressemblent presque jamais à ça - les adresses font 16 ou 32 bits, les bus de données 8 ou 64, les pixels RGB 24. Verilog te donne un seul mécanisme pour rendre n'importe quel signal multi-bits : ajouter une plage entre crochets.

wire [7:0] data;       // wire 8 bits, bit 7 est MSB, bit 0 est LSB
reg  [15:0] address;   // reg 16 bits
output reg [31:0] result; // sortie de module 32 bits

Les nombres entre crochets sont les positions de bits des bits les plus et les moins significatifs. [7:0] veut dire « ce signal a des bits numérotés de 7 à 0 », ce qui fait 8 bits au total. Le premier nombre est l'indice haut. Le second est le bas.

Tu verras occasionnellement [0:7] à la place - même nombre de bits, endianness opposée pour le slicing. La forme [haut:bas] est la convention industrielle écrasante ; reste là-dessus sauf raison forte de t'en écarter.

Un exemple complet : un additionneur 8 bits

Chaque + additionne les deux vecteurs 8 bits et produit un résultat 9 bits. Les littéraux comme 8'd10 veulent dire « une valeur décimale 10 de 8 bits de large » - on couvre ça dans Littéraux numériques.

Slicing : extraire des bits

Une fois que tu as un vecteur, tu peux extraire des bits individuels ou des plages contiguës :

Ne te focalise pas sur la ligne force du testbench - on a juste besoin d'un moyen d'injecter une valeur pour démontrer le slicing. Ce qui est intéressant, ce sont les slices eux-mêmes.

Quelques règles :

  • La direction du slice doit correspondre à la déclaration. Si tu as déclaré [7:0], slice avec [haut:bas]. Inverser la direction est une erreur de syntaxe.
  • Slicer hors-plage produit x (inconnu) en simulation. L'outil de synthèse peut warner ou erreur.
  • La sélection de bit est zéro-basée sur l'indice écrit - data[0] est le bit nommé 0, qui (pour une déclaration [7:0]) est le LSB.

Slices à base variable : +: et -:

Un besoin courant : « donne-moi 8 bits à partir du bit N ». Tu ne peux pas écrire data[N+7:N] directement parce que Verilog exige que les deux bouts de la plage soient des constantes. La syntaxe qui résout ça :

data[base +: width]   // width bits à partir de `base`, en MONTANT
data[base -: width]   // width bits à partir de `base`, en DESCENDANT

La largeur est constante (on prend 8 bits à la fois), mais la base peut être une expression runtime. C'est exactement ce dont tu as besoin pour des mémoires adressables par octet, des taps de registre à décalage, etc.

Tableaux : un cran au-dessus des vecteurs

Un vecteur est un seul signal multi-bits. Un tableau est une collection de vecteurs, indexés indépendamment :

reg [31:0] mem [0:1023];

Cette déclaration a deux plages, et les deux plages veulent dire des choses différentes :

  • [31:0] est la dimension packée - la largeur de chaque mot individuel.
  • [0:1023] est la dimension non packée - combien il y a de mots.

Donc mem est 1024 registres 32 bits séparés. Tu accèdes à un mot avec un seul indice :

mem[5] = 32'hCAFE_BABE;       // écris le mot 5
data   = mem[address];        // lis le mot à l'adresse `address`

C'est une mini mémoire qui contient des carrés. Les vrais designs utilisent le même motif pour contenir des banques de registres, des tables de lookup, des FIFOs, et tout autre stockage on-chip plus grand qu'un seul vecteur.

Packed vs unpacked : pourquoi ça compte

Le split entre dimensions packées et non packées apparaît partout. Savoir lequel est lequel évite beaucoup de debug :

  • Un vecteur packé est un seul signal. Tu peux traiter l'ensemble comme un nombre : data + 1 marche, data == 32'h0 marche, data[7:0] marche.
  • Un tableau non packé est plusieurs signaux. Tu ne peux pas traiter l'ensemble comme un nombre : mem + 1 est une erreur de syntaxe. Tu dois d'abord extraire un mot spécifique.

Plusieurs dimensions packées sont aussi légales :

reg [3:0][7:0] regs;   // 4 octets packés ensemble en un signal 32 bits

regs[0] est un octet (l'octet bas). regs dans l'ensemble fait 32 bits. SystemVerilog s'en sert beaucoup.

Plusieurs dimensions non packées créent une mémoire 2D :

reg [31:0] frame [0:479][0:639];  // 480x640 pixels 32 bits

Tu accèdes à un seul pixel avec frame[y][x]. C'est à ça que ressemblerait un buffer d'image en HDL.

La suite

Tu peux maintenant déclarer et manipuler n'importe quel signal de la largeur dont tu as besoin. Le prochain document - Paramètres - montre comment rendre ces largeurs configurables pour que le même module marche en 8 bits dans une instance et en 32 bits dans une autre. Puis on passera aux règles d'écriture des littéraux numériques (8'b1010_1100, 32'hDEAD_BEEF), et aux valeurs x/z qui apparaissent dès que quelque chose n'est pas piloté.

Questions fréquentes

Qu'est-ce qu'un vecteur en Verilog ?

Un vecteur est un signal multi-bits. Tu en déclares un en ajoutant une plage à un wire ou un reg : wire [7:0] data est un wire 8 bits. Les nombres entre crochets sont les positions des bits - ici bit 7 est le plus significatif et bit 0 est le moins significatif. Tu peux slicer des bits individuels (data[3]) ou des plages contiguës (data[7:4]).

Que veut dire [7:0] en Verilog ?

[7:0] déclare une plage du bit 7 au bit 0, inclusive - un signal 8 bits avec le bit 7 comme MSB. Le premier nombre est l'indice haut, le second est le bas. Tu peux aussi écrire [0:7] pour de l'indexation little-endian, mais la forme [haut:bas] est de loin la convention la plus répandue dans le code industriel.

Comment slicer des bits en Verilog ?

Utilise l'indexation avec crochets. data[3] prend un seul bit. data[7:4] prend les quatre bits du haut comme un vecteur 4 bits. Le slice doit être dans la même direction que la déclaration - si tu as déclaré [7:0], slice avec [haut:bas]. SystemVerilog ajoute aussi data[3 +: 4] pour des slices à base variable et largeur constante.

Quelle est la différence entre un tableau packé et un tableau non packé en Verilog ?

Un tableau packé est un seul bus contigu - reg [31:0] word est un signal 32 bits unique. Un tableau non packé (ou « mémoire ») est une collection de mots indépendants - reg [31:0] mem [0:1023] correspond à 1024 registres 32 bits séparés. Tu peux lire ou écrire un mot entier d'un tableau non packé mais tu ne peux pas opérer sur l'ensemble comme un seul signal.

Comment déclarer une mémoire en Verilog ?

reg [31:0] mem [0:1023]; déclare une mémoire de 1024 entrées, chacune 32 bits de large. Le premier jeu de crochets est la largeur de mot (packed) ; le second est le nombre de mots (unpacked). Accède à une entrée avec mem[address], et tu peux lire ou écrire un slice de cette entrée avec mem[address][7:0] une fois l'indexation SystemVerilog-2005 activée.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER