simulate.cc revision 10153:936a3a8006f6
112396SRiken.Gohil@arm.com/*
212811Sandreas.sandberg@arm.com * Copyright (c) 2006 The Regents of The University of Michigan
312396SRiken.Gohil@arm.com * Copyright (c) 2013 Advanced Micro Devices, Inc.
412396SRiken.Gohil@arm.com * Copyright (c) 2013 Mark D. Hill and David A. Wood
512396SRiken.Gohil@arm.com * All rights reserved.
612396SRiken.Gohil@arm.com *
712396SRiken.Gohil@arm.com * Redistribution and use in source and binary forms, with or without
812396SRiken.Gohil@arm.com * modification, are permitted provided that the following conditions are
912396SRiken.Gohil@arm.com * met: redistributions of source code must retain the above copyright
1012396SRiken.Gohil@arm.com * notice, this list of conditions and the following disclaimer;
1112396SRiken.Gohil@arm.com * redistributions in binary form must reproduce the above copyright
1212396SRiken.Gohil@arm.com * notice, this list of conditions and the following disclaimer in the
1312396SRiken.Gohil@arm.com * documentation and/or other materials provided with the distribution;
1412396SRiken.Gohil@arm.com * neither the name of the copyright holders nor the names of its
1512396SRiken.Gohil@arm.com * contributors may be used to endorse or promote products derived from
1612396SRiken.Gohil@arm.com * this software without specific prior written permission.
1712396SRiken.Gohil@arm.com *
1812396SRiken.Gohil@arm.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1912396SRiken.Gohil@arm.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2012396SRiken.Gohil@arm.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2112396SRiken.Gohil@arm.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2212396SRiken.Gohil@arm.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2312396SRiken.Gohil@arm.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2412396SRiken.Gohil@arm.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2512396SRiken.Gohil@arm.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2612396SRiken.Gohil@arm.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2712396SRiken.Gohil@arm.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2812396SRiken.Gohil@arm.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2912396SRiken.Gohil@arm.com *
3012396SRiken.Gohil@arm.com * Authors: Nathan Binkert
3112396SRiken.Gohil@arm.com *          Steve Reinhardt
3212396SRiken.Gohil@arm.com */
3312396SRiken.Gohil@arm.com
3412396SRiken.Gohil@arm.com#include <mutex>
3512396SRiken.Gohil@arm.com#include <thread>
3612396SRiken.Gohil@arm.com
3712396SRiken.Gohil@arm.com#include "base/misc.hh"
3812396SRiken.Gohil@arm.com#include "base/pollevent.hh"
3912396SRiken.Gohil@arm.com#include "base/types.hh"
4012396SRiken.Gohil@arm.com#include "sim/async.hh"
4112396SRiken.Gohil@arm.com#include "sim/eventq_impl.hh"
4212396SRiken.Gohil@arm.com#include "sim/sim_events.hh"
4312396SRiken.Gohil@arm.com#include "sim/sim_exit.hh"
4412396SRiken.Gohil@arm.com#include "sim/simulate.hh"
4512396SRiken.Gohil@arm.com#include "sim/stat_control.hh"
4612396SRiken.Gohil@arm.com
4712396SRiken.Gohil@arm.com//! Mutex for handling async events.
4812396SRiken.Gohil@arm.comstd::mutex asyncEventMutex;
4912396SRiken.Gohil@arm.com
5012396SRiken.Gohil@arm.com//! Global barrier for synchronizing threads entering/exiting the
5112396SRiken.Gohil@arm.com//! simulation loop.
5212396SRiken.Gohil@arm.comBarrier *threadBarrier;
5312396SRiken.Gohil@arm.com
5412396SRiken.Gohil@arm.com//! forward declaration
5512396SRiken.Gohil@arm.comEvent *doSimLoop(EventQueue *);
5612396SRiken.Gohil@arm.com
5712396SRiken.Gohil@arm.com/**
5812396SRiken.Gohil@arm.com * The main function for all subordinate threads (i.e., all threads
5912396SRiken.Gohil@arm.com * other than the main thread).  These threads start by waiting on
6012396SRiken.Gohil@arm.com * threadBarrier.  Once all threads have arrived at threadBarrier,
6112396SRiken.Gohil@arm.com * they enter the simulation loop concurrently.  When they exit the
6212396SRiken.Gohil@arm.com * loop, they return to waiting on threadBarrier.  This process is
6312396SRiken.Gohil@arm.com * repeated until the simulation terminates.
6412396SRiken.Gohil@arm.com */
6512396SRiken.Gohil@arm.comstatic void
6612396SRiken.Gohil@arm.comthread_loop(EventQueue *queue)
6712396SRiken.Gohil@arm.com{
6812396SRiken.Gohil@arm.com    while (true) {
6912396SRiken.Gohil@arm.com        threadBarrier->wait();
7012396SRiken.Gohil@arm.com        doSimLoop(queue);
7112396SRiken.Gohil@arm.com    }
7212396SRiken.Gohil@arm.com}
7312396SRiken.Gohil@arm.com
7412396SRiken.Gohil@arm.com/** Simulate for num_cycles additional cycles.  If num_cycles is -1
7512396SRiken.Gohil@arm.com * (the default), do not limit simulation; some other event must
7612396SRiken.Gohil@arm.com * terminate the loop.  Exported to Python via SWIG.
7712396SRiken.Gohil@arm.com * @return The SimLoopExitEvent that caused the loop to exit.
7812396SRiken.Gohil@arm.com */
7912396SRiken.Gohil@arm.comGlobalSimLoopExitEvent *
8012396SRiken.Gohil@arm.comsimulate(Tick num_cycles)
8112396SRiken.Gohil@arm.com{
8212396SRiken.Gohil@arm.com    // The first time simulate() is called from the Python code, we need to
8312396SRiken.Gohil@arm.com    // create a thread for each of event queues referenced by the
8412396SRiken.Gohil@arm.com    // instantiated sim objects.
8512396SRiken.Gohil@arm.com    static bool threads_initialized = false;
8612396SRiken.Gohil@arm.com    static std::vector<std::thread *> threads;
8712396SRiken.Gohil@arm.com
8812396SRiken.Gohil@arm.com    if (!threads_initialized) {
8912396SRiken.Gohil@arm.com        threadBarrier = new Barrier(numMainEventQueues);
9012396SRiken.Gohil@arm.com
9112396SRiken.Gohil@arm.com        // the main thread (the one we're currently running on)
9212396SRiken.Gohil@arm.com        // handles queue 0, so we only need to allocate new threads
9312396SRiken.Gohil@arm.com        // for queues 1..N-1.  We'll call these the "subordinate" threads.
9412396SRiken.Gohil@arm.com        for (uint32_t i = 1; i < numMainEventQueues; i++) {
9512396SRiken.Gohil@arm.com            threads.push_back(new std::thread(thread_loop, mainEventQueue[i]));
9612396SRiken.Gohil@arm.com        }
9712396SRiken.Gohil@arm.com
9812396SRiken.Gohil@arm.com        threads_initialized = true;
9912396SRiken.Gohil@arm.com    }
10012396SRiken.Gohil@arm.com
10112396SRiken.Gohil@arm.com    inform("Entering event queue @ %d.  Starting simulation...\n", curTick());
10212396SRiken.Gohil@arm.com
10312396SRiken.Gohil@arm.com    if (num_cycles < MaxTick - curTick())
10412396SRiken.Gohil@arm.com        num_cycles = curTick() + num_cycles;
10512396SRiken.Gohil@arm.com    else // counter would roll over or be set to MaxTick anyhow
10612396SRiken.Gohil@arm.com        num_cycles = MaxTick;
10712396SRiken.Gohil@arm.com
10812396SRiken.Gohil@arm.com    GlobalEvent *limit_event = new GlobalSimLoopExitEvent(num_cycles,
10912396SRiken.Gohil@arm.com                                "simulate() limit reached", 0, 0);
11012396SRiken.Gohil@arm.com
11112396SRiken.Gohil@arm.com    GlobalSyncEvent *quantum_event = NULL;
11212396SRiken.Gohil@arm.com    if (numMainEventQueues > 1) {
11312396SRiken.Gohil@arm.com        if (simQuantum == 0) {
11412396SRiken.Gohil@arm.com            fatal("Quantum for multi-eventq simulation not specified");
11512396SRiken.Gohil@arm.com        }
11612396SRiken.Gohil@arm.com
11712396SRiken.Gohil@arm.com        quantum_event = new GlobalSyncEvent(curTick() + simQuantum, simQuantum,
11812396SRiken.Gohil@arm.com                            EventBase::Progress_Event_Pri, 0);
11912396SRiken.Gohil@arm.com
12012396SRiken.Gohil@arm.com        inParallelMode = true;
12112396SRiken.Gohil@arm.com    }
12212396SRiken.Gohil@arm.com
12312396SRiken.Gohil@arm.com    // all subordinate (created) threads should be waiting on the
12412396SRiken.Gohil@arm.com    // barrier; the arrival of the main thread here will satisfy the
12512396SRiken.Gohil@arm.com    // barrier, and all threads will enter doSimLoop in parallel
12612396SRiken.Gohil@arm.com    threadBarrier->wait();
12712396SRiken.Gohil@arm.com    Event *local_event = doSimLoop(mainEventQueue[0]);
12812396SRiken.Gohil@arm.com    assert(local_event != NULL);
12912396SRiken.Gohil@arm.com
13012396SRiken.Gohil@arm.com    inParallelMode = false;
13112396SRiken.Gohil@arm.com
13212396SRiken.Gohil@arm.com    // locate the global exit event and return it to Python
13312396SRiken.Gohil@arm.com    BaseGlobalEvent *global_event = local_event->globalEvent();
13412396SRiken.Gohil@arm.com    assert(global_event != NULL);
13512396SRiken.Gohil@arm.com
13612396SRiken.Gohil@arm.com    GlobalSimLoopExitEvent *global_exit_event =
13712396SRiken.Gohil@arm.com        dynamic_cast<GlobalSimLoopExitEvent *>(global_event);
13812396SRiken.Gohil@arm.com    assert(global_exit_event != NULL);
13912396SRiken.Gohil@arm.com
14012396SRiken.Gohil@arm.com    // if we didn't hit limit_event, delete it.
14112396SRiken.Gohil@arm.com    if (global_exit_event != limit_event) {
14212396SRiken.Gohil@arm.com        assert(limit_event->scheduled());
14312396SRiken.Gohil@arm.com        limit_event->deschedule();
14412396SRiken.Gohil@arm.com        delete limit_event;
14512396SRiken.Gohil@arm.com    }
14612396SRiken.Gohil@arm.com
14712396SRiken.Gohil@arm.com    //! Delete the simulation quantum event.
14812396SRiken.Gohil@arm.com    if (quantum_event != NULL) {
14912396SRiken.Gohil@arm.com        quantum_event->deschedule();
15012396SRiken.Gohil@arm.com        delete quantum_event;
15112396SRiken.Gohil@arm.com    }
15212396SRiken.Gohil@arm.com
15312396SRiken.Gohil@arm.com    return global_exit_event;
15412396SRiken.Gohil@arm.com}
15512844Sgiacomo.travaglini@arm.com
15612844Sgiacomo.travaglini@arm.com/**
15712396SRiken.Gohil@arm.com * Test and clear the global async_event flag, such that each time the
15812396SRiken.Gohil@arm.com * flag is cleared, only one thread returns true (and thus is assigned
15912396SRiken.Gohil@arm.com * to handle the corresponding async event(s)).
16012396SRiken.Gohil@arm.com */
16112844Sgiacomo.travaglini@arm.comstatic bool
16212396SRiken.Gohil@arm.comtestAndClearAsyncEvent()
16312844Sgiacomo.travaglini@arm.com{
16412396SRiken.Gohil@arm.com    bool was_set = false;
16512396SRiken.Gohil@arm.com    asyncEventMutex.lock();
16612396SRiken.Gohil@arm.com
16712396SRiken.Gohil@arm.com    if (async_event) {
16812396SRiken.Gohil@arm.com        was_set = true;
16912396SRiken.Gohil@arm.com        async_event = false;
17012396SRiken.Gohil@arm.com    }
17112396SRiken.Gohil@arm.com
17212396SRiken.Gohil@arm.com    asyncEventMutex.unlock();
17312396SRiken.Gohil@arm.com    return was_set;
17412396SRiken.Gohil@arm.com}
17512396SRiken.Gohil@arm.com
17612396SRiken.Gohil@arm.com/**
17712396SRiken.Gohil@arm.com * The main per-thread simulation loop. This loop is executed by all
17812396SRiken.Gohil@arm.com * simulation threads (the main thread and the subordinate threads) in
17912396SRiken.Gohil@arm.com * parallel.
18012396SRiken.Gohil@arm.com */
18112396SRiken.Gohil@arm.comEvent *
18212396SRiken.Gohil@arm.comdoSimLoop(EventQueue *eventq)
18312396SRiken.Gohil@arm.com{
18412396SRiken.Gohil@arm.com    // set the per thread current eventq pointer
18512396SRiken.Gohil@arm.com    curEventQueue(eventq);
18612396SRiken.Gohil@arm.com    eventq->handleAsyncInsertions();
18712396SRiken.Gohil@arm.com
18812396SRiken.Gohil@arm.com    while (1) {
18912396SRiken.Gohil@arm.com        // there should always be at least one event (the SimLoopExitEvent
19012396SRiken.Gohil@arm.com        // we just scheduled) in the queue
19112396SRiken.Gohil@arm.com        assert(!eventq->empty());
19212396SRiken.Gohil@arm.com        assert(curTick() <= eventq->nextTick() &&
19312396SRiken.Gohil@arm.com               "event scheduled in the past");
19412396SRiken.Gohil@arm.com
19512396SRiken.Gohil@arm.com        Event *exit_event = eventq->serviceOne();
19612396SRiken.Gohil@arm.com        if (exit_event != NULL) {
19712396SRiken.Gohil@arm.com            return exit_event;
19812396SRiken.Gohil@arm.com        }
19912396SRiken.Gohil@arm.com
20012396SRiken.Gohil@arm.com        if (async_event && testAndClearAsyncEvent()) {
20112396SRiken.Gohil@arm.com            // Take the event queue lock in case any of the service
20212396SRiken.Gohil@arm.com            // routines want to schedule new events.
20312396SRiken.Gohil@arm.com            std::lock_guard<EventQueue> lock(*eventq);
20412396SRiken.Gohil@arm.com            async_event = false;
20512396SRiken.Gohil@arm.com            if (async_statdump || async_statreset) {
20612396SRiken.Gohil@arm.com                Stats::schedStatEvent(async_statdump, async_statreset);
20712396SRiken.Gohil@arm.com                async_statdump = false;
20812396SRiken.Gohil@arm.com                async_statreset = false;
20912396SRiken.Gohil@arm.com            }
21012396SRiken.Gohil@arm.com
21112396SRiken.Gohil@arm.com            if (async_exit) {
21212396SRiken.Gohil@arm.com                async_exit = false;
21312396SRiken.Gohil@arm.com                exitSimLoop("user interrupt received");
214            }
215
216            if (async_io) {
217                async_io = false;
218                pollQueue.service();
219            }
220
221            if (async_exception) {
222                async_exception = false;
223                return NULL;
224            }
225        }
226    }
227
228    // not reached... only exit is return on SimLoopExitEvent
229}
230