63bb17e4bd
The non-standard sc_time constructors - sc_time( uint64, bool scale ) - sc_time( double, bool scale ) have been deprecated in SystemC 2.3.1 and a warning is issued when being used. Insted the new 'sc_time::from_value' function is used to omit the warning message. Signed-off-by: Jason Lowe-Power <jason@lowepower.com>
298 lines
9 KiB
C++
298 lines
9 KiB
C++
/*
|
|
* 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
|
|
* Matthias Jung
|
|
*/
|
|
|
|
/**
|
|
* @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.
|
|
*/
|
|
|
|
#include "base/pollevent.hh"
|
|
#include "base/trace.hh"
|
|
#include "debug/Event.hh"
|
|
#include "sim/async.hh"
|
|
#include "sim/core.hh"
|
|
#include "sim/eventq.hh"
|
|
#include "sim/sim_exit.hh"
|
|
#include "sim/stat_control.hh"
|
|
#include "sc_module.hh"
|
|
|
|
namespace Gem5SystemC
|
|
{
|
|
|
|
/** There are assumptions throughout Gem5SystemC file that a tick is 1ps.
|
|
* Make this the case */
|
|
void
|
|
setTickFrequency()
|
|
{
|
|
::setClockFrequency(1000000000000);
|
|
}
|
|
|
|
Module::Module(sc_core::sc_module_name name) : sc_core::sc_module(name),
|
|
in_simulate(false)
|
|
{
|
|
SC_METHOD(eventLoop);
|
|
sensitive << eventLoopEnterEvent;
|
|
dont_initialize();
|
|
|
|
SC_METHOD(serviceExternalEvent);
|
|
sensitive << externalSchedulingEvent;
|
|
dont_initialize();
|
|
}
|
|
|
|
void
|
|
Module::SCEventQueue::wakeup(Tick when)
|
|
{
|
|
DPRINTF(Event, "waking up SCEventQueue\n");
|
|
/* Don't bother to use 'when' for now */
|
|
module.notify();
|
|
}
|
|
|
|
void
|
|
Module::setupEventQueues(Module &module)
|
|
{
|
|
fatal_if(mainEventQueue.size() != 0,
|
|
"Gem5SystemC::Module::setupEventQueues must be called"
|
|
" before any gem5 event queues are set up");
|
|
|
|
numMainEventQueues = 1;
|
|
mainEventQueue.push_back(new SCEventQueue("events", module));
|
|
curEventQueue(getEventQueue(0));
|
|
}
|
|
|
|
void
|
|
Module::catchup()
|
|
{
|
|
EventQueue *eventq = getEventQueue(0);
|
|
Tick systemc_time = sc_core::sc_time_stamp().value();
|
|
Tick gem5_time = curTick();
|
|
|
|
/* gem5 time *must* lag SystemC as SystemC is the master */
|
|
fatal_if(gem5_time > systemc_time, "gem5 time must lag SystemC time"
|
|
" gem5: %d SystemC: %d", gem5_time, systemc_time);
|
|
|
|
eventq->setCurTick(systemc_time);
|
|
|
|
if (!eventq->empty()) {
|
|
Tick next_event_time M5_VAR_USED = eventq->nextTick();
|
|
|
|
fatal_if(gem5_time > next_event_time,
|
|
"Missed an event at time %d gem5: %d, SystemC: %d",
|
|
next_event_time, gem5_time, systemc_time);
|
|
}
|
|
}
|
|
|
|
void
|
|
Module::notify(sc_core::sc_time time_from_now)
|
|
{
|
|
externalSchedulingEvent.notify(time_from_now);
|
|
}
|
|
|
|
void
|
|
Module::serviceAsyncEvent()
|
|
{
|
|
EventQueue *eventq = getEventQueue(0);
|
|
std::lock_guard<EventQueue> lock(*eventq);
|
|
|
|
assert(async_event);
|
|
|
|
/* Catch up gem5 time with SystemC time so that any event here won't
|
|
* be in the past relative to the current time */
|
|
Tick systemc_time = sc_core::sc_time_stamp().value();
|
|
|
|
/* Move time on to match SystemC */
|
|
catchup();
|
|
|
|
async_event = false;
|
|
if (async_statdump || async_statreset) {
|
|
Stats::schedStatEvent(async_statdump, async_statreset);
|
|
async_statdump = false;
|
|
async_statreset = false;
|
|
}
|
|
|
|
if (async_exit) {
|
|
async_exit = false;
|
|
exitSimLoop("user interrupt received");
|
|
}
|
|
|
|
if (async_io) {
|
|
async_io = false;
|
|
pollQueue.service();
|
|
}
|
|
|
|
if (async_exception)
|
|
fatal("received async_exception, shouldn't be possible");
|
|
}
|
|
|
|
void
|
|
Module::serviceExternalEvent()
|
|
{
|
|
EventQueue *eventq = getEventQueue(0);
|
|
|
|
if (!in_simulate && !async_event)
|
|
warn("Gem5SystemC external event received while not in simulate");
|
|
|
|
if (async_event)
|
|
serviceAsyncEvent();
|
|
|
|
if (in_simulate && !eventq->empty())
|
|
eventLoop();
|
|
}
|
|
|
|
void
|
|
Module::eventLoop()
|
|
{
|
|
EventQueue *eventq = getEventQueue(0);
|
|
|
|
fatal_if(!in_simulate, "Gem5SystemC event loop entered while"
|
|
" outside Gem5SystemC::Module::simulate");
|
|
|
|
if (async_event)
|
|
serviceAsyncEvent();
|
|
|
|
while (!eventq->empty()) {
|
|
Tick next_event_time = eventq->nextTick();
|
|
|
|
/* Move time on to match SystemC */
|
|
catchup();
|
|
|
|
Tick gem5_time = curTick();
|
|
|
|
/* Woken up early */
|
|
if (wait_exit_time > sc_core::sc_time_stamp().value()) {
|
|
DPRINTF(Event, "Woken up early\n");
|
|
wait_exit_time = sc_core::sc_time_stamp().value();
|
|
}
|
|
|
|
if (gem5_time < next_event_time) {
|
|
Tick wait_period = next_event_time - gem5_time;
|
|
wait_exit_time = gem5_time + wait_period;
|
|
|
|
DPRINTF(Event, "Waiting for %d ticks for next gem5 event\n",
|
|
wait_period);
|
|
|
|
/* The next event is scheduled in the future, wait until
|
|
* then or until externalSchedulingEvent */
|
|
eventLoopEnterEvent.notify(sc_core::sc_time::from_value(
|
|
sc_dt::uint64(wait_period)));
|
|
|
|
return;
|
|
} else if (gem5_time > next_event_time) {
|
|
Tick systemc_time = sc_core::sc_time_stamp().value();
|
|
|
|
/* Missed event, for some reason the above test didn't work
|
|
* or an event was scheduled in the past */
|
|
fatal("Missed an event at time %d gem5: %d, SystemC: %d",
|
|
next_event_time, gem5_time, systemc_time);
|
|
} else {
|
|
/* Service an event */
|
|
exitEvent = eventq->serviceOne();
|
|
|
|
if (exitEvent) {
|
|
eventLoopExitEvent.notify(sc_core::SC_ZERO_TIME);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
fatal("Ran out of events without seeing exit event");
|
|
}
|
|
|
|
GlobalSimLoopExitEvent *
|
|
Module::simulate(Tick num_cycles)
|
|
{
|
|
inform("Entering event queue @ %d. Starting simulation...", curTick());
|
|
|
|
if (num_cycles < MaxTick - curTick())
|
|
num_cycles = curTick() + num_cycles;
|
|
else /* counter would roll over or be set to MaxTick anyhow */
|
|
num_cycles = MaxTick;
|
|
|
|
GlobalEvent *limit_event = new GlobalSimLoopExitEvent(num_cycles,
|
|
"simulate() limit reached", 0, 0);
|
|
|
|
exitEvent = NULL;
|
|
|
|
/* Cancel any outstanding events */
|
|
eventLoopExitEvent.cancel();
|
|
externalSchedulingEvent.cancel();
|
|
|
|
in_simulate = true;
|
|
eventLoopEnterEvent.notify(sc_core::SC_ZERO_TIME);
|
|
|
|
/* Wait for event queue to exit, guarded by exitEvent just incase
|
|
* it already has exited and we don't want to completely rely
|
|
* on notify semantics */
|
|
if (!exitEvent)
|
|
wait(eventLoopExitEvent);
|
|
|
|
/* Cancel any outstanding event loop entries */
|
|
eventLoopEnterEvent.cancel();
|
|
in_simulate = false;
|
|
|
|
/* Locate the global exit event */
|
|
BaseGlobalEvent *global_event = exitEvent->globalEvent();
|
|
assert(global_event != NULL);
|
|
|
|
GlobalSimLoopExitEvent *global_exit_event =
|
|
dynamic_cast<GlobalSimLoopExitEvent *>(global_event);
|
|
assert(global_exit_event != NULL);
|
|
|
|
if (global_exit_event != limit_event) {
|
|
limit_event->deschedule();
|
|
delete limit_event;
|
|
}
|
|
|
|
return global_exit_event;
|
|
}
|
|
|
|
}
|