Generierung nicht überlappender Takte auf FPGA mit VHDL

Ich versuche, Schaltungen mit geschalteten Kondensatoren zu implementieren, und muss daher einen zweiphasigen, nicht überlappenden Takt erzeugen. Ich habe versucht, ein FPGA für dasselbe zu verwenden. Leider gibt mein Synthesetool Quartus II Timing-Warnungen aus. Wenn ich den Code auf das FPGA (FPGA der Altera MAX7000S-Serie) speichere, beobachte ich außerdem deutlich metastabile Pegel und unvorhersehbare Ausgaben.

Der Code, den ich geschrieben habe, um dies zu implementieren, ist unten angegeben:

Architektur clock_gen_integrator_arch von clock_gen_integrator ist Signalzähler_15: STD_LOGIC_VECTOR (3 bis 0); Signal phi1_sig, phi2_sig: STD_LOGIC; Signalzähler: STD_LOGIC_VECTOR (14 bis 0);

begin
phi1     <= phi1_sig;
phi2     <= phi2_sig;

signal_gen: process (reset, clock25M) begin
    if(reset = '0') then
        counter_15    <= (others => '0');
        counter       <= (others => '0');
        phi1_sig      <= '1';
        phi2_sig      <= '0';
        reset_int     <= '1';
    elsif(clock25M = '1' and clock25M'EVENT) then
              if(counter < "000010000000000" and counter_15 < "1111") then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter < "011100000000000" and counter_15 < "1111") then
              phi1_sig      <= '1';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter < "100010000000000" and counter_15 < "1111") then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter < "111100000000000" and counter_15 < "1111") then
              phi1_sig      <= '0';
              phi2_sig      <= '1';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter < "111111111111111" and counter_15 < "1111") then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (counter = "111111111111111" and counter_15 < "1111") then
              counter_15    <= counter_15 + 1;
              counter       <= counter + 1;
              reset_int     <= '0';
          else 
              phi1_sig      <= phi1_sig;
              phi2_sig      <= phi2_sig;
              reset_int     <= '0';
          end if;

        end if;
    end process;
end architecture;

Leider kann ich hier nicht den gesamten Code einfügen, da ich Formatierungsprobleme habe. Da es sich jedoch nur um die Deklaration der Entität handelt, habe ich sie weggelassen. phi1(out), phi2(out), reset(in), reset_int(out), clock25M(in) sind in der Portliste vorhanden.

In diesem Code wähle ich willkürlich die Frequenz und die Arbeitszyklen der erforderlichen Uhren aus. Ich möchte speziell 15 Impulse von phi1 und 15 Impulse von phi2 und counter_15 hilft mir dabei.

Mir wird von QuartusII mitgeteilt, dass ich Setup-Zeitverletzungen habe.Von QuartusII gemeldete Überschreitungen der Rüstzeit.  Insgesamt gibt es 100 davon

Dies ist die Ausgabe

Entschuldigung für das Graustufenbild, ich musste das Bild irgendwie verkleinern, um es hochzuladen. Der erste Kanal ist der Phi1-Ausgang und der zweite Kanal ist der Phi2-Ausgang. Da ich sowohl mit dem Tool als auch mit der Timing-Analyse neu bin, wäre ich dankbar, wenn jemand darauf hinweisen könnte, was ich falsch mache und wie ich die Timing-Verletzung beheben kann. Außerdem sind alle Tipps zur Vermeidung dieser Probleme im Allgemeinen willkommen.

