Falsche Ausgaben in der VHDL-Entität

Ich habe Unterricht über VHDL in einer meiner Universitätsklassen und muss einfach schreiben, entitywas einen Takt aus einer 1-MHz-Quelle generiert. Ich verwende das CoolRunner-II CPLD Starter Kit mit ISE Webpack 13.1.

Wenn ich die Simulation meines Codes ausführe, erhalte ich seltsame Ergebnisse. Ich habe keine Ahnung, wo das Problem liegt. Meine VHDL-Entität sieht so aus:

entity clock is
  Port ( clk_in : in  STD_LOGIC;
    clk_1M : out  STD_LOGIC;
    clk_500k : out  STD_LOGIC;
    clk_100k : out  STD_LOGIC;
    clk_1k : out  STD_LOGIC;
    clk_1hz: out STD_LOGIC);
end clock;

Der Eingang ist ein 1-MHz-Signal vom Oszillator, und ich möchte ein Ausgangssignal von 1 MHz, 500 kHz, 100 kHz, 1 kHz und 1 Hz erzeugen. Ich habe mehrere Signale definiert:

signal c100k: std_logic_vector(3 downto 0) := (others => '0' );
signal c1k:  std_logic_vector(9 downto 0) := (others => '0' );
signal c1hz: std_logic_vector(9 downto 0) := (others => '0' );
--
signal c500k_out: std_logic := '0';
signal c100k_out: std_logic := '0';
signal c1k_out: std_logic := '0';
signal c1hz_out: std_logic := '0';

Und schließlich ist mein Code:

process (clk_in) begin
  if clk_in'event and clk_in = '1' then
    -- 500kHz
    c500k_out <= not c500k_out;
  end if;
end process;

process (clk_in) begin
  if clk_in'event and clk_in = '1' then
    -- 100kHz
    c100k <= c100k + '1';
    if c100k = X"A" then
      c100k <= (others => '0' );
      c100k_out <= '1';
    else
      c100k_out <= '0';
    end if;
  end if;
end process;

--
-- Code for 1kHz and 1Hz is same as 100kHz
--

clk_1M <= clk_in;           -- Clock source 1Mhz
clk_500k <= c500k_out;  -- Clock source 500kHz
clk_100k <= c100k_out;  -- Clock source 100kHz
clk_1k <= c1k_out;      -- Clock source 1kHz
clk_1hz <= c1hz_out;        -- Clock source 1Hz

Wenn ich die Simulation durchführe, habe ich diese seltsamen Ergebnisse erhalten:

Uhrensimulation

Was ist falsch an meinem Code?

Ich habe hier funktionierenden Code hinzugefügt – pub.uart.cz/vhdl

Antworten (4)

Das erste, was ich sehe, was wahrscheinlich nicht Ihr Problem verursacht, ist das Fehlen jeglicher Art von Reset. ModelSim deklariert nicht automatisch alle Signale als '0', und der Versuch, s <= nicht s zu sagen, wenn s nicht 1 oder 0 ist, wird Ihnen nicht das geben, was Sie wollen. Ich sehe, dass Sie sie alle oben mit '0' deklarieren, aber eine richtige Schaltung hätte einen Reset-Eingang, den Sie zu Beginn Ihrer Simulation kurz ansteuern.

