4. Schritt: Abschlussarbeiten

In diesem Schritt wird der Rest der Spezifikation erfüllt.

Zuerst erweitern wir die Testbench entsprechend der Spezifikation. D.h. wir stimulieren die Schaltung durch mehrmaliges Drücken der Taster und überprüfen, ob sich das FPGA entsprechend der Spezifikation verhält.

Die neuen Test Cases werden nach dem Test Case 1 eingefügt.

In Test Case 2 wird 4 mal auf den Button 3 gedrückt, wodurch sich die Frequenz halbieren soll, bis die minimale Frequenz erreicht wird.

In Test Case 3 wird noch mal auf den Butten 3 gedrückt und geprüft ob die minimale Frequenz auch beibehalten wird.

        TestCase <= 2;
        tb_report("Test Case 2: Reducing LED speed to minimum");
        for i in 1 to 4 loop
            -- press frequency reduce button
            Button(3) <= '1';
            WaitCycles(10);
            Button(3) <= '0';
            expected_time := 2 * expected_time;
            CheckLedPeriod(expected_time, LED_per, TestErr);
        end loop;

        TestCase <= 3;
        tb_report("Test Case 3: Reducing LED speed after minimum");
        -- press frequency reduce button
        Button(3) <= '1';
        WaitCycles(10);
        Button(3) <= '0';
        -- expected time does not change any more
        CheckLedPeriod(expected_time, LED_per, TestErr);

In Test Case 4 wird auf den Button 0 (Reset) gedrückt und geprüft ob die LED wieder mit der Reset-Frequenz blinkt.

In Test Case 5 wird 8 mal auf den Button 2 gedrückt, wodurch sich die Frequenz verdoppeln soll, bis die maximale Frequenz erreicht wird.

In Test Case 6 wird noch mal auf den Butten 2 gedrückt und geprüft ob die maximale Frequenz auch beibehalten wird.

        TestCase <= 4;
        tb_report("Test Case 4: Reset FPGA and check reset period...");
        Button(0) <= '1';
        WaitCycles(10);
        Button(0) <= '0';
        expected_time := LED_res_per;
        CheckLedPeriod(expected_time, LED_per, TestErr);

        TestCase <= 5;
        tb_report("Test Case 5: Increase LED speed to maximum");
        for i in 1 to 8 loop
            -- press frequency reduce button
            Button(2) <= '1';
            WaitCycles(10);
            Button(2) <= '0';
            expected_time := expected_time / 2;
            CheckLedPeriod(expected_time, LED_per, TestErr);
        end loop;

        TestCase <= 6;
        tb_report("Test Case 6: Increase LED speed after maximum");
        -- press frequency reduce button
        Button(2) <= '1';
        WaitCycles(10);
        Button(2) <= '0';
        -- expected time does not change any more
        CheckLedPeriod(expected_time, LED_per, TestErr);

Lässt man jetzt die Simulation erneut laufen schlägt sie natürlich fehl, da die UUT ja noch nicht entsprechend angepasst worden ist:

TestBlinkingLed.log

Testing BlinkingLed:
====================
Test Case 1: LED Period after power on...
  Expected 102400000 ps: passed.
Test Case 2: Reducing LED speed to minimum
  Expected 204800000 ps; Found 102400000 ps: Failed!
  Expected 409600000 ps; Found 102400000 ps: Failed!
  Expected 819200000 ps; Found 102400000 ps: Failed!
  Expected 1638400000 ps; Found 102400000 ps: Failed!
Test Case 3: Reducing LED speed after minimum
  Expected 1638400000 ps; Found 102400000 ps: Failed!
Test Case 4: Reset FPGA and check reset period...
  Expected 102400000 ps: passed.
Test Case 5: Increase LED speed to maximum
  Expected 51200000 ps; Found 0 ps: Failed!
  Expected 25600000 ps; Found 0 ps: Failed!
  Expected 12800000 ps; Found 0 ps: Failed!
  Expected 6400000 ps; Found 0 ps: Failed!
  Expected 3200000 ps; Found 0 ps: Failed!
  Expected 1600000 ps; Found 0 ps: Failed!
  Expected 800000 ps; Found 0 ps: Failed!
  Expected 400000 ps; Found 0 ps: Failed!