Haben Sie darüber nachgedacht, den Code zu ändern, um die "<"-Vergleiche loszuwerden? Diese sind logischerweise teuer. Es könnte sein, dass Ihre Logik einfach zu langsam ist, um das Timing einzuhalten. Da Ihr Zähler immer inkrementiert, können Sie einfach bestimmte Punkte in der Zählung auswählen, damit sich die PHI-Ausgänge ändern, und einen Gleichheitsvergleich für diese Punkte durchführen. Gleichheitsvergleiche sind viel schneller, da sie keinen Addierer und die damit verbundene Laufzeitverzögerung erfordern.
Ich glaube nicht, dass Sie dies simuliert haben, da der Zähler nach einem Zurücksetzen immer inkrementiert wird. VHDL mag das nicht.
@crj11, danke für deinen Kommentar. Ich hatte früher einen Code, der Gleichheiten anstelle des <-Operators verwendete. Es hatte die gleichen Timing-Warnungen und ähnliche Probleme mit der Ausgabe; logisch 0 und logisch 1 waren keine einzelne, konstante Spannung. Während der logischen 1-Phase oszilliert es mit einem Mittelwert, der wahrscheinlich gleich dem von 3,3 V ist, und während der logischen 0-Phase mit einem Mittelwert, der wahrscheinlich gleich 0 V ist.
@Oldfart, danke für deinen Kommentar. Ich habe es auch simuliert. Sowohl die Pre-Synthese- als auch die Post-Synthese-Simulationen waren perfekt und genau so, wie ich es mir erhofft hatte. Meinen Sie jedoch in Bezug auf Ihre Beobachtung, wie ich meinen Zähler inkrementiere, dass ich Zähler <= "00 ... 0" hätte geben sollen, wenn es "11 ... 1" wird? Ich habe das gerade ausprobiert und es macht weder bei den Synthese- noch bei den Simulationsergebnissen einen Unterschied.
Ich kenne Quartus nicht, also kämpfe ich mit dem Timing. -11 Schlupf und 9 Verzögerung würde ich als 20 (ns?) Periode interpretieren, was einem 50-MHz-Takt entsprechen würde. Aber das passt nicht zu -7 Slack und 5 Delay. Die einzige Konsistenz ist das Delta zwischen Schlupf und Verzögerung, das überall 2 (nS?) beträgt, aber das wären 500 MHz. Uhr. Bei 25 MHz würde ich erwarten, dass Ihre Schaltung das Timing erfüllt. Es ist nicht so komplex.
Was genau wird auf dem Oszilloskop-Trace angezeigt? Treibt der FPGA-Pin irgendetwas anderes als die Oszilloskopsonde an? Um was für eine Sonde handelt es sich? Für welchen Ausgangstyp und Ausgangsstrom haben Sie die Ausgänge konfiguriert?
@crj11, die Scope-Spur zeigt die Spannungswellenform der beiden angeblich nicht überlappenden Taktphasen - phi1 und phi2. Der FPGA-Pin wurde mit den Gate-Anschlüssen eines MOSFET-Schalters (von CD4066) verbunden. Ich habe es jedoch getestet, indem ich die MOSFET-Gates isoliert habe, und die Ergebnisse waren nicht allzu unterschiedlich. Über die Art der Sonde bin ich mir über die Details nicht sicher, aber höchstwahrscheinlich so: keyight.com/en/pd-1938439-pn-N2862B/… Und über den O / P-Strom habe ich ihn und den nicht gemessen O / P ist eine V-Wellenform (wenn Sie das fragen, verzeihen Sie mir).
@Amogh, normalerweise können Sie in der FPGA-Konfigurationsdatei den Logiktyp für den Ausgang, z. B. LVCMOS, und den maximalen Ausgangsstrom, z. B. 16 mA, angeben. Wenn Sie es nicht angeben, verwendet es einen Standardwert. Sie sollten in einer der von Quartus generierten Berichtsdateien sehen können, welche Ausgangstypen und Ströme vorhanden sind.
Richtig, ich habe den Standard-TTL-Ausgangstyp verwendet und der maximale Ausgangsstrom gemäß Datenblatt beträgt 25 mA pro Pin.
Idealerweise erzeugt man Taktsignale nicht im HDL-Code, sondern mit den dedizierten PLL/DLL-Blöcken, die oft verschiedene Ausgangsphasenoptionen haben. Wenn Sie HDL-abgeleitete Takte ausgeben müssen, sollten Sie dies über ein Ausgangsregister mit einem gemeinsamen schnelleren Takt tun und dem Tool Clock-to-Out-Timing-Beschränkungen geben, damit es eine ausreichende Vereinheitlichung ihrer Verzögerungen erreichen kann.

