diff options
author | Gabe Black <gabeblack@google.com> | 2018-07-04 22:41:29 -0700 |
---|---|---|
committer | Gabe Black <gabeblack@google.com> | 2018-09-05 06:04:19 +0000 |
commit | 7088d69ab5fc70fca4890e0d0169fadd2b19bab8 (patch) | |
tree | c74b56e47f903c7fb8e722393e86a28410ae2c21 /src | |
parent | 0aec777bf2fff0ac61cd36b7c0358dbe9350c784 (diff) | |
download | gem5-7088d69ab5fc70fca4890e0d0169fadd2b19bab8.tar.xz |
systemc: Implement channel updates and rework the scheduler.
This change implements channel updates, and also reworks the scheduler
to delegate more to the gem5 event queue by taking advantage of
event priorities to ensure things happen in the right order. There's
a lengthy comment in scheduler.hh describes how that all works.
Change-Id: I5dee71b86b2e612bb720a4429f3a72e4b7c6d01f
Reviewed-on: https://gem5-review.googlesource.com/11710
Reviewed-by: Gabe Black <gabeblack@google.com>
Maintainer: Gabe Black <gabeblack@google.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/systemc/core/SConscript | 1 | ||||
-rw-r--r-- | src/systemc/core/channel.cc | 50 | ||||
-rw-r--r-- | src/systemc/core/channel.hh | 58 | ||||
-rw-r--r-- | src/systemc/core/kernel.cc | 16 | ||||
-rw-r--r-- | src/systemc/core/sc_prim.cc | 124 | ||||
-rw-r--r-- | src/systemc/core/scheduler.cc | 77 | ||||
-rw-r--r-- | src/systemc/core/scheduler.hh | 108 | ||||
-rw-r--r-- | src/systemc/ext/core/sc_prim.hh | 14 |
8 files changed, 339 insertions, 109 deletions
diff --git a/src/systemc/core/SConscript b/src/systemc/core/SConscript index 045300478..8fd5f7af9 100644 --- a/src/systemc/core/SConscript +++ b/src/systemc/core/SConscript @@ -30,6 +30,7 @@ Import('*') if env['USE_SYSTEMC']: SimObject('SystemC.py') + Source('channel.cc') Source('kernel.cc') Source('module.cc') Source('object.cc') diff --git a/src/systemc/core/channel.cc b/src/systemc/core/channel.cc new file mode 100644 index 000000000..4a862b8aa --- /dev/null +++ b/src/systemc/core/channel.cc @@ -0,0 +1,50 @@ +/* + * Copyright 2018 Google, Inc. + * + * 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: Gabe Black + */ + +#include "systemc/core/channel.hh" + +#include "systemc/core/scheduler.hh" + +namespace sc_gem5 +{ + +void +Channel::requestUpdate() +{ + scheduler.requestUpdate(this); +} + +void +Channel::asyncRequestUpdate() +{ + //TODO This should probably not request an update directly. + scheduler.requestUpdate(this); +} + +} // namespace sc_gem5 diff --git a/src/systemc/core/channel.hh b/src/systemc/core/channel.hh new file mode 100644 index 000000000..7ce437572 --- /dev/null +++ b/src/systemc/core/channel.hh @@ -0,0 +1,58 @@ +/* + * Copyright 2018 Google, Inc. + * + * 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: Gabe Black + */ + +#ifndef __SYSTEMC_CORE_CHANNEL_HH__ +#define __SYSTEMC_CORE_CHANNEL_HH__ + +#include "systemc/core/list.hh" +#include "systemc/ext/core/sc_prim.hh" + +namespace sc_gem5 +{ + +class Channel : public ListNode +{ + public: + Channel(sc_core::sc_prim_channel *_sc_chan) : _sc_chan(_sc_chan) {} + + virtual ~Channel() {} + + void requestUpdate(); + void asyncRequestUpdate(); + void update() { _sc_chan->update(); } + + sc_core::sc_prim_channel *sc_chan() { return _sc_chan; } + + private: + sc_core::sc_prim_channel *_sc_chan; +}; + +} // namespace sc_gem5 + +#endif //__SYSTEMC_CORE_CHANNEL_HH__ diff --git a/src/systemc/core/kernel.cc b/src/systemc/core/kernel.cc index 82281c085..e93236541 100644 --- a/src/systemc/core/kernel.cc +++ b/src/systemc/core/kernel.cc @@ -33,18 +33,30 @@ namespace SystemC { -Kernel::Kernel(Params *params) : SimObject(params), t0Event(this) {} +Kernel::Kernel(Params *params) : + SimObject(params), t0Event(this, false, EventBase::Default_Pri - 1) {} void Kernel::startup() { schedule(t0Event, curTick()); + // Install ourselves as the scheduler's event manager. + ::sc_gem5::scheduler.setEventQueue(eventQueue()); + // Run update once before the event queue starts. + ::sc_gem5::scheduler.update(); } void Kernel::t0Handler() { - ::sc_gem5::scheduler.initialize(); + // Now that the event queue has started, mark all the processes that + // need to be initialized as ready to run. + // + // This event has greater priority than delta notifications and so will + // happen before them, honoring the ordering for the initialization phase + // in the spec. The delta phase will happen at normal priority, and then + // the event which runs the processes which is at a lower priority. + ::sc_gem5::scheduler.initToReady(); } } // namespace SystemC diff --git a/src/systemc/core/sc_prim.cc b/src/systemc/core/sc_prim.cc index 4b5cf1780..91befa836 100644 --- a/src/systemc/core/sc_prim.cc +++ b/src/systemc/core/sc_prim.cc @@ -28,110 +28,106 @@ */ #include "base/logging.hh" +#include "systemc/core/channel.hh" #include "systemc/ext/core/sc_prim.hh" namespace sc_core { -const char * -sc_prim_channel::kind() const -{ - warn("%s not implemented.\n", __PRETTY_FUNCTION__); - return ""; -} +sc_prim_channel::sc_prim_channel() : + _gem5_channel(new sc_gem5::Channel(this)) +{} -sc_prim_channel::sc_prim_channel() -{ - warn("%s not implemented.\n", __PRETTY_FUNCTION__); -} +sc_prim_channel::sc_prim_channel(const char *_name) : + sc_object(_name), _gem5_channel(new sc_gem5::Channel(this)) +{} -sc_prim_channel::sc_prim_channel(const char *) -{ - warn("%s not implemented.\n", __PRETTY_FUNCTION__); -} +sc_prim_channel::~sc_prim_channel() { delete _gem5_channel; } void sc_prim_channel::request_update() { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + _gem5_channel->requestUpdate(); } void sc_prim_channel::async_request_update() { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + _gem5_channel->asyncRequestUpdate(); } void sc_prim_channel::next_trigger() { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::next_trigger(); } void -sc_prim_channel::next_trigger(const sc_event &) +sc_prim_channel::next_trigger(const sc_event &e) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::next_trigger(e); } void -sc_prim_channel::next_trigger(const sc_event_or_list &) +sc_prim_channel::next_trigger(const sc_event_or_list &eol) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::next_trigger(eol); } void -sc_prim_channel::next_trigger(const sc_event_and_list &) +sc_prim_channel::next_trigger(const sc_event_and_list &eal) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::next_trigger(eal); } void -sc_prim_channel::next_trigger(const sc_time &) +sc_prim_channel::next_trigger(const sc_time &t) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::next_trigger(t); } void -sc_prim_channel::next_trigger(double, sc_time_unit) +sc_prim_channel::next_trigger(double d, sc_time_unit u) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::next_trigger(d, u); } void -sc_prim_channel::next_trigger(const sc_time &, const sc_event &) +sc_prim_channel::next_trigger(const sc_time &t, const sc_event &e) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::next_trigger(t, e); } void -sc_prim_channel::next_trigger(double, sc_time_unit, const sc_event &) +sc_prim_channel::next_trigger(double d, sc_time_unit u, const sc_event &e) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::next_trigger(d, u, e); } void -sc_prim_channel::next_trigger(const sc_time &, const sc_event_or_list &) +sc_prim_channel::next_trigger(const sc_time &t, const sc_event_or_list &eol) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::next_trigger(t, eol); } void -sc_prim_channel::next_trigger(double, sc_time_unit, const sc_event_or_list &) +sc_prim_channel::next_trigger( + double d, sc_time_unit u, const sc_event_or_list &eol) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::next_trigger(d, u, eol); } void -sc_prim_channel::next_trigger(const sc_time &, const sc_event_and_list &) +sc_prim_channel::next_trigger(const sc_time &t, const sc_event_and_list &eal) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::next_trigger(t, eal); } void -sc_prim_channel::next_trigger(double, sc_time_unit, const sc_event_and_list &) +sc_prim_channel::next_trigger( + double d, sc_time_unit u, const sc_event_and_list &eal) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::next_trigger(d, u, eal); } bool @@ -144,79 +140,79 @@ sc_prim_channel::timed_out() void sc_prim_channel::wait() { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::wait(); } void -sc_prim_channel::wait(int) +sc_prim_channel::wait(int i) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::wait(i); } void -sc_prim_channel::wait(const sc_event &) +sc_prim_channel::wait(const sc_event &e) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::wait(e); } void -sc_prim_channel::wait(const sc_event_or_list &) +sc_prim_channel::wait(const sc_event_or_list &eol) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::wait(eol); } void -sc_prim_channel::wait(const sc_event_and_list &) +sc_prim_channel::wait(const sc_event_and_list &eal) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::wait(eal); } void -sc_prim_channel::wait(const sc_time &) +sc_prim_channel::wait(const sc_time &t) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::wait(t); } void -sc_prim_channel::wait(double, sc_time_unit) +sc_prim_channel::wait(double d, sc_time_unit u) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::wait(d, u); } void -sc_prim_channel::wait(const sc_time &, const sc_event &) +sc_prim_channel::wait(const sc_time &t, const sc_event &e) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::wait(t, e); } void -sc_prim_channel::wait(double, sc_time_unit, const sc_event &) +sc_prim_channel::wait(double d, sc_time_unit u, const sc_event &e) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::wait(d, u, e); } void -sc_prim_channel::wait(const sc_time &, const sc_event_or_list &) +sc_prim_channel::wait(const sc_time &t, const sc_event_or_list &eol) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::wait(t, eol); } void -sc_prim_channel::wait(double, sc_time_unit, const sc_event_or_list &) +sc_prim_channel::wait(double d, sc_time_unit u, const sc_event_or_list &eol) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::wait(d, u, eol); } void -sc_prim_channel::wait(const sc_time &, const sc_event_and_list &) +sc_prim_channel::wait(const sc_time &t, const sc_event_and_list &eal) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::wait(t, eal); } void -sc_prim_channel::wait(double, sc_time_unit, const sc_event_and_list &) +sc_prim_channel::wait(double d, sc_time_unit u, const sc_event_and_list &eal) { - warn("%s not implemented.\n", __PRETTY_FUNCTION__); + ::sc_core::wait(d, u, eal); } } // namespace sc_core diff --git a/src/systemc/core/scheduler.cc b/src/systemc/core/scheduler.cc index 41c64f876..17e7dc43e 100644 --- a/src/systemc/core/scheduler.cc +++ b/src/systemc/core/scheduler.cc @@ -30,31 +30,22 @@ #include "systemc/core/scheduler.hh" #include "base/fiber.hh" +#include "base/logging.hh" +#include "sim/eventq.hh" namespace sc_gem5 { -Scheduler::Scheduler() : _numCycles(0), _current(nullptr) {} +Scheduler::Scheduler() : + eq(nullptr), readyEvent(this, false, EventBase::Default_Pri + 1), + _numCycles(0), _current(nullptr) +{} void -Scheduler::initialize() +Scheduler::initToReady() { - update(); - while (!initList.empty()) ready(initList.getNext()); - - delta(); -} - -void -Scheduler::runCycles() -{ - while (!readyList.empty()) { - evaluate(); - update(); - delta(); - } } void @@ -77,24 +68,62 @@ Scheduler::yield() } void -Scheduler::evaluate() +Scheduler::ready(Process *p) { - if (!readyList.empty()) - _numCycles++; + // Clump methods together to minimize context switching. + if (p->procKind() == ::sc_core::SC_METHOD_PROC_) + readyList.pushFirst(p); + else + readyList.pushLast(p); - do { - yield(); - } while (!readyList.empty()); + scheduleReadyEvent(); } void -Scheduler::update() +Scheduler::requestUpdate(Channel *c) { + updateList.pushLast(c); + scheduleReadyEvent(); } void -Scheduler::delta() +Scheduler::scheduleReadyEvent() { + // Schedule the evaluate and update phases. + if (!readyEvent.scheduled()) { + panic_if(!eq, "Need to schedule ready, but no event manager.\n"); + eq->schedule(&readyEvent, eq->getCurTick()); + } +} + +void +Scheduler::runReady() +{ + bool empty = readyList.empty(); + + // The evaluation phase. + do { + yield(); + } while (!readyList.empty()); + + if (!empty) + _numCycles++; + + // The update phase. + update(); + + // The delta phase will happen naturally through the event queue. +} + +void +Scheduler::update() +{ + Channel *channel = updateList.getNext(); + while (channel) { + channel->popListNode(); + channel->update(); + channel = updateList.getNext(); + } } Scheduler scheduler; diff --git a/src/systemc/core/scheduler.hh b/src/systemc/core/scheduler.hh index a7216231a..e1ad21a57 100644 --- a/src/systemc/core/scheduler.hh +++ b/src/systemc/core/scheduler.hh @@ -30,6 +30,8 @@ #ifndef __SYSTEMC_CORE_SCHEDULER_HH__ #define __SYSTEMC_CORE_SCHEDULER_HH__ +#include "sim/eventq.hh" +#include "systemc/core/channel.hh" #include "systemc/core/list.hh" #include "systemc/core/process.hh" @@ -37,20 +39,84 @@ namespace sc_gem5 { typedef NodeList<Process> ProcessList; +typedef NodeList<Channel> ChannelList; + +/* + * The scheduler supports three different mechanisms, the initialization phase, + * delta cycles, and timed notifications. + * + * INITIALIZATION PHASE + * + * The initialization phase has three parts: + * 1. Run requested channel updates. + * 2. Make processes which need to initialize runnable (methods and threads + * which didn't have dont_initialize called on them). + * 3. Process delta notifications. + * + * First, the Kernel SimObject calls the update() method during its startup() + * callback which handles the requested channel updates. The Kernel also + * schedules an event to be run at time 0 with a slightly elevated priority + * so that it happens before any "normal" event. + * + * When that t0 event happens, it calls the schedulers initToReady method + * which performs step 2 above. That indirectly causes the scheduler's + * readyEvent to be scheduled with slightly lowered priority, ensuring it + * happens after any "normal" event. + * + * Because delta notifications are scheduled at the standard priority, all + * of those events will happen next, performing step 3 above. Once they finish, + * if the readyEvent was scheduled above, there shouldn't be any higher + * priority events in front of it. When it runs, it will start the first + * evaluate phase of the first delta cycle. + * + * DELTA CYCLE + * + * A delta cycle has three phases within it. + * 1. The evaluate phase where runnable processes are allowed to run. + * 2. The update phase where requested channel updates hapen. + * 3. The delta notification phase where delta notifications happen. + * + * The readyEvent runs the first two steps of the delta cycle. It first goes + * through the list of runnable processes and executes them until the set is + * empty, and then immediately runs the update phase. Since these are all part + * of the same event, there's no chance for other events to intervene and + * break the required order above. + * + * During the update phase above, the spec forbids any action which would make + * a process runnable. That means that once the update phase finishes, the set + * of runnable processes will be empty. There may, however, have been some + * delta notifications/timeouts which will have been scheduled during either + * the evaluate or update phase above. Because those are scheduled at the + * normal priority, they will now happen together until there aren't any + * delta events left. + * + * If any processes became runnable during the delta notification phase, the + * readyEvent will have been scheduled and will have been waiting patiently + * behind the delta notification events. That will now run, effectively + * starting the next delta cycle. + * + * TIMED NOTIFICATION PHASE + * + * If no processes became runnable, the event queue will continue to process + * events until it comes across a timed notification, aka a notification + * scheduled to happen in the future. Like delta notification events, those + * will all happen together since the readyEvent priority is lower, + * potentially marking new processes as ready. Once these events finish, the + * readyEvent may run, starting the next delta cycle. + */ class Scheduler { public: Scheduler(); + const std::string name() const { return "systemc_scheduler"; } + uint64_t numCycles() { return _numCycles; } Process *current() { return _current; } - // Run the initialization phase. - void initialize(); - - // Run delta cycles until time needs to advance. - void runCycles(); + // Mark processes that need to be initialized as ready. + void initToReady(); // Put a process on the list of processes to be initialized. void init(Process *p) { initList.pushLast(p); } @@ -59,15 +125,10 @@ class Scheduler void yield(); // Put a process on the ready list. - void - ready(Process *p) - { - // Clump methods together to minimize context switching. - if (p->procKind() == ::sc_core::SC_METHOD_PROC_) - readyList.pushFirst(p); - else - readyList.pushLast(p); - } + void ready(Process *p); + + // Schedule an update for a given channel. + void requestUpdate(Channel *c); // Run the given process immediately, preempting whatever may be running. void @@ -81,7 +142,22 @@ class Scheduler yield(); } + // Set an event queue for scheduling events. + void setEventQueue(EventQueue *_eq) { eq = _eq; } + + // Retrieve the event queue. + EventQueue &eventQueue() const { return *eq; } + + // Run scheduled channel updates. + void update(); + private: + EventQueue *eq; + + void runReady(); + EventWrapper<Scheduler, &Scheduler::runReady> readyEvent; + void scheduleReadyEvent(); + uint64_t _numCycles; Process *_current; @@ -89,9 +165,7 @@ class Scheduler ProcessList initList; ProcessList readyList; - void evaluate(); - void update(); - void delta(); + ChannelList updateList; }; extern Scheduler scheduler; diff --git a/src/systemc/ext/core/sc_prim.hh b/src/systemc/ext/core/sc_prim.hh index 2348f453d..106489280 100644 --- a/src/systemc/ext/core/sc_prim.hh +++ b/src/systemc/ext/core/sc_prim.hh @@ -33,6 +33,13 @@ #include "sc_object.hh" #include "sc_time.hh" +namespace sc_gem5 +{ + +class Channel; + +} // namespace sc_gem5 + namespace sc_core { @@ -43,12 +50,12 @@ class sc_event_or_list; class sc_prim_channel : public sc_object { public: - virtual const char *kind() const; + virtual const char *kind() const { return "sc_prim_channel"; } protected: sc_prim_channel(); explicit sc_prim_channel(const char *); - virtual ~sc_prim_channel() {} + virtual ~sc_prim_channel(); void request_update(); void async_request_update(); @@ -93,6 +100,9 @@ class sc_prim_channel : public sc_object // Disabled sc_prim_channel(const sc_prim_channel &); sc_prim_channel &operator = (const sc_prim_channel &); + + friend class sc_gem5::Channel; + sc_gem5::Channel *_gem5_channel; }; } // namespace sc_core |