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


von Stefan H. (Gast)


Angehängte Dateien:

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
1
ZUSTANDSAKTUALISIERUNG_MODUS_UMSCHALTUNG: PROCESS (takt, reset)
2
begin
3
    if reset = '0' then ZUSTAND_MODUS <= A;
4
    elsif rising_edge(takt) then ZUSTAND_MODUS <= FOLGEZUSTAND_MODUS;
5
    end if;
6
end PROCESS ZUSTANDSAKTUALISIERUNG_MODUS_UMSCHALTUNG;
7
8
FOLGEZUSTANDSBERECHNUNG_MODUS_UMSCHALTUNG: PROCESS (ButtonDEntprellt, UhrStopp, ZUSTAND_MODUS)
9
begin
10
    case ZUSTAND_MODUS is
11
        when A =>
12
            if ButtonDEntprellt = '1' and UhrStopp = '1' then FOLGEZUSTAND_MODUS <= B;
13
            else FOLGEZUSTAND_MODUS <= A;
14
            end if;
15
            ModusNormal     <= '1';
16
            ModusAddition   <= '0';
17
            ModusSplit      <= '0';
18
        when B =>
19
            if ButtonDEntprellt = '1' and UhrStopp = '1' then FOLGEZUSTAND_MODUS <= C;
20
            elsif ButtonDEntprellt = '1' then FOLGEZUSTAND_MODUS <= A;
21
            else FOLGEZUSTAND_MODUS <= B;
22
            end if;
23
            ModusNormal     <= '0';
24
            ModusAddition   <= '1';
25
            ModusSplit      <= '0';
26
        when C =>
27
            if ButtonDEntprellt = '1' then FOLGEZUSTAND_MODUS <= A;
28
            else FOLGEZUSTAND_MODUS <= C;
29
            end if;
30
            ModusNormal     <= '0';
31
            ModusAddition   <= '0';
32
            ModusSplit      <= '1';
33
    end case;            
34
end PROCESS FOLGEZUSTANDSBERECHNUNG_MODUS_UMSCHALTUNG;

Stoppuhr bedienen
1
ZUSTANDSAKTUALISIERUNG_UHR: PROCESS (takt, reset)
2
begin
3
    if reset = '0' then ZUSTAND_UHR <= A;
4
    elsif rising_edge(takt) then ZUSTAND_UHR <= FOLGEZUSTAND_UHR;
5
    end if;
6
end PROCESS ZUSTANDSAKTUALISIERUNG_UHR;
7
8
FOLGEZUSTANDSBERECHNUNG_UHR: PROCESS (ButtonCEntprellt, ModusAddition, ModusSplit, ZUSTAND_UHR)
9
begin
10
    case ZUSTAND_UHR is 
11
        when A =>
12
            if ButtonCEntprellt = '1' then FOLGEZUSTAND_UHR <= B;
13
            else FOLGEZUSTAND_UHR <= A;
14
            end if;
15
            UhrStart            <= '0';
16
            UhrStopp            <= '1';
17
            UhrZuruecksetzen    <= '1';
18
        when B =>
19
            if ButtonCEntprellt = '1' and ModusSplit = '1' then FOLGEZUSTAND_UHR <= E;
20
            elsif ButtonCEntprellt = '1' and ModusSplit = '0'  then FOLGEZUSTAND_UHR <= C;
21
            else FOLGEZUSTAND_UHR <= B;
22
            end if;
23
            UhrStart            <= '1';
24
            UhrStopp            <= '0';
25
            UhrZuruecksetzen    <= '0';
26
        when C =>
27
            if ButtonCEntprellt = '1' and ModusAddition = '1'  then FOLGEZUSTAND_UHR <= B;
28
            elsif ButtonCEntprellt = '1' and ModusAddition = '0'  then FOLGEZUSTAND_UHR <= D;
29
            else FOLGEZUSTAND_UHR <= C;
30
            end if;
31
            UhrStart            <= '0';
32
            UhrStopp            <= '1';
33
            UhrZuruecksetzen    <= '0';
34
        when D =>
35
            FOLGEZUSTAND_UHR <= B;
36
            UhrStart            <= '0';
37
            UhrStopp            <= '1';
38
            UhrZuruecksetzen    <= '1';
39
        when E =>
40
            if ButtonCEntprellt = '1' and ModusSplit = '1'  then FOLGEZUSTAND_UHR <= B;
