Lösung Testbench

Zuerst wird die Testbench angepasst.

Die Messfunktion misst jetzt anstatt der Periode die Anteile für High und Low:

    --! measure LED period
    procedure MeasureLedPeriod(constant maxCycles : in integer;
            signal Led_high : out time; signal Led_low : out time) is
        variable i : integer;
        variable s : integer;
        variable rise_1 : integer;
        variable fall_1 : integer;
        variable rise_2 : integer;
    begin
        s := 0; -- initial state
        Led_high <= 0 ns; -- timeout return value
        Led_low <= 0 ns; -- timeout return value
        for i in 1 to maxCycles loop    -- timeout condition
            wait until rising_edge(Ext_Clk);        -- measure in clock ticks
            case s is
                when 0 =>               -- wait for initial condition
                    if (Led = '0') then
                        s := 1;         -- initial condition reached
                    end if;
                when 1 =>               -- wait for rising edge
                    if (Led = '1') then
                        s := 2;         -- first rising edge reached
                        rise_1 := i;    -- remember clock tick
                    end if;
                when 2 =>               -- wait for LED being off again
                    if (Led = '0') then
                        s := 3;         -- LED is off again reached
                        fall_1 := i;    -- remember clock tick
                        Led_high <= (fall_1 - rise_1) * Clk_period;
                    end if;
                when 3 =>               -- wait for rising edge
                    if (Led = '1') then
                        s := 4;         -- nothing more to do
                        rise_2 := i;    -- remember clock tick
                        Led_low <= (rise_2 - fall_1) * Clk_period;
                        wait for 10 ps; -- Let variable value settle
                        return;         -- got result
                    end if;
                when others =>
                    return;
            end case;
        end loop;
    end MeasureLedPeriod;

Die Überprüfungsfunktion überprüft jetzt diese Anteil und passt auch die Ausgabe entsprechend an:

    --! check LED period
    procedure CheckLedPeriod(constant expected : in time;
            signal Led_high : inout time; signal Led_low : inout time;
            signal TestErr : out std_logic) is
        variable high_expected : time;
        variable low_expected : time;
    begin
        high_expected := expected / 4;
        low_expected := expected - high_expected;
        MeasureLedPeriod(3*expected/Clk_period, Led_high, Led_low);
        if ((Led_high = high_expected) and (Led_low = low_expected)) then
            tb_report("  Expected high: " & time'Image(high_expected) &
                      ",  low: " & time'Image(low_expected) & ": passed.");
        else
            TestErr <= '1';
            tb_report("  Expected high: " & time'Image(high_expected) &
                      ",  low: " & time'Image(low_expected) &
                      "; Found high: " & time'Image(Led_high) &
                      ",  low: " & time'Image(Led_low) &
                      ": Failed!");
        end if;
    end CheckLedPeriod;

Der Test Case 5 wird angepasst, da es jetzt nur noch 7 anstatt 8 Schritte mit der Frequenz nach oben geht und die Testaufrufe werden natürlich auch angepasst, da anstatt der Periode jetzt _high und _low-Werte analysiert werden (diese Signale müssen natürlich auch deklariert werden!)

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

Lösung UUT

Der eingeschränkte Frequenzbereich verändert die Grenzen des Selektors.

Um den Duty-Cycle von 25 % zu erhalten, ist es am einfachsten den Frequenzgenerator um ein Bit zu schieben und wieder mit dem Frequenzgenerator zu verunden.

signal CtrlFreqR        : integer range 1 to 12 := 8;   --! Control toggle freqency
signal FreqGen25C       : std_logic_vector(12 downto 1);    --! FreqGenR 25 % duty cycle

...
    FreqGen25C      <= FreqGenR(12 downto 1) and FreqGenR(11 downto 0);

Für die unteren beiden Bits schaut das dann so aus:

Frequenzgenerator mit 25 % Duty-Cycle

Schließlich muss noch der Multiplexer angepasst werden.

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

Weiter geht es mit dem Projekt: SimpleState