mikrocontroller.net

Forum: FPGA, VHDL & Co. Stoppuhr Normal/Addition/Split, Funktion abhängig von "Taktrate"


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Stefan H. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

folgendes Problem bei dem ich über jede Hilfe dankbar bin:

Für ein Hochschulprojekt soll eine Stoppuhr auf einem Xilinx Artix-7 
(Board: Digilent Nexys 4 DDR) realisiert werden. Die Uhr soll 3 Modi 
haben: Normal (Nach Stopp neustarten), Addition (Nach Stopp bei 
Zwischenstand weiterlaufen) und Split (Bei Stopp läuft die Uhr im 
Hintergrund weiter).
Der Systemtakt liegt bei 100MHz und wird durch zählen bis 1'000'000 auf 
eine Hundertstel Sekunde heruntergebrochen.

Mein Problem: Wenn ich den Frequenzteiler bis 10'000'000 zählen lasse 
(kleinste Einheit Zehntel-Sekunde) funktionieren alle Modi wie sie 
sollen. Lasse ich den Frequenzteiler korrekterweise bis 1'000'000 zählen 
(kleinste Einheit Hundertstel-Sekunde) startet die Uhr auch im 
Additions- oder Split-Modus nach einem Stopp wieder von Null.

Habe bisher hauptsächlich in C++ programmiert und noch relativ wenig 
Erfahrung mit parallelen Abläufen. Vielleicht liegt der Fehler hier!? 
Die meines Erachtens relevanten Code-Schnipsel poste ich direkt hier, 
der vollständige Code hängt an.

Vielen Dank für eure Hilfe!!

Modi durchschalten
ZUSTANDSAKTUALISIERUNG_MODUS_UMSCHALTUNG: PROCESS (takt, reset)
begin
    if reset = '0' then ZUSTAND_MODUS <= A;
    elsif rising_edge(takt) then ZUSTAND_MODUS <= FOLGEZUSTAND_MODUS;
    end if;
end PROCESS ZUSTANDSAKTUALISIERUNG_MODUS_UMSCHALTUNG;

FOLGEZUSTANDSBERECHNUNG_MODUS_UMSCHALTUNG: PROCESS (ButtonDEntprellt, UhrStopp, ZUSTAND_MODUS)
begin
    case ZUSTAND_MODUS is
        when A =>
            if ButtonDEntprellt = '1' and UhrStopp = '1' then FOLGEZUSTAND_MODUS <= B;
            else FOLGEZUSTAND_MODUS <= A;
            end if;
            ModusNormal     <= '1';
            ModusAddition   <= '0';
            ModusSplit      <= '0';
        when B =>
            if ButtonDEntprellt = '1' and UhrStopp = '1' then FOLGEZUSTAND_MODUS <= C;
            elsif ButtonDEntprellt = '1' then FOLGEZUSTAND_MODUS <= A;
            else FOLGEZUSTAND_MODUS <= B;
            end if;
            ModusNormal     <= '0';
            ModusAddition   <= '1';
            ModusSplit      <= '0';
        when C =>
            if ButtonDEntprellt = '1' then FOLGEZUSTAND_MODUS <= A;
            else FOLGEZUSTAND_MODUS <= C;
            end if;
            ModusNormal     <= '0';
            ModusAddition   <= '0';
            ModusSplit      <= '1';
    end case;            
end PROCESS FOLGEZUSTANDSBERECHNUNG_MODUS_UMSCHALTUNG;

Stoppuhr bedienen
ZUSTANDSAKTUALISIERUNG_UHR: PROCESS (takt, reset)
begin
    if reset = '0' then ZUSTAND_UHR <= A;
    elsif rising_edge(takt) then ZUSTAND_UHR <= FOLGEZUSTAND_UHR;
    end if;
end PROCESS ZUSTANDSAKTUALISIERUNG_UHR;