Antworten (4)

Ein allgemeines Verfahren zur Verbesserung des Timings besteht darin, die Logik über mehrere Zyklen aufzuteilen, indem Ergebnisse registriert werden.

Du könntest so etwas versuchen...

signal_gen: process (reset, clock25M) begin
    if(reset = '0') then
        counter_15    <= (others => '0');
        counter       <= (others => '0');
        phi1_sig      <= '1';
        phi2_sig      <= '0';
        reset_int     <= '1';
        counter_15_not_done <= true;
        count_lt_000010000000000 <= false;
        count_lt_011100000000000 <= false;
        count_lt_100010000000000 <= false;
        count_lt_111100000000000 <= false;
        count_lt_111111111111111 <= false;
        count_eq_111111111111111 <= false;
    elsif(clock25M = '1' and clock25M'EVENT) then
          --the comparisons are computed in parallel and registered
          counter_15_not_done <= counter_15 < "1111";
          count_lt_000010000000000 <= count < "000010000000000";
          count_lt_011100000000000 <= count < "011100000000000";
          count_lt_100010000000000 <= count < "100010000000000";
          count_lt_111100000000000 <= count < "111100000000000";
          count_lt_111111111111111 <= count < "111111111111111";
          count_eq_111111111111111 <= count = "111111111111111";
          --the logic now only depends on the registered results.
          --The registered results are only 7 bits rather than 19 bits
          --this should have much better timing
          if(count_lt_000010000000000 and counter_15_not_done) then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_lt_011100000000000 and counter_15_not_done) then
              phi1_sig      <= '1';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_lt_100010000000000 and counter_15_not_done) then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_lt_111100000000000 and counter_15_not_done) then
              phi1_sig      <= '0';
              phi2_sig      <= '1';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_lt_111111111111111 and counter_15_not_done) then
              phi1_sig      <= '0';
              phi2_sig      <= '0';
              counter       <= counter + 1;
              reset_int     <= '0';
          elsif (count_eq_111111111111111 and counter_15_not_done) then
              counter_15    <= counter_15 + 1;
              counter       <= counter + 1;
              reset_int     <= '0';
          else 
              phi1_sig      <= phi1_sig;
              phi2_sig      <= phi2_sig;
              reset_int     <= '0';
          end if;

        end if;
    end process;
end architecture;

Beachten Sie, dass die registrierten Ergebnisse den tatsächlichen Zählungen um 1 Taktzyklus nacheilen, sodass Sie möglicherweise die Vergleichspunkte um 1 Zyklus zurücksetzen müssen.

Ich hatte etwas in dieser Richtung versucht; Ich habe einen temporären Vektor gesetzt, der wie folgt zugewiesen werden würde: counter_nxt <= counter + 1 auf der positiven Flanke. Und später, auf der negativen Flanke (in einem anderen Prozess) hatte ich Zähler <= Zähler_nxt zugewiesen. Das hat jedoch kaum einen Unterschied gemacht. Ich habe gerade Ihren Code auch auf der Hardware ausprobiert. Während es auf meinem Laptop perfekt simuliert wird, zeigt der Timing-Analysator immer noch Timing-Verletzungen an und liefert nicht die erwarteten Ergebnisse auf der Hardware. Ich beginne jetzt zu zweifeln, ob mit dem FPGA alles in Ordnung ist. Vielen Dank für Ihre Antwort, ich habe etwas Neues gelernt!

Gleichheit wird mit weniger 'Gatter'-Verzögerungsstufen implementiert als Magnitude ("<"), was eine Subtraktion beinhaltet und nicht vollständig wegoptimiert wird, wenn es Operandenwerte gibt, die größer als der größte statische Magnitude-Operand sind. (Der höchste Zählervergleichsoperand ist x"7800", der höchste Zählerstand ist x"7FFF".)

