Comment puis-je spécifier des signaux «peu importe» en VHDL?

11

Dans les cours de conception logique, nous avons tous appris qu'il était possible de minimiser une fonction logique, par exemple en utilisant une carte de Karnaugh ou l' algorithme Quine – McCluskey . Nous avons également appris que les valeurs "Don't Care" augmentent le potentiel de minimisation.

Par exemple, prenez un fichier de registre. Les signaux write_addresset write_datan'ont pas vraiment d'importance lorsque le write_enablesignal l'est '0'. Ainsi, il faut leur attribuer une valeur "Don't Care" pour permettre plus d'optimisations dans la logique qui pilote ces signaux (c'est-à-dire pas dans le fichier de registre lui-même).

Quelle est la bonne façon de spécifier de telles valeurs "Don't Care" dans VHDL afin de laisser à l'outil de synthèse plus de place pour les optimisations possibles?


Jusqu'à présent, j'ai trouvé les choses suivantes qui pourraient convenir. Mais je ne sais pas vraiment quels sont les avantages et les inconvénients de chaque approche:

  • Ne pas simplement assigner le signal. Cela semble pouvoir fonctionner. Cependant, j'ai trouvé que cela ne fonctionne pas lorsque vous souhaitez définir une "ne rien faire constante" d'un certain recordtype, car les constantes d'enregistrement doivent être entièrement spécifiées (au moins, Modelsim me le dit).
  • Le std_logic_1164package définit la valeur '-' -- Don't carede std_ulogic. Il semble que ce soit le choix sémantiquement correct pour un "indifférent" explicite, mais je ne l'ai jamais vu utilisé nulle part (sauf dans les case?constructions VHDL-2008 non apparentées ).
  • Modelsim utilise la valeur 'X'pour afficher des signaux non définis. Cependant, je ne sais pas si les outils de synthèse comprennent une 'X'affectation explicite comme "ne se soucient pas".

Voici un extrait de code simplifié à des fins de clarification, où j'ai initialisé les signaux indifférents avec '-'.

Comme vous pouvez le voir, le signal control.reg_write_addresspeut avoir 3 valeurs différentes: "----", instruction(11 downto 8);et instruction(3 downto 0);. Maintenant, je m'attends à ce que cela soit synthétisé sur un multiplexeur à 2 entrées s'il '-'est interprété comme "ne se soucie pas". Si j'avais initialisé le signal avec (others => '0')au lieu de '-', l'outil aurait dû générer un multiplexeur à 3 entrées à la place.

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

package mytypes is
    type control_signals_t is record
        write_enable  : std_logic;
        write_address : std_ulogic_vector(3 downto 0);
        read_address  : std_ulogic_vector(3 downto 0);
    end record;

    -- All members of this constant must be fully specified.
    -- So it's not possible to simply not assign a value.
    constant CONTROL_NOP : control_signals_t := (
        write_enable  => '0',
        write_address => (others => '-'),
        read_address  => (others => '-')
    );
end package;

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

entity control_unit is
    port(
        instruction : in  std_ulogic_vector(15 downto 0);
        write_data  : out std_ulogic_vector(15 downto 0);
        ctrl        : out control_signals_t
    );
end entity;