Test Case 6: Increase LED speed after maximum
  Expected 400000 ps; Found 0 ps: Failed!
Test completed with error(s)

Taster einbauen

Die vier vorhandenen Taster sollen folgendermaßen verwendet werden.

  • Taster 0: Reset Funktion so lange gedrückt
  • Taster 2: Frequenz verdoppeln, einmal pro Betätigung
  • Taster 3: Frequenz halbieren, einmal pro Betätigung

Für die Taster 2 und 3 benötigt man also noch eine Flankenerkennung, damit pro Betätigung nur ein Ereignis ausgelöst wird.

Folgende Code Snippets müssen also eingefügt werden:

-- input and output
signal Clk              : std_logic;                    --! Internal processing clock
signal ButtonR          : std_logic_vector(3 downto 0); --! Button inputs after input flipflops
signal LedC             : std_logic;                    --! LED output control
signal LedR             : std_logic;                    --! LED output control

-- button functions
signal Button1R         : std_logic_vector(3 downto 0); --! Delayed button signals for edge detection
signal ButtonPressC     : std_logic_vector(3 downto 0); --! Button was pressed
signal ResetR           : std_logic;                    --! Reset event
signal FreqUpR          : std_logic;                    --! Frequency up event
signal FreqDownR        : std_logic;                    --! Frequency down event
    --! Simple flipflops
    p_simple : process (Clk)
    begin
        if rising_edge(Clk) then
            GenInitR    <= GenInitR and not InitR;  -- Release InitR after it was activated
            InitR       <= InitC;
            ButtonR     <= BTN;  -- input FF for Button signals
            LED         <= LedR; -- outpuf FF for LED
            LedR        <= LedC; -- Register LED signal from multiplexer
            Button1R    <= ButtonR; -- Delayed button inputs
            FreqUpR     <= ButtonPressC(2); -- Frequency up event
            FreqDownR   <= ButtonPressC(3); -- Frequency up event
        end if;
    end process;

    ResetR              <=  ButtonR(0);                 -- Button 0 is reset input
    ButtonPressC        <=  ButtonR and not Button1R;   -- rising edge detection of buttons
    InitC               <=  ResetR or GenInitR;         -- Reset on button and power on

Die Taster werden hintereinander durch 2 Flip Flops geschickt: BTN --> ButtonR --> Button1R

BTN --> ButtonR dient dazu das externe asynchrone Signal BTN zu übernehmen. ButtonR ist synchron zu unserem Verarbeitungstakt und kann weiterverarbeitet werden.

ButtonR --> Button1R ist eine Verzögerung um einen Takt für die Flankenerkennung.

Da alle Signale vom Typ std_logic_vector sind, werden diese Flip Flops für jedes einzelne Bit des Vektors angelegt.

Die Flankenerkennung "ButtonPressC <= ButtonR and not Button1R" funktioniert auch bitweise. Wird ein Taster gedrückt, zeigt sich das zuerst im Signal BTN, dann in ButtonR und einen Takt später in Button1R. D.h. wenn ButtonR schon gesetzt, Button1R aber noch nicht haben wir eine steigende Flanke erkannt.

Am Beispiel des Tasters 3 der zum Signal FreqDownR führt sieht man das in der Simulation:

Tastendruckerkennung

An diesem Beispiel kann man auch die Verzögerung um eine Taktperiode im Prozess p_simple erkennen.

Selektor einbauen

Der Selektor wählt ein Bit aus dem Frequenzgenerator aus welches über die LED ausgegeben wird.

Dazu brauchen wir zum einen neue Signale:

signal CtrlFreqR        : integer range 0 to 12;        --! Control toggle freqency
signal IncCtrlFreqC     : std_logic;                    --! Increment CtrlFreqR
signal DecCtrlFreqC     : std_logic;                    --! Decrement CtrlFreqR
signal ResCtrlFreqC     : std_logic;                    --! Reset CtrlFreqR

