Pourquoi ce modèle VHDL simple pour un registre à décalage ne fonctionne-t-il pas comme prévu

8

À première vue, vous vous attendez à ce que le code source VHDL ci-dessous se comporte comme un registre à décalage. Dans ce q, au fil du temps serait

"UUUU0", "UUU00", "UU000", "U0000", "00000", ....

mais c'est toujours Uaprès cinq (ou plus) cycles d'horloge consécutifs.

Pourquoi est-ce?

Ce code est en fait une version beaucoup plus simplifiée d'une simulation beaucoup plus compliquée. Mais cela démontre les symptômes que je vois.

Il présente ce résultat intéressant et inattendu lors de la simulation sous ModelSim et ActiveHDL, je n'ai pas essayé d'autres simulateurs et j'aimerais (deuxièmement pour une explication de la cause) savoir si d'autres agissent de la même manière.

Pour répondre correctement à cette question, vous devez comprendre que:

  • Je sais que ce n'est pas la meilleure façon de mettre en place un registre à décalage
  • Je sais que pour la synthèse RTL, cela devrait avoir une réinitialisation.
  • Je sais qu'un tableau de std_logic est un std_logic_vector.
  • Je sais que l'opérateur d'agrégation, &.

Ce que j'ai également trouvé:

  • Si l'affectation temp(0)<='0';est déplacée à l'intérieur du processus, cela fonctionne.
  • Si la boucle est déroulée (voir le code commenté), cela fonctionne.

Je répéterai qu'il s'agit d'une version très simplifiée d'une conception beaucoup plus compliquée (pour un processeur pipeliné), configurée pour afficher uniquement les résultats de simulation inattendus. Les types de signaux réels ne sont qu'une simplification. Pour cette raison, vous devez considérer vos réponses avec le code dans le formulaire tel quel.

Je suppose que l'optimiseur du moteur de simulation VHDL ne prend pas la peine (ou peut-être selon les spécifications) d'exécuter les expressions à l'intérieur de la boucle car aucun signal extérieur ne change, bien que je puisse réfuter cela en plaçant la boucle non enveloppée dans une boucle.

Je m'attends donc à ce que la réponse à cette question soit davantage liée aux normes de simulation VHDL de la syntaxe VHDL inexplicite et à la façon dont les moteurs de simulation VHDL effectuent leurs optimisations, plutôt que si un exemple de code donné est la meilleure façon de faire quelque chose ou non.

Et maintenant, au code que je simule:

 library ieee;
 use ieee.std_logic_1164.all;   

 entity test_simple is
    port (
        clk : in  std_logic;
        q   : out std_logic
    );                   
 end entity;

 architecture example of test_simple is
    type   t_temp is array(4 downto 0) of std_logic;
    signal temp : t_temp;
 begin

    temp(0) <= '0';

    p : process (clk)
    begin               
        if rising_edge(clk) then
            for i in 1 to 4 loop
                    temp(i) <= temp(i - 1);
            end loop;

            --temp(1) <= temp(0);   
            --temp(2) <= temp(1);
            --temp(3) <= temp(2);
            --temp(4) <= temp(3);
        end if;
    end process p;
    q <= temp(4);
 end architecture;

Et le banc d'essai:

library ieee;
use ieee.std_logic_1164.all;

entity Bench is
end entity;

architecture tb of bench is

component test_simple is
    port (
        clk : in  std_logic;
        q   : out std_logic
    );                   
end component;

signal clk:std_logic:='0';
signal q:std_logic;     
signal rst:std_logic;

constant freq:real:=100.0e3;

begin                       
    clk<=not clk after 0.5 sec / freq;

    TB:process
    begin
        rst<='1';
        wait for 10 us;
        rst<='0';
        wait for 100 us;
        wait;
    end process;

     --Note: rst is not connected
    UUT:test_simple  port map (clk=>clk,q=>q) ;
