/*
 * Copyright (c) 2014 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2006 The Regents of The University of Michigan
 * Copyright (c) 2013 Advanced Micro Devices, Inc.
 * Copyright (c) 2013 Mark D. Hill and David A. Wood
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Nathan Binkert
 *          Steve Reinhardt
 *          Andrew Bardsley
 *          Christian Menard
 */

/**
 * @file
 *
 * Defines an sc_module type to wrap a gem5 simulation.  The 'evaluate'
 * thread on that module implements the gem5 event loop.
 *
 * This currently only supports a single event queue and strictly
 * cooperatively threaded SystemC threads and so there should be at
 * most one Gem5Module instantiated in any simulation.
 */

#ifndef __SIM_SC_MODULE_HH__
#define __SIM_SC_MODULE_HH__

#include <systemc>

#include "sim/eventq.hh"
#include "sim/sim_events.hh"

namespace Gem5SystemC
{

/** A SystemC module implementing the gem5 event queue.  This object
 *  doesn't actually own any of the simulation SimObjects (those need
 *  to be administered separately) but it does control the event
 *  queue.
 *
 *  The event loop here services gem5 events in order at the current time
 *  and then yielding to another SystemC thread.  gem5 events are not
 *  individually scheduled in SystemC.  For this reason, asynchronous events
 *  and function interaction (for example TLM) with gem5 from SystemC must
 *  notify the module so that the yielding 'wait' can be interrupted.
 *  From the point of view of another SystemC module calling into gem5,
 *  curTick can lag SystemC time, be exactly the same time but *never*
 *  lead SystemC time.
 *
 *  This functionality is wrapped in an sc_module as its intended that
 *  the a class representing top level simulation control should be derived
 *  from this class. */
class Module : public sc_core::sc_channel
{
  protected:
    /** Event to trigger (via. ::notify) for event scheduling from
     *  outside gem5 */
    sc_core::sc_event externalSchedulingEvent;

    /** Event to trigger on exit of eventLoop */
    sc_core::sc_event eventLoopExitEvent;

    /** Event to trigger to enter eventLoop */
    sc_core::sc_event eventLoopEnterEvent;

    /** Expected exit time of last eventLoop sleep */
    Tick wait_exit_time;

    /** Are we in Module::simulate?  Used to mask events when not inside
     *  the simulate loop */
    bool in_simulate;

    /** Placeholder base class for a variant event queue if this becomes
     *  useful */
    class SCEventQueue : public EventQueue
    {
      protected:
        Module &module;

      public:
        SCEventQueue(const std::string &name,
            Module &module_) : EventQueue(name), module(module_)
        { }

        /** Signal module to wakeup */
        void wakeup(Tick when);
    };

    /** Service any async event marked up in the globals event_... */
    void serviceAsyncEvent();

  public:
    /** Simulate is a process */
    SC_HAS_PROCESS(Module);

    Module(sc_core::sc_module_name name);

    /** Last exitEvent from eventLoop */
    Event *exitEvent;

    /** Setup global event queues.  Call this before any other event queues
     *  are created */
    static void setupEventQueues(Module &module);

    /** Catch gem5 time up with SystemC */
    void catchup();

    /** Notify an externalSchedulingEvent at the given time from the
     *  current SystemC time */
    void notify(sc_core::sc_time time_from_now = sc_core::SC_ZERO_TIME);

    /** Process an event triggered by externalSchedulingEvent and also
     *  call eventLoop (to try and mop up any events at this time) if there
     *  are any scheduled events */
    void serviceExternalEvent();

    /** Process gem5 events up until an exit event or there are no events
     *  left. */
    void eventLoop();

    /** Run eventLoop up to num_cycles and return the final event */
    GlobalSimLoopExitEvent *simulate(Tick num_cycles = MaxTick);
};

/** There are assumptions throughout Gem5SystemC file that a tick is 1ps.
 *  Make this the case */
void setTickFrequency();

}

#endif // __SIM_SC_MODULE_HH__