/* * Copyright (c) 2000-2005 The Regents of The University of Michigan * Copyright (c) 2008 The Hewlett-Packard Development Company * Copyright (c) 2013 Advanced Micro Devices, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Steve Reinhardt * Nathan Binkert * Steve Raasch */ #include #include #include #include #include #include "base/logging.hh" #include "base/trace.hh" #include "cpu/smt.hh" #include "debug/Checkpoint.hh" #include "sim/core.hh" #include "sim/eventq_impl.hh" using namespace std; Tick simQuantum = 0; // // Main Event Queues // // Events on these queues are processed at the *beginning* of each // cycle, before the pipeline simulation is performed. // uint32_t numMainEventQueues = 0; vector mainEventQueue; __thread EventQueue *_curEventQueue = NULL; bool inParallelMode = false; EventQueue * getEventQueue(uint32_t index) { while (numMainEventQueues <= index) { numMainEventQueues++; mainEventQueue.push_back( new EventQueue(csprintf("MainEventQueue-%d", index))); } return mainEventQueue[index]; } #ifndef NDEBUG Counter Event::instanceCounter = 0; #endif Event::~Event() { assert(!scheduled()); flags = 0; } const std::string Event::name() const { #ifndef NDEBUG return csprintf("Event_%d", instance); #else return csprintf("Event_%x", (uintptr_t)this); #endif } Event * Event::insertBefore(Event *event, Event *curr) { // Either way, event will be the top element in the 'in bin' list // which is the pointer we need in order to look into the list, so // we need to insert that into the bin list. if (!curr || *event < *curr) { // Insert the event before the current list since it is in the future. event->nextBin = curr; event->nextInBin = NULL; } else { // Since we're on the correct list, we need to point to the next list event->nextBin = curr->nextBin; // curr->nextBin can now become stale // Insert event at the top of the stack event->nextInBin = curr; } return event; } void EventQueue::insert(Event *event) { // Deal with the head case if (!head || *event <= *head) { head = Event::insertBefore(event, head); return; } // Figure out either which 'in bin' list we are on, or where a new list // needs to be inserted Event *prev = head; Event *curr = head->nextBin; while (curr && *curr < *event) { prev = curr; curr = curr->nextBin; } // Note: this operation may render all nextBin pointers on the // prev 'in bin' list stale (except for the top one) prev->nextBin = Event::insertBefore(event, curr); } Event * Event::removeItem(Event *event, Event *top) { Event *curr = top; Event *next = top->nextInBin; // if we removed the top item, we need to handle things specially // and just remove the top item, fixing up the next bin pointer of // the new top item if (event == top) { if (!next) return top->nextBin; next->nextBin = top->nextBin; return next; } // Since we already checked the current element, we're going to // keep checking event against the next element. while (event != next) { if (!next) panic("event not found!"); curr = next; next = next->nextInBin; } // remove next from the 'in bin' list since it's what we're looking for curr->nextInBin = next->nextInBin; return top; } void EventQueue::remove(Event *event) { if (head == NULL) panic("event not found!"); assert(event->queue == this); // deal with an event on the head's 'in bin' list (event has the same // time as the head) if (*head == *event) { head = Event::removeItem(event, head); return; } // Find the 'in bin' list that this event belongs on Event *prev = head; Event *curr = head->nextBin; while (curr && *curr < *event) { prev = curr; curr = curr->nextBin; } if (!curr || *curr != *event) panic("event not found!"); // curr points to the top item of the the correct 'in bin' list, when // we remove an item, it returns the new top item (which may be // unchanged) prev->nextBin = Event::removeItem(event, curr); } Event * EventQueue::serviceOne() { std::lock_guard lock(*this); Event *event = head; Event *next = head->nextInBin; event->flags.clear(Event::Scheduled); if (next) { // update the next bin pointer since it could be stale next->nextBin = head->nextBin; // pop the stack head = next; } else { // this was the only element on the 'in bin' list, so get rid of // the 'in bin' list and point to the next bin list head = head->nextBin; } // handle action if (!event->squashed()) { // forward current cycle to the time when this event occurs. setCurTick(event->when()); event->process(); if (event->isExitEvent()) { assert(!event->flags.isSet(Event::Managed) || !event->flags.isSet(Event::IsMainQueue)); // would be silly return event; } } else { event->flags.clear(Event::Squashed); } event->release(); return NULL; } void Event::serialize(CheckpointOut &cp) const { SERIALIZE_SCALAR(_when); SERIALIZE_SCALAR(_priority); short _flags = flags; SERIALIZE_SCALAR(_flags); } void Event::unserialize(CheckpointIn &cp) { assert(!scheduled()); UNSERIALIZE_SCALAR(_when); UNSERIALIZE_SCALAR(_priority); FlagsType _flags; UNSERIALIZE_SCALAR(_flags); // Old checkpoints had no concept of the Initialized flag // so restoring from old checkpoints always fail. // Events are initialized on construction but original code // "flags = _flags" would just overwrite the initialization. // So, read in the checkpoint flags, but then set the Initialized // flag on top of it in order to avoid failures. assert(initialized()); flags = _flags; flags.set(Initialized); // need to see if original event was in a scheduled, unsquashed // state, but don't want to restore those flags in the current // object itself (since they aren't immediately true) if (flags.isSet(Scheduled) && !flags.isSet(Squashed)) { flags.clear(Squashed | Scheduled); } else { DPRINTF(Checkpoint, "Event '%s' need to be scheduled @%d\n", name(), _when); } } void EventQueue::checkpointReschedule(Event *event) { // It's safe to call insert() directly here since this method // should only be called when restoring from a checkpoint (which // happens before thread creation). if (event->flags.isSet(Event::Scheduled)) insert(event); } void EventQueue::dump() const { cprintf("============================================================\n"); cprintf("EventQueue Dump (cycle %d)\n", curTick()); cprintf("------------------------------------------------------------\n"); if (empty()) cprintf("\n"); else { Event *nextBin = head; while (nextBin) { Event *nextInBin = nextBin; while (nextInBin) { nextInBin->dump(); nextInBin = nextInBin->nextInBin; } nextBin = nextBin->nextBin; } } cprintf("============================================================\n"); } bool EventQueue::debugVerify() const { std::unordered_map map; Tick time = 0; short priority = 0; Event *nextBin = head; while (nextBin) { Event *nextInBin = nextBin; while (nextInBin) { if (nextInBin->when() < time) { cprintf("time goes backwards!"); nextInBin->dump(); return false; } else if (nextInBin->when() == time && nextInBin->priority() < priority) { cprintf("priority inverted!"); nextInBin->dump(); return false; } if (map[reinterpret_cast(nextInBin)]) { cprintf("Node already seen"); nextInBin->dump(); return false; } map[reinterpret_cast(nextInBin)] = true; time = nextInBin->when(); priority = nextInBin->priority(); nextInBin = nextInBin->nextInBin; } nextBin = nextBin->nextBin; } return true; } Event* EventQueue::replaceHead(Event* s) { Event* t = head; head = s; return t; } void dumpMainQueue() { for (uint32_t i = 0; i < numMainEventQueues; ++i) { mainEventQueue[i]->dump(); } } const char * Event::description() const { return "generic"; } void Event::trace(const char *action) { // This DPRINTF is unconditional because calls to this function // are protected by an 'if (DTRACE(Event))' in the inlined Event // methods. // // This is just a default implementation for derived classes where // it's not worth doing anything special. If you want to put a // more informative message in the trace, override this method on // the particular subclass where you have the information that // needs to be printed. DPRINTFN("%s event %s @ %d\n", description(), action, when()); } void Event::dump() const { cprintf("Event %s (%s)\n", name(), description()); cprintf("Flags: %#x\n", flags); #ifdef EVENTQ_DEBUG cprintf("Created: %d\n", whenCreated); #endif if (scheduled()) { #ifdef EVENTQ_DEBUG cprintf("Scheduled at %d\n", whenScheduled); #endif cprintf("Scheduled for %d, priority %d\n", when(), _priority); } else { cprintf("Not Scheduled\n"); } } EventQueue::EventQueue(const string &n) : objName(n), head(NULL), _curTick(0) { } void EventQueue::asyncInsert(Event *event) { async_queue_mutex.lock(); async_queue.push_back(event); async_queue_mutex.unlock(); } void EventQueue::handleAsyncInsertions() { assert(this == curEventQueue()); async_queue_mutex.lock(); while (!async_queue.empty()) { insert(async_queue.front()); async_queue.pop_front(); } async_queue_mutex.unlock(); }