41
            elsif ButtonCEntprellt = '1' and ModusSplit = '0'  then FOLGEZUSTAND_UHR <= D;
42
            else FOLGEZUSTAND_UHR <= E;
43
            end if;
44
            UhrStart            <= '1';
45
            UhrStopp            <= '1';
46
            UhrZuruecksetzen    <= '0';
47
    end case;
48
end PROCESS FOLGEZUSTANDSBERECHNUNG_UHR;

Hundertstel Takt aus Systemtakt
1
Generiere_Hundertstel_Takt : PROCESS (takt, reset, ZaehlerHundertstelTakt)
2
begin
3
    if reset = '0' then ZaehlerHundertstelTakt <= 0;
4
    elsif rising_edge(takt) then
5
        if ZaehlerHundertstelTakt < 10000000 then
6
            ZaehlerHundertstelTakt <= ZaehlerHundertstelTakt + 1;
7
            HundertstelTakt <= '0';
8
        else
9
            ZaehlerHundertstelTakt <= 0;
10
            HundertstelTakt <= '1';
11
        end if;
12
    end if;
13
end PROCESS Generiere_Hundertstel_Takt;

Hundertstel-Sekunde Zähler (alle weiteren Zähler folgen in gleicher 
Struktur)
1
UHR_HUNDERTSTELSEKUNDEN: PROCESS (takt, reset, HundertstelTakt, UhrStart, UhrZuruecksetzen, UhrUeberlauf, ZaehlerSekundenHundertstel)
2
begin
3
    if reset = '0' or UhrZuruecksetzen = '1' then
4
        ZaehlerSekundenHundertstel <= 0;
5
        ZaehlerSekundenHundertstelUeberlauf <= '0';
6
    elsif rising_edge(takt) then
7
        if UhrStart = '1' and UhrUeberlauf = '0' and HundertstelTakt = '1' then
8
            if ZaehlerSekundenHundertstel < 9 then
9
                ZaehlerSekundenHundertstel <= ZaehlerSekundenHundertstel + 1;
10
                ZaehlerSekundenHundertstelUeberlauf <= '0';
11
            else
12
                ZaehlerSekundenHundertstel <= 0;
13
                ZaehlerSekundenHundertstelUeberlauf <= '1';
14
            end if;
15
        else
16
            ZaehlerSekundenHundertstel <= ZaehlerSekundenHundertstel;
17
            ZaehlerSekundenHundertstelUeberlauf <= '0';
18
        end if;
19
    end if;
20
end PROCESS UHR_HUNDERTSTELSEKUNDEN;

von Schlumpf (Gast)


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.

von Burkhard K. (buks)


Lesenswert?

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

Was auf die Schnelle auffällt:
1
        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:
1
            UhrStart            <= '1';
2
            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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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:
1
    if reset = '0' or UhrZuruecksetzen = '1' then
2
        ZaehlerSekundenHundertstel <= 0;
3
        ZaehlerSekundenHundertstelUeberlauf <= '0';
4
    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:
1
UHR_HUNDERTSTELSEKUNDEN: PROCESS (takt, reset)
2
begin
3
    if reset = '0' then
4
        ZaehlerSekundenHundertstel <= 0;
5
        ZaehlerSekundenHundertstelUeberlauf <= '0';
6
    elsif rising_edge(takt) then
7
        if UhrStart = '1' and UhrUeberlauf = '0' and HundertstelTakt = '1' then
8
            if ZaehlerSekundenHundertstel < 9 then
9
                ZaehlerSekundenHundertstel <= ZaehlerSekundenHundertstel + 1;
10
                ZaehlerSekundenHundertstelUeberlauf <= '0';
11
            else
12
                ZaehlerSekundenHundertstel <= 0;
13
                ZaehlerSekundenHundertstelUeberlauf <= '1';
14
            end if;
15
        else
16
            ZaehlerSekundenHundertstel <= ZaehlerSekundenHundertstel;
17
            ZaehlerSekundenHundertstelUeberlauf <= '0';
18
        end if;
19
        if UhrZuruecksetzen = '1' then   -- Reset: die letzte Zuweisung an ein Signal "gewinnt"
20
           ZaehlerSekundenHundertstel <= 0;
21
           ZaehlerSekundenHundertstelUeberlauf <= '0';
22
        end if;
23
    end if;
24
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
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.