Warum funktioniert dieses einfache VHDL-Muster für ein Schieberegister nicht wie erwartet?

8

Auf den ersten Blick würde man erwarten, dass sich der folgende VHDL-Quellcode wie ein Schieberegister verhält. In diesem q wäre im Laufe der Zeit

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

Stattdessen ist es immer Unach fünf (oder mehr) aufeinanderfolgenden Taktzyklen.

Warum ist das?

Dieser Code ist eigentlich eine stark vereinfachte Version einer weitaus komplizierteren Simulation. Aber es zeigt die Symptome, die ich sehe.

Es zeigt dieses interessante und unerwartete Ergebnis während der Simulation unter ModelSim und ActiveHDL. Ich habe keine anderen Simulatoren ausprobiert und möchte (zweitens zur Erklärung der Ursache) wissen, ob andere auf die gleiche Weise handeln.

Um diese Frage richtig zu beantworten, müssen Sie Folgendes verstehen:

  • Ich weiß, dass dies nicht der beste Weg ist, ein Schieberegister zu implementieren
  • Ich weiß, dass dies für die RTL-Synthese zurückgesetzt werden sollte.
  • Ich weiß, dass ein Array von std_logic ein std_logic_vector ist.
  • Ich kenne den Aggregationsoperator &.

Was ich auch gefunden habe:

  • Wenn die Zuordnung temp(0)<='0';innerhalb des Prozesses verschoben wird, funktioniert sie.
  • Wenn die Schleife entpackt ist (siehe kommentierten Code), funktioniert sie.

Ich werde noch einmal betonen, dass dies eine sehr vereinfachte Version eines viel komplizierteren Designs (für eine Pipeline-CPU) ist, das so konfiguriert ist, dass nur die unerwarteten Simulationsergebnisse angezeigt werden. Die tatsächlichen Signaltypen sind nur eine Vereinfachung. Aus diesem Grund müssen Sie Ihre Antworten mit dem Code im Formular so wie er ist betrachten.

Ich vermute, dass der Optimierer der VHDL-Simulations-Engine fälschlicherweise (oder möglicherweise gemäß Spezifikation) nicht die Mühe macht, die Ausdrücke innerhalb der Schleife auszuführen, da sich keine Signale außerhalb ändern, obwohl ich dies widerlegen kann, indem ich die nicht verpackte Schleife in eine Schleife setze.

Ich gehe daher davon aus, dass die Antwort auf diese Frage eher mit den Standards für die VHDL-Simulation unerklärlicher VHDL-Syntax und der Art und Weise zu tun hat, wie VHDL-Simulationsmodule ihre Optimierungen vornehmen, als ob ein gegebenes Codebeispiel der beste Weg ist, etwas zu tun oder nicht.

Und nun zum Code, den ich simuliere:

 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;

Und der Prüfstand:

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
quelle
Versuchen Sie zuerst, die Temperatur in der Signaldeklaration zu initialisieren. Ich habe festgestellt, dass VHDL-Simulatoren nicht genau wissen, wo Sie die Dinge initialisieren
Matt
Es scheint, dass der Simulator die gleichzeitige Zuordnung zu ignoriert, da der Literalkonstante temp(0)keine "Ereignisse" zugeordnet sind. Durch das Einfügen der Zuweisung in processwird eine Zuordnung zu den Uhrereignissen erstellt, die dazu führen, dass sie funktioniert. Ich frage mich, ob das Hinzufügen einer afterKlausel zur Zuweisung eine mögliche Problemumgehung wäre.
Dave Tweed

Antworten:

7

Es hat mit dem zu tun, was zur Ausarbeitungszeit formal leicht ausgewertet werden kann, was als "lokal statischer Ausdruck" bezeichnet wird. Dies ist eine obskur aussehende Regel, aber sie verdient einige Überlegungen - schließlich macht sie Sinn, und Ihr Simulator warnt Sie ganz korrekt, indem er nicht offensichtliche Ergebnisse generiert.

