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_address
et write_data
n'ont pas vraiment d'importance lorsque le write_enable
signal 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
record
type, car les constantes d'enregistrement doivent être entièrement spécifiées (au moins, Modelsim me le dit). - Le
std_logic_1164
package définit la valeur'-' -- Don't care
destd_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 lescase?
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_address
peut 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;
la source
write_address
etwrite_data
? À quelle optimisation vous attendez-vous?Réponses:
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:
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é avecstd_match()
, que j'envisagerais d'utiliser pour votre décodage, par exemple:Bien qu'à ce stade, vous feriez probablement mieux d'utiliser simplement
case?
.la source
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 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.la source
if signal = '1' then a; else b; end if;
. Malheureusement, cesignal
n'était pas1
ou0
mais-
. Donc, dans la simulation, laelse
branche a été exécutée, mais dans le matériel, il-
s'est avéré être un1
, donc la vraie branche a été exécutée ...'U'
s, communs au début des simulations, qui ont été utilisés menant à l'else
exé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.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;
etif 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 pourwrite_data
. Ensemble, cela devrait détecter presque toutes ces erreurs.