    --------------------------------------------------------------------------------
    -- light52_tb.vhdl --
    --------------------------------------------------------------------------------
    -- This test bench simulates the execution of some program (whose object code
    -- is in package obj_code_pkg, in the form of a memory init constant) and logs
    -- the execution to a text file called 'hw_sim_log.txt' (light52_tb_pkg.vhdl).
    --
    -- This test bench does no actual tests on the core. Instead, the simulation log
    -- is meant to be matched against the simulation log produced by running the
    -- same program on the software simulator B51 (also included with this project).
    --
    -- This will catch errors in the implementation of the CPU if the simulated
    -- program has anough coverage -- the opcode tester is meant to cover all CPU
    -- opcodes in many (not all) of their corner cases.
    -- This scheme will not help in catching errors in the peripheral modules,
    -- mainly because the current version of B51 does not simulate them.
    --
    --------------------------------------------------------------------------------
    -- Copyright (C) 2012 Jose A. Ruiz
    --
    -- This source file may be used and distributed without
    -- restriction provided that this copyright statement is not
    -- removed from the file and that any derivative work contains
    -- the original copyright notice and the associated disclaimer.
    --
    -- This source file is free software; you can redistribute it
    -- and/or modify it under the terms of the GNU Lesser General
    -- Public License as published by the Free Software Foundation;
    -- either version 2.1 of the License, or (at your option) any
    -- later version.
    --
    -- This source is distributed in the hope that it will be
    -- useful, but WITHOUT ANY WARRANTY; without even the implied
    -- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
    -- PURPOSE.  See the GNU Lesser General Public License for more
    -- details.
    --
    -- You should have received a copy of the GNU Lesser General
    -- Public License along with this source; if not, download it
    -- from http://www.opencores.org/lgpl.shtml
    --------------------------------------------------------------------------------

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_arith.all;
    use ieee.std_logic_unsigned.all;
    use std.textio.all;

    use work.light52_pkg.all;
    use work.obj_code_pkg.all;
    use work.light52_tb_pkg.all;
    use work.txt_util.all;

    entity light52_tb is
    generic (BCD : boolean := true);
    end;

    architecture testbench of light52_tb is

    --------------------------------------------------------------------------------
    -- Simulation parameters
    -- FIXME these should be in parameter package

    -- Simulated clock period is the same as the usual target, the DE-1 board
    constant T : time := 20 ns; -- 50MHz
    constant SIMULATION_LENGTH : integer := 400000;

    --------------------------------------------------------------------------------
    -- MPU interface

    signal clk :                std_logic := '0';
    signal reset :              std_logic := '1';

    signal p0_out :             std_logic_vector(7 downto 0);
    signal p1_out :             std_logic_vector(7 downto 0);
    signal p2_in :              std_logic_vector(7 downto 0);
    signal p3_in :              std_logic_vector(7 downto 0);

    signal external_irq :       std_logic_vector(7 downto 0);

    signal txd, rxd :           std_logic;

    --------------------------------------------------------------------------------
    -- Logging signals & simulation control

    -- Asserted high to disable the clock and terminate the simulation.
    signal done :               std_logic := '0';

    -- Log file
    file log_file: TEXT open write_mode is "hw_sim_log.txt";
    -- Console output log file
    file con_file: TEXT open write_mode is "hw_sim_console_log.txt";
    -- Info record needed by the logging fuctions
    signal log_info :           t_log_info;

    begin

    ---- UUT instantiation ---------------------------------------------------------

    uut: entity work.light52_mcu
        generic map (
            IMPLEMENT_BCD_INSTRUCTIONS => BCD,
            CODE_ROM_SIZE =>    work.obj_code_pkg.XCODE_SIZE,
            XDATA_RAM_SIZE =>   work.obj_code_pkg.XDATA_SIZE,
            OBJ_CODE =>         work.obj_code_pkg.object_code
        )
        port map (
            clk             => clk,
            reset           => reset,

            txd             => txd,
            rxd             => rxd,

            external_irq    => external_irq,

            p0_out          => p0_out,
            p1_out          => p1_out,
            p2_in           => p2_in,
            p3_in           => p3_in
        );

        -- UART is looped back in the test bench.
        rxd <= txd;

        -- I/O ports are looped back and otherwise unused.
        p2_in <= p0_out;
        p3_in <= p1_out;

        -- External IRQ inputs are tied to port P1 for test purposes
        external_irq <= p1_out;

        ---- Master clock: free running clock used as main module clock ------------
        run_master_clock: process(done, clk)
        begin
            if done = '0' then
                clk <= not clk after T/2;
            end if;
        end process run_master_clock;

        ---- Main simulation process: reset MCU and wait for fixed period ----------

        drive_uut: process
        begin
            -- Leave reset asserted for a few clock cycles...
            reset <= '1';
            wait for T*4;
            reset <= '0';

            -- ...and wait for the test to hit a termination condition (evaluated by
            -- function log_cpu_activity) or to just timeout.
            wait for T*SIMULATION_LENGTH;

            -- If we arrive here, the simulation timed out (termination conditions
            -- trigger a failed assertion).
            -- So print a timeout message and quit.
            print("TB timed out.");
            done <= '1';
            wait;

        end process drive_uut;

        -- Logging process: launch logger functions --------------------------------
        log_execution: process
        begin
            -- Log cpu activity until done='1'.
            log_cpu_activity(clk, reset, done, "/uut",
                             log_info, work.obj_code_pkg.XCODE_SIZE, "log_info",
                             X"0000", log_file, con_file);

            -- Flush console log file when finished.
            log_flush_console(log_info, con_file);

            wait;
        end process log_execution;

    end architecture testbench;