Aller au contenu

Types et numeric_std

VHDL est fortement typé. Cette rigueur peut sembler lourde au début, mais elle évite des erreurs silencieuses de largeur, de signe et d'interprétation particulièrement dangereuses en matériel.

Types logiques IEEE

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

std_logic possède neuf valeurs :

Valeur Signification
U Non initialisé
X Valeur inconnue forcée ou conflit de pilotes
0, 1 Niveaux logiques forcés
Z Haute impédance
W Inconnue faible
L, H Bas/haut faibles
- Indifférent

Dans un RTL synchrone normal, les signaux internes doivent finir à 0 ou 1. Une valeur U ou X révèle utilement un défaut d'initialisation, de reset, d'affectation ou de pilotes multiples.

Warning

Ne masquez pas les inconnues par une conversion imprudente en entier. Corrigez leur cause.

Vecteur, non signé ou signé ?

Type Interprétation Usage conseillé
std_logic_vector Bits sans valeur numérique Bus, champs codés, ports
unsigned Entier binaire positif ou nul Compteurs, adresses, tailles
signed Entier en complément à deux Données signées et DSP

Choisissez le type qui exprime l'intention. Un std_logic_vector ne dit pas si son motif binaire est signé.

signal raw_i    : std_logic_vector(7 downto 0);
signal count_u  : unsigned(7 downto 0);
signal sample_s : signed(7 downto 0);

count_u  <= unsigned(raw_i);
sample_s <= signed(raw_i);

Ces opérations réinterprètent les bits sans changer leur taille.

Carte des conversions

u8 <= to_unsigned(integer_value, u8'length);
s8 <= to_signed(integer_value, s8'length);

integer_value := to_integer(u8);
integer_value := to_integer(s8);

slv <= std_logic_vector(u8);
slv <= std_logic_vector(s8);

u8 <= unsigned(slv);
s8 <= signed(slv);

Le signe doit toujours être explicite

signed(x"FF") vaut −1 sur 8 bits, tandis que unsigned(x"FF") vaut 255. Les bits sont identiques, leur sens arithmétique ne l'est pas.

Règles de largeur

Une addition ne fournit pas automatiquement un bit supplémentaire de retenue. Étendez d'abord les opérandes :

full_sum <= ('0' & a_i) + ('0' & b_i);
sum_o    <= full_sum(a_i'range);
carry_o  <= full_sum(full_sum'high);

Utilisez resize :

wide_u <= resize(narrow_u, wide_u'length); -- extension par zéro
wide_s <= resize(narrow_s, wide_s'length); -- extension du signe
small  <= resize(wide_u, small'length);    -- troncature volontaire

Lors d'une réduction de largeur, choisissez explicitement :

  • retour modulo/troncature ;
  • saturation au minimum ou maximum ;
  • signalement du dépassement ;
  • arrondi puis troncature.

Entiers et plages

Les entiers conviennent aux génériques, modèles de référence et compteurs à plage claire :

signal index_q : natural range 0 to 15 := 0;

Une plage contrainte documente l'intention et peut réduire le matériel, mais son dépassement provoque une erreur en simulation. Pour les chemins de données bit-exacts, unsigned ou signed rend souvent la largeur plus évidente.

Tableaux

type sample_array_t is array (natural range <>) of signed(15 downto 0);
signal history : sample_array_t(0 to 7);

type word_array_t is array (natural range <>) of std_logic_vector(7 downto 0);
signal registers : word_array_t(0 to 15);

Le symbole <> définit un tableau non contraint réutilisable avec plusieurs longueurs.

Enregistrements

type stream_t is record
  valid : std_logic;
  ready : std_logic;
  data  : std_logic_vector(7 downto 0);
end record;

Les enregistrements regroupent les signaux liés et facilitent l'évolution des interfaces internes. Des ports à plat restent souvent plus simples au sommet du FPGA pour les contraintes XDC.

Énumérations et sous-types

type state_t is (IDLE, LOAD, RUN, DONE);
signal state_q, state_d : state_t := IDLE;

subtype byte_t is std_logic_vector(7 downto 0);
subtype percentage_t is natural range 0 to 100;

Vivado choisit normalement l'encodage des états. Écrivez le comportement avec les noms, sans supposer leur motif binaire.

Virgule fixe

Pour commencer :

  1. Positionner la virgule binaire sur papier.
  2. Stocker les bits dans signed ou unsigned.
  3. Élargir volontairement produits et additions.
  4. Définir des fonctions nommées d'arrondi et de saturation.

En Q1.7 signé sur 8 bits, l'entier stocké 64 représente 0,5 car 64 / 128 = 0,5. Le paquetage IEEE fixed_pkg devient utile dans les projets plus vastes.

Règle sur les paquetages arithmétiques

Utilisez ieee.numeric_std. Évitez les anciens paquetages Synopsys non normalisés :

  • std_logic_arith
  • std_logic_unsigned
  • std_logic_signed

Les mélanger à numeric_std crée des surcharges ambiguës et du code non portable.

Liste de contrôle

  • Chaque vecteur possède-t-il un sens et un ordre de bits documentés ?
  • Les nombres signés et non signés ont-ils le bon type ?
  • Les opérandes sont-ils étendus avant une opération nécessitant un bit de plus ?
  • Chaque troncature, saturation ou retour modulo est-il volontaire ?
  • Les unités physiques sont-elles visibles dans les noms ou les types ?
  • Un générique peut-il créer une plage nulle ou négative ? Ajouter une assertion.