TestBlinkingLed.vhd

-------------------------------------------------------------------------------
-- Copyright (c) 2016 by Stefan Milch.  All rights reserved.
-------------------------------------------------------------------------------
--! @file        TestBlinkingLed.vhd
--! @brief       Test shell for module BlinkingLed. \n
--!
-------------------------------------------------------------------------------
library ieee;
    use ieee.std_logic_1164.all;
    use std.textio.all;
 
entity TestBlinkingLed is
end TestBlinkingLed;
 
architecture behavior of TestBlinkingLed is
 
    -- Component Declaration for the Unit Under Test (UUT)
    component BlinkingLed
        port (
            EXT_CLK         : in  std_logic; --! 125 MHz system clock
            BTN             : in  std_logic_vector(3 downto 0); --! Button inputs, high acitve
            LED             : out std_logic --! LED output, high acitve
        );
    end component;

    -- Time definitions
    constant Clk_period     : time := 8 ns;             --! ZYBO Clock frequency is 125 MHz
--  constant Clk_period     : time := 10 ns;            --! ARTY Clock frequency is 100 MHz
    constant LED_min_per    : time := 400 ns;           --! LED period in fastest mode
    constant LED_res_per    : time := LED_min_per*256;  --! LED period after reset
    constant LED_max_per    : time := LED_res_per*16;   --! LED period in slowest mode

    --Inputs
    signal Button       : std_logic_vector(3 downto 0) := (others => '0');
    signal Ext_Clk      : std_logic := '0';
    
    --Outputs
    signal Led          : std_logic;
    
    --Test
    file report_file    : text;
    signal TestCase     : integer;
    signal TestErr      : std_logic := '0';
    signal LED_per      : time := 0 ns;

    --! print to output file
    procedure tb_report(constant report_text : in string) is
        variable line_out: line;
    begin
        write(line_out,report_text);
        writeline(report_file, line_out);
    end tb_report;

    --! wait a number of cycles
    procedure WaitCycles(constant cycles : in integer) is
        variable i : integer;
    begin
        for i in 1 to cycles loop
            wait until rising_edge(Ext_Clk);
        end loop;
    end WaitCycles;

    --! measure LED period
    procedure MeasureLedPeriod(constant maxCycles : in integer; signal Led_period : out time) is
        variable i : integer;
        variable s : integer;
        variable rise_1 : integer;
        variable rise_2 : integer;
    begin
        s := 0; -- initial state
        Led_period <= 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
                    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_period <= (rise_2 - rise_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;

    --! measure LED period
    procedure CheckLedPeriod(constant expected : in time; signal LED_period : inout time;
                            signal TestErr : out std_logic) is
    begin
        MeasureLedPeriod(2*expected/Clk_period, LED_period);
        if (LED_period = expected) then
            tb_report("  Expected " & time'Image(expected) & ": passed.");
        else
            TestErr <= '1';
            tb_report("  Expected " & time'Image(expected) & "; Found " & time'Image(LED_per) & ": Failed!");
        end if;
    end CheckLedPeriod;

begin
    -- Instantiate the Unit Under Test (UUT)
    uut: BlinkingLed port map (
        EXT_CLK     => Ext_Clk,
        BTN         => Button,
        LED         => Led
    );

    --! Generate system clock
    p_clk : process
    begin
        Ext_Clk <= not Ext_Clk;
        wait for Clk_period/2;
    end process;

    --! Check for test bench time out
    p_control : process
    begin
        wait for 20 ms;
        -- the test bench should already have terminated!
        assert false
        report "Test timed out!"
        severity failure;
    end process;

    -- Stimulus process
    stim_proc: process
        variable expected_time : time;
    begin
        -- prepare reporting
        file_open(report_file,"../../../TestBlinkingLed.log",WRITE_MODE);
        tb_report("Testing BlinkingLed:");
        tb_report("====================");
        
        TestCase <= 1;

        TestCase <= 0;
        WaitCycles(1);
        TestErr <= not TestErr;
        WaitCycles(1);
        TestErr <= not TestErr;
        WaitCycles(500);
        if (TestErr = '0') then
            tb_report("Test completed successfully");
        else
            tb_report("Test completed with error(s)");
        end if;
        file_close(report_file);
        assert false
        report "Test terminated. This is not a failiure!"
        severity failure;
        wait;
    end process;

end;