FOLGEZUSTANDSBERECHNUNG_UHR: PROCESS (ButtonCEntprellt, ModusAddition, ModusSplit, ZUSTAND_UHR)
begin
    case ZUSTAND_UHR is 
        when A =>
            if ButtonCEntprellt = '1' then FOLGEZUSTAND_UHR <= B;
            else FOLGEZUSTAND_UHR <= A;
            end if;
            UhrStart            <= '0';
            UhrStopp            <= '1';
            UhrZuruecksetzen    <= '1';
        when B =>
            if ButtonCEntprellt = '1' and ModusSplit = '1' then FOLGEZUSTAND_UHR <= E;
            elsif ButtonCEntprellt = '1' and ModusSplit = '0'  then FOLGEZUSTAND_UHR <= C;
            else FOLGEZUSTAND_UHR <= B;
            end if;
            UhrStart            <= '1';
            UhrStopp            <= '0';
            UhrZuruecksetzen    <= '0';
        when C =>
            if ButtonCEntprellt = '1' and ModusAddition = '1'  then FOLGEZUSTAND_UHR <= B;
            elsif ButtonCEntprellt = '1' and ModusAddition = '0'  then FOLGEZUSTAND_UHR <= D;
            else FOLGEZUSTAND_UHR <= C;
            end if;
            UhrStart            <= '0';
            UhrStopp            <= '1';
            UhrZuruecksetzen    <= '0';
        when D =>
            FOLGEZUSTAND_UHR <= B;
            UhrStart            <= '0';
            UhrStopp            <= '1';
            UhrZuruecksetzen    <= '1';
        when E =>
            if ButtonCEntprellt = '1' and ModusSplit = '1'  then FOLGEZUSTAND_UHR <= B;
            elsif ButtonCEntprellt = '1' and ModusSplit = '0'  then FOLGEZUSTAND_UHR <= D;
            else FOLGEZUSTAND_UHR <= E;
            end if;
            UhrStart            <= '1';
            UhrStopp            <= '1';
            UhrZuruecksetzen    <= '0';
    end case;
end PROCESS FOLGEZUSTANDSBERECHNUNG_UHR;

Hundertstel Takt aus Systemtakt
Generiere_Hundertstel_Takt : PROCESS (takt, reset, ZaehlerHundertstelTakt)
begin
    if reset = '0' then ZaehlerHundertstelTakt <= 0;
    elsif rising_edge(takt) then
        if ZaehlerHundertstelTakt < 10000000 then
            ZaehlerHundertstelTakt <= ZaehlerHundertstelTakt + 1;
            HundertstelTakt <= '0';
        else
            ZaehlerHundertstelTakt <= 0;
            HundertstelTakt <= '1';
        end if;
    end if;
end PROCESS Generiere_Hundertstel_Takt;

Hundertstel-Sekunde Zähler (alle weiteren Zähler folgen in gleicher 
Struktur)
UHR_HUNDERTSTELSEKUNDEN: PROCESS (takt, reset, HundertstelTakt, UhrStart, UhrZuruecksetzen, UhrUeberlauf, ZaehlerSekundenHundertstel)
begin
    if reset = '0' or UhrZuruecksetzen = '1' then
        ZaehlerSekundenHundertstel <= 0;
        ZaehlerSekundenHundertstelUeberlauf <= '0';
    elsif rising_edge(takt) then
        if UhrStart = '1' and UhrUeberlauf = '0' and HundertstelTakt = '1' then
            if ZaehlerSekundenHundertstel < 9 then
                ZaehlerSekundenHundertstel <= ZaehlerSekundenHundertstel + 1;
                ZaehlerSekundenHundertstelUeberlauf <= '0';
            else
                ZaehlerSekundenHundertstel <= 0;
                ZaehlerSekundenHundertstelUeberlauf <= '1';
            end if;
        else
            ZaehlerSekundenHundertstel <= ZaehlerSekundenHundertstel;
            ZaehlerSekundenHundertstelUeberlauf <= '0';
        end if;
    end if;
end PROCESS UHR_HUNDERTSTELSEKUNDEN;

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab jetzt den Code noch nicht angeschaut.
Aber siehst du das Verhalten in der Simulation oder  nur nach der 
Synthese im FPGA.

Ich frage deshalb um zu klären, ob der Code auf logische Fehler oder 
ungünstige Konstrukte untersucht werden muss, die bei der Synthese Ärger 
machen können.

Autor: Burkhard K. (buks)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Woran es liegt weiss ich auch nicht, Du wirst Simulieren müssen!

Was auf die Schnelle auffällt:
        if UhrStart = '1' and UhrUeberlauf = '0' and HundertstelTakt = '1' then

Der HundertstelTakt ist doch eigentlich ein Enable-"Tick". Bist sicher, 
dass diese drei Bedingungen zeitlich immer so zusammenfallen, wie 
vorgesehen? (Der Tick dauert nur einen Takt).

Im Code sehe ich nicht, wo der UhrUeberlauf gesetzt wird (ausser als 
Initialisierung) - warum also überhaupt abfragen?

Und dann noch in Zustand E:
            UhrStart            <= '1';
            UhrStopp            <= '1';
Ist das so gewollt?

Für die Simulation wäre es wohl einfacher, die Überlaufbedingung der 
Hundertsteltakt-Generierung per GENERIC einstellbar zu machen. Die 
Simulation kann dann z.B. einen Faktor 1000 schneller laufen als auf dem 
FPGA.

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan H. schrieb:
> Vielleicht liegt der Fehler hier!?
Wenn ich diese Abhängigkeit von der Taktfrequenz höre und sowas sehe, 
dann weiß ich, was zu ändern ist:
    if reset = '0' or UhrZuruecksetzen = '1' then
        ZaehlerSekundenHundertstel <= 0;
        ZaehlerSekundenHundertstelUeberlauf <= '0';
    elsif rising_edge(takt) then