Ihr grundlegender Gegenprozess sieht falsch aus; Wenn die Zählung zehn erreicht, geben Sie im Grunde eine „1“ aus und für die anderen 9/10 der Zeit geben Sie eine „0“ aus (für das 100k-Beispiel wäre das 1-Hz-Beispiel ein 1us-Impuls hoch und ein 999us Niedrige Zeit. Ich denke, was Sie wollen, ist ungefähr Folgendes:

gen_clk100: process(clk, rst)
begin
    if rising_edge(clk) then
        if count = 10 then
            clk100 <= not clk100;
            count <= 0;
        else
            count <= count + 1;
        end if;
    end if;

    if rst = '1' then
        clk100 <= '0';
        count <= 0;
    end if;
end process;

Beachten Sie mehrere Dinge:

  • Ich steigere nicht ständig. Ich inkrementiere, wenn die Zählung nicht auf ihrem Maximum ist, und inkrementiere andernfalls.

  • Ich schalte den Ausgangstakt um, wenn die maximale Anzahl erreicht ist

  • Ich habe ein asynchrones Zurücksetzen (mit synchronem Löschen) eingefügt - dies stellt sicher, dass Ihre Signale korrekt initialisiert werden und keine Metastabilitätsprobleme auftreten, wenn das Zurücksetzen freigegeben wird.

Sie erwähnen auch, dass die anderen Abschnitte gleich sind. Ich denke, Sie haben ein Problem mit mehreren Treibern, wie Yann erwähnt hat. Überprüfen Sie Ihren Code mehrmals, um sicherzustellen, dass Sie die Ausgabe nicht in zwei verschiedenen Prozessen zuweisen, denn genau das sagt die Simulation. Besser entweder ein generisches Zählermodul bauen oder ...

Warum so viele Zähler? Alle Ihre Frequenzteiler können mit einer einzigen Zählung behandelt werden und dann die Bedingungen "abziehen", um die Divisionen durchzuführen, an denen Sie interessiert sind. Sie könnten auch einen einzelnen Zähler haben und den (Modulus) -Operator verwenden, um jeden Teilerfall zu behandeln mod.

Schließlich würde ich anstelle von s auch reelle oder ganzzahlige Typen für die Anzahl verwenden, std_logic_vectoraber das bin nur ich.

Das Fehlen eines Resets wird keine Probleme in ModelSim oder einem Simulator verursachen (er verwendet ISIM, denke ich). Allen Signalen wird bei der Deklaration des Signals ein Anfangswert gegeben, der für den Simulator ausreicht. Ihr Beispiel-Zählercode würde aufgrund mehrerer Treiber zu einer schlechten Logik führen (die Blöcke „wenn steigend“ und „wenn zurückgesetzt“ werden als separate Prozesse betrachtet). Der richtige Weg wäre "if reset...else if ascending_edge...". Ich rate davon ab, reelle oder ganzzahlige Typen für Signale zu verwenden, die in tatsächliche Logik synthetisiert (nicht nur simuliert) werden. Aber das bin nur ich.
Ich stimme zu, und deshalb habe ich ausdrücklich gesagt, dass die Resets das Problem nicht verursachen. Das Prozessbeispiel, das ich gegeben habe, ist ein Lehrbuchbeispiel, das in Dutzenden von Designs verwendet wird; Es gibt keine Probleme damit, zumindest nicht mit Quartus oder ISE. Real/Integer-Typen werden meiner Erfahrung nach auch korrekt synthetisiert. Sind Sie auf Probleme gestoßen?
Real synthetisiert nicht. Sie können eine reelle Zahl verwenden, aber sie muss zur Synthesezeit in einen Integer- oder Bit-Vektortyp aufgelöst werden. Sie können beispielsweise keinen Zähler vom Typ "Real" haben, der bei jedem Taktzyklus um 0,184739 zählt. Während Integer synthetisiert werden, gibt Ihnen die Verwendung von SLV stattdessen viel mehr Kontrolle. Oft kann ich die Logik so optimieren, dass sie mit SLV viel kleiner ist und eine viel höhere Leistung als Integer (in Xilinx FPGAs) verwendet. Bei Ganzzahlen haben Sie keine Kontrolle oder Zugriff auf die interne Logikdarstellung im FPGA, während Sie mit SLV Tricks wie die effiziente Nutzung der Übertragskette ausführen können.
Sie haben Recht; Das einzige Mal, dass ich real verwendet habe, war eine Ganzzahl, also ist das Ei auf meinem Gesicht, weil ich es ohne einen richtigen Haftungsausschluss vorgeschlagen habe. :-/ Ich habe jedoch fast ausschließlich Ranged-Integer-Typen für meine Zähler verwendet (Signal/Variable foo: Integer-Bereich 0 bis 255;) -- Ich glaube nicht, dass es in dieser Hinsicht weniger kontrolliert ist als slv, und es macht Ihr Code viel sauberer zum Booten (nichts von diesem unsigned(some_slv) oder x"0400" Unsinn.). Ich weiß, dass die Methodik von ISE und Quartus ganz anders ist. Das ist einer der Gründe, warum ich Altera Xilinx vorziehe, aber das ist ein ganz anderer heiliger Krieg. :-)
Danke schön. Deine Antwort hat mir sehr geholfen. Jetzt funktionieren alle Taktquellen gut und die Simulation liefert die richtigen Ergebnisse. Im Moment versuche ich, meinen Code in einen Prozess mit einem Zähler umzuschreiben.
Angesichts der Tatsache, dass es sich um eine Dekade (10 = 2 * 5, und 5 ist keine Potenz von 2) Taktteilung handelt, wäre das Tippen auf einen einzelnen Zähler sehr kostspielig. Ich denke, die kaskadierenden Modulo-10-Zähler sind in Ordnung, was der Verwendung eines BCD-Zählers mit Logik entspricht, die sich in die frühen Übertragssignale einklinkt.
Übrigens ist der Stil mit einem Impuls pro Periode genau richtig, wenn Sie sie als Taktfreigaben und nicht als Taktgeber verwenden möchten, wodurch mehr Taktdomänen vermieden werden. Sie erfüllen auch die Anforderungen an die Impulslänge innerhalb eines FPGA, sodass die Verwendung von Posedge möglich ist ... aber verwechseln Sie sie nicht mit synchron zu clk_in (es gibt mindestens eine Registerlaufzeitverzögerung).
Es hängt wirklich davon ab, was die tatsächlichen Anforderungen sind; Ich nahm seine Frage als eine Art Hausaufgabenhilfefrage und antwortete entsprechend. Ja, Sie möchten Uhraktivierungen ( if rising_edge(clk) then if ce_100k = '1' then ... end if; end if;) verwenden, anstatt neue Uhren zu erstellen ( if rising_edge(clk100k) then ... end if;), und es gibt verschiedene Möglichkeiten, das System zu optimieren, wie @DavidKessner betonte, aber ich denke, was ich vorgeschlagen habe, war der beste "allgemeine Zweck" und "sicherste" Ratschlag , und Beratung, die eine sehr hohe Wahrscheinlichkeit hat, das zu tun, was er braucht.
@DavidKessner: Haben Sie ein Beispiel für ganze Zahlen, die die Carry-Kette nicht effektiv nutzen? Meines Wissens nach war das vor langer Zeit so, wenn überhaupt!
@MartinThompson Dies ist ein riesiges Thema, das nicht in einem Kommentar angesprochen werden kann. Wenn Sie eine Frage stellen, gehe ich gerne ausführlich darauf ein. Aber hier ist die schnelle Antwort: Es ist nicht so, dass Integer selbst die Carry-Kette nicht effektiv nutzen, es ist so, dass Sie keinen einfachen Zugriff auf die interne logische Darstellung der Integer haben und daher einige mathematische Tricks nicht anwenden können, die dies tun würden Machen Sie Ihre Logik kleiner und schneller. Die meisten dieser mathematischen Tricks machen sich die Übertragskette zunutze, weshalb ich sage, dass Sie mit ganzen Zahlen die Übertragskette nicht effektiv nutzen können.
@DavidKessner: Ahh, OK, jetzt verstehe ich, was du meinst – ja, das stimmt. Und damit Sie weiter darauf eingehen können, stelle ich die Frage so, wie Sie es vorschlagen :) electronic.stackexchange.com/q/27921/763

