Übungsaufgabe
- Entwurf einer universeller Zählerkomponente
- Erkennung eines Tastendrucks
- Implementierung einer Zustandsmaschine
- Aufbau eines Top Levels
Die Stoppuhr entspricht in ihrer Bedienung einer klassichen digitalen Stoppuhr. Zwei Tasten reichen für die Bedienung aus:
- Start/Stop - die Zeitnehmung wird gestartet bzw. gestopped
- Reset/Lap - die Zeitnehmung wird zurückgesetzt bzw. die Zwischenzeit angezeigt
Die vier 7-Segment Anzeigen werden für die Ausgabe der Zeit verwendet (hier 0 Minuten, 13 Sekunden und 5 Zehntelsekunden):
Vorbereitung
- Projektordner herunterladen und entpacken
- Projekt
stopwatch.xise
öffnen
Entwicklung einer universellen Zählerkomponente
Spezifikation
Der Zähler soll folgende Spezifikation erfüllen (immer synchron zur steigenden Taktflanke von clk
):
- Wenn
enable_i
auf0
ist soll der Zähler nicht zählen - Wenn
enable_i
auf1
ist soll der Zähler sich um eins erhöhen - Wenn der Zählerstand
MAXIMUM
erreicht soll der Zähler (im nächsten Schritt) auf 0 gesetzt werden - Solange
reset_i
auf1
ist, soll der Zählerstand auf 0 gesetzt werden - Ein gesetzes
reset_i
hat eine höhere Priorität als ein gesetzesenable_i
value_o
entspricht dem internen Zählerstandoverflow_o
ist1
, wenn der interne Zählerstand beiMAXIMUM
steht undenable_i
auf1
ist (kombinatorisch verknüpft)
Implementierung
- Öffne
counter.vhd
- Erweitere das Design entsprechend der folgenden Spezifikation (an den mit
TODO
markierte Stellen)
Test mittels Testbench
- Wechsle im Design Tab bei View nach Simulation
- Wähle die Testbench
counter_tb
(Dateicounter_tb.vhd
) - Starte die Simulation mittels Simulate Behavioral Model
Beim Durchlaufen der Testbench werden etwaige Fehler des Design angezeigt. Eine erfolgreiche Testbench endet ohne Fehlerausgaben:
Tastendruck Erkennung
Spezifikation
Tasteneingänge sind asynchron zum globalen Takt, deswegen müssen sie einsynchronisiert werden. Nachdem der Eingang synchronisiert ist benötigt man im allgemeinen eine synchrone Flankenerkennung, d.h. bei einer steigenden Flanke soll der Ausgang detect_o
für einen Taktzyklus lang auf 1
sein. Eine fallende Flanke soll ignoriert werden.
Timingdiagramm
Implementierung
Bearbeite dazu die Datei button_detect.vhd
. Die beiden Register button_reg1
und button_reg2
dienen der Einsynchronisierung des asynchronen Signals von button_i
.
Test mittels Testbench
- Wechsle im Design Tab bei View nach Simulation
- Wähle die Testbench
button_detec_tb
(Dateibutton_detect_tb.vhd
) - Starte die Simulation mittels Simulate Behavioral Model
Beim Durchlaufen der Testbench werden etwaige Fehler des Design angezeigt. Eine erfolgreiche Testbench endet ohne Fehlerausgaben (siehe Test des universellen Zählers).
Zustandsmaschine für die Stoppuhr
Spezifikation
Die Zustandsmaschine für die Stoppuhr soll im ersten Schritt drei Zustände umfassen:
CLEARED
- Zeit läuft nicht
- Stoppuhr steht auf "0:00:0"
- Mittels Start/Stopp soll die Zeitnehmung starten
- Reset/Lap hat keine Auswirkung
RUNNING
- Zeit läuft
- Zeigt die aktuelle Zeit an
- Mittels Start/Stopp soll die Zeitnehmung gestoppt werden
- Reset/Lap hat keine Auswirkung
STOPPED
- Stoppuhr läuft nicht
- Zeigt die (gestoppte) Zeit an
- Mittels Start/Stopp soll die Zeitnehmung fortgesetzt werden
- Mittels Reset/Lap soll die Zeit auf "0:00:0" zurückgesetzt werden
Die Zustandmaschine hat zwei Eingaben (Taster Start/Stopp und Reset/Lap). Die Ausgabe clear_o
setzt die Zähler zurück. Solange enable_o
auf '1'
ist laufen die Zähler.
Zustandsdiagramm
Zeichne das Zustandsdiagramm. Wähle einen passenden Zustandsmaschinentyp aus (Mealy oder Moore).
Implementierung
Vervollständige die Vorlage von stopwatch_fsm
. Dort sind die Zustände CLEARED
, RUNNING
und STOPPED
als Typ definiert. Die Behandlung des Zustands CLEARED
und die Ausgabe von clear_o
ist als Beispiel bereits implementiert.
Der Ausgang mode_o
wird erst später benötigt und soll vorerst nur '0'
ausgeben.
architecture behave of stopwatch_fsm is type state_t is (CLEARED, RUNNING, STOPPED); signal state : state_t := CLEARED; begin fsm_process: process (clk) begin if rising_edge(clk) then case state is when CLEARED => if ss_i='1' then state <= RUNNING; end if; when RUNNING => -- TODO when others => -- includes STOPPED -- TODO end case; end if; end process; clear_o <= '1' when state=CLEARED else '0'; enable_o <= '0'; -- <<< TODO mode_o <= '0'; end architecture;
Test mittels Testbench
Teste die Zustandmaschine mittels der Testbench stopwatch_fsm_simple_tb
(Achte auf das simple
im Name!).
Integration aller Komponenten im Top Level
Mit den erstellten Komponenten wird nun ein Stoppuhr-Design aufgebaut. Dazu erweitern wird das Top Level Design in stopwatch.vhd
.
Tastendruckerkennung
Die Komponente button_dect
wird verwendet, um das Signal button_ss_i
und button_rl_i
einzusynchronisieren und auf eine steigende Flanke zu überprüfen. Dazu wird folgende Komponente in der architecture
von stopwatch.vhd
hinzugefügt:
button_ss_detect_component: entity work.button_detect port map ( clk => clk, button_i => button_ss_i, detect_o => button_ss_detect );
Analoges Vorgehen für die Taste button_rl_i
.
Zustandsmaschine
Die Zustandmaschine wird nun auch entsprechend eingebunden.
- Erstelle eine Instanz von
stopwatch_fsm
mit dem Namenstopwatch_fsm_component
- Verbinde den Eingang
clk
mit dem Signalclk
- Verbinde den Eingang
ss_i
mit dem Signalbutton_ss_detect
- Verbinde den Eingang
rl_i
mit dem Signalbutton_rl_detect
- Verbinde den Ausgang
mode_o
mit nichts (open
) - Verbinde den Ausgang
clear_o
mit dem Signalclear
- Verbinde den Ausgang
enable_o
mit dem Signalenable
Vorteiler
Um Zehntelsekunden zu erzeugen wird ein Vorteiler mit dem Faktor 5 Millionen verwendet.
prescale_component: entity work.counter generic map ( WIDTH => 23, MAXIMUM => 5000000 ) port map ( clk => clk, reset_i => clear, enable_i => enable, value_o => open, overflow_o => tenth_second_enable );
- Diese
counter
wird über dasgeneric map
als Zähler mit 23 Bit und Maximum bei 5 Millionen definiert - Der Ausgang
value_o
wird nicht verwendet (wird durchopen
signalisiert) overflow_o
steuert das Signaltenth_second_enable
an (ist nun alle 5 Millionen Takte für einen Takt high)enable_i
wird durchenable
angesteuert (Ausgang der Zustandsmaschine)reset_i
wird durchclear
angesteuert (Ausgang der Zustandsmaschine)
Zehntelsekunde
- Erstelle eine weitere Instanz eines Zählers(
tenth_second_component
) analog zuprescale_component
WIDTH
undMAXIMUM
müssen nicht extra definiert werden (generic map
kann wegfallen), da diese Werte der Standardeinstellung entsprechenenable_i
wird durchtenth_second_enable
angesteuertreset_i
wird durchclear
angesteuertvalue_o
steuertdigit0
anoverflow_o
steuertsecond_enable
an
Sekunde
- Erstelle eine weitere Instanz eines Zählers(
second_component
) analog zutenth_second_component
enable_i
wird durchsecond_enable
angesteuertvalue_o
steuertdigit1
anoverflow_o
steuertten_second_enable
an
Zehner Sekunde
- Erstelle eine weitere Instanz eines Zählers(
ten_second_component
) analog zutenth_second_component
MAXIMUM
wird auf 5 gestellt (Sekunden gehen bis 59) - dazu wird diegeneric map
genutztenable_i
wird durchten_second_enable
angesteuertvalue_o
steuertdigit2
anoverflow_o
steuertminute_enable
an
Minute
- Erstelle eine weitere Instanz eines Zählers(
minute_component
) analog zutenth_second_component
enable_i
wird durchminute_enable
angesteuertvalue_o
steuertdigit3
anoverflow_o
wird nicht verwendet (mitopen
verbunden)
display
hinzufügen
display_component: entity work.display port map ( clk => clk, digit0_i => digit0, digit1_i => digit1, digit2_i => digit2, digit3_i => digit3, dots_i => "1010", segments_o => segments_o, an_o => an_o );
Test auf der Hardware
Wechsle in die Implementierungsansicht und synthetisiere das Top Level Desgin stopwatch
. Teste die Funktionsweise auf dem Basys2 Board aus.
Erweiterung um Zwischenzeitnehmung
Spezifikation
Im letzen Schritt gibt es eine Erweiterung des bestehenden Designs: Die Zwischenzeitnehmung.
- Im Zustand
RUNNING
wird durch Drücken von Reset/Lap in den ZustandLAP
gwechselt werden - In
LAP
wird nicht die aktuelle Zeit angezeigt, sondern eine zwischengespeicherter Zeit - Im Zustand
LAP
wird durch Drücken von Reset/Lap wieder zurück in den ZustandRUNNING
gewechselt werden - Im Zustand
LAP
wird durch Drücken von Start/Stopp in den ZustandLAP_STOPPED
gewechselt werden - Im Zustand
LAP_STOPPED
läuft die Zeit nicht weiter, es wird weiterhin die zwischengespeicherte Zeit angezeigt - Im Zustand
LAP_STOPPED
wird durch Drücken von Reset/Lap in den Zustand STOPPED gewechselt
Implementierung
Die Eingänge digit0
bis digit3
der display_component
werden nun nicht mehr über die Zähler angesteuert sondern mittels Register (digit0_reg
, ... ist bereits definiert). Dieses Register soll die Eingänge digit0
, ... übernehmen, wenn das Signal mode
auf '1'
ist (Ausgang der Zustandsmaschine). Füge dazu einen process
im Top Level ein.
Erweitere die Zustandsmaschine um die zusätzlichen Zustände und steuere mode_o
entsprechend der Spezifikation an.
Test mittels Testbench
Für die überarbeitete Zustandsmaschine steht eine Testbench bereit (stopwatch_fsm_tb
).
Test auf der Hardware
Wechsle in die Implementierungsansicht und synthetisiere das Top Level Desgin stopwatch
. Teste die Funktionsweise auf dem Basys2 Board aus.