3f890009de50af24a6fb6e119b4481af70ece642
[gem5.git] / util / systemc / sc_module.cc
1 /*
2 * Copyright (c) 2014 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2006 The Regents of The University of Michigan
15 * Copyright (c) 2013 Advanced Micro Devices, Inc.
16 * Copyright (c) 2013 Mark D. Hill and David A. Wood
17 * All rights reserved.
18 *
19 * Redistribution and use in source and binary forms, with or without
20 * modification, are permitted provided that the following conditions are
21 * met: redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer;
23 * redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution;
26 * neither the name of the copyright holders nor the names of its
27 * contributors may be used to endorse or promote products derived from
28 * this software without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 *
42 * Authors: Nathan Binkert
43 * Steve Reinhardt
44 * Andrew Bardsley
45 * Matthias Jung
46 * Christian Menard
47 */
48
49 /**
50 * @file
51 *
52 * Defines an sc_module type to wrap a gem5 simulation. The 'evaluate'
53 * thread on that module implements the gem5 event loop.
54 *
55 * This currently only supports a single event queue and strictly
56 * cooperatively threaded SystemC threads and so there should be at
57 * most one Gem5Module instantiated in any simulation.
58 */
59
60 #include "base/misc.hh"
61 #include "base/pollevent.hh"
62 #include "base/trace.hh"
63 #include "debug/Event.hh"
64 #include "sc_module.hh"
65 #include "sim/async.hh"
66 #include "sim/core.hh"
67 #include "sim/eventq.hh"
68 #include "sim/sim_exit.hh"
69 #include "sim/stat_control.hh"
70
71 namespace Gem5SystemC
72 {
73
74 /** There are assumptions throughout Gem5SystemC file that a tick is 1ps.
75 * Make this the case */
76 void
77 setTickFrequency()
78 {
79 ::setClockFrequency(1000000000000);
80 }
81
82 Module::Module(sc_core::sc_module_name name) : sc_core::sc_channel(name),
83 in_simulate(false)
84 {
85 SC_METHOD(eventLoop);
86 sensitive << eventLoopEnterEvent;
87 dont_initialize();
88
89 SC_METHOD(serviceExternalEvent);
90 sensitive << externalSchedulingEvent;
91 dont_initialize();
92 }
93
94 void
95 Module::SCEventQueue::wakeup(Tick when)
96 {
97 DPRINTF(Event, "waking up SCEventQueue\n");
98 /* Don't bother to use 'when' for now */
99 module.notify();
100 }
101
102 void
103 Module::setupEventQueues(Module &module)
104 {
105 fatal_if(mainEventQueue.size() != 0,
106 "Gem5SystemC::Module::setupEventQueues must be called"
107 " before any gem5 event queues are set up");
108
109 numMainEventQueues = 1;
110 mainEventQueue.push_back(new SCEventQueue("events", module));
111 curEventQueue(getEventQueue(0));
112 }
113
114 void
115 Module::catchup()
116 {
117 EventQueue *eventq = getEventQueue(0);
118 Tick systemc_time = sc_core::sc_time_stamp().value();
119 Tick gem5_time = curTick();
120
121 /* gem5 time *must* lag SystemC as SystemC is the master */
122 fatal_if(gem5_time > systemc_time, "gem5 time must lag SystemC time"
123 " gem5: %d SystemC: %d", gem5_time, systemc_time);
124
125 eventq->setCurTick(systemc_time);
126
127 if (!eventq->empty()) {
128 Tick next_event_time M5_VAR_USED = eventq->nextTick();
129
130 fatal_if(gem5_time > next_event_time,
131 "Missed an event at time %d gem5: %d, SystemC: %d",
132 next_event_time, gem5_time, systemc_time);
133 }
134 }
135
136 void
137 Module::notify(sc_core::sc_time time_from_now)
138 {
139 externalSchedulingEvent.notify(time_from_now);
140 }
141
142 void
143 Module::serviceAsyncEvent()
144 {
145 EventQueue *eventq = getEventQueue(0);
146 std::lock_guard<EventQueue> lock(*eventq);
147
148 assert(async_event);
149
150 /* Catch up gem5 time with SystemC time so that any event here won't
151 * be in the past relative to the current time */
152 Tick systemc_time = sc_core::sc_time_stamp().value();
153
154 /* Move time on to match SystemC */
155 catchup();
156
157 async_event = false;
158 if (async_statdump || async_statreset) {
159 Stats::schedStatEvent(async_statdump, async_statreset);
160 async_statdump = false;
161 async_statreset = false;
162 }
163
164 if (async_exit) {
165 async_exit = false;
166 exitSimLoop("user interrupt received");
167 }
168
169 if (async_io) {
170 async_io = false;
171 pollQueue.service();
172 }
173
174 if (async_exception)
175 fatal("received async_exception, shouldn't be possible");
176 }
177
178 void
179 Module::serviceExternalEvent()
180 {
181 EventQueue *eventq = getEventQueue(0);
182
183 if (!in_simulate && !async_event)
184 warn("Gem5SystemC external event received while not in simulate");
185
186 if (async_event)
187 serviceAsyncEvent();
188
189 if (in_simulate && !eventq->empty())
190 eventLoop();
191 }
192
193 void
194 Module::eventLoop()
195 {
196 EventQueue *eventq = getEventQueue(0);
197
198 fatal_if(!in_simulate, "Gem5SystemC event loop entered while"
199 " outside Gem5SystemC::Module::simulate");
200
201 if (async_event)
202 serviceAsyncEvent();
203
204 while (!eventq->empty()) {
205 Tick next_event_time = eventq->nextTick();
206
207 /* Move time on to match SystemC */
208 catchup();
209
210 Tick gem5_time = curTick();
211
212 /* Woken up early */
213 if (wait_exit_time > sc_core::sc_time_stamp().value()) {
214 DPRINTF(Event, "Woken up early\n");
215 wait_exit_time = sc_core::sc_time_stamp().value();
216 }
217
218 if (gem5_time < next_event_time) {
219 Tick wait_period = next_event_time - gem5_time;
220 wait_exit_time = gem5_time + wait_period;
221
222 DPRINTF(Event, "Waiting for %d ticks for next gem5 event\n",
223 wait_period);
224
225 /* The next event is scheduled in the future, wait until
226 * then or until externalSchedulingEvent */
227 eventLoopEnterEvent.notify(sc_core::sc_time::from_value(
228 sc_dt::uint64(wait_period)));
229
230 return;
231 } else if (gem5_time > next_event_time) {
232 Tick systemc_time = sc_core::sc_time_stamp().value();
233
234 /* Missed event, for some reason the above test didn't work
235 * or an event was scheduled in the past */
236 fatal("Missed an event at time %d gem5: %d, SystemC: %d",
237 next_event_time, gem5_time, systemc_time);
238 } else {
239 /* Service an event */
240 exitEvent = eventq->serviceOne();
241
242 if (exitEvent) {
243 eventLoopExitEvent.notify(sc_core::SC_ZERO_TIME);
244 return;
245 }
246 }
247 }
248
249 fatal("Ran out of events without seeing exit event");
250 }
251
252 GlobalSimLoopExitEvent *
253 Module::simulate(Tick num_cycles)
254 {
255 inform("Entering event queue @ %d. Starting simulation...", curTick());
256
257 if (num_cycles < MaxTick - curTick())
258 num_cycles = curTick() + num_cycles;
259 else /* counter would roll over or be set to MaxTick anyhow */
260 num_cycles = MaxTick;
261
262 GlobalEvent *limit_event = new GlobalSimLoopExitEvent(num_cycles,
263 "simulate() limit reached", 0, 0);
264
265 exitEvent = NULL;
266
267 /* Cancel any outstanding events */
268 eventLoopExitEvent.cancel();
269 externalSchedulingEvent.cancel();
270
271 in_simulate = true;
272 eventLoopEnterEvent.notify(sc_core::SC_ZERO_TIME);
273
274 /* Wait for event queue to exit, guarded by exitEvent just incase
275 * it already has exited and we don't want to completely rely
276 * on notify semantics */
277 if (!exitEvent)
278 wait(eventLoopExitEvent);
279
280 /* Cancel any outstanding event loop entries */
281 eventLoopEnterEvent.cancel();
282 in_simulate = false;
283
284 /* Locate the global exit event */
285 BaseGlobalEvent *global_event = exitEvent->globalEvent();
286 assert(global_event != NULL);
287
288 GlobalSimLoopExitEvent *global_exit_event =
289 dynamic_cast<GlobalSimLoopExitEvent *>(global_event);
290 assert(global_exit_event != NULL);
291
292 if (global_exit_event != limit_event) {
293 limit_event->deschedule();
294 delete limit_event;
295 }
296
297 return global_exit_event;
298 }
299
300 }