2 * Copyright (c) 2006 The Regents of The University of Michigan
3 * Copyright (c) 2013 Advanced Micro Devices, Inc.
4 * Copyright (c) 2013 Mark D. Hill and David A. Wood
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met: redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer;
11 * redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution;
14 * neither the name of the copyright holders nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * Authors: Nathan Binkert
37 #include "base/misc.hh"
38 #include "base/pollevent.hh"
39 #include "base/types.hh"
40 #include "sim/async.hh"
41 #include "sim/eventq_impl.hh"
42 #include "sim/sim_events.hh"
43 #include "sim/sim_exit.hh"
44 #include "sim/simulate.hh"
45 #include "sim/stat_control.hh"
47 //! Mutex for handling async events.
48 std::mutex asyncEventMutex
;
50 //! Global barrier for synchronizing threads entering/exiting the
52 Barrier
*threadBarrier
;
54 //! forward declaration
55 Event
*doSimLoop(EventQueue
*);
58 * The main function for all subordinate threads (i.e., all threads
59 * other than the main thread). These threads start by waiting on
60 * threadBarrier. Once all threads have arrived at threadBarrier,
61 * they enter the simulation loop concurrently. When they exit the
62 * loop, they return to waiting on threadBarrier. This process is
63 * repeated until the simulation terminates.
66 thread_loop(EventQueue
*queue
)
69 threadBarrier
->wait();
74 GlobalSimLoopExitEvent
*simulate_limit_event
= nullptr;
76 /** Simulate for num_cycles additional cycles. If num_cycles is -1
77 * (the default), do not limit simulation; some other event must
78 * terminate the loop. Exported to Python via SWIG.
79 * @return The SimLoopExitEvent that caused the loop to exit.
81 GlobalSimLoopExitEvent
*
82 simulate(Tick num_cycles
)
84 // The first time simulate() is called from the Python code, we need to
85 // create a thread for each of event queues referenced by the
86 // instantiated sim objects.
87 static bool threads_initialized
= false;
88 static std::vector
<std::thread
*> threads
;
90 if (!threads_initialized
) {
91 threadBarrier
= new Barrier(numMainEventQueues
);
93 // the main thread (the one we're currently running on)
94 // handles queue 0, so we only need to allocate new threads
95 // for queues 1..N-1. We'll call these the "subordinate" threads.
96 for (uint32_t i
= 1; i
< numMainEventQueues
; i
++) {
97 threads
.push_back(new std::thread(thread_loop
, mainEventQueue
[i
]));
100 threads_initialized
= true;
101 simulate_limit_event
=
102 new GlobalSimLoopExitEvent(mainEventQueue
[0]->getCurTick(),
103 "simulate() limit reached", 0);
106 inform("Entering event queue @ %d. Starting simulation...\n", curTick());
108 if (num_cycles
< MaxTick
- curTick())
109 num_cycles
= curTick() + num_cycles
;
110 else // counter would roll over or be set to MaxTick anyhow
111 num_cycles
= MaxTick
;
113 simulate_limit_event
->reschedule(num_cycles
);
115 GlobalSyncEvent
*quantum_event
= NULL
;
116 if (numMainEventQueues
> 1) {
117 if (simQuantum
== 0) {
118 fatal("Quantum for multi-eventq simulation not specified");
121 quantum_event
= new GlobalSyncEvent(curTick() + simQuantum
, simQuantum
,
122 EventBase::Progress_Event_Pri
, 0);
124 inParallelMode
= true;
127 // all subordinate (created) threads should be waiting on the
128 // barrier; the arrival of the main thread here will satisfy the
129 // barrier, and all threads will enter doSimLoop in parallel
130 threadBarrier
->wait();
131 Event
*local_event
= doSimLoop(mainEventQueue
[0]);
132 assert(local_event
!= NULL
);
134 inParallelMode
= false;
136 // locate the global exit event and return it to Python
137 BaseGlobalEvent
*global_event
= local_event
->globalEvent();
138 assert(global_event
!= NULL
);
140 GlobalSimLoopExitEvent
*global_exit_event
=
141 dynamic_cast<GlobalSimLoopExitEvent
*>(global_event
);
142 assert(global_exit_event
!= NULL
);
144 //! Delete the simulation quantum event.
145 if (quantum_event
!= NULL
) {
146 quantum_event
->deschedule();
147 delete quantum_event
;
150 return global_exit_event
;
154 * Test and clear the global async_event flag, such that each time the
155 * flag is cleared, only one thread returns true (and thus is assigned
156 * to handle the corresponding async event(s)).
159 testAndClearAsyncEvent()
161 bool was_set
= false;
162 asyncEventMutex
.lock();
169 asyncEventMutex
.unlock();
174 * The main per-thread simulation loop. This loop is executed by all
175 * simulation threads (the main thread and the subordinate threads) in
179 doSimLoop(EventQueue
*eventq
)
181 // set the per thread current eventq pointer
182 curEventQueue(eventq
);
183 eventq
->handleAsyncInsertions();
186 // there should always be at least one event (the SimLoopExitEvent
187 // we just scheduled) in the queue
188 assert(!eventq
->empty());
189 assert(curTick() <= eventq
->nextTick() &&
190 "event scheduled in the past");
192 if (async_event
&& testAndClearAsyncEvent()) {
193 // Take the event queue lock in case any of the service
194 // routines want to schedule new events.
195 std::lock_guard
<EventQueue
> lock(*eventq
);
196 if (async_statdump
|| async_statreset
) {
197 Stats::schedStatEvent(async_statdump
, async_statreset
);
198 async_statdump
= false;
199 async_statreset
= false;
209 exitSimLoop("user interrupt received");
212 if (async_exception
) {
213 async_exception
= false;
218 Event
*exit_event
= eventq
->serviceOne();
219 if (exit_event
!= NULL
) {
224 // not reached... only exit is return on SimLoopExitEvent