Maîtriser les types Signé vs Non-signé en VHDL : insights pratiques
Les concepteurs numériques s'appuient sur l'arithmétique précise à l'intérieur des FPGA et ASIC. Un concept central est la distinction entre les types de données signés et non-signés en VHDL.
Les deux types sont définis dans le package numeric_std de la bibliothèque IEEE. Bien que de nombreux concepteurs utilisent encore std_logic_arith, ce package n'est pas standard IEEE et n'est pas recommandé pour les designs de production.
En VHDL, un signal signé est interprété comme un nombre en complément à deux, permettant valeurs positives et négatives. Un signal non-signé représente seulement des valeurs non-négatives. En interne, le FPGA utilise toujours le complément à deux pour tous les vecteurs de bits, quel que soit le type que vous déclarez.
| Bits | Valeur Non-signée | Valeur Signée |
|---|---|---|
| 011 | 3 | 3 |
| 010 | 2 | 2 |
| 001 | 1 | 1 |
| 000 | 0 | 0 |
| 111 | 7 | -1 |
| 110 | 6 | -2 |
| 101 | 5 | -3 |
| 100 | 4 | -4 |
Au premier abord ce mapping peut sembler contre-intuitif, mais une fois que vous comprenez le complément à deux, le comportement devient prévisible.
Il est important de noter que le résultat binaire d'une opération arithmétique est identique que les opérandes soient déclarés signés ou non-signés. Ce qui change, c'est l'interprétation de ce résultat.
Par exemple, l'addition de deux vecteurs 5 bits :
- Signé : 10001 + 00010 = 10011 (interprété comme –13)
- Non-signé : 10001 + 00010 = 10011 (interprété comme 19)
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity signed_unsigned is port ( i_rst_l : in std_logic; i_clk : in std_logic; i_a : in std_logic_vector(4 downto 0); i_b : in std_logic_vector(4 downto 0) ); end signed_unsigned; architecture behave of signed_unsigned is signal rs_SUM_RESULT : signed(4 downto 0) := (others => '0'); signal ru_SUM_RESULT : unsigned(4 downto 0) := (others => '0'); signal rs_SUB_RESULT : signed(4 downto 0) := (others => '0'); signal ru_SUB_RESULT : unsigned(4 downto 0) := (others => '0'); begin p_SUM : process (i_clk, i_rst_l) begin if i_rst_l = '0' then rs_SUM_RESULT <= (others => '0'); ru_SUM_RESULT <= (others => '0'); elsif rising_edge(i_clk) then ru_SUM_RESULT <= unsigned(i_a) + unsigned(i_b); rs_SUM_RESULT <= signed(i_a) + signed(i_b); end if; end process p_SUM; p_SUB : process (i_clk, i_rst_l) begin if i_rst_l = '0' then rs_SUB_RESULT <= (others => '0'); ru_SUB_RESULT <= (others => '0'); elsif rising_edge(i_clk) then ru_SUB_RESULT <= unsigned(i_a) - unsigned(i_b); rs_SUB_RESULT <= signed(i_a) - signed(i_b); end if; end process p_SUB; end behave;
Testbench
library ieee; use ieee.std_logic_1164.all; entity example_signed_unsigned_tb is end example_signed_unsigned_tb; architecture behave of example_signed_unsigned_tb is signal r_CLK : std_logic := '0'; signal r_RST_L : std_logic := '0'; signal r_A : natural := 0; signal r_B : natural := 0; signal r_A_SLV : std_logic_vector(4 downto 0) := (others => '0'); signal r_B_SLV : std_logic_vector(4 downto 0) := (others => '0'); constant c_CLK_PERIOD : time := 10 ns; component example_signed_unsigned is port ( i_rst_l : in std_logic; i_clk : in std_logic; i_a : in std_logic_vector(4 downto 0); i_b : in std_logic_vector(4 downto 0) ); end component example_signed_unsigned; begin i_DUT: example_signed_unsigned port map ( i_rst_l => r_RST_L, i_clk => r_CLK, i_a => r_A_SLV, i_b => r_B_SLV ); clk_gen : process is begin r_CLK <= '0' after c_CLK_PERIOD/2, '1' after c_CLK_PERIOD; wait for c_CLK_PERIOD; end process clk_gen; process begin r_RST_L <= '0'; wait for 20 ns; r_RST_L <= '1'; wait for 20 ns; r_A_SLV <= "01001"; r_B_SLV <= "00110"; wait for 20 ns; r_A_SLV <= "10001"; r_B_SLV <= "00110"; wait for 20 ns; r_A_SLV <= "10001"; r_B_SLV <= "00001"; wait for 20 ns; r_A_SLV <= "10001"; r_B_SLV <= "00010"; wait for 20 ns; r_A_SLV <= "11111"; r_B_SLV <= "00001"; wait for 20 ns; r_A_SLV <= "00000"; r_B_SLV <= "00001"; wait for 20 ns; wait; end process; end behave;
Simulation ModelSim – valeurs hexadécimales
Simulation ModelSim – valeurs décimales
Les deux captures démontrent que les résultats binaires sous-jacents sont identiques quel que soit le type. La différence n'apparaît que quand le simulateur affiche les valeurs : en hexadécimal le même motif de bits apparaît, tandis qu'en décimal les résultats signés peuvent apparaître négatifs. Une manipulation soignée des types est essentielle lors de la conception de logique FPGA.
Si une partie de ce sujet reste floue, n'hésitez pas à nous envoyer un email via le lien de contact dans la barre latérale. Nous serons ravis de vous aider à maîtriser l'arithmétique signée et non-signée.
VHDL
- Introduction à VHDL : construire votre première porte ET
- Guide complet des conversions de types VHDL avec Numeric_Std et Std_Logic_Arith
- Exemple de procédure VHDL : incrémenter un std_logic_vector
- Exploiter les records VHDL pour des interfaces FIFO propres et réutilisables
- Variables VHDL expliquées : exemples pratiques et règles pour un design fiable
- Systèmes de signaux de courant : la boucle 4–20 mA expliquée
- Feux de signalisation : ingénierie, histoire et innovations futures
- Créer des listes de chaînes en VHDL : Bonnes pratiques et exemple
- Assurer l'Intégrité du Signal dans la Conception PCB Haute Vitesse