end architecture;
Jason Morgan
la source
essayez d'abord d'initialiser le temp dans la déclaration du signal, j'ai trouvé que les simulateurs vhdl sont excentriques sur l'endroit où vous initialisez les choses
Matt
Il semblerait que le simulateur ignore l'affectation simultanée à temp(0)car aucun "événement" n'est associé à la constante littérale. Placer l'affectation à l'intérieur du processcrée une association avec les événements d'horloge qui le fait fonctionner. Je me demande si l'ajout d'une afterclause à la cession serait une solution de contournement potentielle.
Dave Tweed

Réponses:

7

Il s'agit de ce qui peut être facilement évalué au moment de l'élaboration, formellement, de ce qu'on appelle une "expression localement statique". C'est une règle d'apparence obscure, mais elle mérite une réflexion - elle a finalement un sens, et votre simulateur est tout à fait correct pour vous alerter en générant des résultats non évidents.

Maintenant, temp(1)peut être évalué au moment de la compilation (même plus tôt que le temps d'élaboration) et il peut générer un pilote sur le bit 1 de "temp".

Cependant, cela temp(i)implique un peu plus de travail pour les outils. Étant donné la nature triviale des limites de la boucle ici (1 à 4), il est évident pour nous, les humains, que temp (0) ne peut pas être piloté et ce que vous faites est sûr. Mais imaginez que les limites étaient des fonctions lower(foo) to upper(bar)dans un package déclaré ailleurs ... maintenant, tout ce que vous pouvez dire avec certitude est qu'il tempest piloté - ainsi l'expression "localement statique" l'est temp.

Et cela signifie que le processus est contraint par ces règles à tout piloter temp, auquel cas vous avez plusieurs pilotes sur temp(0)- le pilotage du processus (pas de valeur initiale, c'est-à-dire «u») et l'externe temp(0) <= '0';. Donc, naturellement, les deux pilotes se résolvent en «U».

L'alternative serait une "petite règle hacky" (opinion) que si les limites de la boucle étaient des constantes, faites une chose, mais si elles étaient déclarées comme autre chose, faites autre chose, et ainsi de suite ... plus de petites règles bizarres il y en a, plus la langue devient complexe ... à mon avis, pas une meilleure solution.

Brian Drummond
la source
Bonne réponse (+1), mais je ne suis pas d'accord avec votre qualification de "petite règle hacky". L'intérêt de la simulation est de représenter le comportement d'un matériel réel. Je comprends les contraintes créées par la compilation indépendante de modules individuels, mais je pense que la règle devrait être que tout ce qui peut être évalué au moment de la compilation devrait l' être. Ce serait une règle beaucoup plus générale qui aiderait le système à adhérer au principe de la "moindre surprise". Permettre aux outils de ne pas effectuer ces évaluations me semble plus "hacky".
Dave Tweed
Bon commentaire - Ada par exemple a (et exprime formellement) beaucoup plus de complexité sur des règles comme celle-ci, et parvient à présenter une vue beaucoup plus simple pour nous, utilisateurs (sans le facteur WTF de C!). VHDL a été à l'origine simplifié (OMI un peu trop loin) d'Ada. Mais peut-être pourrait-il adopter les règles de "gel de type" d'Ada qui permettraient ce type d'optimisation lorsqu'il est clairement sûr (comme ici) et l'interdiraient autrement ...
Brian Drummond
Merci Brian, ce que vous dites est certainement logique. L'idée d'une règle simple plutôt que de nombreuses règles obscures semble également logique. Diriez-vous que ce comportement est vrai (et en fait spécifié) pour tous les simulateurs ou est-ce juste les deux que j'ai essayés?
Jason Morgan
2
Si j'en trouvais un qui faisait quelque chose de différent, je déposerais un bug contre! Une chose que les plus grands détracteurs de VHDL diront en sa faveur est qu'elle garantit des résultats de simulation cohérents dans les cas où d'autres langues (pas seulement Verilog) ne le font pas. (bien que oui, parfois ses défauts me dérangent aussi!)
Brian Drummond
1
Expérience de correction rapide: si ma réponse est correcte, vous pouvez conduire "temp (0) <= 'Z';" dans le processus, donc "déconnecter" le pilote fantôme, et le pilote externe fonctionnera ...
Brian Drummond