14123Sbinkertn@umich.edu/*
24123Sbinkertn@umich.edu * Copyright (c) 2006 The Regents of The University of Michigan
39983Sstever@gmail.com * Copyright (c) 2013 Advanced Micro Devices, Inc.
49983Sstever@gmail.com * Copyright (c) 2013 Mark D. Hill and David A. Wood
54123Sbinkertn@umich.edu * All rights reserved.
64123Sbinkertn@umich.edu *
74123Sbinkertn@umich.edu * Redistribution and use in source and binary forms, with or without
84123Sbinkertn@umich.edu * modification, are permitted provided that the following conditions are
94123Sbinkertn@umich.edu * met: redistributions of source code must retain the above copyright
104123Sbinkertn@umich.edu * notice, this list of conditions and the following disclaimer;
114123Sbinkertn@umich.edu * redistributions in binary form must reproduce the above copyright
124123Sbinkertn@umich.edu * notice, this list of conditions and the following disclaimer in the
134123Sbinkertn@umich.edu * documentation and/or other materials provided with the distribution;
144123Sbinkertn@umich.edu * neither the name of the copyright holders nor the names of its
154123Sbinkertn@umich.edu * contributors may be used to endorse or promote products derived from
164123Sbinkertn@umich.edu * this software without specific prior written permission.
174123Sbinkertn@umich.edu *
184123Sbinkertn@umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
194123Sbinkertn@umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
204123Sbinkertn@umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
214123Sbinkertn@umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
224123Sbinkertn@umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
234123Sbinkertn@umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
244123Sbinkertn@umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
254123Sbinkertn@umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
264123Sbinkertn@umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
274123Sbinkertn@umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
284123Sbinkertn@umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
294123Sbinkertn@umich.edu *
304123Sbinkertn@umich.edu * Authors: Nathan Binkert
314123Sbinkertn@umich.edu *          Steve Reinhardt
324123Sbinkertn@umich.edu */
334123Sbinkertn@umich.edu
3411793Sbrandon.potter@amd.com#include "sim/simulate.hh"
3511793Sbrandon.potter@amd.com
369983Sstever@gmail.com#include <mutex>
379983Sstever@gmail.com#include <thread>
389983Sstever@gmail.com
3912334Sgabeblack@google.com#include "base/logging.hh"
404123Sbinkertn@umich.edu#include "base/pollevent.hh"
416216Snate@binkert.org#include "base/types.hh"
424123Sbinkertn@umich.edu#include "sim/async.hh"
439356Snilay@cs.wisc.edu#include "sim/eventq_impl.hh"
444123Sbinkertn@umich.edu#include "sim/sim_events.hh"
454123Sbinkertn@umich.edu#include "sim/sim_exit.hh"
466216Snate@binkert.org#include "sim/stat_control.hh"
474123Sbinkertn@umich.edu
489983Sstever@gmail.com//! Mutex for handling async events.
499983Sstever@gmail.comstd::mutex asyncEventMutex;
509983Sstever@gmail.com
519983Sstever@gmail.com//! Global barrier for synchronizing threads entering/exiting the
529983Sstever@gmail.com//! simulation loop.
539983Sstever@gmail.comBarrier *threadBarrier;
549983Sstever@gmail.com
559983Sstever@gmail.com//! forward declaration
569983Sstever@gmail.comEvent *doSimLoop(EventQueue *);
579983Sstever@gmail.com
589983Sstever@gmail.com/**
599983Sstever@gmail.com * The main function for all subordinate threads (i.e., all threads
609983Sstever@gmail.com * other than the main thread).  These threads start by waiting on
619983Sstever@gmail.com * threadBarrier.  Once all threads have arrived at threadBarrier,
629983Sstever@gmail.com * they enter the simulation loop concurrently.  When they exit the
639983Sstever@gmail.com * loop, they return to waiting on threadBarrier.  This process is
649983Sstever@gmail.com * repeated until the simulation terminates.
659983Sstever@gmail.com */
669983Sstever@gmail.comstatic void
679983Sstever@gmail.comthread_loop(EventQueue *queue)
689983Sstever@gmail.com{
699983Sstever@gmail.com    while (true) {
709983Sstever@gmail.com        threadBarrier->wait();
719983Sstever@gmail.com        doSimLoop(queue);
729983Sstever@gmail.com    }
739983Sstever@gmail.com}
749983Sstever@gmail.com
7510762SCurtis.Dunham@arm.comGlobalSimLoopExitEvent *simulate_limit_event = nullptr;
7610756SCurtis.Dunham@arm.com
774123Sbinkertn@umich.edu/** Simulate for num_cycles additional cycles.  If num_cycles is -1
784123Sbinkertn@umich.edu * (the default), do not limit simulation; some other event must
7911990Sandreas.sandberg@arm.com * terminate the loop.  Exported to Python.
804123Sbinkertn@umich.edu * @return The SimLoopExitEvent that caused the loop to exit.
814123Sbinkertn@umich.edu */
829983Sstever@gmail.comGlobalSimLoopExitEvent *
834123Sbinkertn@umich.edusimulate(Tick num_cycles)
844123Sbinkertn@umich.edu{
859983Sstever@gmail.com    // The first time simulate() is called from the Python code, we need to
869983Sstever@gmail.com    // create a thread for each of event queues referenced by the
879983Sstever@gmail.com    // instantiated sim objects.
889983Sstever@gmail.com    static bool threads_initialized = false;
899983Sstever@gmail.com    static std::vector<std::thread *> threads;
909983Sstever@gmail.com
919983Sstever@gmail.com    if (!threads_initialized) {
929983Sstever@gmail.com        threadBarrier = new Barrier(numMainEventQueues);
939983Sstever@gmail.com
949983Sstever@gmail.com        // the main thread (the one we're currently running on)
959983Sstever@gmail.com        // handles queue 0, so we only need to allocate new threads
969983Sstever@gmail.com        // for queues 1..N-1.  We'll call these the "subordinate" threads.
979983Sstever@gmail.com        for (uint32_t i = 1; i < numMainEventQueues; i++) {
989983Sstever@gmail.com            threads.push_back(new std::thread(thread_loop, mainEventQueue[i]));
999983Sstever@gmail.com        }
1009983Sstever@gmail.com
1019983Sstever@gmail.com        threads_initialized = true;
10210762SCurtis.Dunham@arm.com        simulate_limit_event =
10310762SCurtis.Dunham@arm.com            new GlobalSimLoopExitEvent(mainEventQueue[0]->getCurTick(),
10410762SCurtis.Dunham@arm.com                                       "simulate() limit reached", 0);
1059983Sstever@gmail.com    }
1069983Sstever@gmail.com
1077823Ssteve.reinhardt@amd.com    inform("Entering event queue @ %d.  Starting simulation...\n", curTick());
1084123Sbinkertn@umich.edu
1099174Satgutier@umich.edu    if (num_cycles < MaxTick - curTick())
1109174Satgutier@umich.edu        num_cycles = curTick() + num_cycles;
1119174Satgutier@umich.edu    else // counter would roll over or be set to MaxTick anyhow
1124123Sbinkertn@umich.edu        num_cycles = MaxTick;
1134123Sbinkertn@umich.edu
11410762SCurtis.Dunham@arm.com    simulate_limit_event->reschedule(num_cycles);
1159983Sstever@gmail.com
1169983Sstever@gmail.com    GlobalSyncEvent *quantum_event = NULL;
1179983Sstever@gmail.com    if (numMainEventQueues > 1) {
1189983Sstever@gmail.com        if (simQuantum == 0) {
1199983Sstever@gmail.com            fatal("Quantum for multi-eventq simulation not specified");
1209983Sstever@gmail.com        }
1219983Sstever@gmail.com
12210101Sandreas@sandberg.pp.se        quantum_event = new GlobalSyncEvent(curTick() + simQuantum, simQuantum,
1239983Sstever@gmail.com                            EventBase::Progress_Event_Pri, 0);
1249983Sstever@gmail.com
1259983Sstever@gmail.com        inParallelMode = true;
1269983Sstever@gmail.com    }
1279983Sstever@gmail.com
1289983Sstever@gmail.com    // all subordinate (created) threads should be waiting on the
1299983Sstever@gmail.com    // barrier; the arrival of the main thread here will satisfy the
1309983Sstever@gmail.com    // barrier, and all threads will enter doSimLoop in parallel
1319983Sstever@gmail.com    threadBarrier->wait();
1329983Sstever@gmail.com    Event *local_event = doSimLoop(mainEventQueue[0]);
1339983Sstever@gmail.com    assert(local_event != NULL);
1349983Sstever@gmail.com
1359983Sstever@gmail.com    inParallelMode = false;
1369983Sstever@gmail.com
1379983Sstever@gmail.com    // locate the global exit event and return it to Python
1389983Sstever@gmail.com    BaseGlobalEvent *global_event = local_event->globalEvent();
1399983Sstever@gmail.com    assert(global_event != NULL);
1409983Sstever@gmail.com
1419983Sstever@gmail.com    GlobalSimLoopExitEvent *global_exit_event =
1429983Sstever@gmail.com        dynamic_cast<GlobalSimLoopExitEvent *>(global_event);
1439983Sstever@gmail.com    assert(global_exit_event != NULL);
1449983Sstever@gmail.com
1459983Sstever@gmail.com    //! Delete the simulation quantum event.
1469983Sstever@gmail.com    if (quantum_event != NULL) {
1479983Sstever@gmail.com        quantum_event->deschedule();
1489983Sstever@gmail.com        delete quantum_event;
1499983Sstever@gmail.com    }
1509983Sstever@gmail.com
1519983Sstever@gmail.com    return global_exit_event;
1529983Sstever@gmail.com}
1539983Sstever@gmail.com
1549983Sstever@gmail.com/**
1559983Sstever@gmail.com * Test and clear the global async_event flag, such that each time the
1569983Sstever@gmail.com * flag is cleared, only one thread returns true (and thus is assigned
1579983Sstever@gmail.com * to handle the corresponding async event(s)).
1589983Sstever@gmail.com */
1599983Sstever@gmail.comstatic bool
1609983Sstever@gmail.comtestAndClearAsyncEvent()
1619983Sstever@gmail.com{
1629983Sstever@gmail.com    bool was_set = false;
1639983Sstever@gmail.com    asyncEventMutex.lock();
1649983Sstever@gmail.com
1659983Sstever@gmail.com    if (async_event) {
1669983Sstever@gmail.com        was_set = true;
1679983Sstever@gmail.com        async_event = false;
1689983Sstever@gmail.com    }
1699983Sstever@gmail.com
1709983Sstever@gmail.com    asyncEventMutex.unlock();
1719983Sstever@gmail.com    return was_set;
1729983Sstever@gmail.com}
1739983Sstever@gmail.com
1749983Sstever@gmail.com/**
1759983Sstever@gmail.com * The main per-thread simulation loop. This loop is executed by all
1769983Sstever@gmail.com * simulation threads (the main thread and the subordinate threads) in
1779983Sstever@gmail.com * parallel.
1789983Sstever@gmail.com */
1799983Sstever@gmail.comEvent *
1809983Sstever@gmail.comdoSimLoop(EventQueue *eventq)
1819983Sstever@gmail.com{
1829983Sstever@gmail.com    // set the per thread current eventq pointer
1839983Sstever@gmail.com    curEventQueue(eventq);
1849983Sstever@gmail.com    eventq->handleAsyncInsertions();
1854123Sbinkertn@umich.edu
1864123Sbinkertn@umich.edu    while (1) {
1874123Sbinkertn@umich.edu        // there should always be at least one event (the SimLoopExitEvent
1884123Sbinkertn@umich.edu        // we just scheduled) in the queue
1899983Sstever@gmail.com        assert(!eventq->empty());
1909983Sstever@gmail.com        assert(curTick() <= eventq->nextTick() &&
1914123Sbinkertn@umich.edu               "event scheduled in the past");
1924123Sbinkertn@umich.edu
1939983Sstever@gmail.com        if (async_event && testAndClearAsyncEvent()) {
19410153Sandreas@sandberg.pp.se            // Take the event queue lock in case any of the service
19510153Sandreas@sandberg.pp.se            // routines want to schedule new events.
19610153Sandreas@sandberg.pp.se            std::lock_guard<EventQueue> lock(*eventq);
1974123Sbinkertn@umich.edu            if (async_statdump || async_statreset) {
1987822Ssteve.reinhardt@amd.com                Stats::schedStatEvent(async_statdump, async_statreset);
1994123Sbinkertn@umich.edu                async_statdump = false;
2004123Sbinkertn@umich.edu                async_statreset = false;
2014123Sbinkertn@umich.edu            }
2024123Sbinkertn@umich.edu
20310670SCurtis.Dunham@arm.com            if (async_io) {
20410670SCurtis.Dunham@arm.com                async_io = false;
20510670SCurtis.Dunham@arm.com                pollQueue.service();
20610670SCurtis.Dunham@arm.com            }
20710670SCurtis.Dunham@arm.com
2084123Sbinkertn@umich.edu            if (async_exit) {
2094123Sbinkertn@umich.edu                async_exit = false;
2104123Sbinkertn@umich.edu                exitSimLoop("user interrupt received");
2114123Sbinkertn@umich.edu            }
2124123Sbinkertn@umich.edu
2134123Sbinkertn@umich.edu            if (async_exception) {
2144123Sbinkertn@umich.edu                async_exception = false;
2154123Sbinkertn@umich.edu                return NULL;
2164123Sbinkertn@umich.edu            }
2174123Sbinkertn@umich.edu        }
21810670SCurtis.Dunham@arm.com
21910670SCurtis.Dunham@arm.com        Event *exit_event = eventq->serviceOne();
22010670SCurtis.Dunham@arm.com        if (exit_event != NULL) {
22110670SCurtis.Dunham@arm.com            return exit_event;
22210670SCurtis.Dunham@arm.com        }
2234123Sbinkertn@umich.edu    }
2244123Sbinkertn@umich.edu
2254123Sbinkertn@umich.edu    // not reached... only exit is return on SimLoopExitEvent
2264123Sbinkertn@umich.edu}
227