Sie ordnen die Signale clk_1M, clk_500k, clk_100k, clk_1k und clk_1hz in verschiedenen Prozessen im Prüfstand zu. Gleichzeitig haben Sie Ihr DUT instanziiert, das (wie Yann Vernier vorschlägt) dieselben Signale ansteuert. Entkommentieren Sie die Prüfstandsprozesse (außer clk_in!) Und alles wird gut.

Außerdem würde ich Ihnen raten, der Clock-Entität ein asynchrones Reset-Signal hinzuzufügen, um sie synthetisierbar zu machen.

-1 Sie brauchen kein asynchrones Zurücksetzen, um es synthetisierbar zu machen - in vielen Fällen möchten Sie kein asynchrones Zurücksetzen (obwohl es in einem Coolrunner2-CPLD nicht sehr weh tut).
@Per E, Sie scheinen für Ihren letzten Rat abgelehnt worden zu sein, nicht für Ihr Fleisch des Rates. Anscheinend hat niemand Ihre Antwort gestohlen, es scheint, dass sie sich stattdessen die Zeit genommen haben, die Ursache des Problems genauer zu erklären. Entschuldigen Sie die Verwirrung, die dadurch entstanden sein könnte.
Die Testbench wurde automatisch von ISE generiert, daher ging ich davon aus, dass der Code fehlerfrei ist. Ich habe alle clk_*k-Prozesse entfernt und jetzt läuft die Simulation ohne Probleme.
Sorry für die Verwirrung. Lassen Sie mich näher darauf eingehen. Kürzlich hatte mein Arbeitgeber wegen schlechter VHDL-Codierung einen Projektfehler von mehr als 6 Monaten. Als Reaktion darauf leitete ich Anfang dieser Woche eine VHDL-Schulung für eine Gruppe von Ingenieuren. Die erste Folie in meiner Präsentation war die Titelfolie. Auf Folie Nr. 2 ging es um all die schlechten VHDL-Codes/Ratschläge, die Sie im Internet finden. Leider war Ihr Kommentar zu Async-Resets wie Salz in einer kürzlichen Wunde. Ich hatte das Gefühl, dass andere nicht ausführlich genug geantwortet haben, deshalb habe ich selbst geantwortet. Ich habe Yann Vernier sogar +1 gegeben, da er der erste war, der "mehrere Fahrer" sagte.
@DavidKessner: Punkt genommen. Ich denke, es läuft dann auf die Stilpräferenzen meines Lehrers für digitales Design hinaus. Obwohl ich mir keinen Grund vorstellen kann, Register beim Zurücksetzen nicht zu initialisieren, denke ich, nach einigem Lesen werde ich es tun.