Als nächstes benötigen wir den Zähler mit Ansteuerung.

    p_sync : process (Clk)
    begin
        if rising_edge(Clk) then
            ...
            -- Handle the frequency control counter
            -- Pay attention: incrementing the bit number reduces the frequency!
            if (ResCtrlFreqC = '1') then
                CtrlFreqR   <= 8;
            else
                if (IncCtrlFreqC = '1') then
                    CtrlFreqR   <= CtrlFreqR - 1;
                elsif (DecCtrlFreqC = '1') then
                    CtrlFreqR   <= CtrlFreqR + 1;
                end if;
            end if;
        end if;
    end process;

    ...
    ResCtrlFreqC        <=  '1' when InitR = '1'                else    '0';
    IncCtrlFreqC        <=  '0' when CtrlFreqR = 0              else
                            '1' when FreqUpR = '1'              else
                            '0';
    DecCtrlFreqC        <=  '0' when CtrlFreqR = 12             else
                            '1' when FreqDownR = '1'            else
                            '0';

Nun fehlt nur noch der Multiplexer, der das gewählte Bit auswählt:

    --! Multiplexer
    p_mux : process (FreqGenR, CtrlFreqR)
    begin
        case CtrlFreqR is
            when  0 =>  LedC <=  FreqGenR(0);
            when  1 =>  LedC <=  FreqGenR(1);
            when  2 =>  LedC <=  FreqGenR(2);
            when  3 =>  LedC <=  FreqGenR(3);
            when  4 =>  LedC <=  FreqGenR(4);
            when  5 =>  LedC <=  FreqGenR(5);
            when  6 =>  LedC <=  FreqGenR(6);
            when  7 =>  LedC <=  FreqGenR(7);
            when  8 =>  LedC <=  FreqGenR(8);
            when  9 =>  LedC <=  FreqGenR(9);
            when 10 =>  LedC <=  FreqGenR(10);
            when 11 =>  LedC <=  FreqGenR(11);
            when 12 =>  LedC <=  FreqGenR(12);
            when others => LedC <= 'X';
        end case;
    end process;

Dieser Multiplexer wird durch einen Prozess erzeugt der nicht getaktet ist, denn egal welches Eingangssignal sich ändert, hat dies sofortige Auswirkung auf den Ausgang LedC.

Wichtig bei dieser Art von Prozessen ist, dass wirklich alle Signale, die Einfluss auf das Ergebnis haben in der Sensitivitätsliste des Prozesses aufgenommen werden. Ansonsten kommt es zu unvorhergesehenem Verhalten.

Nachdem das LED-Signal nun aus dem Multiplexer kommt muss natürlich noch die alte Zuweisung "LED <= FreqGenR(8);" entfernt werden.

Test

Nachdem alle Änderungen eingebaut worden sind sollte die Simulation durchlaufen und das Board sich entsprechend Spezifikation verhalten.

TestBlinkingLed.log

Testing BlinkingLed:
====================
Test Case 1: LED Period after power on...
  Expected 102400000 ps: passed.
Test Case 2: Reducing LED speed to minimum
  Expected 204800000 ps: passed.
  Expected 409600000 ps: passed.
  Expected 819200000 ps: passed.
  Expected 1638400000 ps: passed.
Test Case 3: Reducing LED speed after minimum
  Expected 1638400000 ps: passed.
Test Case 4: Reset FPGA and check reset period...
  Expected 102400000 ps: passed.
Test Case 5: Increase LED speed to maximum
  Expected 51200000 ps: passed.
  Expected 25600000 ps: passed.
  Expected 12800000 ps: passed.
  Expected 6400000 ps: passed.
  Expected 3200000 ps: passed.
  Expected 1600000 ps: passed.
  Expected 800000 ps: passed.
  Expected 400000 ps: passed.
Test Case 6: Increase LED speed after maximum
  Expected 400000 ps: passed.
Test completed successfully

Test auf der Hardware

Nachdem die Schaltung in der Simulation läuft, sollte man sich natürlich auch noch die Zeit gönnen den Bitstream zu generieren und auf die Hardware zu laden.

Auch ohne ein Oszilloskop anzuschließen kann man prinzipiell sehen, dass das Verhalten der Schaltung der Spezifikation entspricht.

Weiter zu den Erläuterungen