Verwenden Sie nur Gleichheitsvergleiche, zum Beispiel:

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

entity clock_gen_integrator is
    port (
        clock25M:   in  std_logic;
        reset:      in  std_logic;
        reset_int:  out std_logic;
        phi1:       out std_logic;
        phi2:       out std_logic
    );
end entity;

architecture foo of clock_gen_integrator is 

    signal counter_15:  unsigned (3 downto 0); -- WAS std_logic_vector
    signal phi1_sig:    std_logic;
    signal phi2_sig:    std_logic; 
    signal counter:     unsigned(14 downto 0); -- WAS std_logic_vector 
    signal notcount15:  boolean;
    signal notcount15h: boolean; 
begin

    phi1 <= phi1_sig;
    phi2 <= phi2_sig;

    notcount15 <= counter_15 /= 15;  -- 4 input NAND gate

signal_gen: 
    process (reset, clock25M) 
    begin
        if reset = '0' then
            counter_15    <= (others => '0');
            notcount15h     <= true;
            counter       <= (others => '0');
            phi1_sig      <= '1';
            phi2_sig      <= '0';
            reset_int     <= '1';
        -- elsif clock25M = '1' and clock25M'EVENT then 
        elsif rising_edge (clock25M) then
            -- the counters:
            counter <= counter + 1;
            if counter = x"3800" and notcount15 then
                counter_15 <= counter_15 + 1;
            end if;
            -- notcount15h holdover flip flop for phi2
            if counter = x"3800" and not notcount15 then
                notcount15h <= false;
            end if;
            -- reset_int flip flop:
            reset_int <= not reset;
            -- toggle phi1:
            if counter = x"400"  and notcount15 then
                phi1_sig <= '1';
            elsif counter = x"3800" and notcount15 then
                phi1_sig <= '0';
            end if;
            -- toggle phi2:
            if counter = x"4400" and notcount15h then
                phi2_sig <=  '1';
            elsif counter = x"7800" and notcount15h then
                phi2_sig <= '0';
            end if;  
            -- four equality comparators to specific 15 bits of counter
        end if;
    end process;
end architecture;


library ieee;
use ieee.std_logic_1164.all;

entity clock_gen_integrator_tb is
end entity;

architecture foo of clock_gen_integrator_tb is
    signal clock25M:   std_logic := '0';
    signal reset:      std_logic := '1';
    signal reset_int:  std_logic;
    signal phi1:       std_logic;
    signal phi2:       std_logic;
begin

CLOCK:
    process
    begin
        wait for 20 ns;
        clock25M <= not clock25m;
        if now > 21 ms then
            wait;
        end if;
    end process;
DUT:
    entity work.clock_gen_integrator
        port map (
            clock25M => clock25M,
            reset => reset,
            reset_int => reset_int,
            phi1 => phi1,
            phi2 => phi2
        );

STIMULUS:
    process
    begin
        wait for 30 ns;
        reset <= '0';
        wait for 80 ns;
        reset <= '1';
        wait;
    end process;

end architecture;

Die Testbench demonstriert 15 phi1- und phi2-Taktgeber ("Ich möchte speziell 15 Impulse von phi1 und 15 Impulse von phi2 und Zähler_15 hilft mir dabei, dies zu erreichen") auf denselben Zählerwerten wie in der ursprünglichen Architektur clock_gen_integrator_arch:

clock_gen_integrator_tb.jpg

Ich habe Ihren vorgeschlagenen Code auf der Hardware ausprobiert und die Spannungspegel waren nicht konstant. Ich beobachte auch einige kleine Pulse nach den fünfzehn Taktzyklen. Ich zweifle jetzt an meiner Hardware. Vielen Dank auch für die empfohlenen Änderungen; sieht so aus, als hätte ich noch viel zu lernen.