architecture rtl of control_unit is
begin
    decode_instruction : process(instruction) is
    begin
        -- Set sensible default values that do nothing.
        -- Especially all "write_enable" signals should be '0'.
        -- Everything else is mostly irrelevant (don't care).
        ctrl       <= CONTROL_NOP;
        write_data <= (others => '-');

        if instruction(15 downto 12) = "1100" then
            -- Load 8 bit of data into the register file
            ctrl.write_enable  <= '1';
            write_data         <= std_ulogic_vector(resize(signed(instruction(7 downto 0)), 16));
            ctrl.write_address <= instruction(11 downto 8);
        elsif instruction(15 downto 8) = "11111001" then
            -- Load 4 bit of data into the register file
            write_data         <= std_ulogic_vector(resize(signed(instruction(7 downto 4)), 16));
            ctrl.write_address <= instruction(3 downto 0);
        elsif instruction(15 downto 8) = "10110101" then
            -- Read from the register file. Don't use the write signals at all.
            ctrl.read_address <= instruction(3 downto 0);
        end if;
    end process;
end architecture;
Fritz
la source
Pourriez-vous nous expliquer ce que vous essayez de faire avec write_addresset write_data? À quelle optimisation vous attendez-vous?
fru1tbat
J'espère que l'exemple montre plus clairement ce que je veux réaliser.
Fritz

Réponses:

9

Je laisse le soin à un expert LRM de fournir une réponse plus détaillée, mais en bref, votre approche devrait être valide - j'ai effectué un test rapide avec une version récente de Quartus, et il gère '-'comme il est censé - la logique générée est réduite comme prévu lorsque la sortie est par défaut '-'( 'X'fonctionne aussi, soit dit en passant). En savoir plus sur les approches que vous avez énumérées:

  • Ne pas attribuer le signal n'est pas vraiment une option pour votre exemple, bien sûr, si vous ne voulez pas de verrous. Si c'est un processus cadencé, vous êtes un peu mieux, mais vous obtiendrez toujours des permis là où vous n'en aurez peut-être pas besoin. Peut-être que je manque votre intention ici.

  • '-', comme indiqué précédemment, est probablement la meilleure option, à la fois pour des raisons sémantiques et pratiques.

  • Cela dépend de ce que vous entendez par «non défini». 'X'est techniquement "inconnu". 'U'est pour les signaux non initialisés, que ModelSim affiche comme "X"pour les représentations hexadécimales. 'X'semble fonctionner, comme je l'ai noté ci-dessus.

Une autre alternative serait de faire l'optimisation vous-même et de supprimer explicitement un cas de test:

if instruction(15 downto 8) = "11111001" then
  write_data <= std_ulogic_vector(resize(signed(instruction(7 downto 4)), 16));
else
  write_data <= std_ulogic_vector(resize(signed(instruction(7 downto 0)), 16));
end if;

Cela a cependant des inconvénients importants (principalement liés à la clarté du code), et j'opterais probablement pour une solution plus idéale.

Soit dit en passant, '-'est également couramment utilisé avec std_match(), que j'envisagerais d'utiliser pour votre décodage, par exemple:

if std_match(instruction(15 downto 8), "1100----") then

Bien qu'à ce stade, vous feriez probablement mieux d'utiliser simplement case?.

fru1tbat
la source
6

En bref: c'est du VHDL légal et il est généralement pris en charge par des outils de synthèse.

Il est cependant assez rare de le voir utilisé. Je ne sais pas vraiment pourquoi. Votre code me semble être un bon exemple du moment où il serait utile de l'utiliser.

Il y a cependant un inconvénient dont il faut être conscient: lors de la synthèse, les fonctions de pilotage des sorties où les soucis ne sont pas impliqués peuvent être différentes entre les cycles de synthèse. Cela rend la synthèse moins déterministe. Si des sorties définies comme non importantes sont utilisées (par erreur), cela peut rendre l'erreur plus difficile à trouver.

Support d'outils

Au moins, les outils suivants accepteront les soucis et utiliseront les possibilités d'optimisation:

  • Xilinx (réf.: "Guide de l'utilisateur XST")
  • Altera (réf.: "Styles de codage HDL recommandés")
  • Synplify (réf.: "Manuel de référence Synplify")

Xilinx et Altera traiteront '-'et 'X'comme ne s'en soucient pas, Synplify les traitera et en plus 'U'et 'W'(faibles) comme ne s'en soucient pas.

Carl
la source
1
J'ai eu une autre application de cet inconvénient. Le code a travaillé dans la simulation, mais pas sur le FPGA, parce que mon code ressemblait à : if signal = '1' then a; else b; end if;. Malheureusement, ce signaln'était pas 1ou 0mais -. Donc, dans la simulation, la elsebranche a été exécutée, mais dans le matériel, il -s'est avéré être un 1, donc la vraie branche a été exécutée ...
Fritz
Oui, j'ai eu des simulations de bogues similaires, mais le plus souvent dans mon cas, il y a les 'U's, communs au début des simulations, qui ont été utilisés menant à l' elseexécution d'un bloc de code. Ce serait merveilleux si les conditions pouvaient être en quelque sorte faites pour propager 'U's, similaire au comportement d'expressions booléennes simultanées.
Carl
Après avoir trouvé ce bug, je me suis assuré d' écrire toujours quelque chose comme if signal = '1' then output <= '1'; elsif signal='0' then output <= '0'; else output <= '-'; end if;. Et j'ai ajouté ce qui suit à tous les registres et mémoires: assert not is_X(write_enable) report "we=" & str(A_write_enable) severity ERROR;et if write_enable = '1' then assert not is_X(write_addr) report "write_addr=str(write_addr) severity ERROR; end if;. Plus la même chose pour write_data. Ensemble, cela devrait détecter presque toutes ces erreurs.
Fritz
C'est une façon, mais c'est beaucoup trop verbeux pour moi. Je souhaiterais cette possibilité dans le langage VHDL.
Carl
1
Eh bien oui, VHDL est un peu bavard, mais c'est juste la façon VHDL. : D Par contre il est aussi très explicite, et ne fait pas de "magie noire" derrière mon dos, ce que je trouve assez sympa (cf. Le Zen de Python "Explicit vaut mieux qu'implicite").
Fritz