diff -r 0db5bb5bf244 -r e1faec27d07b src/cpu/base.cc --- a/src/cpu/base.cc Tue Sep 06 14:25:20 2011 -0700 +++ b/src/cpu/base.cc Tue Sep 06 14:27:50 2011 -0700 @@ -140,7 +140,7 @@ if (p->max_insts_any_thread != 0) { const char *cause = "a thread reached the max instruction count"; for (ThreadID tid = 0; tid < numThreads; ++tid) { - Event *event = new SimLoopExitEvent(cause, 0); + Event *event = new LocalSimLoopExitEvent(cause, 0); comInstEventQueue[tid]->schedule(event, p->max_insts_any_thread); } } @@ -170,7 +170,7 @@ if (p->max_loads_any_thread != 0) { const char *cause = "a thread reached the max load count"; for (ThreadID tid = 0; tid < numThreads; ++tid) { - Event *event = new SimLoopExitEvent(cause, 0); + Event *event = new LocalSimLoopExitEvent(cause, 0); comLoadEventQueue[tid]->schedule(event, p->max_loads_any_thread); } } diff -r 0db5bb5bf244 -r e1faec27d07b src/cpu/simple/atomic.cc --- a/src/cpu/simple/atomic.cc Tue Sep 06 14:25:20 2011 -0700 +++ b/src/cpu/simple/atomic.cc Tue Sep 06 14:27:50 2011 -0700 @@ -197,7 +197,7 @@ UNSERIALIZE_ENUM(so_state); UNSERIALIZE_SCALAR(locked); BaseSimpleCPU::unserialize(cp, section); - tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); + tickEvent.unserialize(cp, csprintf("%s.tickEvent", section), this); } void diff -r 0db5bb5bf244 -r e1faec27d07b src/mem/ruby/system/System.cc --- a/src/mem/ruby/system/System.cc Tue Sep 06 14:25:20 2011 -0700 +++ b/src/mem/ruby/system/System.cc Tue Sep 06 14:27:50 2011 -0700 @@ -74,7 +74,7 @@ m_memory_size_bits = floorLog2(m_memory_size_bytes); } - g_eventQueue_ptr = new RubyEventQueue(p->eventq, m_clock); + g_eventQueue_ptr = new RubyEventQueue(queue(), m_clock); g_system_ptr = this; if (p->no_mem_vec) { m_mem_vec_ptr = NULL; diff -r 0db5bb5bf244 -r e1faec27d07b src/python/m5/SimObject.py --- a/src/python/m5/SimObject.py Tue Sep 06 14:25:20 2011 -0700 +++ b/src/python/m5/SimObject.py Tue Sep 06 14:27:50 2011 -0700 @@ -481,6 +481,16 @@ code('%import "params/${cls}_type.hh"') code('%include "params/${cls}.hh"') + +# This *temporary* definition is required to support calls from the +# SimObject class definition to the MetaSimObject methods (in +# particular _set_param, which gets called for parameters with default +# values defined on the SimObject class itself). It will get +# overridden by the permanent definition (which requires that +# SimObject be defined) lower in this file. +def isSimObjectOrVector(value): + return False + # The SimObject class is the root of the special hierarchy. Most of # the code in this class deals with the configuration hierarchy itself # (parent/child node relationships). @@ -491,6 +501,8 @@ type = 'SimObject' abstract = True + eventq_index = Param.Int(0, "Event queue index") + @classmethod def swig_objdecls(cls, code): code('%include "python/swig/sim_object.i"') diff -r 0db5bb5bf244 -r e1faec27d07b src/python/m5/event.py --- a/src/python/m5/event.py Tue Sep 06 14:25:20 2011 -0700 +++ b/src/python/m5/event.py Tue Sep 06 14:27:50 2011 -0700 @@ -29,7 +29,7 @@ import m5 import internal.event -from internal.event import PythonEvent, SimLoopExitEvent as SimExit +from internal.event import PythonEvent, GlobalSimLoopExitEvent as SimExit mainq = internal.event.cvar.mainEventQueue diff -r 0db5bb5bf244 -r e1faec27d07b src/python/swig/event.i --- a/src/python/swig/event.i Tue Sep 06 14:25:20 2011 -0700 +++ b/src/python/swig/event.i Tue Sep 06 14:27:50 2011 -0700 @@ -88,12 +88,13 @@ }; // minimal definition of SimExitEvent interface to wrap -class SimLoopExitEvent : public Event +class GlobalSimLoopExitEvent { public: std::string getCause(); int getCode(); - SimLoopExitEvent(const std::string &_cause, int c, Tick _repeat = 0); + GlobalSimLoopExitEvent(Tick when, const std::string &_cause, int c, + Tick _repeat = 0); }; %exception simulate { @@ -102,5 +103,5 @@ return NULL; } } -SimLoopExitEvent *simulate(Tick num_cycles = MaxTick); +GlobalSimLoopExitEvent *simulate(Tick num_cycles = MaxTick); void exitSimLoop(const std::string &message, int exit_code); diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/SConscript --- a/src/sim/SConscript Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/SConscript Tue Sep 06 14:27:50 2011 -0700 @@ -38,6 +38,7 @@ Source('core.cc') Source('debug.cc') Source('eventq.cc') +Source('global_event.cc') Source('init.cc') Source('main.cc', main=True, skip_lib=True) Source('root.cc') diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/core.hh --- a/src/sim/core.hh Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/core.hh Tue Sep 06 14:27:50 2011 -0700 @@ -41,10 +41,10 @@ #include "base/types.hh" /// The universal simulation clock. -extern Tick _curTick; +extern __thread Tick _tls_curTick; -inline Tick curTick() { return _curTick; } -inline void curTick(Tick newVal) { _curTick = newVal; } +inline Tick curTick() { return _tls_curTick; } +inline void curTick(Tick newVal) { _tls_curTick = newVal; } const Tick retryTime = 1000; diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/core.cc --- a/src/sim/core.cc Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/core.cc Tue Sep 06 14:27:50 2011 -0700 @@ -38,7 +38,7 @@ using namespace std; -Tick _curTick = 0; +__thread Tick _tls_curTick = 0; namespace SimClock { /// The simulated frequency of curTick(). (In ticks per second) diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/debug.cc --- a/src/sim/debug.cc Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/debug.cc Tue Sep 06 14:27:50 2011 -0700 @@ -46,9 +46,9 @@ // Debug event: place a breakpoint on the process function and // schedule the event to break at a particular cycle // -struct DebugBreakEvent : public Event +struct DebugBreakEvent : public GlobalEvent { - DebugBreakEvent(); + DebugBreakEvent(Tick when); void process(); // process event virtual const char *description() const; }; @@ -56,8 +56,8 @@ // // constructor: schedule at specified time // -DebugBreakEvent::DebugBreakEvent() - : Event(Debug_Break_Pri, AutoDelete) +DebugBreakEvent::DebugBreakEvent(Tick when) + : GlobalEvent(when, Debug_Break_Pri, AutoDelete) { } @@ -84,7 +84,7 @@ void schedBreakCycle(Tick when) { - mainEventQueue.schedule(new DebugBreakEvent, when); + new DebugBreakEvent(when); warn("need to stop all queues"); } @@ -102,8 +102,9 @@ void eventqDump() { - mainEventQueue.dump(); - warn("need to dump all queues"); + // FIXME + // mainEventQueue.dump(); + warn("multiq dump not implemented: need to dump all queues"); } void diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/eventq.hh --- a/src/sim/eventq.hh Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/eventq.hh Tue Sep 06 14:27:50 2011 -0700 @@ -1,5 +1,6 @@ /* * Copyright (c) 2000-2005 The Regents of The University of Michigan + * Copyright (c) 2011 Advanced Micro Devices, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,6 +37,8 @@ #ifndef __SIM_EVENTQ_HH__ #define __SIM_EVENTQ_HH__ +#include + #include #include #include @@ -50,22 +53,59 @@ #include "debug/Event.hh" #include "sim/serialize.hh" -class EventQueue; // forward declaration +class EventQueue; // forward declarations +class EventManager; +class BaseGlobalEvent; -extern EventQueue mainEventQueue; +/** + * The maximum number of allocated event queues. + */ +#define MAX_MAIN_EVENT_QUEUES 8 -/* - * An item on an event queue. The action caused by a given - * event is specified by deriving a subclass and overriding the - * process() member function. - * - * Caution, the order of members is chosen to maximize data packing. +/** + * The current number of allocated event queues. */ -class Event : public Serializable, public FastAlloc +extern int numMainEventQueues; + +/** + * Array of event queues. + */ +extern EventQueue *mainEventQueue[MAX_MAIN_EVENT_QUEUES]; + +/** + * Return the event queue at the given index. Will allocate a new + * event queue if this is the first request for the event queue at the + * specified index. + */ +EventQueue *getMainEventQueue(int index); + +/** + * The current event queue for the running thread, as recorded in + * thread-local storage. Accesses to this event queue from this + * thread do not require locking. + */ +#ifndef SWIG +extern __thread EventQueue *_tls_curEventQueue; +#endif + +/// Read accessor for the current event queue. +inline EventQueue *curEventQueue() { return _tls_curEventQueue; } +/// Write accessor to set the current event queue. +inline void curEventQueue(EventQueue *q) { _tls_curEventQueue = q; } + +/** + * Are we running multiple threads in parallel? + */ +extern bool inParallelMode; + +/** + * Common base class for Event and GlobalEvent, so they can share flag + * and priority definitions and accessor functions. This class should + * not be used directly. + */ +class EventBase { - friend class EventQueue; - - protected: + protected: typedef short FlagsType; typedef ::Flags Flags; @@ -76,111 +116,12 @@ static const FlagsType AutoDelete = 0x0004; // delete after dispatch static const FlagsType AutoSerialize = 0x0008; // must be serialized static const FlagsType IsExitEvent = 0x0010; // special exit event - static const FlagsType IsMainQueue = 0x0020; // on main event queue static const FlagsType Initialized = 0x7a40; // somewhat random bits static const FlagsType InitMask = 0xffc0; // mask for init bits - bool - initialized() const - { - return this && (flags & InitMask) == Initialized; - } - public: typedef int8_t Priority; - private: - // The event queue is now a linked list of linked lists. The - // 'nextBin' pointer is to find the bin, where a bin is defined as - // when+priority. All events in the same bin will be stored in a - // second linked list (a stack) maintained by the 'nextInBin' - // pointer. The list will be accessed in LIFO order. The end - // result is that the insert/removal in 'nextBin' is - // linear/constant, and the lookup/removal in 'nextInBin' is - // constant/constant. Hopefully this is a significant improvement - // over the current fully linear insertion. - Event *nextBin; - Event *nextInBin; - - static Event *insertBefore(Event *event, Event *curr); - static Event *removeItem(Event *event, Event *last); - - Tick _when; //!< timestamp when event should be processed - Priority _priority; //!< event priority - Flags flags; - -#ifndef NDEBUG - /// Global counter to generate unique IDs for Event instances - static Counter instanceCounter; - - /// This event's unique ID. We can also use pointer values for - /// this but they're not consistent across runs making debugging - /// more difficult. Thus we use a global counter value when - /// debugging. - Counter instance; - - /// queue to which this event belongs (though it may or may not be - /// scheduled on this queue yet) - EventQueue *queue; -#endif - -#ifdef EVENTQ_DEBUG - Tick whenCreated; //!< time created - Tick whenScheduled; //!< time scheduled -#endif - - void - setWhen(Tick when, EventQueue *q) - { - _when = when; -#ifndef NDEBUG - queue = q; -#endif -#ifdef EVENTQ_DEBUG - whenScheduled = curTick(); -#endif - } - - protected: - /// Accessor for flags. - Flags - getFlags() const - { - return flags & PublicRead; - } - - bool - isFlagSet(Flags _flags) const - { - assert(_flags.noneSet(~PublicRead)); - return flags.isSet(_flags); - } - - /// Accessor for flags. - void - setFlags(Flags _flags) - { - assert(_flags.noneSet(~PublicWrite)); - flags.set(_flags); - } - - void - clearFlags(Flags _flags) - { - assert(_flags.noneSet(~PublicWrite)); - flags.clear(_flags); - } - - void - clearFlags() - { - flags.clear(PublicWrite); - } - - // This function isn't really useful if TRACING_ON is not defined - virtual void trace(const char *action); //!< trace event activity - - public: /// Event priorities, to provide tie-breakers for events scheduled /// at the same cycle. Most events are scheduled at the default /// priority; these values are used to control events that need to @@ -235,7 +176,121 @@ /// Maximum priority static const Priority Maximum_Pri = SCHAR_MAX; +}; +/* + * An item on an event queue. The action caused by a given + * event is specified by deriving a subclass and overriding the + * process() member function. + * + * A single Event object can be on only one EventQueue at a time, and + * thus is localized to a single thread. See GlobalEvent in + * global_event.hh for events that span all threads. + * + * Caution, the order of members is chosen to maximize data packing. + */ +class Event : public EventBase, public Serializable, public FastAlloc +{ + friend class EventQueue; + + private: + // The event queue is now a linked list of linked lists. The + // 'nextBin' pointer is to find the bin, where a bin is defined as + // when+priority. All events in the same bin will be stored in a + // second linked list (a stack) maintained by the 'nextInBin' + // pointer. The list will be accessed in LIFO order. The end + // result is that the insert/removal in 'nextBin' is + // linear/constant, and the lookup/removal in 'nextInBin' is + // constant/constant. Hopefully this is a significant improvement + // over the current fully linear insertion. + Event *nextBin; + Event *nextInBin; + + static Event *insertBefore(Event *event, Event *curr); + static Event *removeItem(Event *event, Event *last); + + Tick _when; //!< timestamp when event should be processed + Priority _priority; //!< event priority + Flags flags; + +#ifndef NDEBUG + /// Global counter to generate unique IDs for Event instances + static Counter instanceCounter; + + /// This event's unique ID. We can also use pointer values for + /// this but they're not consistent across runs making debugging + /// more difficult. Thus we use a global counter value when + /// debugging. + Counter instance; + + /// queue to which this event belongs (though it may or may not be + /// scheduled on this queue yet) + EventQueue *queue; +#endif + +#ifdef EVENTQ_DEBUG + Tick whenCreated; //!< time created + Tick whenScheduled; //!< time scheduled +#endif + + void + setWhen(Tick when, EventQueue *q) + { + _when = when; +#ifndef NDEBUG + queue = q; +#endif +#ifdef EVENTQ_DEBUG + whenScheduled = curTick(); +#endif + } + + bool + initialized() const + { + return this && (flags & InitMask) == Initialized; + } + + protected: + /// Accessor for flags. + Flags + getFlags() const + { + return flags & PublicRead; + } + + bool + isFlagSet(Flags _flags) const + { + assert(_flags.noneSet(~PublicRead)); + return flags.isSet(_flags); + } + + /// Accessor for flags. + void + setFlags(Flags _flags) + { + assert(_flags.noneSet(~PublicWrite)); + flags.set(_flags); + } + + void + clearFlags(Flags _flags) + { + assert(_flags.noneSet(~PublicWrite)); + flags.clear(_flags); + } + + void + clearFlags() + { + flags.clear(PublicWrite); + } + + // This function isn't really useful if TRACING_ON is not defined + virtual void trace(const char *action); //!< trace event activity + + public: /* * Event constructor * @param queue that the event gets scheduled on @@ -297,6 +352,11 @@ /// Get the event priority Priority priority() const { return _priority; } + /// If this is part of a GlobalEvent, return the pointer to the + /// Global Event. By default, there is no GlobalEvent, so return + /// NULL. (Overridden in GlobalEvent::BarrierEvent.) + virtual BaseGlobalEvent *globalEvent() { return NULL; } + #ifndef SWIG struct priority_compare : public std::binary_function @@ -310,6 +370,8 @@ virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §ion); + virtual void unserialize(Checkpoint *cp, const std::string §ion, + EventManager *em); #endif }; @@ -362,10 +424,22 @@ private: std::string objName; Event *head; + /// Lock to protect async_queue. + pthread_mutex_t async_queue_lock; + /// List of events added by other threads. + std::list async_queue; + /// Insert event in the queue. When in parallel mode, this + /// function should only be called by the thread that owns this + /// queue. void insert(Event *event); void remove(Event *event); + /// Add event to the async queue for later integration into the + /// main queue. Non-owner threads that want to add to this queue + /// in parallel mode must use this function instead of insert(). + void asyncInsert(Event *event); + EventQueue(const EventQueue &); const EventQueue &operator=(const EventQueue &); @@ -374,9 +448,14 @@ virtual const std::string name() const { return objName; } - // schedule the given event on this queue + /// Schedule the given event on this queue. Can safely be called + /// from any thread in parallel mode. void schedule(Event *event, Tick when); + /// Deschedule the specified event. Must be called from the owner + /// thread when in parallel mode. void deschedule(Event *event); + /// Reschedule the specified event. Must be called from the owner + /// thread when in parallel mode. void reschedule(Event *event, Tick when, bool always = false); Tick nextTick() const { return head->when(); } @@ -404,6 +483,10 @@ // return true if no events are queued bool empty() const { return head == NULL; } + /// Process the async_queue and schedule the events there onto the + /// main queue. + void handleAsyncInsertions(); + void dump() const; bool debugVerify() const; @@ -484,12 +567,14 @@ assert(event->initialized()); event->setWhen(when, this); - insert(event); + + if (this == curEventQueue() || !inParallelMode) { + insert(event); + } else { + asyncInsert(event); + } + event->flags.set(Event::Scheduled); - if (this == &mainEventQueue) - event->flags.set(Event::IsMainQueue); - else - event->flags.clear(Event::IsMainQueue); if (DTRACE(Event)) event->trace("scheduled"); @@ -500,6 +585,7 @@ { assert(event->scheduled()); assert(event->initialized()); + assert(this == curEventQueue() || !inParallelMode); remove(event); @@ -521,6 +607,7 @@ assert((UTick)when >= (UTick)curTick()); assert(always || event->scheduled()); assert(event->initialized()); + assert(this == curEventQueue() || !inParallelMode); if (event->scheduled()) remove(event); @@ -529,10 +616,6 @@ insert(event); event->flags.clear(Event::Squashed); event->flags.set(Event::Scheduled); - if (this == &mainEventQueue) - event->flags.set(Event::IsMainQueue); - else - event->flags.clear(Event::IsMainQueue); if (DTRACE(Event)) event->trace("rescheduled"); @@ -589,6 +672,7 @@ const char *description() const { return "EventWrapped"; } }; -#endif + +#endif // #ifndef SWIG #endif // __SIM_EVENTQ_HH__ diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/eventq.cc --- a/src/sim/eventq.cc Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/eventq.cc Tue Sep 06 14:27:50 2011 -0700 @@ -1,6 +1,7 @@ /* * Copyright (c) 2000-2005 The Regents of The University of Michigan * Copyright (c) 2008 The Hewlett-Packard Development Company + * Copyright (c) 2011 Advanced Micro Devices, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,13 +47,33 @@ using namespace std; -// -// Main Event Queue -// -// Events on this queue are processed at the *beginning* of each -// cycle, before the pipeline simulation is performed. -// -EventQueue mainEventQueue("Main Event Queue"); +// See eventq.hh for comments. + +int numMainEventQueues = 0; + +EventQueue *mainEventQueue[MAX_MAIN_EVENT_QUEUES]; + +__thread EventQueue *_tls_curEventQueue = NULL; + +bool inParallelMode = false; + +EventQueue *getMainEventQueue(int index) +{ + if (index >= MAX_MAIN_EVENT_QUEUES) { + fatal("Event queue index %d >= MAX_MAIN_EVENT_QUEUES, " + "please edit eventq.hh and recompile\n", index); + } + + if (index >= numMainEventQueues) { + for (int i = numMainEventQueues; i <= index; ++i) { + mainEventQueue[i] = new EventQueue(csprintf("EventQueue%d", i)); + } + numMainEventQueues = index + 1; + } + + return mainEventQueue[index]; +} + #ifndef NDEBUG Counter Event::instanceCounter = 0; @@ -153,6 +174,8 @@ void EventQueue::remove(Event *event) { + assert(event->queue == this); + if (head == NULL) panic("event not found!"); @@ -228,9 +251,14 @@ void Event::unserialize(Checkpoint *cp, const string §ion) { - if (scheduled()) - mainEventQueue.deschedule(this); + panic("Event::unserialize unimplemented!\n"); +} + +void +Event::unserialize(Checkpoint *cp, const string §ion, + EventManager *event_mgr) +{ UNSERIALIZE_SCALAR(_when); UNSERIALIZE_SCALAR(_priority); @@ -255,7 +283,7 @@ if (wasScheduled) { DPRINTF(Config, "rescheduling at %d\n", _when); - mainEventQueue.schedule(this, _when); + event_mgr->reschedule(this, _when, true); } } @@ -373,12 +401,13 @@ return true; } +#if 0 void dumpMainQueue() { mainEventQueue.dump(); } - +#endif const char * Event::description() const @@ -421,4 +450,31 @@ EventQueue::EventQueue(const string &n) : objName(n), head(NULL) -{} +{ + pthread_mutex_init(&async_queue_lock, NULL); +} + + +void +EventQueue::asyncInsert(Event *event) +{ + pthread_mutex_lock(&async_queue_lock); + async_queue.push_back(event); + pthread_mutex_unlock(&async_queue_lock); +} + + +void +EventQueue::handleAsyncInsertions() +{ + assert(this == curEventQueue()); + + pthread_mutex_lock(&async_queue_lock); + + while (!async_queue.empty()) { + insert(async_queue.front()); + async_queue.pop_front(); + } + + pthread_mutex_unlock(&async_queue_lock); +} diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/global_event.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sim/global_event.hh Tue Sep 06 14:27:50 2011 -0700 @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2011 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 + */ + +#ifndef __SIM_GLOBAL_EVENT_HH__ +#define __SIM_GLOBAL_EVENT_HH__ + +#include + +#include + +#include "sim/eventq.hh" + +/** + * @file sim/global_event.hh + * Global events and related declarations. + * + * A global event is an event that occurs across all threads, i.e., + * globally. It consists of a set of "local" (regular) Events, one + * per thread/event queue, a barrier object, and common state. The + * local events are scheduled for the same tick. The local event + * process() method enters the barrier to wait for other threads; once + * all threads reach that tick (and enter the associated barrier), the + * global event is triggered and its associated activity is performed. + * + * There are two basic global event patterns, GlobalEvent and + * GlobalSyncEvent. GlobalEvent is the base class for typical global + * events, while GlobalSyncEvent is optimized for global + * synchronization operations. + */ + +/** + * Common base class for GlobalEvent and GlobalSyncEvent. + */ +class BaseGlobalEvent : public EventBase +{ + protected: + + /// The base class for the local events that will synchronize + /// threads to perform the global event. This class is abstract, + /// since it derives from the abstract Event class but still does + /// not define the required process() method. + class BarrierEvent : public Event + { + protected: + BaseGlobalEvent *_globalEvent; + + BarrierEvent(BaseGlobalEvent *global_event, Priority p, Flags f) + : Event(p, f), _globalEvent(global_event) + { + } + + ~BarrierEvent(); + + friend class BaseGlobalEvent; + + bool globalBarrier() + { + int status = pthread_barrier_wait(&_globalEvent->barrier); + assert(status == PTHREAD_BARRIER_SERIAL_THREAD || status == 0); + return (status == PTHREAD_BARRIER_SERIAL_THREAD); + } + + public: + virtual BaseGlobalEvent *globalEvent() { return _globalEvent; } + }; + + /// The barrier that all threads wait on before performing the + /// global event. + pthread_barrier_t barrier; + + /// The individual local event instances (one per thread/event queue). + std::vector barrierEvent; + + public: + BaseGlobalEvent(Priority p, Flags f); + + ~BaseGlobalEvent(); + + virtual void process() = 0; + + virtual const char *description() const = 0; + + void schedule(Tick when); +}; + + +/** + * Funky intermediate class to support CRTP so that we can have a + * common constructor to create the local events, even though the + * types of the local events are defined in the derived classes. + */ +template +class BaseGlobalEventTemplate : public BaseGlobalEvent +{ + protected: + BaseGlobalEventTemplate(Priority p, Flags f) + : BaseGlobalEvent(p, f) + { + for (int i = 0; i < numMainEventQueues; ++i) + barrierEvent[i] = new typename Derived::BarrierEvent(this, p, f); + } +}; + + +/** + * The main global event class. Ordinary global events should derive + * from this class, and define process() to specify the action to be + * taken when the event is reached. All threads will synchronize at a + * barrier, exactly one of the threads will execute the process() + * method, then the threads will synchronize again so that none of + * them continue until process() is complete. + */ +class GlobalEvent : public BaseGlobalEventTemplate +{ + public: + typedef BaseGlobalEventTemplate Base; + + class BarrierEvent : public Base::BarrierEvent + { + public: + void process(); + BarrierEvent(Base *global_event, Priority p, Flags f) + : Base::BarrierEvent(global_event, p, f) + { } + }; + + GlobalEvent(Priority p, Flags f) + : Base(p, f) + { } + + GlobalEvent(Tick when, Priority p, Flags f) + : Base(p, f) + { + schedule(when); + } + + virtual void process() = 0; +}; + +/** + * A special global event that synchronizes all threads and forces + * them to process asynchronously enqueued events. Useful for + * separating quanta in a quantum-based parallel simulation. + */ +class GlobalSyncEvent : public BaseGlobalEventTemplate +{ + public: + typedef BaseGlobalEventTemplate Base; + + class BarrierEvent : public Base::BarrierEvent + { + public: + void process(); + BarrierEvent(Base *global_event, Priority p, Flags f) + : Base::BarrierEvent(global_event, p, f) + { } + }; + + GlobalSyncEvent(Priority p, Flags f) + : Base(p, f) + { } + + GlobalSyncEvent(Tick when, Priority p, Flags f) + : Base(p, f) + { + schedule(when); + } + + void process(); + + const char *description() const; +}; + + +#endif // __SIM_GLOBAL_EVENT_HH__ diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/global_event.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sim/global_event.cc Tue Sep 06 14:27:50 2011 -0700 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2011 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 + */ + +#include "sim/global_event.hh" + + +BaseGlobalEvent::BaseGlobalEvent(Priority p, Flags f) +{ + barrierEvent.resize(numMainEventQueues); + + int status = pthread_barrier_init(&barrier, NULL, numMainEventQueues); + if (status != 0) + panic("GlobalEvent: pthread_barrier_init failed (%d)\n", status); +} + + +BaseGlobalEvent::~BaseGlobalEvent() +{ + // see GlobalEvent::BarrierEvent::~BarrierEvent() comments + if (barrierEvent[0] != NULL) { + for (int i = 0; i < numMainEventQueues; ++i) + delete barrierEvent[i]; + } +} + + +void BaseGlobalEvent::schedule(Tick when) +{ + for (int i = 0; i < numMainEventQueues; ++i) + mainEventQueue[i]->schedule(barrierEvent[i], when); +} + + +BaseGlobalEvent::BarrierEvent::~BarrierEvent() +{ + // if AutoDelete is set, local events will get deleted in event + // loop, but we need to delete GlobalEvent object too... so let + // the local event in slot 0 do it + if (isFlagSet(AutoDelete) && _globalEvent->barrierEvent[0] == this) { + // set backpointer to NULL so that global event knows not to + // turn around and recursively delete local events + _globalEvent->barrierEvent[0] = NULL; + delete _globalEvent; + } +} + + +void +GlobalEvent::BarrierEvent::process() +{ + // wait for all queues to arrive at barrier, then process event + if (globalBarrier()) { + _globalEvent->process(); + } + + // second barrier to force all queues to wait for event processing + // to finish before continuing + globalBarrier(); +} + + +void +GlobalSyncEvent::BarrierEvent::process() +{ + globalBarrier(); + curEventQueue()->handleAsyncInsertions(); +} + +void +GlobalSyncEvent::process() +{ + panic("GlobalSyncEvent::process() should never be called!\n"); +} + +const char * +GlobalSyncEvent::description() const +{ + return "GlobalSyncEvent"; +} diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/serialize.cc --- a/src/sim/serialize.cc Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/serialize.cc Tue Sep 06 14:27:50 2011 -0700 @@ -456,8 +456,9 @@ nameOut(os); paramOut(os, "curTick", curTick()); - nameOut(os, "MainEventQueue"); - mainEventQueue.serialize(os); +// FIXME +// nameOut(os, "MainEventQueue"); +// mainEventQueue.serialize(os); } void @@ -468,7 +469,8 @@ paramIn(cp, section, "curTick", tick); curTick(tick); - mainEventQueue.unserialize(cp, "MainEventQueue"); +// FIXME +// mainEventQueue.unserialize(cp, "MainEventQueue"); } Serializable::Serializable() diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/sim_events.hh --- a/src/sim/sim_events.hh Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/sim_events.hh Tue Sep 06 14:27:50 2011 -0700 @@ -31,12 +31,12 @@ #ifndef __SIM_SIM_EVENTS_HH__ #define __SIM_SIM_EVENTS_HH__ -#include "sim/eventq.hh" +#include "sim/global_event.hh" // // Event to terminate simulation at a particular cycle/instruction // -class SimLoopExitEvent : public Event +class GlobalSimLoopExitEvent : public GlobalEvent { protected: // string explaining why we're terminating @@ -45,7 +45,8 @@ Tick repeat; public: - SimLoopExitEvent(const std::string &_cause, int c, Tick repeat = 0); + GlobalSimLoopExitEvent(Tick when, const std::string &cause, int code, + Tick repeat = 0); std::string getCause() { return cause; } int getCode() { return code; } @@ -55,6 +56,30 @@ virtual const char *description() const; }; + +// +// Event to terminate simulation at a particular cycle/instruction +// +class LocalSimLoopExitEvent : public Event +{ + protected: + // string explaining why we're terminating + std::string cause; + int code; + Tick repeat; + + public: + LocalSimLoopExitEvent(const std::string &cause, int code, Tick repeat = 0); + + std::string getCause() { return cause; } + int getCode() { return code; } + + void process(); // process event + + virtual const char *description() const; +}; + + class CountedDrainEvent : public Event { private: diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/sim_events.cc --- a/src/sim/sim_events.cc Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/sim_events.cc Tue Sep 06 14:27:50 2011 -0700 @@ -39,8 +39,11 @@ using namespace std; -SimLoopExitEvent::SimLoopExitEvent(const std::string &_cause, int c, Tick r) - : Event(Sim_Exit_Pri, IsExitEvent), cause(_cause), code(c), repeat(r) +GlobalSimLoopExitEvent::GlobalSimLoopExitEvent(Tick when, + const std::string &_cause, + int _code, Tick _repeat) + : GlobalEvent(when, Sim_Exit_Pri, IsExitEvent), + cause(_cause), code(_code), repeat(_repeat) { } @@ -49,38 +52,54 @@ // handle termination event // void -SimLoopExitEvent::process() +GlobalSimLoopExitEvent::process() { - // if this got scheduled on a different queue (e.g. the committed - // instruction queue) then make a corresponding event on the main - // queue. - if (!isFlagSet(IsMainQueue)) { - exitSimLoop(cause, code); - delete this; - } + // do nothing... the IsExitEvent flag takes care of exiting the + // simulation loop and returning this object to Python - // otherwise do nothing... the IsExitEvent flag takes care of - // exiting the simulation loop and returning this object to Python - - // but if you are doing this on intervals, don't forget to make another + // but if you are doing this on intervals, don't forget to reschedule if (repeat) { - assert(isFlagSet(IsMainQueue)); - mainEventQueue.schedule(this, curTick() + repeat); + schedule(curTick() + repeat); } } const char * -SimLoopExitEvent::description() const +GlobalSimLoopExitEvent::description() const { - return "simulation loop exit"; + return "global simulation loop exit"; } void exitSimLoop(const std::string &message, int exit_code, Tick when, Tick repeat) { - Event *event = new SimLoopExitEvent(message, exit_code, repeat); - mainEventQueue.schedule(event, when); + new GlobalSimLoopExitEvent(when, message, exit_code, repeat); +} + + + +LocalSimLoopExitEvent::LocalSimLoopExitEvent(const std::string &_cause, + int _code, Tick _repeat) + : Event(Sim_Exit_Pri), + cause(_cause), code(_code), repeat(_repeat) +{ +} + + +// +// handle termination event +// +void +LocalSimLoopExitEvent::process() +{ + exitSimLoop(cause, 0); +} + + +const char * +LocalSimLoopExitEvent::description() const +{ + return "local simulation loop exit"; } CountedDrainEvent::CountedDrainEvent() diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/sim_exit.hh --- a/src/sim/sim_exit.hh Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/sim_exit.hh Tue Sep 06 14:27:50 2011 -0700 @@ -39,8 +39,6 @@ // forward declaration class Callback; -class EventQueue; -class SimLoopExitEvent; /// Register a callback to be called when Python exits. Defined in /// sim/main.cc. diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/sim_object.cc --- a/src/sim/sim_object.cc Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/sim_object.cc Tue Sep 06 14:27:50 2011 -0700 @@ -60,7 +60,7 @@ // SimObject constructor: used to maintain static simObjectList // SimObject::SimObject(const Params *p) - : EventManager(p->eventq), _params(p) + : EventManager(getMainEventQueue(p->eventq_index)), _params(p) { #ifdef DEBUG doDebugBreak = false; diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/sim_object_params.hh --- a/src/sim/sim_object_params.hh Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/sim_object_params.hh Tue Sep 06 14:27:50 2011 -0700 @@ -38,20 +38,14 @@ #include -struct EventQueue; - struct SimObjectParams { - SimObjectParams() - { - extern EventQueue mainEventQueue; - eventq = &mainEventQueue; - } + SimObjectParams() {} virtual ~SimObjectParams() {} std::string name; PyObject *pyobj; - EventQueue *eventq; + int eventq_index; }; diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/simulate.hh --- a/src/sim/simulate.hh Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/simulate.hh Tue Sep 06 14:27:50 2011 -0700 @@ -32,4 +32,4 @@ #include "base/types.hh" #include "sim/sim_events.hh" -SimLoopExitEvent *simulate(Tick num_cycles = MaxTick); +GlobalSimLoopExitEvent *simulate(Tick num_cycles = MaxTick); diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/simulate.cc --- a/src/sim/simulate.cc Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/simulate.cc Tue Sep 06 14:27:50 2011 -0700 @@ -1,5 +1,6 @@ /* * Copyright (c) 2006 The Regents of The University of Michigan + * Copyright (c) 2011 Advanced Micro Devices, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,59 +40,161 @@ #include "sim/simulate.hh" #include "sim/stat_control.hh" + +pthread_mutex_t asyncEventMutex; + +/// Global barrier for synchronizing threads entering/exiting the +/// simulation loop. +pthread_barrier_t threadBarrier; + +// forward declaration +Event *doSimLoop(EventQueue *); + +/** + * The main function for all subordinate threads (i.e., all threads + * other than the main thread). These threads start by waiting on + * threadBarrier. Once all threads have arrived at threadBarrier, + * they enter the simulation loop concurrently. When they exit the + * loop, they return to waiting on threadBarrier. This process is + * repeated until the simulation terminates. + */ +void * +thread_loop(void *arg) +{ + EventQueue *queue = static_cast(arg); + + while (1) { + pthread_barrier_wait(&threadBarrier); + doSimLoop(queue); + } + + // not reached + return NULL; +} + + /** Simulate for num_cycles additional cycles. If num_cycles is -1 * (the default), do not limit simulation; some other event must * terminate the loop. Exported to Python via SWIG. * @return The SimLoopExitEvent that caused the loop to exit. */ -SimLoopExitEvent * +GlobalSimLoopExitEvent * simulate(Tick num_cycles) { + // The first time simulate() is called from Python, we need to + // create a pthread for each of the event queues referenced by the + // instantiated SimObjects + static bool threads_initialized = false; + + if (!threads_initialized) { + // the main thread (the one we're currently running on) + // handles queue 0, so we only need to allocate new threads + // for queues 1..N-1. We'll call these the "subordinate" threads. + for (int i = 1; i < numMainEventQueues; i++) { + pthread_t t; + pthread_create(&t, NULL, &thread_loop, (void *)mainEventQueue[i]); + } + + // Also initialize global pthread synchronization objects here + int status = pthread_mutex_init(&asyncEventMutex, NULL); + if (status != 0) + panic("pthread_mutex_init returns error code %d\n", status); + status = pthread_barrier_init(&threadBarrier, NULL, + numMainEventQueues); + if (status != 0) + panic("pthread_barrier_init returns error code %d\n", status); + + threads_initialized = true; + } + inform("Entering event queue @ %d. Starting simulation...\n", curTick()); if (num_cycles < 0) fatal("simulate: num_cycles must be >= 0 (was %d)\n", num_cycles); - else if (curTick() + num_cycles < 0) //Overflow - num_cycles = MaxTick; - else - num_cycles = curTick() + num_cycles; - Event *limit_event = - new SimLoopExitEvent("simulate() limit reached", 0); - mainEventQueue.schedule(limit_event, num_cycles); + Tick limit_tick = curTick() + num_cycles; + if (limit_tick < 0) // overflow + limit_tick = MaxTick; + + // schedule limit event at limit_tick + exitSimLoop("simulate() limit reached", 0, limit_tick); + + inParallelMode = true; + // all subordinate (created) threads should be waiting on this + // barrier; the arrival of the main thread here will satisfy the + // barrier, and all threads will enter doSimLoop in parallel + pthread_barrier_wait(&threadBarrier); + Event *local_event = doSimLoop(mainEventQueue[0]); + assert(local_event != NULL); + // Once we exit doSimLoop(), all subordinate threads will be back + // to waiting on threadBarrier in thread_loop(), so this is the + // only executing thread + inParallelMode = false; + + // locate the global exit event and return it to Python + BaseGlobalEvent *global_event = local_event->globalEvent(); + assert(global_event != NULL); + + GlobalSimLoopExitEvent *global_exit_event = + dynamic_cast(global_event); + assert(global_exit_event != NULL); + return global_exit_event; +} + + +/** + * Test and clear the global async_event flag, such that each time the + * flag is cleared, only one thread returns true (and thus is assigned + * to handle the corresponding async event(s)). + */ +bool +testAndClearAsyncEvent() +{ + bool was_set = false; + pthread_mutex_lock(&asyncEventMutex); + if (async_event) { + was_set = true; + async_event = false; + } + pthread_mutex_unlock(&asyncEventMutex); + return was_set; +} + + +/** + * The main per-thread simulation loop. This loop is executed by all + * simulation threads (the main thread and the subordinate threads) in + * parallel. + */ +Event * +doSimLoop(EventQueue *queue) +{ + // set per-thread current queue pointer + curEventQueue(queue); + queue->handleAsyncInsertions(); while (1) { // there should always be at least one event (the SimLoopExitEvent // we just scheduled) in the queue - assert(!mainEventQueue.empty()); - assert(curTick() <= mainEventQueue.nextTick() && + assert(!queue->empty()); + assert(curTick() <= queue->nextTick() && "event scheduled in the past"); // forward current cycle to the time of the first event on the // queue - curTick(mainEventQueue.nextTick()); - Event *exit_event = mainEventQueue.serviceOne(); + curTick(queue->nextTick()); + Event *exit_event = queue->serviceOne(); if (exit_event != NULL) { - // hit some kind of exit event; return to Python - // event must be subclass of SimLoopExitEvent... - SimLoopExitEvent *se_event; - se_event = dynamic_cast(exit_event); - - if (se_event == NULL) - panic("Bogus exit event class!"); - - // if we didn't hit limit_event, delete it - if (se_event != limit_event) { - assert(limit_event->scheduled()); - limit_event->squash(); - hack_once("be nice to actually delete the event here"); - } - - return se_event; + assert(exit_event != NULL); + return exit_event; } - if (async_event) { - async_event = false; + // Check the async_event flag for asynchronous events to + // handle. Note that these are the old pre-parallelization + // asynchronous events, and are unrelated to the events that + // get scheduled asynchronously on the per-thread event + // queues. + if (async_event && testAndClearAsyncEvent()) { if (async_statdump || async_statreset) { Stats::schedStatEvent(async_statdump, async_statreset); async_statdump = false; diff -r 0db5bb5bf244 -r e1faec27d07b src/sim/stat_control.cc --- a/src/sim/stat_control.cc Tue Sep 06 14:25:20 2011 -0700 +++ b/src/sim/stat_control.cc Tue Sep 06 14:27:50 2011 -0700 @@ -46,7 +46,7 @@ #include "cpu/base.hh" #endif -#include "sim/eventq.hh" +#include "sim/global_event.hh" #include "sim/stat_control.hh" using namespace std; @@ -166,7 +166,7 @@ static Global global; } -class StatEvent : public Event +class StatEvent : public GlobalEvent { private: bool dump; @@ -174,8 +174,9 @@ Tick repeat; public: - StatEvent(bool _dump, bool _reset, Tick _repeat) - : Event(Stat_Event_Pri, AutoDelete), + StatEvent(Tick when, bool _dump, bool _reset, Tick _repeat) + : GlobalEvent(when, Stat_Event_Pri, + (_repeat == 0) ? AutoDelete : 0), dump(_dump), reset(_reset), repeat(_repeat) { } @@ -190,16 +191,17 @@ Stats::reset(); if (repeat) { - Stats::schedStatEvent(dump, reset, curTick() + repeat, repeat); + schedule(curTick() + repeat); } } + + const char *description() const { return "StatEvent"; } }; void schedStatEvent(bool dump, bool reset, Tick when, Tick repeat) { - Event *event = new StatEvent(dump, reset, repeat); - mainEventQueue.schedule(event, when); + new StatEvent(when, dump, reset, repeat); } } // namespace Stats