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