Sie haben mehrere Treiber. Sie werden nicht in Ihren geposteten Code-Snippets angezeigt, sind aber in den nicht snippetisierten Dateien offensichtlich .

Wenn Sie sich die Datei test_clock.vhd ansehen, instanziieren Sie Ihre zu testende Einheit. In der Portmap weisen Sie clk_1M einen Ausgang von clock() zu. Später haben Sie diesen Codeabschnitt:

clk_1M_process :process
   begin
        clk_1M <= '0';
        wait for clk_1M_period/2;
        clk_1M <= '1';
        wait for clk_1M_period/2;
   end process;

Dieser Code weist auch clk_1M einen Wert zu - daher haben Sie mehrere Treiber für Ihre Signale.

Andere Signale haben ähnliche Probleme, daher werde ich hier nicht darauf eingehen.

Ich sehe es nicht in den Codeausschnitten, aber die Simulation sieht ganz so aus, als hätten Sie mehrere Treiber für einige Signale. Wenn sie zustimmen, erhalten Sie eine Logikebene, aber wenn sie nicht einverstanden sind, erhalten Sie X - einen Konflikt in der Simulation, der wahrscheinlich nicht synthetisierbar ist (wenn dies der Fall wäre, würde dies einen Frittierkurzschluss bedeuten). Eine Vermutung ist, dass Sie beim Kopieren dieses Taktteilerprozesses möglicherweise einen Fehler gemacht haben und mit einer Komponente, die Sie stattdessen instanziieren könnten, gut bedient wären.

Danke, aber ich sehe keinen Teil, wo ich "mehrere Treiber" habe. ISE meldet diese Art von Fehler, also nehme ich an, dass es niemanden gibt. Ich habe den gesamten Code eingefügt und hier getestet: pub.uart.cz/vhdl