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/logging.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
71namespace Gem5SystemC
72{
73
74/** There are assumptions throughout Gem5SystemC file that a tick is 1ps.
75 *  Make this the case */
76void
77setTickFrequency()
78{
79    ::setClockFrequency(1000000000000);
80}
81
82Module::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
94void
95Module::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
102void
103Module::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
114void
115Module::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
136void
137Module::notify(sc_core::sc_time time_from_now)
138{
139    externalSchedulingEvent.notify(time_from_now);
140}
141
142void
143Module::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
178void
179Module::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
193void
194Module::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
252GlobalSimLoopExitEvent *
253Module::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}
301