Nun temp(1)kann bei der Kompilierung ausgewertet (sogar früher als Ausarbeitung Zeit) und es kann einen Fahrer auf Bit 1 von „temp“ erzeugen.

Dies temp(i)erfordert jedoch etwas mehr Arbeit für die Werkzeuge. Angesichts der Trivialität der Schleifengrenzen hier (1 bis 4) ist es für uns Menschen offensichtlich, dass Temp (0) nicht gefahren werden kann und was Sie tun, sicher ist. Aber stellen Sie sich vor, die Grenzen wären Funktionen lower(foo) to upper(bar)in einem Paket, das woanders deklariert wurde ... jetzt können Sie mit Sicherheit nur sagen, dass sie tempgesteuert werden - also ist der "lokal statische" Ausdruck temp.

Und das bedeutet, dass der Prozess durch diese Regeln eingeschränkt wird, um alle zu steuern temp. An diesem Punkt haben Sie mehrere Treiber aktiviert temp(0)- den Prozess, der fährt (kein Anfangswert, dh 'u') und den externen temp(0) <= '0';. Natürlich lösen sich die beiden Treiber in 'U' auf.

Die Alternative wäre eine "hackige kleine Regel" (Meinung): Wenn die Schleifengrenzen Konstanten wären, tun Sie eine Sache, aber wenn sie als etwas anderes deklariert wurden, tun Sie etwas anderes und so weiter ... umso mehr solche seltsamen kleinen Regeln Je komplexer die Sprache wird ... meiner Meinung nach keine bessere Lösung.

Brian Drummond
quelle
Gute Antwort (+1), aber ich bin nicht einverstanden mit Ihrer Charakterisierung von "hacky little rule". Der Sinn der Simulation besteht darin, das Verhalten realer Hardware darzustellen. Ich verstehe die Einschränkungen, die durch die unabhängige Kompilierung einzelner Module entstehen, aber ich denke, dass die Regel lauten sollte, dass alles, was zur Kompilierungszeit ausgewertet werden kann, sein sollte . Dies wäre eine viel allgemeinere Regel und würde dem System helfen, das Prinzip der "geringsten Überraschung" einzuhalten. Das Zulassen, dass die Tools diese Bewertungen nicht durchführen, fühlt sich für mich "hackiger" an.
Dave Tweed
Fairer Kommentar - Ada zum Beispiel hat (und formell ausgedrückt) viel mehr Komplexität in Bezug auf solche Regeln und schafft es, uns Benutzern eine viel einfachere Ansicht zu bieten (ohne den WTF-Faktor C!). VHDL wurde ursprünglich vereinfacht (IMO etwas zu weit entfernt) von Ada. Aber vielleicht könnte es Adas "Type Freezing" -Regeln übernehmen, die diese Art der Optimierung ermöglichen, wenn sie eindeutig sicher sind (wie hier), und sie ansonsten verbieten ...
Brian Drummond
Danke Brian, was du sagst, macht auf jeden Fall Sinn. Die Idee einer einfachen Regel anstelle vieler dunkler Regeln scheint ebenfalls sinnvoll zu sein. Würden Sie sagen, dass dieses Verhalten für alle Simulatoren gilt (und tatsächlich spezifiziert ist), oder sind es nur die beiden, die ich ausprobiert habe?
Jason Morgan
2
Wenn ich einen finden würde, der etwas anderes macht, würde ich einen Fehler dagegen einreichen! Eine Sache, die die größten Kritiker von VHDL zu ihren Gunsten sagen werden, ist, dass sie konsistente Simulationsergebnisse in Fällen garantiert, in denen andere Sprachen (nicht nur Verilog) dies nicht tun. (obwohl ja, manchmal nerven mich auch seine Mängel!)
Brian Drummond
1
Schnellkorrektur-Experiment: Wenn meine Antwort richtig ist, können Sie "temp (0) <= 'Z';" Innerhalb des Prozesses wird daher der Phantomtreiber "getrennt" und der externe Treiber wird funktionieren ...
Brian Drummond