Das ist ein asynchroner kombinatorischer Reset. Der funktioniert in 
Gedanken und in der Simulation tadellos. Aber in der Realität hast du da 
beliebige Probleme mit irgendwelchen Spikes und Glitches, die jedesmal 
entstehen, wenn irgendwas im Prozess FOLGEZUSTANDSBERECHNUNG_UHR 
passiert. Sowas läuft in der Realität niemals zuverlässig. Du hast 
Glück, dass du den Fehler dauernd hast, und nicht nur 1x pro Stunde... 
;-)

Mach aus diesem asynchronen Reset einen synchronen Reset indem du ihn 
ausschließlich im getakteten Teil deiner Prozesse abfrägst!
Das ist übrigens kein "könnte" oder "sollte", sondern ein "muss".

Insgesamt scheint die Beschreibung, naja, sagen wir mal auf maximale 
"Lines of Code" optimiert nach dem Motto "Viel hilft viel". Aber das 
kommt natürlich auch von der m.E. umständlichen 2-Prozess-Schreibweise.
Dazu das:
http://www.lothar-miller.de/s9y/archives/43-Ein-oder-Zwei-Prozess-Schreibweise-fuer-FSM.html
Aber das hängt natürlich leider auch vom Lehrer, dessen Bücher und 
seinen Vorlieben ab...

Zudem reichen bei einem synchronen Prozess der Reset (wenn für die 
Funktion überhaupt nötig!) und der Takt in der Sensitivliste. Denn der 
Simulator (und nur der schert sich überhaupt um diese Liste, dem 
Synthesizer ist die schnuppe) berechnet bei einer Änderung eines der 
Signale darin den betroffenen Prozess neu.

Nachdem in diesem Prozess also das mit dem fehlerhaften asynchronen 
UhrZuruecksetzen korrigiert ist, seht da nur noch:
UHR_HUNDERTSTELSEKUNDEN: PROCESS (takt, reset)
begin
    if reset = '0' then
        ZaehlerSekundenHundertstel <= 0;
        ZaehlerSekundenHundertstelUeberlauf <= '0';
    elsif rising_edge(takt) then
        if UhrStart = '1' and UhrUeberlauf = '0' and HundertstelTakt = '1' then
            if ZaehlerSekundenHundertstel < 9 then
                ZaehlerSekundenHundertstel <= ZaehlerSekundenHundertstel + 1;
                ZaehlerSekundenHundertstelUeberlauf <= '0';
            else
                ZaehlerSekundenHundertstel <= 0;
                ZaehlerSekundenHundertstelUeberlauf <= '1';
            end if;
        else
            ZaehlerSekundenHundertstel <= ZaehlerSekundenHundertstel;
            ZaehlerSekundenHundertstelUeberlauf <= '0';
        end if;
        if UhrZuruecksetzen = '1' then   -- Reset: die letzte Zuweisung an ein Signal "gewinnt"
           ZaehlerSekundenHundertstel <= 0;
           ZaehlerSekundenHundertstelUeberlauf <= '0';
        end if;
    end if;
end PROCESS UHR_HUNDERTSTELSEKUNDEN;

Und die Entprellung kannst du mitsamt der Flankenerkenn statt mit einer 
FSM auch einfach mit einem Schieberegister machen:
http://www.lothar-miller.de/s9y/categories/5-Entprellung
http://www.lothar-miller.de/s9y/categories/18-Flankenerkennung

Sieh dir mal als Denkanstoß meine Stoppuhr an:
http://www.lothar-miller.de/s9y/archives/88-VHDL-vs.-Verilog-am-Beispiel-einer-Stoppuhr.html
Die kann mal schon starten und zählen. Die anderen Funktionen lassen 
sich aber recht einfach integrieren. Für den Split (Rundenzeit) müsste 
nur noch mit einem Registersatz als Zwischenspeicher die Anzeige vom 
Zähler entkoppelt werden.
Mit zusätzlichen 30 Zeilen müsste das leicht zu schaffen sein.

: Bearbeitet durch Moderator
Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lothar M. schrieb:
> Und die Entprellung kannst du mitsamt der Flankenerkenn statt mit einer
> FSM auch einfach mit einem Schieberegister machen
Eine mögliche Umsetzung mit einem Schieberegister und Bedenken, ob das 
so, wie im obigen Code mit der FSM derzeit umgesetzt, überhaupt 
funktioniert, ist im 
Beitrag "Re: Ampelsteuerungsprogramm" zu 
sehen...   ;-)

: Bearbeitet durch Moderator

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.