Alle unteren Bits in Ihren Übergangspunkten sind Null (ohne den Fall zu zählen, in dem Sie phi2zweimal löschen, sodass Sie die Uhr einige Male teilen und einen kleineren Zähler verwenden können.

Sie können die Erzeugung einer kontinuierlichen Impulsfolge beliebiger Länge (niederwertige Bits des Zählers) und die Weiterleitung an einen Ausgang (höherwertige Bits) auch aufteilen, z. B. 16 Impulse mit an 1 8 Einschaltdauer und zwei Ausgänge könnten mit einem 8-Bit-Zähler erreicht werden (nicht einmal kompiliert getestet, aber Sie sollten auf die Idee kommen):

pulse <= '1' WHEN counter(2 downto 0) = "000" ELSE '0';
output <= counter(3);
phi1 <= pulse AND NOT output;
phi2 <= pulse AND output;

Habe counternach oben gelaufen und danach angehalten "11111111".

Abhängig davon, was sonst noch im Design vor sich geht, würde ich wahrscheinlich auch eine PLL verwenden, um einen langsamen (1 MHz) Takt abzuleiten und von dort zu teilen.

Leider ist dies eine schlechte Implementierung eines Designs, das keine Spezifikationen oder Toleranzen hat. Beginnen Sie immer mit Konstruktionsspezifikationen und Toleranzen, bevor Sie versuchen, eine Lösung zu entwerfen.

Ihr Mangel an Impedanzdesign (RdsOn oder Schalter-ESR) und Durchhang durch Leckage zeigen Hinweise auf quantisiertes Klingeln und Kommutierungsrauschen.

Die Auswahl der Kappen sollte nicht aus Keramik sein, die Hysterese- und Mikrofonieprobleme in S&H-Designs haben, es sei denn, es handelt sich um den Typ NP0.

Gegeben;

Schaltungen mit geschalteten Kondensatoren und ich muss daher einen zweiphasigen, nicht überlappenden Takt erzeugen

Was sind die folgenden? Schließen Sie jeweils die erwarteten Toleranzen ein.

  • gewünschte Filterantworten und Toleranz
  • Eingangsuhr f
  • Ausgangstakt f
  • Ausgangstaktstrombegrenzung
  • Ausgangslastkapazität (daher Anstiegszeit)
  • Ausgangsverzögerungsverzerrungen durch Latenz im schlimmsten Fall
  • Widerstand schalten
  • geschaltete Kapazität, daher Anstiegszeit
  • nicht überlappende Totzeit von Zweiphasentakten
  • Einfluss auf RC-Zeitkonstante mit Totzeit (RdsOn/d)
  • Erforderliches Pulsweiten-Timing für ansteigende und abfallende Flanken bei V+/2 oder „PW50“
  • Empfindlichkeit nach oben mit Temperatur und Versorgungsspannung.

Je mehr Spezifikationen Sie im Voraus definieren, die Fehler verursachen können, desto größer ist die Chance, dass Sie es gleich beim ersten Mal richtig machen . Andernfalls Iterationen von Designspezifikationen und Wiederholung oder Verwirrung, wenn es fehlschlägt.

Welche Methode ist nun am einfachsten, um diese Anforderungen zu erstellen? analog? Wie bei allen Vollbrückendesigns oder digital mit Quantisierungszeitauflösungsgrenzen oder?

Welche Schaltungen haben Sie gelesen, die in CMOS-App-Büchern veröffentlicht wurden, die bereits funktionieren? Und wenn nicht, warum nicht?

ps
Es gibt viele Tools zum Ändern der Bildgröße oder des Komprimierungsverhältnisses, um die Dateigröße zu reduzieren und die 32-Bit-Farbe beizubehalten.

Erhöhen Sie den Abstand und die ruhige Hand mit der Kamera, dann Autofokus, direkt zum Monitor, um das klarste Bild zu erhalten, und schneiden Sie es dann nach Bedarf zu.