summaryrefslogtreecommitdiff
path: root/src/mem/ruby/tester
diff options
context:
space:
mode:
authorNathan Binkert <nate@binkert.org>2009-05-11 10:38:43 -0700
committerNathan Binkert <nate@binkert.org>2009-05-11 10:38:43 -0700
commit2f30950143cc70bc42a3c8a4111d7cf8198ec881 (patch)
tree708f6c22edb3c6feb31dd82866c26623a5329580 /src/mem/ruby/tester
parentc70241810d4e4f523f173c1646b008dc40faad8e (diff)
downloadgem5-2f30950143cc70bc42a3c8a4111d7cf8198ec881.tar.xz
ruby: Import ruby and slicc from GEMS
We eventually plan to replace the m5 cache hierarchy with the GEMS hierarchy, but for now we will make both live alongside eachother.
Diffstat (limited to 'src/mem/ruby/tester')
-rw-r--r--src/mem/ruby/tester/BarrierGenerator.cc333
-rw-r--r--src/mem/ruby/tester/BarrierGenerator.hh138
-rw-r--r--src/mem/ruby/tester/Check.cc251
-rw-r--r--src/mem/ruby/tester/Check.hh107
-rw-r--r--src/mem/ruby/tester/CheckTable.cc128
-rw-r--r--src/mem/ruby/tester/CheckTable.hh93
-rw-r--r--src/mem/ruby/tester/DetermGETXGenerator.cc151
-rw-r--r--src/mem/ruby/tester/DetermGETXGenerator.hh104
-rw-r--r--src/mem/ruby/tester/DetermInvGenerator.cc202
-rw-r--r--src/mem/ruby/tester/DetermInvGenerator.hh109
-rw-r--r--src/mem/ruby/tester/DetermSeriesGETSGenerator.cc149
-rw-r--r--src/mem/ruby/tester/DetermSeriesGETSGenerator.hh106
-rw-r--r--src/mem/ruby/tester/DeterministicDriver.cc282
-rw-r--r--src/mem/ruby/tester/DeterministicDriver.hh125
-rw-r--r--src/mem/ruby/tester/Instruction.cc51
-rw-r--r--src/mem/ruby/tester/Instruction.hh57
-rw-r--r--src/mem/ruby/tester/RaceyDriver.cc139
-rw-r--r--src/mem/ruby/tester/RaceyDriver.hh112
-rw-r--r--src/mem/ruby/tester/RequestGenerator.cc196
-rw-r--r--src/mem/ruby/tester/RequestGenerator.hh102
-rw-r--r--src/mem/ruby/tester/SpecifiedGenerator.cc48
-rw-r--r--src/mem/ruby/tester/SpecifiedGenerator.hh69
-rw-r--r--src/mem/ruby/tester/SyntheticDriver.cc296
-rw-r--r--src/mem/ruby/tester/SyntheticDriver.hh118
-rw-r--r--src/mem/ruby/tester/Tester.cc116
-rw-r--r--src/mem/ruby/tester/Tester.hh93
-rw-r--r--src/mem/ruby/tester/XactAbortRequestGenerator.cc403
-rw-r--r--src/mem/ruby/tester/XactAbortRequestGenerator.hh122
-rw-r--r--src/mem/ruby/tester/XactRequestGenerator.cc637
-rw-r--r--src/mem/ruby/tester/XactRequestGenerator.hh134
-rw-r--r--src/mem/ruby/tester/main.cc51
-rw-r--r--src/mem/ruby/tester/main.hh42
-rw-r--r--src/mem/ruby/tester/test_framework.cc431
-rw-r--r--src/mem/ruby/tester/test_framework.hh46
34 files changed, 5541 insertions, 0 deletions
diff --git a/src/mem/ruby/tester/BarrierGenerator.cc b/src/mem/ruby/tester/BarrierGenerator.cc
new file mode 100644
index 000000000..79b9c6d2b
--- /dev/null
+++ b/src/mem/ruby/tester/BarrierGenerator.cc
@@ -0,0 +1,333 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id: BarrierGenerator.C 1.3 2005/01/19 13:12:35-06:00 mikem@maya.cs.wisc.edu $
+ *
+ */
+
+#include "BarrierGenerator.hh"
+#include "Sequencer.hh"
+#include "System.hh"
+#include "RubyConfig.hh"
+#include "SubBlock.hh"
+#include "SyntheticDriver.hh"
+#include "Chip.hh"
+
+BarrierGenerator::BarrierGenerator(NodeID node, SyntheticDriver& driver) :
+ m_driver(driver)
+{
+ m_status = BarrierGeneratorStatus_Thinking;
+ m_last_transition = 0;
+ m_node = node;
+ m_counter = 0;
+ proc_counter = 0;
+ m_local_sense = false;
+
+ m_total_think = 0;
+ m_think_periods = 0;
+
+ g_eventQueue_ptr->scheduleEvent(this, 1+(random() % 200));
+}
+
+BarrierGenerator::~BarrierGenerator()
+{
+}
+
+void BarrierGenerator::wakeup()
+{
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_node);
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_status);
+
+ if (m_status == BarrierGeneratorStatus_Thinking) {
+ m_barrier_done = false;
+ m_local_sense = !m_local_sense;
+ m_status = BarrierGeneratorStatus_Test_Pending;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateTest(); // Test
+ } else if (m_status == BarrierGeneratorStatus_Test_Waiting) {
+ m_status = BarrierGeneratorStatus_Test_Pending;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateTest(); // Test
+ } else if (m_status == BarrierGeneratorStatus_Release_Waiting) {
+ m_status = BarrierGeneratorStatus_Release_Pending;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateRelease(); // Test
+ } else if (m_status == BarrierGeneratorStatus_StoreBarrierCounter_Waiting) {
+ m_status = BarrierGeneratorStatus_StoreBarrierCounter_Pending;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateStoreCtr();
+ } else if (m_status == BarrierGeneratorStatus_StoreFlag_Waiting) {
+ m_status = BarrierGeneratorStatus_StoreFlag_Pending;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateStoreFlag();
+ } else if (m_status == BarrierGeneratorStatus_Holding) {
+ m_status = BarrierGeneratorStatus_Release_Pending;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateRelease(); // Release
+ } else if (m_status == BarrierGeneratorStatus_Before_Swap) {
+ m_status = BarrierGeneratorStatus_Swap_Pending;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateSwap();
+ } else if (m_status == BarrierGeneratorStatus_SpinFlag_Ready) {
+ m_status = BarrierGeneratorStatus_SpinFlag_Pending;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateLoadFlag();
+ } else {
+ WARN_EXPR(m_status);
+ ERROR_MSG("Invalid status");
+ }
+}
+
+void BarrierGenerator::performCallback(NodeID proc, SubBlock& data)
+{
+ Address address = data.getAddress();
+ assert(proc == m_node);
+
+ DEBUG_EXPR(TESTER_COMP, LowPrio, proc);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, m_status);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, address);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, data);
+
+ if (m_status == BarrierGeneratorStatus_Test_Pending) {
+ uint8 dat = data.readByte();
+ uint8 lock = dat >> 7;
+ if (lock == 1) {
+ // Locked - keep spinning
+ m_status = BarrierGeneratorStatus_Test_Waiting;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ } else {
+ // Unlocked - try the swap
+ m_driver.recordTestLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ m_status = BarrierGeneratorStatus_Before_Swap;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ }
+ } else if (m_status == BarrierGeneratorStatus_Swap_Pending) {
+ m_driver.recordSwapLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ uint8 dat = data.readByte();
+ uint8 lock = dat >> 7;
+ if (lock == 1) {
+ // We failed to aquire the lock
+ m_status = BarrierGeneratorStatus_Test_Waiting;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ } else {
+ // We acquired the lock
+ dat = dat | 0x80;
+ data.writeByte(dat);
+ m_status = BarrierGeneratorStatus_StoreBarrierCounter_Waiting;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ DEBUG_MSG(TESTER_COMP, HighPrio, "Acquired");
+ DEBUG_EXPR(TESTER_COMP, HighPrio, proc);
+ DEBUG_EXPR(TESTER_COMP, HighPrio, g_eventQueue_ptr->getTime());
+ // g_eventQueue_ptr->scheduleEvent(this, holdTime());
+
+ g_eventQueue_ptr->scheduleEvent(this, 1);
+
+ // initiateLoadCtr();
+ }
+ } else if (m_status == BarrierGeneratorStatus_StoreBarrierCounter_Pending) {
+
+ // if value == p, reset counter and set local sense flag
+ uint8 ctr = data.readByte();
+ //uint8 sense = ctr >> 4;
+ ctr = ctr & 0x0F;
+
+ ctr++;
+ data.writeByte( ctr | 0x80); // store counter and lock
+
+ //cout << m_node << " incremented Barrier_ctr to " << (int)ctr << ", " << data << "\n";
+
+ if (ctr == (uint8) 16) {
+
+ data.writeByte( 0x0 );
+ m_status = BarrierGeneratorStatus_StoreFlag_Waiting;
+ m_barrier_done = true;
+
+ g_eventQueue_ptr->scheduleEvent(this, 1);
+ }
+ else {
+
+ m_status = BarrierGeneratorStatus_Release_Waiting;
+ g_eventQueue_ptr->scheduleEvent(this, 1);
+ }
+ } else if (m_status == BarrierGeneratorStatus_StoreFlag_Pending) {
+
+ // write flag
+ if (m_local_sense) {
+ data.writeByte( 0x01 );
+ }
+ else {
+ data.writeByte( 0x00 );
+ }
+
+ m_status = BarrierGeneratorStatus_Release_Waiting;
+ g_eventQueue_ptr->scheduleEvent(this, 1);
+
+ } else if (m_status == BarrierGeneratorStatus_Release_Pending) {
+ m_driver.recordReleaseLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ // We're releasing the lock
+ uint8 dat = data.readByte();
+ dat = dat & 0x7F;
+ data.writeByte(dat);
+
+ if (m_barrier_done) {
+ m_counter++;
+ proc_counter++;
+ if (m_counter < g_tester_length) {
+ m_status = BarrierGeneratorStatus_Thinking;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+ } else {
+
+ m_driver.reportDone(proc_counter, m_node);
+ m_last_transition = g_eventQueue_ptr->getTime();
+ }
+ }
+ else {
+ m_status = BarrierGeneratorStatus_SpinFlag_Ready;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ }
+ } else if (m_status == BarrierGeneratorStatus_SpinFlag_Pending) {
+
+ uint8 sense = data.readByte();
+
+
+ if (sense != m_local_sense) {
+ m_status = BarrierGeneratorStatus_SpinFlag_Ready;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ }
+ else {
+ m_counter++;
+ proc_counter++;
+ if (m_counter < g_tester_length) {
+ m_status = BarrierGeneratorStatus_Thinking;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+ } else {
+ m_driver.reportDone(proc_counter, m_node);
+ m_status = BarrierGeneratorStatus_Done;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ }
+ }
+
+ } else {
+ WARN_EXPR(m_status);
+ ERROR_MSG("Invalid status");
+ }
+}
+
+int BarrierGenerator::thinkTime()
+{
+ int ret;
+ float ratio = g_think_fudge_factor;
+
+ // return 400;
+
+ if (ratio == 0) {
+ return g_think_time;
+ }
+
+ int r = random();
+ int x = (int) ( (float)g_think_time*ratio*2.0);
+ int mod = r % x;
+
+
+ int rand = ( mod+1 - ((float)g_think_time*ratio) );
+
+ ret = (g_think_time + rand);
+
+ m_total_think += ret;
+ m_think_periods++;
+
+ return ret;
+}
+
+int BarrierGenerator::waitTime() const
+{
+ return g_wait_time;
+}
+
+
+void BarrierGenerator::initiateTest()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Test");
+ sequencer()->makeRequest(CacheMsg(Address(0x40), CacheRequestType_LD, Address(1), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false));
+}
+
+void BarrierGenerator::initiateSwap()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Swap");
+ sequencer()->makeRequest(CacheMsg(Address(0x40), CacheRequestType_ATOMIC, Address(2), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false));
+}
+
+void BarrierGenerator::initiateRelease()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Release");
+ sequencer()->makeRequest(CacheMsg(Address(0x40), CacheRequestType_ST, Address(3), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false));
+}
+
+void BarrierGenerator::initiateLoadCtr()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating load of barrier counter");
+ sequencer()->makeRequest(CacheMsg(Address(0x40), CacheRequestType_LD, Address(3), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false));
+}
+
+void BarrierGenerator::initiateStoreCtr()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating load of barrier counter");
+ sequencer()->makeRequest(CacheMsg(Address(0x40), CacheRequestType_ST, Address(3), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false));
+}
+
+void BarrierGenerator::initiateStoreFlag()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating load of barrier counter");
+ sequencer()->makeRequest(CacheMsg(Address(0x00), CacheRequestType_ST, Address(3), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false));
+}
+
+void BarrierGenerator::initiateLoadFlag()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating load of barrier counter");
+ sequencer()->makeRequest(CacheMsg(Address(0x00), CacheRequestType_LD, Address(3), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false));
+}
+
+
+Sequencer* BarrierGenerator::sequencer() const
+{
+ return g_system_ptr->getChip(m_node/RubyConfig::numberOfProcsPerChip())->getSequencer(m_node%RubyConfig::numberOfProcsPerChip());
+}
+
+void BarrierGenerator::print(ostream& out) const
+{
+}
+
diff --git a/src/mem/ruby/tester/BarrierGenerator.hh b/src/mem/ruby/tester/BarrierGenerator.hh
new file mode 100644
index 000000000..1b16755a5
--- /dev/null
+++ b/src/mem/ruby/tester/BarrierGenerator.hh
@@ -0,0 +1,138 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+#ifndef BARRIERGENERATOR_H
+#define BARRIERGENERATOR_H
+
+#include "Global.hh"
+#include "Consumer.hh"
+#include "NodeID.hh"
+#include "Address.hh"
+
+class Sequencer;
+class SubBlock;
+class SyntheticDriver;
+
+
+enum BarrierGeneratorStatus {
+ BarrierGeneratorStatus_FIRST,
+ BarrierGeneratorStatus_Thinking = BarrierGeneratorStatus_FIRST,
+ BarrierGeneratorStatus_Test_Pending,
+ BarrierGeneratorStatus_Test_Waiting,
+ BarrierGeneratorStatus_Before_Swap,
+ BarrierGeneratorStatus_Swap_Pending,
+ BarrierGeneratorStatus_Holding,
+ BarrierGeneratorStatus_Release_Pending,
+ BarrierGeneratorStatus_Release_Waiting,
+ BarrierGeneratorStatus_StoreFlag_Waiting,
+ BarrierGeneratorStatus_StoreFlag_Pending,
+ BarrierGeneratorStatus_Done,
+ BarrierGeneratorStatus_SpinFlag_Ready,
+ BarrierGeneratorStatus_SpinFlag_Pending,
+ BarrierGeneratorStatus_LoadBarrierCounter_Pending,
+ BarrierGeneratorStatus_StoreBarrierCounter_Pending,
+ BarrierGeneratorStatus_StoreBarrierCounter_Waiting,
+ BarrierGeneratorStatus_NUM
+};
+
+
+// UNCOMMENT THIS FOR A SINGLE WORK QUEUE
+// static int m_counter;
+
+class BarrierGenerator : public Consumer {
+public:
+ // Constructors
+ BarrierGenerator(NodeID node, SyntheticDriver& driver);
+
+ // Destructor
+ ~BarrierGenerator();
+
+ // Public Methods
+ void wakeup();
+ void performCallback(NodeID proc, SubBlock& data);
+
+ void print(ostream& out) const;
+private:
+ // Private Methods
+ int thinkTime() ;
+ int waitTime() const;
+ void initiateTest();
+ void initiateSwap();
+ void initiateRelease();
+ void initiateLoadCtr();
+ void initiateStoreCtr();
+ void initiateLoadFlag();
+ void initiateStoreFlag();
+ Sequencer* sequencer() const;
+
+ // Private copy constructor and assignment operator
+ BarrierGenerator(const BarrierGenerator& obj);
+ BarrierGenerator& operator=(const BarrierGenerator& obj);
+
+ // Data Members (m_ prefix)
+ SyntheticDriver& m_driver;
+ NodeID m_node;
+ BarrierGeneratorStatus m_status;
+ int proc_counter;
+
+ int m_counter;
+
+ bool m_local_sense;
+ bool m_barrier_done;
+
+ Time m_last_transition;
+ Address m_address;
+
+ int m_total_think;
+ int m_think_periods;
+};
+
+// Output operator declaration
+ostream& operator<<(ostream& out, const BarrierGenerator& obj);
+
+// ******************* Definitions *******************
+
+// Output operator definition
+extern inline
+ostream& operator<<(ostream& out, const BarrierGenerator& obj)
+{
+ obj.print(out);
+ out << flush;
+ return out;
+}
+
+#endif //REQUESTGENERATOR_H
+
diff --git a/src/mem/ruby/tester/Check.cc b/src/mem/ruby/tester/Check.cc
new file mode 100644
index 000000000..3e2649709
--- /dev/null
+++ b/src/mem/ruby/tester/Check.cc
@@ -0,0 +1,251 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ */
+
+#include "Check.hh"
+#include "Sequencer.hh"
+#include "System.hh"
+#include "SubBlock.hh"
+#include "Chip.hh"
+
+Check::Check(const Address& address, const Address& pc)
+{
+ m_status = TesterStatus_Idle;
+
+ pickValue();
+ pickInitiatingNode();
+ changeAddress(address);
+ m_pc = pc;
+ m_access_mode = AccessModeType(random() % AccessModeType_NUM);
+ m_store_count = 0;
+}
+
+void Check::initiate()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating");
+ DEBUG_EXPR(TESTER_COMP, MedPrio, *this);
+
+ // current CMP protocol doesn't support prefetches
+ if (!Protocol::m_CMP && (random() & 0xf) == 0) { // 1 in 16 chance
+ initiatePrefetch(); // Prefetch from random processor
+ }
+
+ if(m_status == TesterStatus_Idle) {
+ initiateAction();
+ } else if(m_status == TesterStatus_Ready) {
+ initiateCheck();
+ } else {
+ // Pending - do nothing
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating action/check - failed: action/check is pending\n");
+ }
+}
+
+void Check::initiatePrefetch(Sequencer* targetSequencer_ptr)
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating prefetch");
+
+ CacheRequestType type;
+ if ((random() & 0x7) != 0) { // 1 in 8 chance
+ if ((random() & 0x1) == 0) { // 50% chance
+ type = CacheRequestType_LD;
+ } else {
+ type = CacheRequestType_IFETCH;
+ }
+ } else {
+ type = CacheRequestType_ST;
+ }
+ assert(targetSequencer_ptr != NULL);
+ CacheMsg request(m_address, m_address, type, m_pc, m_access_mode, 0, PrefetchBit_Yes, 0, Address(0), 0 /* only 1 SMT thread */, 0, false);
+ if (targetSequencer_ptr->isReady(request)) {
+ targetSequencer_ptr->makeRequest(request);
+ }
+}
+
+void Check::initiatePrefetch()
+{
+ // Any sequencer can issue a prefetch for this address
+ Sequencer* targetSequencer_ptr = g_system_ptr->getChip(random() % RubyConfig::numberOfChips())->getSequencer(random() % RubyConfig::numberOfProcsPerChip());
+ assert(targetSequencer_ptr != NULL);
+ initiatePrefetch(targetSequencer_ptr);
+}
+
+void Check::initiateAction()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Action");
+ assert(m_status == TesterStatus_Idle);
+
+ CacheRequestType type = CacheRequestType_ST;
+ if ((random() & 0x1) == 0) { // 50% chance
+ type = CacheRequestType_ATOMIC;
+ }
+
+ CacheMsg request(Address(m_address.getAddress()+m_store_count), Address(m_address.getAddress()+m_store_count), type, m_pc, m_access_mode, 1, PrefetchBit_No, 0, Address(0), 0 /* only 1 SMT thread */, 0, false);
+ Sequencer* sequencer_ptr = initiatingSequencer();
+ if (sequencer_ptr->isReady(request) == false) {
+ DEBUG_MSG(TESTER_COMP, MedPrio, "failed to initiate action - sequencer not ready\n");
+ } else {
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating action - successful\n");
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_status);
+ m_status = TesterStatus_Action_Pending;
+ sequencer_ptr->makeRequest(request);
+ }
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_status);
+}
+
+void Check::initiateCheck()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Check");
+ assert(m_status == TesterStatus_Ready);
+
+ CacheRequestType type = CacheRequestType_LD;
+ if ((random() & 0x1) == 0) { // 50% chance
+ type = CacheRequestType_IFETCH;
+ }
+
+ CacheMsg request(m_address, m_address, type, m_pc, m_access_mode, CHECK_SIZE, PrefetchBit_No, 0, Address(0), 0 /* only 1 SMT thread */, 0, false);
+ Sequencer* sequencer_ptr = initiatingSequencer();
+ if (sequencer_ptr->isReady(request) == false) {
+ DEBUG_MSG(TESTER_COMP, MedPrio, "failed to initiate check - sequencer not ready\n");
+ } else {
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating check - successful\n");
+ DEBUG_MSG(TESTER_COMP, MedPrio, m_status);
+ m_status = TesterStatus_Check_Pending;
+ sequencer_ptr->makeRequest(request);
+ }
+ DEBUG_MSG(TESTER_COMP, MedPrio, m_status);
+}
+
+void Check::performCallback(NodeID proc, SubBlock& data)
+{
+ Address address = data.getAddress();
+ // assert(getAddress() == address); // This isn't exactly right since we now have multi-byte checks
+ assert(getAddress().getLineAddress() == address.getLineAddress());
+
+ DEBUG_MSG(TESTER_COMP, MedPrio, "Callback");
+ DEBUG_EXPR(TESTER_COMP, MedPrio, *this);
+
+ if (m_status == TesterStatus_Action_Pending) {
+ DEBUG_MSG(TESTER_COMP, MedPrio, "Action callback");
+ // Perform store
+ data.setByte(0, m_value+m_store_count); // We store one byte at a time
+ m_store_count++;
+
+ if (m_store_count == CHECK_SIZE) {
+ m_status = TesterStatus_Ready;
+ } else {
+ m_status = TesterStatus_Idle;
+ }
+ } else if (m_status == TesterStatus_Check_Pending) {
+ DEBUG_MSG(TESTER_COMP, MedPrio, "Check callback");
+ // Perform load/check
+ for(int byte_number=0; byte_number<CHECK_SIZE; byte_number++) {
+ if (uint8(m_value+byte_number) != data.getByte(byte_number) && (DATA_BLOCK == true)) {
+ WARN_EXPR(proc);
+ WARN_EXPR(address);
+ WARN_EXPR(data);
+ WARN_EXPR(byte_number);
+ WARN_EXPR((int)m_value+byte_number);
+ WARN_EXPR((int)data.getByte(byte_number));
+ WARN_EXPR(*this);
+ WARN_EXPR(g_eventQueue_ptr->getTime());
+ ERROR_MSG("Action/check failure");
+ }
+ }
+ DEBUG_MSG(TESTER_COMP, HighPrio, "Action/check success:");
+ DEBUG_EXPR(TESTER_COMP, HighPrio, *this);
+ DEBUG_EXPR(TESTER_COMP, MedPrio, data);
+
+ m_status = TesterStatus_Idle;
+ pickValue();
+
+ } else {
+ WARN_EXPR(*this);
+ WARN_EXPR(proc);
+ WARN_EXPR(data);
+ WARN_EXPR(m_status);
+ WARN_EXPR(g_eventQueue_ptr->getTime());
+ ERROR_MSG("Unexpected TesterStatus");
+ }
+
+ DEBUG_EXPR(TESTER_COMP, MedPrio, proc);
+ DEBUG_EXPR(TESTER_COMP, MedPrio, data);
+ DEBUG_EXPR(TESTER_COMP, MedPrio, getAddress().getLineAddress());
+ DEBUG_MSG(TESTER_COMP, MedPrio, "Callback done");
+ DEBUG_EXPR(TESTER_COMP, MedPrio, *this);
+}
+
+void Check::changeAddress(const Address& address)
+{
+ assert((m_status == TesterStatus_Idle) || (m_status == TesterStatus_Ready));
+ m_status = TesterStatus_Idle;
+ m_address = address;
+ m_store_count = 0;
+}
+
+Sequencer* Check::initiatingSequencer() const
+{
+ return g_system_ptr->getChip(m_initiatingNode/RubyConfig::numberOfProcsPerChip())->getSequencer(m_initiatingNode%RubyConfig::numberOfProcsPerChip());
+}
+
+void Check::pickValue()
+{
+ assert(m_status == TesterStatus_Idle);
+ m_status = TesterStatus_Idle;
+ // DEBUG_MSG(TESTER_COMP, MedPrio, m_status);
+ DEBUG_MSG(TESTER_COMP, MedPrio, *this);
+ m_value = random() & 0xff; // One byte
+ // DEBUG_MSG(TESTER_COMP, MedPrio, m_value);
+ DEBUG_MSG(TESTER_COMP, MedPrio, *this);
+ m_store_count = 0;
+}
+
+void Check::pickInitiatingNode()
+{
+ assert((m_status == TesterStatus_Idle) || (m_status == TesterStatus_Ready));
+ m_status = TesterStatus_Idle;
+ DEBUG_MSG(TESTER_COMP, MedPrio, m_status);
+ m_initiatingNode = (random() % RubyConfig::numberOfProcessors());
+ DEBUG_MSG(TESTER_COMP, MedPrio, m_initiatingNode);
+ m_store_count = 0;
+}
+
+void Check::print(ostream& out) const
+{
+ out << "["
+ << m_address << ", value: "
+ << (int) m_value << ", status: "
+ << m_status << ", initiating node: "
+ << m_initiatingNode << ", store_count: "
+ << m_store_count
+ << "]" << flush;
+}
diff --git a/src/mem/ruby/tester/Check.hh b/src/mem/ruby/tester/Check.hh
new file mode 100644
index 000000000..31959262d
--- /dev/null
+++ b/src/mem/ruby/tester/Check.hh
@@ -0,0 +1,107 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+#ifndef CHECK_H
+#define CHECK_H
+
+#include "Global.hh"
+#include "Address.hh"
+#include "NodeID.hh"
+#include "TesterStatus.hh"
+#include "AccessModeType.hh"
+class Sequencer;
+class SubBlock;
+
+const int CHECK_SIZE_BITS = 2;
+const int CHECK_SIZE = (1<<CHECK_SIZE_BITS);
+
+class Check {
+public:
+ // Constructors
+ Check(const Address& address, const Address& pc);
+
+ // Default Destructor
+ //~Check();
+
+ // Public Methods
+
+ void initiate(); // Does Action or Check or nether
+ void performCallback(NodeID proc, SubBlock& data);
+ const Address& getAddress() { return m_address; }
+ void changeAddress(const Address& address);
+
+ void print(ostream& out) const;
+private:
+ // Private Methods
+ void initiatePrefetch(Sequencer* targetSequencer_ptr);
+ void initiatePrefetch();
+ void initiateAction();
+ void initiateCheck();
+
+ Sequencer* initiatingSequencer() const;
+
+ void pickValue();
+ void pickInitiatingNode();
+
+ // Using default copy constructor and assignment operator
+ // Check(const Check& obj);
+ // Check& operator=(const Check& obj);
+
+ // Data Members (m_ prefix)
+ TesterStatus m_status;
+ uint8 m_value;
+ int m_store_count;
+ NodeID m_initiatingNode;
+ Address m_address;
+ Address m_pc;
+ AccessModeType m_access_mode;
+};
+
+// Output operator declaration
+ostream& operator<<(ostream& out, const Check& obj);
+
+// ******************* Definitions *******************
+
+// Output operator definition
+extern inline
+ostream& operator<<(ostream& out, const Check& obj)
+{
+ obj.print(out);
+ out << flush;
+ return out;
+}
+
+#endif //CHECK_H
diff --git a/src/mem/ruby/tester/CheckTable.cc b/src/mem/ruby/tester/CheckTable.cc
new file mode 100644
index 000000000..488b58144
--- /dev/null
+++ b/src/mem/ruby/tester/CheckTable.cc
@@ -0,0 +1,128 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ */
+
+#include "CheckTable.hh"
+#include "Check.hh"
+#include "Map.hh"
+
+CheckTable::CheckTable()
+{
+ m_lookup_map_ptr = new Map<Address, Check*>;
+ physical_address_t physical = 0;
+ Address address;
+
+ const int size1 = 32;
+ const int size2 = 100;
+
+ // The first set is to get some false sharing
+ physical = 1000;
+ for (int i=0; i<size1; i++) {
+ // Setup linear addresses
+ address.setAddress(physical);
+ addCheck(address);
+ physical += CHECK_SIZE;
+ }
+
+ // The next two sets are to get some limited false sharing and cache conflicts
+ physical = 1000;
+ for (int i=0; i<size2; i++) {
+ // Setup linear addresses
+ address.setAddress(physical);
+ addCheck(address);
+ physical += 256;
+ }
+
+ physical = 1000 + CHECK_SIZE;
+ for (int i=0; i<size2; i++) {
+ // Setup linear addresses
+ address.setAddress(physical);
+ addCheck(address);
+ physical += 256;
+ }
+}
+
+CheckTable::~CheckTable()
+{
+ int size = m_check_vector.size();
+ for (int i=0; i<size; i++) {
+ delete m_check_vector[i];
+ }
+ delete m_lookup_map_ptr;
+}
+
+void CheckTable::addCheck(const Address& address)
+{
+ if (log_int(CHECK_SIZE) != 0) {
+ if (address.bitSelect(0,CHECK_SIZE_BITS-1) != 0) {
+ ERROR_MSG("Check not aligned");
+ }
+ }
+
+ for (int i=0; i<CHECK_SIZE; i++) {
+ if (m_lookup_map_ptr->exist(Address(address.getAddress()+i))) {
+ // A mapping for this byte already existed, discard the entire check
+ return;
+ }
+ }
+
+ Check* check_ptr = new Check(address, Address(100+m_check_vector.size()));
+ for (int i=0; i<CHECK_SIZE; i++) {
+ // Insert it once per byte
+ m_lookup_map_ptr->add(Address(address.getAddress()+i), check_ptr);
+ }
+ m_check_vector.insertAtBottom(check_ptr);
+}
+
+Check* CheckTable::getRandomCheck()
+{
+ return m_check_vector[random() % m_check_vector.size()];
+}
+
+Check* CheckTable::getCheck(const Address& address)
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "Looking for check by address");
+ DEBUG_EXPR(TESTER_COMP, MedPrio, address);
+
+ if (m_lookup_map_ptr->exist(address)) {
+ Check* check = m_lookup_map_ptr->lookup(address);
+ assert(check != NULL);
+ return check;
+ } else {
+ return NULL;
+ }
+}
+
+void CheckTable::print(ostream& out) const
+{
+}
diff --git a/src/mem/ruby/tester/CheckTable.hh b/src/mem/ruby/tester/CheckTable.hh
new file mode 100644
index 000000000..4a162f5bc
--- /dev/null
+++ b/src/mem/ruby/tester/CheckTable.hh
@@ -0,0 +1,93 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+#ifndef CHECKTABLE_H
+#define CHECKTABLE_H
+
+#include "Global.hh"
+#include "Vector.hh"
+
+class Address;
+class Check;
+template <class KEY_TYPE, class VALUE_TYPE> class Map;
+
+class CheckTable {
+public:
+ // Constructors
+ CheckTable();
+
+ // Destructor
+ ~CheckTable();
+
+ // Public Methods
+
+ Check* getRandomCheck();
+ Check* getCheck(const Address& address);
+
+ // bool isPresent(const Address& address) const;
+ // void removeCheckFromTable(const Address& address);
+ // bool isTableFull() const;
+ // Need a method to select a check or retrieve a check
+
+ void print(ostream& out) const;
+private:
+ // Private Methods
+ void addCheck(const Address& address);
+
+ // Private copy constructor and assignment operator
+ CheckTable(const CheckTable& obj);
+ CheckTable& operator=(const CheckTable& obj);
+
+ // Data Members (m_ prefix)
+ Vector<Check*> m_check_vector;
+ Map<Address, Check*>* m_lookup_map_ptr;
+};
+
+// Output operator declaration
+ostream& operator<<(ostream& out, const CheckTable& obj);
+
+// ******************* Definitions *******************
+
+// Output operator definition
+extern inline
+ostream& operator<<(ostream& out, const CheckTable& obj)
+{
+ obj.print(out);
+ out << flush;
+ return out;
+}
+
+#endif //CHECKTABLE_H
diff --git a/src/mem/ruby/tester/DetermGETXGenerator.cc b/src/mem/ruby/tester/DetermGETXGenerator.cc
new file mode 100644
index 000000000..1caebbdab
--- /dev/null
+++ b/src/mem/ruby/tester/DetermGETXGenerator.cc
@@ -0,0 +1,151 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ */
+
+// This Deterministic Generator generates GETX requests for all nodes in the system
+// The GETX requests are generated one at a time in round-robin fashion 0...1...2...etc.
+
+#include "DetermGETXGenerator.hh"
+#include "DetermGETXGeneratorStatus.hh"
+#include "LockStatus.hh"
+#include "Sequencer.hh"
+#include "System.hh"
+#include "RubyConfig.hh"
+#include "SubBlock.hh"
+#include "DeterministicDriver.hh"
+#include "Chip.hh"
+
+DetermGETXGenerator::DetermGETXGenerator(NodeID node, DeterministicDriver& driver) :
+ m_driver(driver)
+{
+ m_status = DetermGETXGeneratorStatus_Thinking;
+ m_last_transition = 0;
+ m_node = node;
+ m_address = Address(9999); // initialize to null value
+ m_counter = 0;
+
+ // don't know exactly when this node needs to request so just guess randomly
+ g_eventQueue_ptr->scheduleEvent(this, 1+(random() % 200));
+}
+
+DetermGETXGenerator::~DetermGETXGenerator()
+{
+}
+
+void DetermGETXGenerator::wakeup()
+{
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_node);
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_status);
+
+ // determine if this node is next for the GETX round robin request
+ if (m_status == DetermGETXGeneratorStatus_Thinking) {
+ if (m_driver.isStoreReady(m_node)) {
+ pickAddress();
+ m_status = DetermGETXGeneratorStatus_Store_Pending; // Store Pending
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateStore(); // GETX
+ } else { // I'll check again later
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+ }
+ } else {
+ WARN_EXPR(m_status);
+ ERROR_MSG("Invalid status");
+ }
+
+}
+
+void DetermGETXGenerator::performCallback(NodeID proc, SubBlock& data)
+{
+ Address address = data.getAddress();
+ assert(proc == m_node);
+ assert(address == m_address);
+
+ DEBUG_EXPR(TESTER_COMP, LowPrio, proc);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, m_status);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, address);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, data);
+
+ if (m_status == DetermGETXGeneratorStatus_Store_Pending) {
+ m_driver.recordStoreLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ data.writeByte(m_node);
+ m_driver.storeCompleted(m_node, data.getAddress()); // advance the store queue
+
+ m_counter++;
+ if (m_counter < g_tester_length) {
+ m_status = DetermGETXGeneratorStatus_Thinking;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ } else {
+ m_driver.reportDone();
+ m_status = DetermGETXGeneratorStatus_Done;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ }
+
+ } else {
+ WARN_EXPR(m_status);
+ ERROR_MSG("Invalid status");
+ }
+}
+
+int DetermGETXGenerator::thinkTime() const
+{
+ return g_think_time;
+}
+
+int DetermGETXGenerator::waitTime() const
+{
+ return g_wait_time;
+}
+
+void DetermGETXGenerator::pickAddress()
+{
+ assert(m_status == DetermGETXGeneratorStatus_Thinking);
+
+ m_address = m_driver.getNextStoreAddr(m_node);
+}
+
+void DetermGETXGenerator::initiateStore()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Store");
+ sequencer()->makeRequest(CacheMsg(m_address, m_address, CacheRequestType_ST, Address(3), AccessModeType_UserMode, 1, PrefetchBit_No, 0, Address(0), 0 /* only 1 SMT thread */, 0, false));
+}
+
+Sequencer* DetermGETXGenerator::sequencer() const
+{
+ return g_system_ptr->getChip(m_node/RubyConfig::numberOfProcsPerChip())->getSequencer(m_node%RubyConfig::numberOfProcsPerChip());
+}
+
+void DetermGETXGenerator::print(ostream& out) const
+{
+}
+
diff --git a/src/mem/ruby/tester/DetermGETXGenerator.hh b/src/mem/ruby/tester/DetermGETXGenerator.hh
new file mode 100644
index 000000000..eff1eb6b3
--- /dev/null
+++ b/src/mem/ruby/tester/DetermGETXGenerator.hh
@@ -0,0 +1,104 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+// This Deterministic Generator generates GETX requests for all nodes in the system
+// The GETX requests are generated one at a time in round-robin fashion 0...1...2...etc.
+
+#ifndef DETERMGETXGENERATOR_H
+#define DETERMGETXGENERATOR_H
+
+#include "Global.hh"
+#include "Consumer.hh"
+#include "DetermGETXGeneratorStatus.hh"
+#include "NodeID.hh"
+#include "Address.hh"
+#include "SpecifiedGenerator.hh"
+
+class Sequencer;
+class SubBlock;
+class DeterministicDriver;
+
+class DetermGETXGenerator : public SpecifiedGenerator {
+public:
+ // Constructors
+ DetermGETXGenerator(NodeID node, DeterministicDriver& driver);
+
+ // Destructor
+ ~DetermGETXGenerator();
+
+ // Public Methods
+ void wakeup();
+ void performCallback(NodeID proc, SubBlock& data);
+
+ void print(ostream& out) const;
+private:
+ // Private Methods
+ int thinkTime() const;
+ int waitTime() const;
+ void initiateStore();
+ void pickAddress();
+
+ Sequencer* sequencer() const;
+
+ // copy constructor and assignment operator
+ DetermGETXGenerator(const DetermGETXGenerator& obj);
+ DetermGETXGenerator& operator=(const DetermGETXGenerator& obj);
+
+ // Data Members (m_ prefix)
+ DetermGETXGeneratorStatus m_status;
+ int m_counter;
+ Address m_address;
+ NodeID m_node;
+ DeterministicDriver& m_driver;
+ Time m_last_transition;
+};
+
+// Output operator declaration
+ostream& operator<<(ostream& out, const DetermGETXGenerator& obj);
+
+// ******************* Definitions *******************
+
+// Output operator definition
+extern inline
+ostream& operator<<(ostream& out, const DetermGETXGenerator& obj)
+{
+ obj.print(out);
+ out << flush;
+ return out;
+}
+
+#endif //DETERMGETXGENERATOR_H
+
diff --git a/src/mem/ruby/tester/DetermInvGenerator.cc b/src/mem/ruby/tester/DetermInvGenerator.cc
new file mode 100644
index 000000000..020c2fe96
--- /dev/null
+++ b/src/mem/ruby/tester/DetermInvGenerator.cc
@@ -0,0 +1,202 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ */
+
+// This Deterministic Generator generates GETS request for all nodes in the system
+// then Invalidates them with a GETX. The GETS and GETX request are generated one
+// at a time in round-robin fashion 0...1...2...etc.
+
+#include "DetermInvGenerator.hh"
+#include "DetermInvGeneratorStatus.hh"
+#include "LockStatus.hh"
+#include "Sequencer.hh"
+#include "System.hh"
+#include "RubyConfig.hh"
+#include "SubBlock.hh"
+#include "DeterministicDriver.hh"
+#include "Chip.hh"
+
+DetermInvGenerator::DetermInvGenerator(NodeID node, DeterministicDriver& driver) :
+ m_driver(driver)
+{
+ m_status = DetermInvGeneratorStatus_Thinking;
+ m_last_transition = 0;
+ m_node = node;
+ m_address = Address(9999); // initiate to a NULL value
+ m_counter = 0;
+
+ // don't know exactly when this node needs to request so just guess randomly
+ g_eventQueue_ptr->scheduleEvent(this, 1+(random() % 200));
+}
+
+DetermInvGenerator::~DetermInvGenerator()
+{
+}
+
+void DetermInvGenerator::wakeup()
+{
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_node);
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_status);
+
+ // determine if this node is next for the load round robin request
+ if (m_status == DetermInvGeneratorStatus_Thinking) {
+ // is a load ready and waiting and are my transactions insync with global transactions
+ if (m_driver.isLoadReady(m_node) && m_counter == m_driver.getStoresCompleted()) {
+ pickLoadAddress();
+ m_status = DetermInvGeneratorStatus_Load_Pending; // Load Pending
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateLoad(); // GETS
+ } else { // I'll check again later
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+ }
+ } else if (m_status == DetermInvGeneratorStatus_Load_Complete) {
+ if (m_driver.isStoreReady(m_node, m_address)) { // do a store in this transaction or start the next one
+ if (m_driver.isLoadReady((0), m_address)) { // everyone is in S for this address i.e. back to node 0
+ m_status = DetermInvGeneratorStatus_Store_Pending;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateStore(); // GETX
+ } else { // I'm next, I just have to wait for all loads to complete
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+ }
+ } else { // I'm not next to store, go back to thinking
+ m_status = DetermInvGeneratorStatus_Thinking;
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+ }
+ } else {
+ WARN_EXPR(m_status);
+ ERROR_MSG("Invalid status");
+ }
+
+}
+
+void DetermInvGenerator::performCallback(NodeID proc, SubBlock& data)
+{
+ Address address = data.getAddress();
+ assert(proc == m_node);
+ assert(address == m_address);
+
+ DEBUG_EXPR(TESTER_COMP, LowPrio, proc);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, m_status);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, address);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, data);
+
+ if (m_status == DetermInvGeneratorStatus_Load_Pending) {
+ m_driver.recordLoadLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ NodeID firstByte = data.readByte(); // dummy read
+
+ m_driver.loadCompleted(m_node, data.getAddress());
+
+ if (!m_driver.isStoreReady(m_node, m_address)) { // if we don't have to store, we are done for this transaction
+ m_counter++;
+ }
+ if (m_counter < g_tester_length) {
+ m_status = DetermInvGeneratorStatus_Load_Complete;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ } else {
+ m_driver.reportDone();
+ m_status = DetermInvGeneratorStatus_Done;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ }
+
+ } else if (m_status == DetermInvGeneratorStatus_Store_Pending) {
+ m_driver.recordStoreLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ data.writeByte(m_node);
+ m_driver.storeCompleted(m_node, data.getAddress()); // advance the store queue
+
+ m_counter++;
+ if (m_counter < g_tester_length) {
+ m_status = DetermInvGeneratorStatus_Thinking;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ } else {
+ m_driver.reportDone();
+ m_status = DetermInvGeneratorStatus_Done;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ }
+ } else {
+ WARN_EXPR(m_status);
+ ERROR_MSG("Invalid status");
+ }
+
+ DEBUG_EXPR(TESTER_COMP, LowPrio, proc);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, m_status);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, address);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, data);
+
+}
+
+int DetermInvGenerator::thinkTime() const
+{
+ return g_think_time;
+}
+
+int DetermInvGenerator::waitTime() const
+{
+ return g_wait_time;
+}
+
+int DetermInvGenerator::holdTime() const
+{
+ return g_hold_time;
+}
+
+void DetermInvGenerator::pickLoadAddress()
+{
+ assert(m_status == DetermInvGeneratorStatus_Thinking);
+
+ m_address = m_driver.getNextLoadAddr(m_node);
+}
+
+void DetermInvGenerator::initiateLoad()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Load");
+ sequencer()->makeRequest(CacheMsg(m_address, m_address, CacheRequestType_LD, Address(1), AccessModeType_UserMode, 1, PrefetchBit_No, 0, Address(0), 0 /* only 1 SMT thread */, 0, false));
+}
+
+void DetermInvGenerator::initiateStore()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Store");
+ sequencer()->makeRequest(CacheMsg(m_address, m_address, CacheRequestType_ST, Address(3), AccessModeType_UserMode, 1, PrefetchBit_No, 0, Address(0), 0 /* only 1 SMT thread */, 0, false));
+}
+
+Sequencer* DetermInvGenerator::sequencer() const
+{
+ return g_system_ptr->getChip(m_node/RubyConfig::numberOfProcsPerChip())->getSequencer(m_node%RubyConfig::numberOfProcsPerChip());
+}
+
+void DetermInvGenerator::print(ostream& out) const
+{
+ out << "[DetermInvGenerator]" << endl;
+}
+
diff --git a/src/mem/ruby/tester/DetermInvGenerator.hh b/src/mem/ruby/tester/DetermInvGenerator.hh
new file mode 100644
index 000000000..a72895f3f
--- /dev/null
+++ b/src/mem/ruby/tester/DetermInvGenerator.hh
@@ -0,0 +1,109 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+// This Deterministic Generator generates GETS request for all nodes in the system
+// then Invalidates them with a GETX. The GETS and GETX request are generated one
+// at a time in round-robin fashion 0...1...2...etc.
+
+#ifndef DETERMINVGENERATOR_H
+#define DETERMINVGENERATOR_H
+
+#include "Global.hh"
+#include "Consumer.hh"
+#include "DetermInvGeneratorStatus.hh"
+#include "NodeID.hh"
+#include "Address.hh"
+#include "SpecifiedGenerator.hh"
+
+class Sequencer;
+class SubBlock;
+class DeterministicDriver;
+
+class DetermInvGenerator : public SpecifiedGenerator {
+public:
+ // Constructors
+ DetermInvGenerator(NodeID node, DeterministicDriver& driver);
+
+ // Destructor
+ ~DetermInvGenerator();
+
+ // Public Methods
+ void wakeup();
+ void performCallback(NodeID proc, SubBlock& data);
+
+ void print(ostream& out) const;
+private:
+ // Private Methods
+ int thinkTime() const;
+ int waitTime() const;
+ int holdTime() const;
+ void initiateLoad();
+ void initiateStore();
+ void pickLoadAddress();
+ void pickStoreAddress();
+
+ Sequencer* sequencer() const;
+
+ // copy constructor and assignment operator
+ DetermInvGenerator(const DetermInvGenerator& obj);
+ DetermInvGenerator& operator=(const DetermInvGenerator& obj);
+
+ // Data Members (m_ prefix)
+ DetermInvGeneratorStatus m_status;
+ int m_counter;
+ Address m_address;
+ NodeID m_node;
+ DeterministicDriver& m_driver;
+ Time m_last_transition;
+
+};
+
+// Output operator declaration
+ostream& operator<<(ostream& out, const DetermInvGenerator& obj);
+
+// ******************* Definitions *******************
+
+// Output operator definition
+extern inline
+ostream& operator<<(ostream& out, const DetermInvGenerator& obj)
+{
+ obj.print(out);
+ out << flush;
+ return out;
+}
+
+#endif //DETERMINVGENERATOR_H
+
diff --git a/src/mem/ruby/tester/DetermSeriesGETSGenerator.cc b/src/mem/ruby/tester/DetermSeriesGETSGenerator.cc
new file mode 100644
index 000000000..815919559
--- /dev/null
+++ b/src/mem/ruby/tester/DetermSeriesGETSGenerator.cc
@@ -0,0 +1,149 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ */
+
+#include "DetermSeriesGETSGenerator.hh"
+#include "DetermSeriesGETSGeneratorStatus.hh"
+#include "LockStatus.hh"
+#include "Sequencer.hh"
+#include "System.hh"
+#include "RubyConfig.hh"
+#include "SubBlock.hh"
+#include "DeterministicDriver.hh"
+#include "Chip.hh"
+
+DetermSeriesGETSGenerator::DetermSeriesGETSGenerator(NodeID node, DeterministicDriver& driver) :
+ m_driver(driver)
+{
+ m_status = DetermSeriesGETSGeneratorStatus_Thinking;
+ m_last_transition = 0;
+ m_node = node;
+ m_address = Address(9999); // initialize to null value
+ m_counter = 0;
+
+ // don't know exactly when this node needs to request so just guess randomly
+ g_eventQueue_ptr->scheduleEvent(this, 1+(random() % 200));
+}
+
+DetermSeriesGETSGenerator::~DetermSeriesGETSGenerator()
+{
+}
+
+void DetermSeriesGETSGenerator::wakeup()
+{
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_node);
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_status);
+
+ // determine if this node is next for the SeriesGETS round robin request
+ if (m_status == DetermSeriesGETSGeneratorStatus_Thinking) {
+ if (m_driver.isLoadReady(m_node)) {
+ pickAddress();
+ m_status = DetermSeriesGETSGeneratorStatus_Load_Pending; // Load Pending
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateLoad(); // SeriesGETS
+ } else { // I'll check again later
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+ }
+ } else {
+ WARN_EXPR(m_status);
+ ERROR_MSG("Invalid status");
+ }
+
+}
+
+void DetermSeriesGETSGenerator::performCallback(NodeID proc, SubBlock& data)
+{
+ Address address = data.getAddress();
+ assert(proc == m_node);
+ assert(address == m_address);
+
+ DEBUG_EXPR(TESTER_COMP, LowPrio, proc);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, m_status);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, address);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, data);
+
+ if (m_status == DetermSeriesGETSGeneratorStatus_Load_Pending) {
+ m_driver.recordLoadLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ data.writeByte(m_node);
+ m_driver.loadCompleted(m_node, data.getAddress()); // advance the load queue
+
+ m_counter++;
+ // do we still have more requests to complete before the next proc starts?
+ if (m_counter < g_tester_length*g_NUM_COMPLETIONS_BEFORE_PASS) {
+ m_status = DetermSeriesGETSGeneratorStatus_Thinking;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ } else {
+ m_driver.reportDone();
+ m_status = DetermSeriesGETSGeneratorStatus_Done;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ }
+
+ } else {
+ WARN_EXPR(m_status);
+ ERROR_MSG("Invalid status");
+ }
+}
+
+int DetermSeriesGETSGenerator::thinkTime() const
+{
+ return g_think_time;
+}
+
+int DetermSeriesGETSGenerator::waitTime() const
+{
+ return g_wait_time;
+}
+
+void DetermSeriesGETSGenerator::pickAddress()
+{
+ assert(m_status == DetermSeriesGETSGeneratorStatus_Thinking);
+
+ m_address = m_driver.getNextLoadAddr(m_node);
+}
+
+void DetermSeriesGETSGenerator::initiateLoad()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Load");
+ sequencer()->makeRequest(CacheMsg(m_address, m_address, CacheRequestType_IFETCH, Address(3), AccessModeType_UserMode, 1, PrefetchBit_No, 0, Address(0), 0 /* only 1 SMT thread */, 0, false));
+}
+
+Sequencer* DetermSeriesGETSGenerator::sequencer() const
+{
+ return g_system_ptr->getChip(m_node/RubyConfig::numberOfProcsPerChip())->getSequencer(m_node%RubyConfig::numberOfProcsPerChip());
+}
+
+void DetermSeriesGETSGenerator::print(ostream& out) const
+{
+}
+
diff --git a/src/mem/ruby/tester/DetermSeriesGETSGenerator.hh b/src/mem/ruby/tester/DetermSeriesGETSGenerator.hh
new file mode 100644
index 000000000..25d4886a0
--- /dev/null
+++ b/src/mem/ruby/tester/DetermSeriesGETSGenerator.hh
@@ -0,0 +1,106 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+// This Deterministic Generator generates a series of GETS requests for a given node.
+// Sequentially goes through all nodes in the system
+// This generator is used to tune the HW prefetcher
+// The GETS requests are generated one at a time in round-robin fashion 0...1...2...etc.
+
+#ifndef DETERMSERIESGETSGENERATOR_H
+#define DETERMSERIESGETSGENERATOR_H
+
+#include "Global.hh"
+#include "Consumer.hh"
+#include "DetermSeriesGETSGeneratorStatus.hh"
+#include "NodeID.hh"
+#include "Address.hh"
+#include "SpecifiedGenerator.hh"
+
+class Sequencer;
+class SubBlock;
+class DeterministicDriver;
+
+class DetermSeriesGETSGenerator : public SpecifiedGenerator {
+public:
+ // Constructors
+ DetermSeriesGETSGenerator(NodeID node, DeterministicDriver& driver);
+
+ // Destructor
+ ~DetermSeriesGETSGenerator();
+
+ // Public Methods
+ void wakeup();
+ void performCallback(NodeID proc, SubBlock& data);
+
+ void print(ostream& out) const;
+private:
+ // Private Methods
+ int thinkTime() const;
+ int waitTime() const;
+ void initiateLoad();
+ void pickAddress();
+
+ Sequencer* sequencer() const;
+
+ // copy constructor and assignment operator
+ DetermSeriesGETSGenerator(const DetermSeriesGETSGenerator& obj);
+ DetermSeriesGETSGenerator& operator=(const DetermSeriesGETSGenerator& obj);
+
+ // Data Members (m_ prefix)
+ DetermSeriesGETSGeneratorStatus m_status;
+ int m_counter;
+ Address m_address;
+ NodeID m_node;
+ DeterministicDriver& m_driver;
+ Time m_last_transition;
+};
+
+// Output operator declaration
+ostream& operator<<(ostream& out, const DetermSeriesGETSGenerator& obj);
+
+// ******************* Definitions *******************
+
+// Output operator definition
+extern inline
+ostream& operator<<(ostream& out, const DetermSeriesGETSGenerator& obj)
+{
+ obj.print(out);
+ out << flush;
+ return out;
+}
+
+#endif //DETERMSeriesGETSGENERATOR_H
+
diff --git a/src/mem/ruby/tester/DeterministicDriver.cc b/src/mem/ruby/tester/DeterministicDriver.cc
new file mode 100644
index 000000000..dd0507201
--- /dev/null
+++ b/src/mem/ruby/tester/DeterministicDriver.cc
@@ -0,0 +1,282 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ */
+
+#include "Global.hh"
+#include "System.hh"
+#include "DeterministicDriver.hh"
+#include "EventQueue.hh"
+#include "SpecifiedGenerator.hh"
+#include "DetermGETXGenerator.hh"
+#include "DetermInvGenerator.hh"
+#include "DetermSeriesGETSGenerator.hh"
+#include "SubBlock.hh"
+#include "Chip.hh"
+
+DeterministicDriver::DeterministicDriver(System* sys_ptr)
+{
+ if (g_SIMICS) {
+ ERROR_MSG("g_SIMICS should not be defined.");
+ }
+
+ m_finish_time = 0;
+ m_last_issue = -11;
+ m_done_counter = 0;
+ m_loads_completed = 0;
+ m_stores_completed = 0;
+
+ m_numCompletionsPerNode = g_NUM_COMPLETIONS_BEFORE_PASS;
+
+ m_last_progress_vector.setSize(RubyConfig::numberOfProcessors());
+ for (int i=0; i<m_last_progress_vector.size(); i++) {
+ m_last_progress_vector[i] = 0;
+ }
+
+ m_load_vector.setSize(g_deterministic_addrs);
+ for (int i=0; i<m_load_vector.size(); i++) {
+ m_load_vector[i] = -1; // No processor last held it
+ }
+
+ m_store_vector.setSize(g_deterministic_addrs);
+ for (int i=0; i<m_store_vector.size(); i++) {
+ m_store_vector[i] = -1; // No processor last held it
+ }
+
+ m_generator_vector.setSize(RubyConfig::numberOfProcessors());
+
+ SpecifiedGeneratorType generator = string_to_SpecifiedGeneratorType(g_SpecifiedGenerator);
+
+ for (int i=0; i<m_generator_vector.size(); i++) {
+ switch (generator) {
+ case SpecifiedGeneratorType_DetermGETXGenerator:
+ m_generator_vector[i] = new DetermGETXGenerator(i, *this);
+ break;
+ case SpecifiedGeneratorType_DetermSeriesGETSGenerator:
+ m_generator_vector[i] = new DetermSeriesGETSGenerator(i, *this);
+ break;
+ case SpecifiedGeneratorType_DetermInvGenerator:
+ m_generator_vector[i] = new DetermInvGenerator(i, *this);
+ break;
+ default:
+ ERROR_MSG("Unexpected specified generator type");
+ }
+ }
+
+ // add the tester consumer to the global event queue
+ g_eventQueue_ptr->scheduleEvent(this, 1);
+}
+
+DeterministicDriver::~DeterministicDriver()
+{
+ for (int i=0; i<m_last_progress_vector.size(); i++) {
+ delete m_generator_vector[i];
+ }
+}
+
+void DeterministicDriver::hitCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread)
+{
+ DEBUG_EXPR(TESTER_COMP, MedPrio, data);
+
+ m_generator_vector[proc]->performCallback(proc, data);
+
+ // Mark that we made progress
+ m_last_progress_vector[proc] = g_eventQueue_ptr->getTime();
+}
+
+bool DeterministicDriver::isStoreReady(NodeID node)
+{
+ return isAddrReady(node, m_store_vector);
+}
+
+bool DeterministicDriver::isStoreReady(NodeID node, Address addr)
+{
+ return isAddrReady(node, m_store_vector, addr);
+}
+
+bool DeterministicDriver::isLoadReady(NodeID node)
+{
+ return isAddrReady(node, m_load_vector);
+}
+
+bool DeterministicDriver::isLoadReady(NodeID node, Address addr)
+{
+ return isAddrReady(node, m_load_vector, addr);
+}
+
+// searches for any address in the addr_vector
+bool DeterministicDriver::isAddrReady(NodeID node, Vector<NodeID> addr_vector)
+{
+ for (int i=0; i<addr_vector.size(); i++) {
+ if (((addr_vector[i]+1)%RubyConfig::numberOfProcessors() == node) &&
+ (m_loads_completed+m_stores_completed >= m_numCompletionsPerNode*node) && // is this node next
+ (g_eventQueue_ptr->getTime() >= m_last_issue + 10)) { // controll rate of requests
+ return true;
+ }
+ }
+ return false;
+}
+
+// test for a particular addr
+bool DeterministicDriver::isAddrReady(NodeID node, Vector<NodeID> addr_vector, Address addr)
+{
+ int addr_number = addr.getAddress()/RubyConfig::dataBlockBytes();
+
+ ASSERT ((addr_number >= 0) && (addr_number < addr_vector.size()));
+
+ if (((addr_vector[addr_number]+1)%RubyConfig::numberOfProcessors() == node) &&
+ (m_loads_completed+m_stores_completed >= m_numCompletionsPerNode*node) && // is this node next
+ (g_eventQueue_ptr->getTime() >= m_last_issue + 10)) { // controll rate of requests
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void DeterministicDriver::loadCompleted(NodeID node, Address addr)
+{
+ m_loads_completed++;
+ setNextAddr(node, addr, m_load_vector);
+}
+
+void DeterministicDriver::storeCompleted(NodeID node, Address addr)
+{
+ m_stores_completed++;
+ setNextAddr(node, addr, m_store_vector);
+}
+
+void DeterministicDriver::setNextAddr(NodeID node, Address addr, Vector<NodeID>& addr_vector)
+{
+ // mark the addr vector that this proc was the last to use the particular address
+ int addr_number = addr.getAddress()/RubyConfig::dataBlockBytes();
+ addr_vector[addr_number] = node;
+}
+
+Address DeterministicDriver::getNextLoadAddr(NodeID node)
+{
+ return getNextAddr(node, m_load_vector);
+}
+
+Address DeterministicDriver::getNextStoreAddr(NodeID node)
+{
+ return getNextAddr(node, m_store_vector);
+}
+
+Address DeterministicDriver::getNextAddr(NodeID node, Vector<NodeID> addr_vector)
+{
+
+ // This method deterministically picks the next addr the node should acquirer
+ // The addrs cycle through according to NodeID 0->1->...->lastID->0...
+
+ Address addr;
+
+ // should only be called if we know a addr is ready for the node
+ ASSERT(isAddrReady(node, addr_vector));
+
+ for (int addr_number=0; addr_number<addr_vector.size(); addr_number++) {
+ //for (int addr_number=addr_vector.size()-1; addr_number>0; addr_number--) {
+
+ // is this node next in line for the addr
+ if (((addr_vector[addr_number]+1)%RubyConfig::numberOfProcessors()) == node) {
+
+ // One addr per cache line
+ addr.setAddress(addr_number * RubyConfig::dataBlockBytes());
+ }
+ }
+
+ m_last_issue = g_eventQueue_ptr->getTime();
+
+ return addr;
+}
+
+
+void DeterministicDriver::reportDone()
+{
+ m_done_counter++;
+ if ((m_done_counter == RubyConfig::numberOfProcessors())) {
+ //|| (m_done_counter == g_tester_length)) {
+ m_finish_time = g_eventQueue_ptr->getTime();
+ }
+}
+
+void DeterministicDriver::recordLoadLatency(Time time)
+{
+ m_load_latency.add(time);
+}
+
+void DeterministicDriver::recordStoreLatency(Time time)
+{
+ m_store_latency.add(time);
+}
+
+void DeterministicDriver::wakeup()
+{
+ // checkForDeadlock();
+ if (m_done_counter < RubyConfig::numberOfProcessors()) {
+ g_eventQueue_ptr->scheduleEvent(this, g_DEADLOCK_THRESHOLD);
+ }
+}
+
+void DeterministicDriver::checkForDeadlock()
+{
+ int size = m_last_progress_vector.size();
+ Time current_time = g_eventQueue_ptr->getTime();
+ for (int processor=0; processor<size; processor++) {
+ if ((current_time - m_last_progress_vector[processor]) > g_DEADLOCK_THRESHOLD) {
+ WARN_EXPR(processor);
+ Sequencer* seq_ptr = g_system_ptr->getChip(processor/RubyConfig::numberOfProcsPerChip())->getSequencer(processor%RubyConfig::numberOfProcsPerChip());
+ assert(seq_ptr != NULL);
+ // if (seq_ptr->isRequestPending()) {
+ // WARN_EXPR(seq_ptr->pendingAddress());
+ // }
+ WARN_EXPR(current_time);
+ WARN_EXPR(m_last_progress_vector[processor]);
+ WARN_EXPR(current_time - m_last_progress_vector[processor]);
+ ERROR_MSG("Deadlock detected.");
+ }
+ }
+}
+
+void DeterministicDriver::printStats(ostream& out) const
+{
+ out << endl;
+ out << "DeterministicDriver Stats" << endl;
+ out << "---------------------" << endl;
+
+ out << "finish_time: " << m_finish_time << endl;
+ out << "load_latency: " << m_load_latency << endl;
+ out << "store_latency: " << m_store_latency << endl;
+}
+
+void DeterministicDriver::print(ostream& out) const
+{
+}
diff --git a/src/mem/ruby/tester/DeterministicDriver.hh b/src/mem/ruby/tester/DeterministicDriver.hh
new file mode 100644
index 000000000..3d0bae73d
--- /dev/null
+++ b/src/mem/ruby/tester/DeterministicDriver.hh
@@ -0,0 +1,125 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+#ifndef DETERMINISTICDRIVER_H
+#define DETERMINISTICDRIVER_H
+
+#include "Global.hh"
+#include "Driver.hh"
+#include "Histogram.hh"
+#include "CacheRequestType.hh"
+
+class System;
+class SpecifiedGenerator;
+
+class DeterministicDriver : public Driver, public Consumer {
+public:
+ // Constructors
+ DeterministicDriver(System* sys_ptr);
+
+ // Destructor
+ ~DeterministicDriver();
+
+ // Public Methods
+ bool isStoreReady(NodeID node);
+ bool isLoadReady(NodeID node);
+ bool isStoreReady(NodeID node, Address addr);
+ bool isLoadReady(NodeID node, Address addr);
+ void loadCompleted(NodeID node, Address addr);
+ void storeCompleted(NodeID node, Address addr);
+ Address getNextLoadAddr(NodeID node);
+ Address getNextStoreAddr(NodeID node);
+ int getLoadsCompleted() { return m_loads_completed; }
+ int getStoresCompleted() { return m_stores_completed; }
+
+ void reportDone();
+ void recordLoadLatency(Time time);
+ void recordStoreLatency(Time time);
+
+ void hitCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread);
+ void wakeup();
+ void printStats(ostream& out) const;
+ void clearStats() {}
+ void printConfig(ostream& out) const {}
+
+ void print(ostream& out) const;
+private:
+ // Private Methods
+ void checkForDeadlock();
+
+ Address getNextAddr(NodeID node, Vector<NodeID> addr_vector);
+ bool isAddrReady(NodeID node, Vector<NodeID> addr_vector);
+ bool isAddrReady(NodeID node, Vector<NodeID> addr_vector, Address addr);
+ void setNextAddr(NodeID node, Address addr, Vector<NodeID>& addr_vector);
+
+ // Private copy constructor and assignment operator
+ DeterministicDriver(const DeterministicDriver& obj);
+ DeterministicDriver& operator=(const DeterministicDriver& obj);
+
+ // Data Members (m_ prefix)
+ Vector<Time> m_last_progress_vector;
+ Vector<SpecifiedGenerator*> m_generator_vector;
+ Vector<NodeID> m_load_vector; // Processor last to load the addr
+ Vector<NodeID> m_store_vector; // Processor last to store the addr
+
+ int m_done_counter;
+ int m_loads_completed;
+ int m_stores_completed;
+ // enforces the previous node to have a certain # of completions
+ // before next node starts
+ int m_numCompletionsPerNode;
+
+ Histogram m_load_latency;
+ Histogram m_store_latency;
+ Time m_finish_time;
+ Time m_last_issue;
+};
+
+// Output operator declaration
+ostream& operator<<(ostream& out, const DeterministicDriver& obj);
+
+// ******************* Definitions *******************
+
+// Output operator definition
+extern inline
+ostream& operator<<(ostream& out, const DeterministicDriver& obj)
+{
+ obj.print(out);
+ out << flush;
+ return out;
+}
+
+#endif //DETERMINISTICDRIVER_H
diff --git a/src/mem/ruby/tester/Instruction.cc b/src/mem/ruby/tester/Instruction.cc
new file mode 100644
index 000000000..8528a4094
--- /dev/null
+++ b/src/mem/ruby/tester/Instruction.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1999 by Mark Hill and David Wood for the Wisconsin
+ * Multifacet Project. ALL RIGHTS RESERVED.
+ *
+ * ##HEADER##
+ *
+ * This software is furnished under a license and may be used and
+ * copied only in accordance with the terms of such license and the
+ * inclusion of the above copyright notice. This software or any
+ * other copies thereof or any derivative works may not be provided or
+ * otherwise made available to any other persons. Title to and
+ * ownership of the software is retained by Mark Hill and David Wood.
+ * Any use of this software must include the above copyright notice.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS". THE LICENSOR MAKES NO
+ * WARRANTIES ABOUT ITS CORRECTNESS OR PERFORMANCE.
+ * */
+
+/*
+ * $Id: Instruction.C 1.2 05/08/26 00:54:48-05:00 xu@s0-32.cs.wisc.edu $
+ *
+ * Description:
+ *
+ */
+
+#include "Instruction.hh"
+
+Instruction::Instruction(){
+ m_opcode = Opcode_NUM_OPCODES;
+ m_address = Address(physical_address_t(0));
+}
+
+Instruction::Instruction(Opcode op, Address addr){
+ m_opcode = op;
+ m_address = addr;
+ assert(addr.getAddress() == 0);
+}
+
+void Instruction::init(Opcode op, Address addr){
+ m_opcode = op;
+ m_address = addr;
+ //cout << "Instruction(" << op << ", " << m_address << ")" << endl;
+}
+
+Opcode Instruction::getOpcode(){
+ return m_opcode;
+}
+
+Address Instruction::getAddress(){
+ return m_address;
+}
diff --git a/src/mem/ruby/tester/Instruction.hh b/src/mem/ruby/tester/Instruction.hh
new file mode 100644
index 000000000..674447056
--- /dev/null
+++ b/src/mem/ruby/tester/Instruction.hh
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1999 by Mark Hill and David Wood for the Wisconsin
+ * Multifacet Project. ALL RIGHTS RESERVED.
+ *
+ * ##HEADER##
+ *
+ * This software is furnished under a license and may be used and
+ * copied only in accordance with the terms of such license and the
+ * inclusion of the above copyright notice. This software or any
+ * other copies thereof or any derivative works may not be provided or
+ * otherwise made available to any other persons. Title to and
+ * ownership of the software is retained by Mark Hill and David Wood.
+ * Any use of this software must include the above copyright notice.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS". THE LICENSOR MAKES NO
+ * WARRANTIES ABOUT ITS CORRECTNESS OR PERFORMANCE.
+ * */
+
+/*
+ * $Id: Instruction.h 1.2 05/05/24 12:15:47-05:00 kmoore@balder.cs.wisc.edu $
+ *
+ * Description:
+ *
+ */
+
+#ifndef INSTRUCTION_H
+#define INSTRUCTION_H
+
+#include "Address.hh"
+
+
+enum Opcode {
+ Opcode_BEGIN,
+ Opcode_LD,
+ Opcode_ST,
+ Opcode_INC,
+ Opcode_COMMIT,
+ Opcode_DONE,
+ Opcode_NUM_OPCODES
+};
+
+class Instruction {
+ public:
+ Instruction();
+ Instruction(Opcode op, Address addr);
+
+ void init(Opcode op, Address addr);
+ Opcode getOpcode();
+ Address getAddress();
+
+ private:
+ Opcode m_opcode;
+ Address m_address;
+
+};
+
+#endif
diff --git a/src/mem/ruby/tester/RaceyDriver.cc b/src/mem/ruby/tester/RaceyDriver.cc
new file mode 100644
index 000000000..4ed26da31
--- /dev/null
+++ b/src/mem/ruby/tester/RaceyDriver.cc
@@ -0,0 +1,139 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ */
+
+#include "Global.hh"
+#include "System.hh"
+#include "RaceyDriver.hh"
+#include "EventQueue.hh"
+#include "RaceyPseudoThread.hh"
+#include "SubBlock.hh"
+
+RaceyDriver::RaceyDriver()
+{
+ if (g_SIMICS) {
+ ERROR_MSG("g_SIMICS should not be defined.");
+ }
+
+ // debug transition?
+ if(false) {
+ assert(g_debug_ptr);
+ g_debug_ptr->setDebugTime(1);
+ }
+
+ m_finish_time = 0;
+ m_done_counter = 0;
+ m_wakeup_thread0 = false;
+
+ // racey at least need two processors
+ assert(RubyConfig::numberOfProcessors() >= 2);
+
+ // init all racey pseudo threads
+ m_racey_pseudo_threads.setSize(RubyConfig::numberOfProcessors());
+ for (int i=0; i<m_racey_pseudo_threads.size(); i++) {
+ m_racey_pseudo_threads[i] = new RaceyPseudoThread(i, *this);
+ }
+
+ // add this driver to the global event queue, for deadlock detection
+ g_eventQueue_ptr->scheduleEvent(this, g_DEADLOCK_THRESHOLD);
+}
+
+RaceyDriver::~RaceyDriver()
+{
+ for (int i=0; i<m_racey_pseudo_threads.size(); i++) {
+ delete m_racey_pseudo_threads[i];
+ }
+}
+
+void RaceyDriver::hitCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread)
+{
+ DEBUG_EXPR(TESTER_COMP, MedPrio, data);
+ m_racey_pseudo_threads[proc]->performCallback(proc, data);
+}
+
+integer_t RaceyDriver::getInstructionCount(int procID) const
+{
+ return m_racey_pseudo_threads[procID]->getInstructionCounter();
+}
+
+int RaceyDriver::runningThreads()
+{
+ return RubyConfig::numberOfProcessors() - m_done_counter;
+}
+
+// used to wake up thread 0 whenever other thread finishes
+void RaceyDriver::registerThread0Wakeup()
+{
+ m_wakeup_thread0 = true;
+}
+
+void RaceyDriver::joinThread()
+{
+ m_done_counter++;
+ if (m_done_counter == RubyConfig::numberOfProcessors()) {
+ m_finish_time = g_eventQueue_ptr->getTime();
+ }
+
+ if(m_wakeup_thread0) {
+ g_eventQueue_ptr->scheduleEvent(m_racey_pseudo_threads[0], 1);
+ m_wakeup_thread0 = false;
+ }
+}
+
+void RaceyDriver::wakeup()
+{
+ // check for deadlock
+ for(int i = 0 ; i < m_racey_pseudo_threads.size(); i++) {
+ m_racey_pseudo_threads[i]->checkForDeadlock();
+ }
+
+ // schedule next wakeup
+ if (m_done_counter < RubyConfig::numberOfProcessors()) {
+ g_eventQueue_ptr->scheduleEvent(this, g_DEADLOCK_THRESHOLD);
+ }
+}
+
+void RaceyDriver::printStats(ostream& out) const
+{
+ assert(m_done_counter == RubyConfig::numberOfProcessors());
+ out << endl;
+ out << "RaceyDriver Stats" << endl;
+ out << "---------------------" << endl;
+
+ out << "execution signature: " << m_racey_pseudo_threads[0]->getSignature() << endl;
+ out << "finish_time: " << m_finish_time << endl;
+}
+
+void RaceyDriver::print(ostream& out) const
+{
+}
diff --git a/src/mem/ruby/tester/RaceyDriver.hh b/src/mem/ruby/tester/RaceyDriver.hh
new file mode 100644
index 000000000..a1a821b96
--- /dev/null
+++ b/src/mem/ruby/tester/RaceyDriver.hh
@@ -0,0 +1,112 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description: The driver interface between racey pseudo threads and ruby
+ * memory timing simulator.
+ *
+ */
+
+#ifndef RACEYDRIVER_H
+#define RACEYDRIVER_H
+
+#include "Global.hh"
+#include "Driver.hh"
+
+class RaceyPseudoThread;
+
+class RaceyDriver : public Driver, public Consumer {
+public:
+ // Constructors
+ RaceyDriver();
+
+ // Destructor
+ ~RaceyDriver();
+
+ // Public Methods
+ int runningThreads();
+ void registerThread0Wakeup();
+ void joinThread();
+ bool Thread0Initialized() {
+ return m_racey_pseudo_threads[0]->getInitializedState();
+ };
+
+ void hitCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread);
+ void wakeup();
+ void printStats(ostream& out) const;
+ void clearStats() {}
+ void printConfig(ostream& out) const {}
+
+ integer_t getInstructionCount(int procID) const;
+
+ // save/load cpu states
+ void saveCPUStates(int cpu_id, string filename) {
+ m_racey_pseudo_threads[cpu_id]->saveCPUStates(filename);
+ };
+ void loadCPUStates(int cpu_id, string filename) {
+ m_racey_pseudo_threads[cpu_id]->loadCPUStates(filename);
+ };
+
+ // reset IC
+ void resetIC(int cpu_id) {
+ m_racey_pseudo_threads[cpu_id]->resetIC();
+ }
+
+ void print(ostream& out) const;
+private:
+
+ // Private copy constructor and assignment operator
+ RaceyDriver(const RaceyDriver& obj);
+ RaceyDriver& operator=(const RaceyDriver& obj);
+
+ // Data Members (m_ prefix)
+ Vector<RaceyPseudoThread*> m_racey_pseudo_threads;
+ int m_done_counter;
+ bool m_wakeup_thread0;
+
+ Time m_finish_time;
+};
+
+// Output operator declaration
+ostream& operator<<(ostream& out, const RaceyDriver& obj);
+
+// ******************* Definitions *******************
+
+// Output operator definition
+extern inline
+ostream& operator<<(ostream& out, const RaceyDriver& obj)
+{
+ obj.print(out);
+ out << flush;
+ return out;
+}
+
+#endif //RACEYDRIVER_H
diff --git a/src/mem/ruby/tester/RequestGenerator.cc b/src/mem/ruby/tester/RequestGenerator.cc
new file mode 100644
index 000000000..71a183315
--- /dev/null
+++ b/src/mem/ruby/tester/RequestGenerator.cc
@@ -0,0 +1,196 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ */
+
+#include "RequestGenerator.hh"
+#include "RequestGeneratorStatus.hh"
+#include "LockStatus.hh"
+#include "Sequencer.hh"
+#include "System.hh"
+#include "RubyConfig.hh"
+#include "SubBlock.hh"
+#include "SyntheticDriver.hh"
+#include "Chip.hh"
+
+RequestGenerator::RequestGenerator(NodeID node, SyntheticDriver& driver) :
+ m_driver(driver)
+{
+ m_status = RequestGeneratorStatus_Thinking;
+ m_last_transition = 0;
+ m_node = node;
+ pickAddress();
+ m_counter = 0;
+
+ //g_eventQueue_ptr->scheduleEvent(this, 1+(random() % 200));
+}
+
+RequestGenerator::~RequestGenerator()
+{
+}
+
+void RequestGenerator::wakeup()
+{
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_node);
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_status);
+
+ if (m_status == RequestGeneratorStatus_Thinking) {
+ m_status = RequestGeneratorStatus_Test_Pending;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateTest(); // Test
+ } else if (m_status == RequestGeneratorStatus_Holding) {
+ m_status = RequestGeneratorStatus_Release_Pending;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateRelease(); // Release
+ } else if (m_status == RequestGeneratorStatus_Before_Swap) {
+ m_status = RequestGeneratorStatus_Swap_Pending;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ initiateSwap();
+ } else {
+ WARN_EXPR(m_status);
+ ERROR_MSG("Invalid status");
+ }
+}
+
+void RequestGenerator::performCallback(NodeID proc, SubBlock& data)
+{
+ Address address = data.getAddress();
+ assert(proc == m_node);
+ assert(address == m_address);
+
+ DEBUG_EXPR(TESTER_COMP, LowPrio, proc);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, m_status);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, address);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, data);
+
+ if (m_status == RequestGeneratorStatus_Test_Pending) {
+ // m_driver.recordTestLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ if (data.readByte() == LockStatus_Locked) {
+ // Locked - keep spinning
+ m_status = RequestGeneratorStatus_Thinking;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ } else {
+ // Unlocked - try the swap
+ m_driver.recordTestLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ m_status = RequestGeneratorStatus_Before_Swap;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ }
+ } else if (m_status == RequestGeneratorStatus_Swap_Pending) {
+ m_driver.recordSwapLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ if (data.readByte() == LockStatus_Locked) {
+ // We failed to aquire the lock
+ m_status = RequestGeneratorStatus_Thinking;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ } else {
+ // We acquired the lock
+ data.writeByte(LockStatus_Locked);
+ m_status = RequestGeneratorStatus_Holding;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ DEBUG_MSG(TESTER_COMP, HighPrio, "Acquired");
+ DEBUG_EXPR(TESTER_COMP, HighPrio, proc);
+ DEBUG_EXPR(TESTER_COMP, HighPrio, g_eventQueue_ptr->getTime());
+ g_eventQueue_ptr->scheduleEvent(this, holdTime());
+ }
+ } else if (m_status == RequestGeneratorStatus_Release_Pending) {
+ m_driver.recordReleaseLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ // We're releasing the lock
+ data.writeByte(LockStatus_Unlocked);
+
+ m_counter++;
+ if (m_counter < g_tester_length) {
+ m_status = RequestGeneratorStatus_Thinking;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ pickAddress();
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+ } else {
+ m_driver.reportDone();
+ m_status = RequestGeneratorStatus_Done;
+ m_last_transition = g_eventQueue_ptr->getTime();
+ }
+ } else {
+ WARN_EXPR(m_status);
+ ERROR_MSG("Invalid status");
+ }
+}
+
+int RequestGenerator::thinkTime() const
+{
+ return g_think_time;
+}
+
+int RequestGenerator::waitTime() const
+{
+ return g_wait_time;
+}
+
+int RequestGenerator::holdTime() const
+{
+ return g_hold_time;
+}
+
+void RequestGenerator::pickAddress()
+{
+ assert(m_status == RequestGeneratorStatus_Thinking);
+ m_address = m_driver.pickAddress(m_node);
+}
+
+void RequestGenerator::initiateTest()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Test");
+ sequencer()->makeRequest(CacheMsg(m_address, m_address, CacheRequestType_LD, Address(1), AccessModeType_UserMode, 1, PrefetchBit_No, 0, Address(0), 0 /* only 1 SMT thread */, 0, false));
+}
+
+void RequestGenerator::initiateSwap()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Swap");
+ sequencer()->makeRequest(CacheMsg(m_address, m_address, CacheRequestType_ATOMIC, Address(2), AccessModeType_UserMode, 1, PrefetchBit_No, 0, Address(0), 0 /* only 1 SMT thread */, 0, false));
+}
+
+void RequestGenerator::initiateRelease()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Release");
+ sequencer()->makeRequest(CacheMsg(m_address, m_address, CacheRequestType_ST, Address(3), AccessModeType_UserMode, 1, PrefetchBit_No, 0, Address(0), 0 /* only 1 SMT thread */, 0, false));
+}
+
+Sequencer* RequestGenerator::sequencer() const
+{
+ return g_system_ptr->getChip(m_node/RubyConfig::numberOfProcsPerChip())->getSequencer(m_node%RubyConfig::numberOfProcsPerChip());
+}
+
+void RequestGenerator::print(ostream& out) const
+{
+ out << "[RequestGenerator]" << endl;
+}
+
diff --git a/src/mem/ruby/tester/RequestGenerator.hh b/src/mem/ruby/tester/RequestGenerator.hh
new file mode 100644
index 000000000..3296f7951
--- /dev/null
+++ b/src/mem/ruby/tester/RequestGenerator.hh
@@ -0,0 +1,102 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+#ifndef REQUESTGENERATOR_H
+#define REQUESTGENERATOR_H
+
+#include "Global.hh"
+#include "Consumer.hh"
+#include "RequestGeneratorStatus.hh"
+#include "NodeID.hh"
+#include "Address.hh"
+
+class Sequencer;
+class SubBlock;
+class SyntheticDriver;
+
+class RequestGenerator : public Consumer {
+public:
+ // Constructors
+ RequestGenerator(NodeID node, SyntheticDriver& driver);
+
+ // Destructor
+ ~RequestGenerator();
+
+ // Public Methods
+ void wakeup();
+ void performCallback(NodeID proc, SubBlock& data);
+
+ void print(ostream& out) const;
+private:
+ // Private Methods
+ int thinkTime() const;
+ int waitTime() const;
+ int holdTime() const;
+ void initiateTest();
+ void initiateSwap();
+ void initiateRelease();
+ void pickAddress();
+ Sequencer* sequencer() const;
+
+ // Private copy constructor and assignment operator
+ RequestGenerator(const RequestGenerator& obj);
+ RequestGenerator& operator=(const RequestGenerator& obj);
+
+ // Data Members (m_ prefix)
+ SyntheticDriver& m_driver;
+ NodeID m_node;
+ RequestGeneratorStatus m_status;
+ int m_counter;
+ Time m_last_transition;
+ Address m_address;
+};
+
+// Output operator declaration
+ostream& operator<<(ostream& out, const RequestGenerator& obj);
+
+// ******************* Definitions *******************
+
+// Output operator definition
+extern inline
+ostream& operator<<(ostream& out, const RequestGenerator& obj)
+{
+ obj.print(out);
+ out << flush;
+ return out;
+}
+
+#endif //REQUESTGENERATOR_H
+
diff --git a/src/mem/ruby/tester/SpecifiedGenerator.cc b/src/mem/ruby/tester/SpecifiedGenerator.cc
new file mode 100644
index 000000000..e6ee802d4
--- /dev/null
+++ b/src/mem/ruby/tester/SpecifiedGenerator.cc
@@ -0,0 +1,48 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ */
+
+#include "SpecifiedGenerator.hh"
+#include "Sequencer.hh"
+#include "System.hh"
+#include "SubBlock.hh"
+#include "SyntheticDriver.hh"
+
+SpecifiedGenerator::SpecifiedGenerator()
+{
+}
+
+SpecifiedGenerator::~SpecifiedGenerator()
+{
+}
+
diff --git a/src/mem/ruby/tester/SpecifiedGenerator.hh b/src/mem/ruby/tester/SpecifiedGenerator.hh
new file mode 100644
index 000000000..d22c56f49
--- /dev/null
+++ b/src/mem/ruby/tester/SpecifiedGenerator.hh
@@ -0,0 +1,69 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+#ifndef SPECIFIEDGENERATOR_H
+#define SPECIFIEDGENERATOR_H
+
+#include "Global.hh"
+#include "Consumer.hh"
+#include "NodeID.hh"
+
+class Sequencer;
+class SubBlock;
+
+class SpecifiedGenerator : public Consumer {
+public:
+ // Constructors
+ SpecifiedGenerator();
+
+ // Destructor
+ virtual ~SpecifiedGenerator() = 0;
+
+ // Public Methods
+ virtual void wakeup() = 0;
+ virtual void performCallback(NodeID proc, SubBlock& data) = 0;
+
+ virtual void print(ostream& out) const = 0;
+protected:
+ // accessible by subclasses
+
+private:
+ // inaccessible by subclasses
+
+};
+
+#endif //SPECIFIEDGENERATOR_H
+
diff --git a/src/mem/ruby/tester/SyntheticDriver.cc b/src/mem/ruby/tester/SyntheticDriver.cc
new file mode 100644
index 000000000..d2028ba07
--- /dev/null
+++ b/src/mem/ruby/tester/SyntheticDriver.cc
@@ -0,0 +1,296 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ */
+
+#include "Global.hh"
+#include "System.hh"
+#include "SyntheticDriver.hh"
+#include "EventQueue.hh"
+//#ifndef XACT_MEM
+#include "RequestGenerator.hh"
+//#endif
+//#include "XactAbortRequestGenerator.hh"
+//#include "XactRequestGenerator.hh"
+#include "SubBlock.hh"
+#include "Chip.hh"
+
+SyntheticDriver::SyntheticDriver(System* sys_ptr)
+{
+ cout << "SyntheticDriver::SyntheticDriver" << endl;
+ if (g_SIMICS) {
+ ERROR_MSG("g_SIMICS should not be defined.");
+ }
+
+ m_finish_time = 0;
+ m_done_counter = 0;
+
+ m_last_progress_vector.setSize(RubyConfig::numberOfProcessors());
+ for (int i=0; i<m_last_progress_vector.size(); i++) {
+ m_last_progress_vector[i] = 0;
+ }
+
+ m_lock_vector.setSize(g_synthetic_locks);
+ for (int i=0; i<m_lock_vector.size(); i++) {
+ m_lock_vector[i] = -1; // No processor last held it
+ }
+
+ m_request_generator_vector.setSize(RubyConfig::numberOfProcessors());
+ for (int i=0; i<m_request_generator_vector.size(); i++) {
+ if(XACT_MEMORY){
+ //m_request_generator_vector[i] = new XactRequestGenerator(i, *this);
+ } else {
+ m_request_generator_vector[i] = new RequestGenerator(i, *this);
+ }
+ }
+
+ // add the tester consumer to the global event queue
+ g_eventQueue_ptr->scheduleEvent(this, 1);
+}
+
+SyntheticDriver::~SyntheticDriver()
+{
+ for (int i=0; i<m_last_progress_vector.size(); i++) {
+ delete m_request_generator_vector[i];
+ }
+}
+
+void SyntheticDriver::hitCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread)
+{
+ DEBUG_EXPR(TESTER_COMP, MedPrio, data);
+ //cout << " " << proc << " in S.D. hitCallback" << endl;
+ if(XACT_MEMORY){
+ //XactRequestGenerator* reqGen = static_cast<XactRequestGenerator*>(m_request_generator_vector[proc]);
+ //reqGen->performCallback(proc, data);
+ } else {
+ m_request_generator_vector[proc]->performCallback(proc, data);
+ }
+
+ // Mark that we made progress
+ m_last_progress_vector[proc] = g_eventQueue_ptr->getTime();
+}
+
+void SyntheticDriver::abortCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread)
+{
+ //cout << "SyntheticDriver::abortCallback" << endl;
+ DEBUG_EXPR(TESTER_COMP, MedPrio, data);
+
+ if(XACT_MEMORY){
+ //XactRequestGenerator* reqGen = static_cast<XactRequestGenerator*>(m_request_generator_vector[proc]);
+ //reqGen->abortTransaction();
+ //reqGen->performCallback(proc, data);
+ } else {
+ m_request_generator_vector[proc]->performCallback(proc, data);
+ }
+
+ // Mark that we made progress
+ m_last_progress_vector[proc] = g_eventQueue_ptr->getTime();
+}
+
+// For Transactional Memory
+/*
+// called whenever we send a nack
+void SyntheticDriver::notifySendNack( int proc, const Address & addr, uint64 remote_timestamp, const MachineID & remote_id ){
+ if(XACT_MEMORY){
+ //XactRequestGenerator* reqGen = static_cast<XactRequestGenerator*>(m_request_generator_vector[proc]);
+ //reqGen->notifySendNack(addr, remote_timestamp, remote_id);
+ }
+ else{
+ cout << "notifySendNack NOT USING TM" << endl;
+ ASSERT(0);
+ }
+}
+
+// called whenever we receive a NACK
+// Either for a demand request or log store
+void SyntheticDriver::notifyReceiveNack( int proc, const Address & addr, uint64 remote_timestamp, const MachineID & remote_id ){
+ if(XACT_MEMORY){
+ //XactRequestGenerator* reqGen = static_cast<XactRequestGenerator*>(m_request_generator_vector[proc]);
+ //reqGen->notifyReceiveNack(addr, remote_timestamp, remote_id);
+ }
+ else{
+ cout << "notifyReceiveNack NOT USING TM" << endl;
+ ASSERT(0);
+ }
+}
+
+// called whenever we received ALL the NACKs. Take abort or retry action here
+void SyntheticDriver::notifyReceiveNackFinal(int proc, const Address & addr){
+ if(XACT_MEMORY){
+ //XactRequestGenerator* reqGen = static_cast<XactRequestGenerator*>(m_request_generator_vector[proc]);
+ //reqGen->notifyReceiveNackFinal(addr);
+ }
+ else{
+ cout << "notifyReceiveNackFinal NOT USING TM" << endl;
+ ASSERT(0);
+ }
+}
+
+// called during abort handling
+// void SyntheticDriver::notifyAbortStart( const Address & handlerPC ){
+
+// }
+
+// void SyntheticDriver::notifyAbortComplete( const Address & newPC ){
+
+// }
+*/
+
+Address SyntheticDriver::pickAddress(NodeID node)
+{
+ // This methods picks a random lock that we were NOT that last
+ // processor to acquire. Why? Without this change 2 and 4
+ // processor runs, the odds of having the lock in your cache in
+ // read/write state is 50% or 25%, respectively. This effect can
+ // make our 'throughput per processor' results look too strange.
+
+ Address addr;
+ // FIXME - make this a parameter of the workload
+ bool done = false;
+ int lock_number = 0;
+ int counter = 0;
+ while (1) {
+ // Pick a random lock
+ lock_number = random() % m_lock_vector.size();
+
+ // Were we the last to acquire the lock?
+ if (m_lock_vector[lock_number] != node) {
+ break;
+ }
+
+ // Don't keep trying forever, since if there is only one lock, we're always the last to try to obtain the lock
+ counter++;
+ if (counter > 10) {
+ break;
+ }
+ }
+
+ // We're going to acquire it soon, so we can update the last
+ // processor to hold the lock at this time
+ m_lock_vector[lock_number] = node;
+
+ // One lock per cache line
+ addr.setAddress(lock_number * RubyConfig::dataBlockBytes());
+ return addr;
+}
+
+void SyntheticDriver::reportDone()
+{
+ m_done_counter++;
+ if (m_done_counter == RubyConfig::numberOfProcessors()) {
+ m_finish_time = g_eventQueue_ptr->getTime();
+ }
+}
+
+void SyntheticDriver::recordTestLatency(Time time)
+{
+ m_test_latency.add(time);
+}
+
+void SyntheticDriver::recordSwapLatency(Time time)
+{
+ m_swap_latency.add(time);
+}
+
+void SyntheticDriver::recordReleaseLatency(Time time)
+{
+ m_release_latency.add(time);
+}
+
+void SyntheticDriver::wakeup()
+{
+ // checkForDeadlock();
+ if (m_done_counter < RubyConfig::numberOfProcessors()) {
+ g_eventQueue_ptr->scheduleEvent(this, g_DEADLOCK_THRESHOLD);
+ }
+}
+
+void SyntheticDriver::checkForDeadlock()
+{
+ int size = m_last_progress_vector.size();
+ Time current_time = g_eventQueue_ptr->getTime();
+ for (int processor=0; processor<size; processor++) {
+ if ((current_time - m_last_progress_vector[processor]) > g_DEADLOCK_THRESHOLD) {
+ WARN_EXPR(processor);
+ Sequencer* seq_ptr = g_system_ptr->getChip(processor/RubyConfig::numberOfProcsPerChip())->getSequencer(processor%RubyConfig::numberOfProcsPerChip());
+ assert(seq_ptr != NULL);
+ // if (seq_ptr->isRequestPending()) {
+ // WARN_EXPR(seq_ptr->pendingAddress());
+ // }
+ WARN_EXPR(current_time);
+ WARN_EXPR(m_last_progress_vector[processor]);
+ WARN_EXPR(current_time - m_last_progress_vector[processor]);
+ ERROR_MSG("Deadlock detected.");
+ }
+ }
+}
+
+integer_t SyntheticDriver::readPhysicalMemory(int procID, physical_address_t address,
+ int len ){
+ char buffer[8];
+ ASSERT(len <= 8);
+ Sequencer* seq = g_system_ptr->getChip(procID/RubyConfig::numberOfProcsPerChip())->getSequencer(procID%RubyConfig::numberOfProcsPerChip());
+ assert(seq != NULL);
+ bool found = seq->getRubyMemoryValue(Address(address), buffer, len );
+ ASSERT(found);
+ return *((integer_t *) buffer);
+}
+
+void SyntheticDriver::writePhysicalMemory( int procID, physical_address_t address,
+ integer_t value, int len ){
+ char buffer[8];
+ ASSERT(len <= 8);
+
+ memcpy(buffer, (const void*) &value, len);
+ DEBUG_EXPR(TESTER_COMP, MedPrio, "");
+ Sequencer* seq = g_system_ptr->getChip(procID/RubyConfig::numberOfProcsPerChip())->getSequencer(procID%RubyConfig::numberOfProcsPerChip());
+ assert(seq != NULL);
+ bool found = seq->setRubyMemoryValue(Address(address), buffer, len );
+ ASSERT(found);
+ //return found;
+}
+
+void SyntheticDriver::printStats(ostream& out) const
+{
+ out << endl;
+ out << "SyntheticDriver Stats" << endl;
+ out << "---------------------" << endl;
+
+ out << "synthetic_finish_time: " << m_finish_time << endl;
+ out << "test_latency: " << m_test_latency << endl;
+ out << "swap_latency: " << m_swap_latency << endl;
+ out << "release_latency: " << m_release_latency << endl;
+}
+
+void SyntheticDriver::print(ostream& out) const
+{
+}
diff --git a/src/mem/ruby/tester/SyntheticDriver.hh b/src/mem/ruby/tester/SyntheticDriver.hh
new file mode 100644
index 000000000..278891ba2
--- /dev/null
+++ b/src/mem/ruby/tester/SyntheticDriver.hh
@@ -0,0 +1,118 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+#ifndef SYNTHETICDRIVER_H
+#define SYNTHETICDRIVER_H
+
+#include "Global.hh"
+#include "Driver.hh"
+#include "Histogram.hh"
+#include "CacheRequestType.hh"
+
+class System;
+class RequestGenerator;
+
+class SyntheticDriver : public Driver, public Consumer {
+public:
+ // Constructors
+ SyntheticDriver(System* sys_ptr);
+
+ // Destructor
+ ~SyntheticDriver();
+
+ // Public Methods
+ Address pickAddress(NodeID node);
+ void reportDone();
+ void recordTestLatency(Time time);
+ void recordSwapLatency(Time time);
+ void recordReleaseLatency(Time time);
+
+ void hitCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread);
+ void conflictCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread) {assert(0)};
+ void abortCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread);
+ void wakeup();
+ void printStats(ostream& out) const;
+ void clearStats() {}
+ void printConfig(ostream& out) const {}
+
+ integer_t readPhysicalMemory(int procID, physical_address_t address,
+ int len );
+
+ void writePhysicalMemory( int procID, physical_address_t address,
+ integer_t value, int len );
+
+ void print(ostream& out) const;
+
+ // For handling NACKs/retries
+ //void notifySendNack( int procID, const Address & addr, uint64 remote_timestamp, const MachineID & remote_id);
+ //void notifyReceiveNack( int procID, const Address & addr, uint64 remote_timestamp, const MachineID & remote_id);
+ //void notifyReceiveNackFinal( int procID, const Address & addr);
+
+private:
+ // Private Methods
+ void checkForDeadlock();
+
+ // Private copy constructor and assignment operator
+ SyntheticDriver(const SyntheticDriver& obj);
+ SyntheticDriver& operator=(const SyntheticDriver& obj);
+
+ // Data Members (m_ prefix)
+ Vector<Time> m_last_progress_vector;
+ Vector<RequestGenerator*> m_request_generator_vector;
+ Vector<NodeID> m_lock_vector; // Processor last to hold the lock
+ int m_done_counter;
+
+ Histogram m_test_latency;
+ Histogram m_swap_latency;
+ Histogram m_release_latency;
+ Time m_finish_time;
+};
+
+// Output operator declaration
+ostream& operator<<(ostream& out, const SyntheticDriver& obj);
+
+// ******************* Definitions *******************
+
+// Output operator definition
+extern inline
+ostream& operator<<(ostream& out, const SyntheticDriver& obj)
+{
+ obj.print(out);
+ out << flush;
+ return out;
+}
+
+#endif //SYNTHETICDRIVER_H
diff --git a/src/mem/ruby/tester/Tester.cc b/src/mem/ruby/tester/Tester.cc
new file mode 100644
index 000000000..0e6f12cdc
--- /dev/null
+++ b/src/mem/ruby/tester/Tester.cc
@@ -0,0 +1,116 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ */
+
+#include "Global.hh"
+#include "System.hh"
+#include "Tester.hh"
+#include "EventQueue.hh"
+#include "SubBlock.hh"
+#include "Check.hh"
+#include "Chip.hh"
+
+Tester::Tester(System* sys_ptr)
+{
+ if (g_SIMICS) {
+ ERROR_MSG("g_SIMICS should not be defined.");
+ }
+
+ g_callback_counter = 0;
+
+ // add the tester consumer to the global event queue
+ g_eventQueue_ptr->scheduleEvent(this, 1);
+
+ m_last_progress_vector.setSize(RubyConfig::numberOfProcessors());
+ for (int i=0; i<m_last_progress_vector.size(); i++) {
+ m_last_progress_vector[i] = 0;
+ }
+}
+
+Tester::~Tester()
+{
+}
+
+void Tester::hitCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread)
+{
+ // Mark that we made progress
+ m_last_progress_vector[proc] = g_eventQueue_ptr->getTime();
+ g_callback_counter++;
+
+ // This tells us our store has 'completed' or for a load gives us
+ // back the data to make the check
+ DEBUG_EXPR(TESTER_COMP, MedPrio, proc);
+ DEBUG_EXPR(TESTER_COMP, MedPrio, data);
+ Check* check_ptr = m_checkTable.getCheck(data.getAddress());
+ assert(check_ptr != NULL);
+ check_ptr->performCallback(proc, data);
+
+}
+
+void Tester::wakeup()
+{
+ if (g_callback_counter < g_tester_length) {
+ // Try to perform an action or check
+ Check* check_ptr = m_checkTable.getRandomCheck();
+ assert(check_ptr != NULL);
+ check_ptr->initiate();
+
+ checkForDeadlock();
+
+ g_eventQueue_ptr->scheduleEvent(this, 2);
+ }
+}
+
+void Tester::checkForDeadlock()
+{
+ int size = m_last_progress_vector.size();
+ Time current_time = g_eventQueue_ptr->getTime();
+ for (int processor=0; processor<size; processor++) {
+ if ((current_time - m_last_progress_vector[processor]) > g_DEADLOCK_THRESHOLD) {
+ WARN_EXPR(current_time);
+ WARN_EXPR(m_last_progress_vector[processor]);
+ WARN_EXPR(current_time - m_last_progress_vector[processor]);
+ WARN_EXPR(processor);
+ Sequencer* seq_ptr = g_system_ptr->getChip(processor/RubyConfig::numberOfProcsPerChip())->getSequencer(processor%RubyConfig::numberOfProcsPerChip());
+ assert(seq_ptr != NULL);
+ WARN_EXPR(*seq_ptr);
+ ERROR_MSG("Deadlock detected.");
+ }
+ }
+}
+
+void Tester::print(ostream& out) const
+{
+ out << "[Tester]" << endl;
+}
+
diff --git a/src/mem/ruby/tester/Tester.hh b/src/mem/ruby/tester/Tester.hh
new file mode 100644
index 000000000..35563a3b4
--- /dev/null
+++ b/src/mem/ruby/tester/Tester.hh
@@ -0,0 +1,93 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+#ifndef TESTER_H
+#define TESTER_H
+
+#include "Global.hh"
+#include "Driver.hh"
+#include "CheckTable.hh"
+#include "CacheRequestType.hh"
+
+class System;
+
+class Tester : public Driver, public Consumer {
+public:
+ // Constructors
+ Tester(System* sys_ptr);
+
+ // Destructor
+ ~Tester();
+
+ // Public Methods
+
+ void hitCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread);
+ void wakeup();
+ void printStats(ostream& out) const {}
+ void clearStats() {}
+ void printConfig(ostream& out) const {}
+
+ void print(ostream& out) const;
+private:
+ // Private Methods
+
+ void checkForDeadlock();
+
+ // Private copy constructor and assignment operator
+ Tester(const Tester& obj);
+ Tester& operator=(const Tester& obj);
+
+ // Data Members (m_ prefix)
+
+ CheckTable m_checkTable;
+ Vector<Time> m_last_progress_vector;
+};
+
+// Output operator declaration
+ostream& operator<<(ostream& out, const Tester& obj);
+
+// ******************* Definitions *******************
+
+// Output operator definition
+extern inline
+ostream& operator<<(ostream& out, const Tester& obj)
+{
+ obj.print(out);
+ out << flush;
+ return out;
+}
+
+#endif //TESTER_H
diff --git a/src/mem/ruby/tester/XactAbortRequestGenerator.cc b/src/mem/ruby/tester/XactAbortRequestGenerator.cc
new file mode 100644
index 000000000..e562aa760
--- /dev/null
+++ b/src/mem/ruby/tester/XactAbortRequestGenerator.cc
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 1999 by Mark Hill and David Wood for the Wisconsin
+ * Multifacet Project. ALL RIGHTS RESERVED.
+ *
+ * ##HEADER##
+ *
+ * This software is furnished under a license and may be used and
+ * copied only in accordance with the terms of such license and the
+ * inclusion of the above copyright notice. This software or any
+ * other copies thereof or any derivative works may not be provided or
+ * otherwise made available to any other persons. Title to and
+ * ownership of the software is retained by Mark Hill and David Wood.
+ * Any use of this software must include the above copyright notice.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS". THE LICENSOR MAKES NO
+ * WARRANTIES ABOUT ITS CORRECTNESS OR PERFORMANCE.
+ * */
+
+/*
+ * $Id$
+ *
+ */
+
+#ifdef XACT_MEM
+
+#include "XactAbortRequestGenerator.hh"
+#include "LockStatus.hh"
+#include "Sequencer.hh"
+#include "System.hh"
+#include "RubyConfig.hh"
+#include "SubBlock.hh"
+#include "SyntheticDriver.hh"
+#include "Chip.hh"
+#include "Instruction.hh"
+#include "TransactionManager.hh"
+
+//uint8 XactAbortRequestGenerator::testArray[MAX_ADDRESS];
+//uint8 XactAbortRequestGenerator::dataArray[MAX_ADDRESS];
+Vector<uint8> XactAbortRequestGenerator::testArray;
+
+XactAbortRequestGenerator::XactAbortRequestGenerator(NodeID node, SyntheticDriver& driver) :
+ RequestGenerator(node, driver), m_driver(driver)
+{
+ //DEBUG_EXPR(TESTER_COMP, MedPrio, "#### -- Creating XactAbortRequestGenerator\n");
+ cout << "#### -- Creating XactAbortRequestGenerator " << node << endl;
+
+ testArray.setSize(g_MEMORY_SIZE_BYTES);
+ assert(testArray.size() == g_MEMORY_SIZE_BYTES);
+ // Create instructions
+ m_instructions = new Instruction[XACT_LENGTH];
+ newTransaction();
+
+ m_xact_status = XactAbortRequestGeneratorStatus_Ready;
+ m_last_transition = 0;
+ m_node = node;
+ //pickAddress();
+ m_counter = 0;
+ m_register = 5;
+ m_pc = 0;
+
+ //for(int i=0; i<XACT_SIZE; ++i){
+ //testArray[i] = 64;
+ //}
+
+ //testArray = new uint8[XACT_SIZE];
+ //dataArray = new uint8[XACT_SIZE];
+ g_eventQueue_ptr->scheduleEvent(this, 1+(random() % 200));
+}
+
+void XactAbortRequestGenerator::newTransaction(){
+ int num_stores = 16;
+ int num_loads = XACT_LENGTH - num_stores - 2;
+
+ for(int i=0; i<XACT_LENGTH; ++i){
+ if (i == 0){
+ m_instructions[i].init(Opcode_BEGIN, Address(1));
+ } else if (i == XACT_LENGTH - 1){
+ m_instructions[i].init(Opcode_COMMIT, Address(1));
+ } else if (i < num_loads) {
+ physical_address_t address = i % XACT_SIZE;
+ ASSERT(address < XACT_SIZE);
+
+ int selectOpcode = random() % 2;
+ Opcode op;
+ switch(selectOpcode){
+ case 0:
+ op = Opcode_LD;
+ break;
+ case 1:
+ op = Opcode_INC;
+ break;
+ };
+ m_instructions[i].init(op, Address(address));
+ } else {
+ physical_address_t address = i - num_loads;
+ ASSERT(address < XACT_SIZE);
+ Opcode op = Opcode_ST;
+ m_instructions[i].init(op, Address(address));
+ }
+ }
+}
+
+XactAbortRequestGenerator::~XactAbortRequestGenerator()
+{
+ delete m_instructions;
+}
+
+void XactAbortRequestGenerator::wakeup()
+{
+ assert(m_xact_status == XactAbortRequestGeneratorStatus_Ready || m_xact_status == XactAbortRequestGeneratorStatus_Aborted);
+ m_xact_status = XactAbortRequestGeneratorStatus_Blocked;
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_node);
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_xact_status);
+
+ m_last_transition = g_eventQueue_ptr->getTime();
+ execute();
+}
+
+void XactAbortRequestGenerator::execute(){
+ assert(m_pc >= 0 && m_pc < XACT_LENGTH);
+ Instruction current = m_instructions[m_pc];
+ //cout << " " << m_node << " executing pc: " << m_pc;
+ switch (current.getOpcode()){
+ case Opcode_BEGIN:
+ //cout << " -- begin.";
+ initiateBeginTransaction();
+ break;
+ case Opcode_LD:
+ //cout << " -- load: " << current.getAddress();
+ initiateLoad(current.getAddress());
+ break;
+ case Opcode_INC:
+ //cout << " -- inc.";
+ initiateInc(current.getAddress());
+ break;
+ case Opcode_ST:
+ //cout << " -- store: " << current.getAddress();
+ initiateStore(current.getAddress());
+ break;
+ case Opcode_COMMIT:
+ //cout << " -- commit.";
+ initiateCommit();
+ break;
+ default:
+ WARN_EXPR(current.getOpcode());
+ ERROR_MSG("Invalid opcode");
+ };
+ //cout << endl;
+}
+
+void XactAbortRequestGenerator::performCallback(NodeID proc, SubBlock& data)
+{
+ assert(m_xact_status == XactAbortRequestGeneratorStatus_Waiting ||
+ m_xact_status == XactAbortRequestGeneratorStatus_Aborted);
+ assert(proc == m_node);
+
+ Address address = data.getAddress();
+
+ DEBUG_EXPR(TESTER_COMP, LowPrio, proc);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, m_xact_status);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, address);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, data);
+
+ m_last_transition = g_eventQueue_ptr->getTime();
+
+ //cout << " " << m_node << " in performCallback, pc:" << m_pc
+ // << ", addr:" << address << endl;
+ if(m_xact_status == XactAbortRequestGeneratorStatus_Aborted){
+ cout << " " << m_node << " aborted, resetting pc." << endl;
+ m_pc = 0;
+ m_register = 5;
+ m_xact_status = XactAbortRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ } else {
+ m_xact_status = XactAbortRequestGeneratorStatus_Blocked;
+
+ bool found;
+ uint8 value;
+ switch (m_instructions[m_pc].getOpcode()){
+ case Opcode_BEGIN:
+ m_driver.recordTestLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ m_register = 5;
+ m_xact_status = XactAbortRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ m_pc++;
+ break;
+ case Opcode_LD:
+ m_driver.recordTestLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ m_register = data.getByte(0);
+ //cout << " " << m_node << " " << g_eventQueue_ptr->getTime() << " Callback--LD: " << (int) m_register << endl;
+ m_xact_status = XactAbortRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ m_pc++;
+ break;
+ //case Opcode_INC: // We shouldn't get a callback for this!
+ //m_driver.recordSwapLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+
+ // break;
+ case Opcode_ST:
+ m_driver.recordReleaseLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ //data.setByte(address.getOffset(), m_register);
+ data.setByte(0, m_register);
+ //cout << " " << m_node << " " << g_eventQueue_ptr->getTime() << " Callback--ST: " << (int) m_register << endl;
+
+ //dataArray[address.getAddress()] = m_register;
+ found = sequencer()->setRubyMemoryValue(address, (char *) (&m_register), 1);
+ assert(found);
+ found = sequencer()->getRubyMemoryValue(address, (char *) (&value), 1);
+ assert(found);
+ assert(value == m_register);
+
+ m_xact_status = XactAbortRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+ m_pc++;
+ break;
+ case Opcode_COMMIT:
+ m_counter++;
+ cout << " " << m_node << " callback--commit, counter is " << m_counter << " length is: " << g_tester_length << endl;
+ // Check for correctness
+ checkCorrectness();
+
+ m_driver.recordReleaseLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+
+ if (m_counter < g_tester_length) {
+ m_last_transition = g_eventQueue_ptr->getTime();
+ //pickAddress(); // Necessary?
+
+ // Create new random transaction
+ newTransaction();
+ m_pc = 0;
+
+ m_xact_status = XactAbortRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+ } else {
+ cout << "Ending" << endl;
+ m_driver.reportDone();
+ m_xact_status = XactAbortRequestGeneratorStatus_Done;
+ }
+ break;
+ default:
+ ERROR_MSG("Invalid Opcode");
+ };
+ }
+}
+
+int XactAbortRequestGenerator::thinkTime() const
+{
+ return g_think_time;
+}
+
+int XactAbortRequestGenerator::waitTime() const
+{
+ return g_wait_time;
+}
+
+int XactAbortRequestGenerator::holdTime() const
+{
+ return g_hold_time;
+}
+
+void XactAbortRequestGenerator::pickAddress()
+{
+ //m_address = m_driver.pickAddress(m_node);
+}
+
+void XactAbortRequestGenerator::initiateBeginTransaction()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "### -- initiating Begin Transaction");
+ cout << "### -- initiating Begin " << m_node << endl;
+ m_xact_status = XactAbortRequestGeneratorStatus_Waiting;
+ sequencer()->makeRequest(CacheMsg(Address(physical_address_t(0)), Address(physical_address_t(0)), CacheRequestType_BEGIN_XACT, Address(m_pc), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false, Address(0), transactionManager()->getTransactionLevel(0), 0, 0 /* only 1 SMT thread */, transactionManager()->getTimestamp(0), transactionManager()->inExposedAction(0), 0));
+ transactionManager()->beginTransaction();
+}
+
+void XactAbortRequestGenerator::initiateStore(Address addr)
+{
+ //DEBUG_MSG(TESTER_COMP, MedPrio, "### -- initiating Store");
+ //cout << "### -- initiating Store " << m_node << endl;
+ m_xact_status = XactAbortRequestGeneratorStatus_Waiting;
+ ASSERT(transactionManager()->inTransaction(0));
+ sequencer()->makeRequest(CacheMsg(addr, addr, CacheRequestType_ST_XACT, Address(m_pc), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false, Address(0), transactionManager()->getTransactionLevel(0), 0, 0 /* only 1 SMT thread */, transactionManager()->getTimestamp(0), transactionManager()->inExposedAction(0), 0));
+}
+
+void XactAbortRequestGenerator::initiateCommit()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "### -- initiating Commit ");
+ cout << "### -- initiating Commit " << m_node << endl;
+
+ m_xact_status = XactAbortRequestGeneratorStatus_Waiting;
+ sequencer()->makeRequest(CacheMsg(Address(physical_address_t(0)), Address(physical_address_t(0)), CacheRequestType_COMMIT_XACT, Address(m_pc), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false, Address(0), transactionManager()->getTransactionLevel(0), 0, 0 /* only 1 SMT thread */, transactionManager()->getTimestamp(0), transactionManager()->inExposedAction(0), 0));
+ transactionManager()->commitTransaction();
+}
+
+void XactAbortRequestGenerator::initiateLoad(Address addr)
+{
+ //DEBUG_MSG(TESTER_COMP, MedPrio, "### -- initiating Load ");
+ //cout << "### -- initiating Load " << m_node << endl;
+ m_xact_status = XactAbortRequestGeneratorStatus_Waiting;
+ ASSERT(transactionManager()->inTransaction(0));
+ sequencer()->makeRequest(CacheMsg(addr, addr, CacheRequestType_LD_XACT, Address(m_pc), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false, Address(0), transactionManager()->getTransactionLevel(0), 0, 0 /* only 1 SMT thread */, transactionManager()->getTimestamp(0), transactionManager()->inExposedAction(0), 0));
+}
+
+void XactAbortRequestGenerator::initiateInc(Address addr)
+{
+ //DEBUG_MSG(TESTER_COMP, MedPrio, "### -- initiating Load ");
+ //cout << "### -- initiating Inc " << m_node << endl;
+ m_register++;
+ m_xact_status = XactAbortRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, holdTime());
+ m_pc++;
+}
+
+void XactAbortRequestGenerator::checkCorrectness(){
+ // Execute the transaction on the test array
+ int testPC = 0;
+ bool done = false;
+ for(int i=0; i<XACT_LENGTH && !done; ++i){
+ Opcode op = m_instructions[i].getOpcode();
+ Address addr = m_instructions[i].getAddress();
+ ASSERT(addr.getAddress() < testArray.size());
+ uint8 reg_val;
+ switch(op){
+ case Opcode_BEGIN:
+ reg_val = 0;
+ break; // do nothing
+ case Opcode_LD:
+ reg_val = testArray[addr.getAddress()];
+ //cout << m_node << " LD: " << addr << ", " << (int) reg_val << endl;
+ break;
+ case Opcode_INC:
+ reg_val++;
+ //cout << m_node << " INC: " << (int) reg_val << endl;
+ break;
+ case Opcode_ST:
+ testArray[addr.getAddress()] = reg_val;
+ //cout << m_node << " ST: " << addr << ", " << (int) reg_val << endl;
+ break;
+ case Opcode_COMMIT:
+ done = true;
+ break;
+ default:
+ ERROR_MSG("Invalid Opcode.");
+ };
+ }
+
+ bool success = true;
+ uint8 ruby_value;
+ bool found = false;
+ for(int i=0; i<XACT_LENGTH && !done; ++i){
+ Opcode op = m_instructions[i].getOpcode();
+ Address addr = m_instructions[i].getAddress();
+
+ uint8 reg_val;
+ switch(op){
+ case Opcode_BEGIN:
+ case Opcode_INC:
+ break; // do nothing
+ case Opcode_LD:
+ case Opcode_ST:
+ found = sequencer()->getRubyMemoryValue(m_instructions[i].getAddress(), (char *) &ruby_value, 1);
+ assert(found);
+
+ if (ruby_value != testArray[i]){
+ success = false;
+ WARN_MSG("DATA MISMATCH!");
+ WARN_EXPR((int) ruby_value);
+ WARN_EXPR((int) testArray[i]);
+ WARN_EXPR(i);
+ assert(success);
+ }
+ break;
+ case Opcode_COMMIT:
+ done = true;
+ break;
+ default:
+ ERROR_MSG("Invalid Opcode.");
+ };
+ }
+ cout << m_node << " CORRECT!" << endl;
+}
+
+Sequencer* XactAbortRequestGenerator::sequencer() const
+{
+ return g_system_ptr->getChip(m_node/RubyConfig::numberOfProcsPerChip())->getSequencer(m_node%RubyConfig::numberOfProcsPerChip());
+}
+
+TransactionManager* XactAbortRequestGenerator::transactionManager() const
+{
+ return g_system_ptr->getChip(m_node/RubyConfig::numberOfProcsPerChip())->getTransactionManager(m_node%RubyConfig::numberOfProcsPerChip());
+}
+
+void XactAbortRequestGenerator::print(ostream& out) const
+{
+}
+
+void XactAbortRequestGenerator::abortTransaction(){
+ cout << " " << m_node << " *** ABORT! ***" << endl;
+ //m_pc = 0;
+ //m_register = 5;
+ m_xact_status = XactAbortRequestGeneratorStatus_Aborted;
+}
+
+#endif //XACT_MEM
diff --git a/src/mem/ruby/tester/XactAbortRequestGenerator.hh b/src/mem/ruby/tester/XactAbortRequestGenerator.hh
new file mode 100644
index 000000000..90ec1bf1b
--- /dev/null
+++ b/src/mem/ruby/tester/XactAbortRequestGenerator.hh
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1999 by Mark Hill and David Wood for the Wisconsin
+ * Multifacet Project. ALL RIGHTS RESERVED.
+ *
+ * ##HEADER##
+ *
+ * This software is furnished under a license and may be used and
+ * copied only in accordance with the terms of such license and the
+ * inclusion of the above copyright notice. This software or any
+ * other copies thereof or any derivative works may not be provided or
+ * otherwise made available to any other persons. Title to and
+ * ownership of the software is retained by Mark Hill and David Wood.
+ * Any use of this software must include the above copyright notice.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS". THE LICENSOR MAKES NO
+ * WARRANTIES ABOUT ITS CORRECTNESS OR PERFORMANCE.
+ * */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+#ifndef XACTABORTREQUESTGENERATOR_H
+#define XACTABORTREQUESTGENERATOR_H
+
+#ifdef XACT_MEM
+
+#include "RequestGenerator.hh"
+#include "global.hh"
+#include "Consumer.hh"
+#include "NodeID.hh"
+#include "Address.hh"
+
+class Sequencer;
+class SubBlock;
+class SyntheticDriver;
+class Instruction;
+class TransactionManager;
+
+#define MAX_ADDRESS 16777216
+
+enum XactAbortRequestGeneratorStatus {
+ XactAbortRequestGeneratorStatus_Waiting,
+ XactAbortRequestGeneratorStatus_Ready,
+ XactAbortRequestGeneratorStatus_Blocked,
+ XactAbortRequestGeneratorStatus_Aborted,
+ XactAbortRequestGeneratorStatus_Done
+};
+
+class XactAbortRequestGenerator : public RequestGenerator {
+public:
+ // Constructors
+ XactAbortRequestGenerator(NodeID node, SyntheticDriver& driver);
+
+ // Destructor
+ ~XactAbortRequestGenerator();
+
+ // Public Methods
+ void wakeup();
+ void performCallback(NodeID proc, SubBlock& data);
+ void abortTransaction();
+
+ void print(ostream& out) const;
+private:
+ // Private Methods
+ int thinkTime() const;
+ int waitTime() const;
+ int holdTime() const;
+ void initiateBeginTransaction();
+ void initiateStore(Address a);
+ void initiateCommit();
+ void initiateInc(Address a);
+ void initiateLoad(Address a);
+ void pickAddress();
+ Sequencer* sequencer() const;
+ TransactionManager* transactionManager() const;
+ void execute();
+
+ void checkCorrectness();
+
+ // Private copy constructor and assignment operator
+ XactAbortRequestGenerator(const XactAbortRequestGenerator& obj);
+ XactAbortRequestGenerator& operator=(const XactAbortRequestGenerator& obj);
+
+ void newTransaction();
+
+ // Data Members (m_ prefix)
+ SyntheticDriver& m_driver;
+ NodeID m_node;
+ XactAbortRequestGeneratorStatus m_xact_status;
+ int m_counter;
+ Time m_last_transition;
+ Address m_address;
+
+ Instruction *m_instructions;
+ int m_pc;
+ uint8 m_register;
+ static Vector<uint8> testArray;
+ //static uint8 dataArray[];
+};
+
+// Output operator declaration
+ostream& operator<<(ostream& out, const XactAbortRequestGenerator& obj);
+
+// ******************* Definitions *******************
+
+// Output operator definition
+extern inline
+ostream& operator<<(ostream& out, const XactAbortRequestGenerator& obj)
+{
+ obj.print(out);
+ out << flush;
+ return out;
+}
+
+#endif //XACT_MEM
+
+#endif //REQUESTGENERATOR_H
+
diff --git a/src/mem/ruby/tester/XactRequestGenerator.cc b/src/mem/ruby/tester/XactRequestGenerator.cc
new file mode 100644
index 000000000..c7870bb25
--- /dev/null
+++ b/src/mem/ruby/tester/XactRequestGenerator.cc
@@ -0,0 +1,637 @@
+/*
+ * Copyright (c) 1999 by Mark Hill and David Wood for the Wisconsin
+ * Multifacet Project. ALL RIGHTS RESERVED.
+ *
+ * ##HEADER##
+ *
+ * This software is furnished under a license and may be used and
+ * copied only in accordance with the terms of such license and the
+ * inclusion of the above copyright notice. This software or any
+ * other copies thereof or any derivative works may not be provided or
+ * otherwise made available to any other persons. Title to and
+ * ownership of the software is retained by Mark Hill and David Wood.
+ * Any use of this software must include the above copyright notice.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS". THE LICENSOR MAKES NO
+ * WARRANTIES ABOUT ITS CORRECTNESS OR PERFORMANCE.
+ * */
+
+/*
+ * $Id: XactRequestGenerator.C 1.7 05/09/22 16:49:19-05:00 xu@s0-29.cs.wisc.edu $
+ *
+ */
+
+#include "XactRequestGenerator.hh"
+#include "LockStatus.hh"
+#include "Sequencer.hh"
+#include "System.hh"
+#include "RubyConfig.hh"
+#include "SubBlock.hh"
+#include "SyntheticDriver.hh"
+#include "Chip.hh"
+#include "Instruction.hh"
+
+XactRequestGenerator::XactRequestGenerator(NodeID node, SyntheticDriver& driver) :
+ m_driver(driver), RequestGenerator(node, driver)
+{
+ DEBUG_EXPR(TESTER_COMP, MedPrio, "#### -- Creating XactRequestGenerator\n");
+ cout << "#### -- Creating XactRequestGenerator " << node << endl;
+
+ assert(XACT_SIZE > 0);
+ testArray = new uint8[g_MEMORY_SIZE_BYTES];;
+ // Create instructions
+ m_instructions = new Instruction[XACT_LENGTH];
+ newTransaction(true);
+
+ m_status = XactRequestGeneratorStatus_Ready;
+ m_last_transition = 0;
+ m_node = node;
+ //pickAddress();
+ m_counter = 0;
+ m_register = 5;
+ m_pc = 0;
+
+ m_abortPending = false;
+ g_eventQueue_ptr->scheduleEvent(this, 1+(random() % 200));
+}
+
+void XactRequestGenerator::newTransaction(bool init){
+ // important: reset abort flag
+ m_abortPending = false;
+
+ int depth = 0;
+ bool prev_ldst = false;
+ m_size = (random() % (XACT_LENGTH-2)) + 2;
+ cout << "XactRequestGenerator::newTransaction m_size=" << m_size << endl;
+ ASSERT(m_size >= 2);
+ if (!init)
+ ASSERT(!transactionManager()->inTransaction(0));
+ m_transaction = (random() % 2);
+
+ if(m_transaction){
+ cout << " " << m_node << " new transaction " << endl;
+ } else {
+ cout << " " << m_node << " new NON-transaction " << endl;
+ }
+
+ cout << "***INSTR STREAM ";
+ for(int i=0; i<m_size; ++i){
+ if (i == 0 && m_transaction){ // new xact must start with begin
+ m_instructions[i].init(Opcode_BEGIN, Address(1));
+ depth++;
+ cout << "begin ";
+ prev_ldst = false;
+ } else if (i == m_size - 1){
+ if(m_transaction) { // new xact must end with commit
+ m_instructions[i].init(Opcode_COMMIT, Address(1));
+ depth--;
+ cout << "commit ";
+ } else {
+ m_instructions[i].init(Opcode_DONE, Address(1));
+ cout << "done ";
+ }
+ } else {
+ int selectAction;
+ if (!m_transaction) { // non-xact: must choose op
+ selectAction = 1;
+ } else { // xact
+ if (depth == m_size - i) { // must choose commit
+ selectAction = 0;
+ } else if (prev_ldst) { // only choose xact if intervenient ld/st
+ if (m_size - i < depth + 3) { // can choose op or
+ // commit (can't choose
+ // begin)
+ selectAction = (random() % 2);
+ } else if (depth == 0) { // can choose begin or op (can't
+ // choose commit)
+ selectAction = (random() % 2);
+ if (selectAction == 0) selectAction = 2;
+ } else { // can choose begin, op, or commit
+ selectAction = (random() % 3);
+ }
+ } else {
+ selectAction = 1;
+ }
+ }
+
+ physical_address_t address;
+ int selectOpcode;
+ switch (selectAction) {
+ case 0: // commit
+ m_instructions[i].init(Opcode_COMMIT, Address(1));
+ depth--;
+ cout << "commit ";
+ prev_ldst = false;
+ break;
+ case 1:
+ address = (random() % XACT_SIZE);
+ //cout << "address: " << address << endl;
+ //cout << "XACT_SIZE: " << XACT_SIZE << endl;
+
+ //assert(address < XACT_SIZE);
+ //physical_address_t address = 0;
+ selectOpcode = random() % 3;
+ Opcode op;
+ switch(selectOpcode){
+ case 0:
+ op = Opcode_LD;
+ cout << "ld ";
+ break;
+ case 1:
+ op = Opcode_ST;
+ cout << "st ";
+ break;
+ case 2:
+ op = Opcode_INC;
+ cout << "inc ";
+ break;
+ default:
+ assert(false);
+ };
+ assert(op < Opcode_NUM_OPCODES);
+ m_instructions[i].init(op, Address(address));
+ prev_ldst = true;
+ break;
+ case 2:
+ m_instructions[i].init(Opcode_BEGIN, Address(1));
+ depth++;
+ cout << "begin ";
+ prev_ldst = false;
+ break;
+ default:
+ assert(false);
+ };
+ }
+ }
+ cout << endl;
+ if(m_transaction){
+ ASSERT(m_instructions[0].getOpcode() == Opcode_BEGIN);
+ }
+}
+
+XactRequestGenerator::~XactRequestGenerator()
+{
+ if(testArray){
+ delete [] testArray;
+ testArray = NULL;
+ }
+ delete m_instructions;
+}
+
+void XactRequestGenerator::wakeup()
+{
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_node);
+ DEBUG_EXPR(TESTER_COMP, MedPrio, m_status);
+
+ assert(m_status == XactRequestGeneratorStatus_Ready || m_status == XactRequestGeneratorStatus_Aborted);
+ m_status = XactRequestGeneratorStatus_Blocked;
+
+ m_last_transition = g_eventQueue_ptr->getTime();
+ execute();
+}
+
+void XactRequestGenerator::execute(){
+ cout << "XactRequestGenerator::execute m_node=" << m_node << " m_pc=" << m_pc << " m_size=" << m_size << endl;
+ assert(m_pc >= 0);
+ assert(m_pc < m_size);
+ assert(m_pc < XACT_LENGTH);
+
+ Instruction current = m_instructions[m_pc];
+ switch (current.getOpcode()){
+ case Opcode_BEGIN:
+ cout << " -- begin.";
+ initiateBeginTransaction();
+ break;
+ case Opcode_LD:
+ cout << " -- load: " << current.getAddress();
+ initiateLoad(current.getAddress());
+ break;
+ case Opcode_INC:
+ cout << " -- inc.";
+ initiateInc(current.getAddress());
+ break;
+ case Opcode_ST:
+ cout << " -- store: " << current.getAddress();
+ initiateStore(current.getAddress());
+ break;
+ case Opcode_COMMIT:
+ cout << " -- commit.";
+ initiateCommit();
+ break;
+ case Opcode_DONE:
+ cout << " -- done.";
+ initiateDone();
+ break;
+ default:
+ WARN_EXPR(current.getOpcode());
+ ERROR_MSG("Invalid opcode");
+ };
+ cout << endl;
+}
+
+void XactRequestGenerator::performCallback(NodeID proc, SubBlock& data)
+{
+ cout << "XactRequestGenerator::performCallback m_node=" << m_node << endl;
+ assert(m_status == XactRequestGeneratorStatus_Waiting ||
+ m_status == XactRequestGeneratorStatus_Aborted);
+ assert(proc == m_node);
+
+ Address address = data.getAddress();
+ //assert(address == m_address);
+
+ DEBUG_EXPR(TESTER_COMP, LowPrio, proc);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, m_status);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, address);
+ DEBUG_EXPR(TESTER_COMP, LowPrio, data);
+
+ m_last_transition = g_eventQueue_ptr->getTime();
+
+ cout << " " << m_node << " in performCallback, pc:" << m_pc
+ << ", addr:" << address << endl;
+
+
+ int depth;
+ if(m_status == XactRequestGeneratorStatus_Aborted){
+ depth = transactionManager()->postAbortIndex(0);
+ m_pc = pc_stack[depth];
+ cout << "XactRequestGenerator::performCallback m_node=" << m_node << " setting m_pc=" << m_pc << endl;
+ printPcStack(depth);
+ m_register = 5;
+ m_status = XactRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, ABORT_RETRY_TIME);
+ } else {
+ m_status = XactRequestGeneratorStatus_Blocked;
+ bool found, outermost;
+ uint8 value;
+ switch (m_instructions[m_pc].getOpcode()){
+ case Opcode_BEGIN:
+ m_driver.recordTestLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ m_register = 5;
+ m_status = XactRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ depth = transactionManager()->getDepth(0);
+ if (!transactionManager()->isSubsuming(0)) {
+ pc_stack[depth - 1] = m_pc;
+ cout << "XactRequestGenerator::performCallback m_node=" << m_node << " SETTING PC_STACK" << endl;
+ printPcStack(depth);
+ }
+ m_pc++;
+ break;
+ case Opcode_LD:
+ m_driver.recordTestLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ m_register = data.getByte(0);
+ //cout << " " << m_node << " " << g_eventQueue_ptr->getTime() << " Callback--LD: " << (int) m_register << endl;
+ m_status = XactRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, waitTime());
+ m_pc++;
+ break;
+ //case Opcode_INC: // We shouldn't get a callback for this!
+ //m_driver.recordSwapLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+
+ // break;
+ case Opcode_ST:
+ m_driver.recordReleaseLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+ //data.setByte(address.getOffset(), m_register);
+ data.setByte(0, m_register);
+ //cout << " " << m_node << " " << g_eventQueue_ptr->getTime() << " Callback--ST: " << (int) m_register << endl;
+
+ //dataArray[address.getAddress()] = m_register;
+ found = sequencer()->setRubyMemoryValue(address, (char *) (&m_register), 1);
+ assert(found);
+ found = sequencer()->getRubyMemoryValue(address, (char *) (&value), 1);
+ assert(found);
+ assert(value == m_register);
+
+ m_status = XactRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+ m_pc++;
+ break;
+ case Opcode_COMMIT:
+ outermost = transactionManager()->getDepth(0) == 1;
+ if (outermost) { // about to commit outermost
+ m_counter++;
+ }
+
+ cout << " " << m_node << " callback--commit, counter is " << m_counter << " length is: " << g_tester_length << endl;
+ // Check for correctness
+
+ checkCorrectness();
+ transactionManager()->commitTransaction();
+
+ m_driver.recordReleaseLatency(g_eventQueue_ptr->getTime() - m_last_transition);
+
+ if (outermost) {
+ if (m_counter < g_tester_length) {
+ m_last_transition = g_eventQueue_ptr->getTime();
+ //pickAddress(); // Necessary?
+
+ // Create new random transaction
+ cout << "CREATING NEW RANDOM XACT SET" << endl;
+ newTransaction(false);
+ m_pc = 0;
+
+ m_status = XactRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+ } else {
+ cout << " " << m_node << " Done." << endl;
+ m_driver.reportDone();
+ m_status = XactRequestGeneratorStatus_Done;
+ }
+ } else {
+ m_status = XactRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+ m_pc++;
+ }
+ break;
+ default:
+ ERROR_MSG("Invalid Opcode");
+ };
+ }
+}
+
+int XactRequestGenerator::thinkTime() const
+{
+ return g_think_time;
+}
+
+int XactRequestGenerator::waitTime() const
+{
+ return g_wait_time;
+}
+
+int XactRequestGenerator::holdTime() const
+{
+ return g_hold_time;
+}
+
+void XactRequestGenerator::pickAddress()
+{
+ //m_address = m_driver.pickAddress(m_node);
+}
+
+void XactRequestGenerator::initiateBeginTransaction()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "### -- initiating Begin Transaction");
+ cout << "### -- initiating Begin " << m_node << endl;
+ m_status = XactRequestGeneratorStatus_Waiting;
+ sequencer()->makeRequest(CacheMsg(Address(physical_address_t(0)), Address(physical_address_t(0)), CacheRequestType_BEGIN_XACT, Address(m_pc), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false, Address(0), transactionManager()->getTransactionLevel(0), 0, 0 /* only 1 SMT thread */, transactionManager()->getTimestamp(0), transactionManager()->inExposedAction(0), 0));
+ transactionManager()->beginTransaction();
+}
+
+void XactRequestGenerator::initiateStore(Address addr)
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "### -- initiating Store");
+ DEBUG_MSG(TESTER_COMP, MedPrio, addr);
+ cout << "### -- initiating Store " << m_node << endl;
+ m_status = XactRequestGeneratorStatus_Waiting;
+ if(m_transaction){
+ sequencer()->makeRequest(CacheMsg(addr, addr, CacheRequestType_ST_XACT, Address(m_pc), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false, Address(0), transactionManager()->getTransactionLevel(0), 0, 0 /* only 1 SMT thread */, transactionManager()->getTimestamp(0), transactionManager()->inExposedAction(0), 0));
+ } else {
+ sequencer()->makeRequest(CacheMsg(addr, addr, CacheRequestType_ST, Address(m_pc), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false, Address(0), transactionManager()->getTransactionLevel(0), 0, 0 /* only 1 SMT thread */, transactionManager()->getTimestamp(0),transactionManager()->inExposedAction(0), 0));
+ }
+}
+
+void XactRequestGenerator::initiateCommit()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "### -- initiating Commit ");
+ cout << "### -- initiating Commit " << m_node << endl;
+
+ m_status = XactRequestGeneratorStatus_Waiting;
+ sequencer()->makeRequest(CacheMsg(Address(physical_address_t(0)), Address(physical_address_t(0)), CacheRequestType_COMMIT_XACT, Address(m_pc), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false, Address(0), transactionManager()->getTransactionLevel(0), 0, 0 /* only 1 SMT thread */, transactionManager()->getTimestamp(0), transactionManager()->inExposedAction(0), 0));
+}
+
+void XactRequestGenerator::initiateLoad(Address addr)
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "### -- initiating Load ");
+ DEBUG_MSG(TESTER_COMP, MedPrio, addr);
+
+ m_status = XactRequestGeneratorStatus_Waiting;
+ if(m_transaction){
+ cout << "### -- initiating Load XACT" << m_node << endl;
+ sequencer()->makeRequest(CacheMsg(addr, addr, CacheRequestType_LD_XACT, Address(m_pc), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false, Address(0), transactionManager()->getTransactionLevel(0), 0, 0 /* only 1 SMT thread */, transactionManager()->getTimestamp(0), transactionManager()->inExposedAction(0), 0 ));
+ } else {
+ cout << "### -- initiating Load " << m_node << endl;
+ sequencer()->makeRequest(CacheMsg(addr, addr, CacheRequestType_LD, Address(m_pc), AccessModeType_UserMode, 1, PrefetchBit_No, 0, false, Address(0), transactionManager()->getTransactionLevel(0), 0, 0 /* only 1 SMT thread */, transactionManager()->getTimestamp(0), transactionManager()->inExposedAction(0), 0));
+ }
+}
+
+void XactRequestGenerator::initiateInc(Address addr)
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "### -- initiating Inc ");
+ DEBUG_MSG(TESTER_COMP, MedPrio, addr);
+ cout << "### -- initiating Inc " << m_node << endl;
+ m_register++;
+ m_status = XactRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, holdTime());
+ m_pc++;
+}
+
+void XactRequestGenerator::initiateDone()
+{
+ DEBUG_MSG(TESTER_COMP, MedPrio, "### -- initiating Done ");
+ cout << "### -- initiating Done " << m_node << endl;
+ newTransaction(false);
+ m_pc = 0;
+ //m_register = 5;
+
+ m_status = XactRequestGeneratorStatus_Ready;
+ g_eventQueue_ptr->scheduleEvent(this, thinkTime());
+
+}
+
+void XactRequestGenerator::checkCorrectness(){
+ // Execute the transaction on the test array
+ int testPC = 0;
+ bool done = false;
+ for(int i=0; i<XACT_LENGTH && !done; ++i){
+ Opcode op = m_instructions[i].getOpcode();
+ Address addr = m_instructions[i].getAddress();
+
+ uint8 reg_val;
+ switch(op){
+ case Opcode_BEGIN:
+ reg_val = 0;
+ break; // do nothing
+ case Opcode_LD:
+ //cout << "\tcheckCorrectness " << m_node << " LD: " << addr << " address = " << hex << addr.getAddress() << endl;
+ ASSERT(addr.getAddress() < g_MEMORY_SIZE_BYTES);
+ reg_val = testArray[addr.getAddress()];
+ //cout << m_node << " LD: " << addr << ", " << (int) reg_val << endl;
+ break;
+ case Opcode_INC:
+ reg_val++;
+ //cout << m_node << " INC: " << (int) reg_val << endl;
+ break;
+ case Opcode_ST:
+ //cout << "\tcheckCorrectness " << m_node << " ST: " << addr << " address = " << hex << addr.getAddress() << endl;
+ ASSERT(addr.getAddress() < g_MEMORY_SIZE_BYTES);
+ testArray[addr.getAddress()] = reg_val;
+ //cout << m_node << " ST: " << addr << ", " << (int) reg_val << endl;
+ break;
+ case Opcode_DONE:
+ case Opcode_COMMIT:
+ done = true;
+ break;
+ default:
+ ERROR_MSG("Invalid Opcode.");
+ };
+ }
+
+ bool success = true;
+ uint8 ruby_value;
+ done = false;
+ bool found = false;
+ for(int i=0; i<XACT_LENGTH && !done; ++i){
+ Opcode op = m_instructions[i].getOpcode();
+ Address addr = m_instructions[i].getAddress();
+
+ uint8 reg_val;
+ switch(op){
+ case Opcode_BEGIN:
+ case Opcode_INC:
+ break; // do nothing
+ case Opcode_LD:
+ case Opcode_ST:
+ found = sequencer()->getRubyMemoryValue(m_instructions[i].getAddress(), (char *) &ruby_value, 1);
+ assert(found);
+
+ if (ruby_value != testArray[i]){
+ success = false;
+ WARN_MSG("DATA MISMATCH!");
+ WARN_EXPR((int) ruby_value);
+ WARN_EXPR((int) testArray[i]);
+ WARN_EXPR(i);
+ assert(success);
+ }
+
+ break;
+ case Opcode_COMMIT:
+ done = true;
+ break;
+ default:
+ ERROR_MSG("Invalid Opcode.");
+ };
+ }
+ cout << m_node << " CORRECT!" << endl;
+}
+
+Sequencer* XactRequestGenerator::sequencer() const
+{
+ return g_system_ptr->getChip(m_node/RubyConfig::numberOfProcsPerChip())->getSequencer(m_node%RubyConfig::numberOfProcsPerChip());
+}
+
+TransactionManager* XactRequestGenerator::transactionManager() const
+{
+ return g_system_ptr->getChip(m_node/RubyConfig::numberOfProcsPerChip())->getTransactionManager(m_node%RubyConfig::numberOfProcsPerChip());
+}
+
+void XactRequestGenerator::print(ostream& out) const
+{
+}
+
+void XactRequestGenerator::abortTransaction(){
+ cout << " " << m_node << " *** ABORT! ***" << endl;
+ m_status = XactRequestGeneratorStatus_Aborted;
+}
+
+void XactRequestGenerator::printPcStack(int depth) {
+ cout << "XactRequestGenerator::printPcStack m_node=" << m_node << " [";
+ for (int i = 0; i < depth; i++) {
+ cout << pc_stack[i] << ", ";
+ }
+ cout << "]" << endl;
+}
+
+void XactRequestGenerator::notifySendNack( const Address & physicalAddr, uint64 remote_timestamp, const MachineID & remote_id) {
+ Address addr = physicalAddr;
+ addr.makeLineAddress();
+ TransactionManager * xact_mgr = transactionManager();
+ bool isOlder = xact_mgr->isOlder(remote_timestamp, remote_id, addr );
+ if(isOlder){
+ bool inReadSet = xact_mgr->isInReadSetFilter(physicalAddr, 0);
+ bool inWriteSet = xact_mgr->isInWriteSetFilter(physicalAddr, 0);
+ // addr should be in perfect or Bloom filter
+ ASSERT( inReadSet || inWriteSet );
+ cout << "notifySendNack addr = " << addr << " setting POSSIBLE CYCLE " << " my_ts = " << xact_mgr->getTimestamp(0) << " remote_ts = " << remote_timestamp << " proc = " << m_node << endl;
+ xact_mgr->setPossibleCycle(addr, L1CacheMachIDToProcessorNum(remote_id), 0 /*remote thread*/ , remote_timestamp, 0 /*our thread*/);
+ }
+ // otherwise don't set the proc possible cycle flag
+}
+
+void XactRequestGenerator::notifyReceiveNack( const Address & physicalAddr, uint64 remote_timestamp, const MachineID & remote_id ) {
+ Address addr = physicalAddr;
+ addr.makeLineAddress();
+ // check whether the possible cycle is set, and whether remote_timestamp is older.
+ // we only have 1 SMT thread
+ TransactionManager * xact_mgr = transactionManager();
+ int local_timestamp = xact_mgr->getTimestamp(0);
+ bool possible_cycle = xact_mgr->possibleCycle(0);
+ // calculate isOlder() only if possible_cycle is set. This is because isOlder assumes proc is in a transaction
+ bool isOlder = false;
+ if(possible_cycle){
+ isOlder = xact_mgr->isOlder(remote_timestamp, remote_id, addr );
+ }
+
+ if(isOlder && possible_cycle){
+ // set our pendingAbort flag
+ cout << "notifyReceiveNack Setting Abort Pending Flag addr= " << addr << " ID = " << m_node << " possible_cycle = " << possible_cycle << " time = " << g_eventQueue_ptr->getTime() << endl;
+ m_abortPending = true;
+ assert(possible_cycle);
+ assert(isOlder);
+ }
+
+ // profile this nack
+ xact_mgr->profileNack(addr, IDToInt(L1CacheMachIDToProcessorNum(remote_id)), remote_timestamp, 0, 0);
+}
+
+void XactRequestGenerator::notifyReceiveNackFinal(const Address & physicalAddr) {
+ Address addr = physicalAddr;
+ addr.makeLineAddress();
+ TransactionManager * xact_mgr = transactionManager();
+ int local_timestamp = xact_mgr->getTimestamp(0);
+ bool possible_cycle = xact_mgr->possibleCycle(0);
+
+ // we should still have an active request
+ if(m_abortPending){
+ cout << "notifyReceiveNackFinal ABORTING addr= " << addr << " ID = " << m_node << " possible_cycle = " << possible_cycle << " time = " << g_eventQueue_ptr->getTime() << endl;
+ assert(possible_cycle);
+
+ // we abort
+ // Step 1: remove the aborting request from sequencer, and mark it as "Trap" in our request table if needed
+ // Note: by marking request as "Trap" we can simulate HW abort delay
+ switch (m_instructions[m_pc].getOpcode()){
+ case Opcode_LD:
+ sequencer()->readCallbackAbort(addr, 0);
+ break;
+ case Opcode_ST:
+ sequencer()->writeCallbackAbort(addr, 0);
+ break;
+ default:
+ cout << "Invalid Opcode = " << m_instructions[m_pc].getOpcode() << endl;
+ ASSERT(0);
+ };
+ // Step 2: call the abort handler explicitly. If using software handler + trap aborts we wait until
+ // Simics transfers control to us again
+ // Note: it is impossible for this request to be a prefetch, so don't need that check
+ xact_mgr->abortTransaction(0, 0, -1 /*dummy remote ID*/, addr);
+ //reset the abort flag
+ m_abortPending = false;
+ }
+ else{
+ // retry the request
+ // figure out whether to retry transactional load or store
+ switch (m_instructions[m_pc].getOpcode()){
+ case Opcode_LD:
+ cout << "RETRYING LOAD " << addr << " of proc = " << m_node << " transactionLevel = " << xact_mgr->getTransactionLevel(0) << " time = " << g_eventQueue_ptr->getTime() << endl;
+ sequencer()->issueInterProcLoadRetryRequest(addr, 0);
+ break;
+ case Opcode_ST:
+ cout << "RETRYING STORE " << addr << " of proc = " << m_node << " transactionLevel = " << xact_mgr->getTransactionLevel(0) << " time = " << g_eventQueue_ptr->getTime() << endl;
+ sequencer()->issueInterProcStoreRetryRequest(addr, 0);
+ break;
+ default:
+ cout << "Invalid Opcode = " << m_instructions[m_pc].getOpcode() << endl;
+ ASSERT(0);
+ };
+ }
+}
diff --git a/src/mem/ruby/tester/XactRequestGenerator.hh b/src/mem/ruby/tester/XactRequestGenerator.hh
new file mode 100644
index 000000000..826a257ce
--- /dev/null
+++ b/src/mem/ruby/tester/XactRequestGenerator.hh
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 1999 by Mark Hill and David Wood for the Wisconsin
+ * Multifacet Project. ALL RIGHTS RESERVED.
+ *
+ * ##HEADER##
+ *
+ * This software is furnished under a license and may be used and
+ * copied only in accordance with the terms of such license and the
+ * inclusion of the above copyright notice. This software or any
+ * other copies thereof or any derivative works may not be provided or
+ * otherwise made available to any other persons. Title to and
+ * ownership of the software is retained by Mark Hill and David Wood.
+ * Any use of this software must include the above copyright notice.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS". THE LICENSOR MAKES NO
+ * WARRANTIES ABOUT ITS CORRECTNESS OR PERFORMANCE.
+ * */
+
+/*
+ * $Id: XactRequestGenerator.h 1.4 05/07/07 10:35:32-05:00 kmoore@s0-30.cs.wisc.edu $
+ *
+ * Description:
+ *
+ */
+
+#ifndef XACTREQUESTGENERATOR_H
+#define XACTREQUESTGENERATOR_H
+
+#include "global.hh"
+#include "RequestGenerator.hh"
+#include "Consumer.hh"
+#include "NodeID.hh"
+#include "Address.hh"
+#include "TransactionManager.hh"
+
+class Sequencer;
+class SubBlock;
+class SyntheticDriver;
+class Instruction;
+class TransactionManager;
+
+#define MAX_ADDRESS 16777216
+const int TESTER_MAX_DEPTH = 16;
+
+enum XactRequestGeneratorStatus {
+ XactRequestGeneratorStatus_Waiting,
+ XactRequestGeneratorStatus_Ready,
+ XactRequestGeneratorStatus_Blocked,
+ XactRequestGeneratorStatus_Aborted,
+ XactRequestGeneratorStatus_Done
+};
+
+class XactRequestGenerator : public RequestGenerator {
+public:
+ // Constructors
+ XactRequestGenerator(NodeID node, SyntheticDriver& driver);
+
+ // Destructor
+ ~XactRequestGenerator();
+
+ // Public Methods
+ void wakeup();
+ void performCallback(NodeID proc, SubBlock& data);
+ void abortTransaction();
+
+ void print(ostream& out) const;
+
+ // For dealing with NACKs/retries
+ void notifySendNack(const Address & addr, uint64 remote_timestamp, const MachineID & remote_id);
+ void notifyReceiveNack(const Address & addr, uint64 remote_timestamp, const MachineID & remote_id);
+ void notifyReceiveNackFinal(const Address & addr);
+private:
+ // Private Methods
+ int thinkTime() const;
+ int waitTime() const;
+ int holdTime() const;
+ void initiateBeginTransaction();
+ void initiateStore(Address a);
+ void initiateCommit();
+ void initiateInc(Address a);
+ void initiateLoad(Address a);
+ void initiateDone();
+ void pickAddress();
+ Sequencer* sequencer() const;
+ TransactionManager* transactionManager() const;
+ void execute();
+ void scheduleEvent(int time);
+ void checkCorrectness();
+
+ // Private copy constructor and assignment operator
+ XactRequestGenerator(const XactRequestGenerator& obj);
+ XactRequestGenerator& operator=(const XactRequestGenerator& obj);
+
+ void newTransaction(bool init);
+ void printPcStack(int depth);
+
+ // Data Members (m_ prefix)
+ SyntheticDriver& m_driver;
+ NodeID m_node;
+ XactRequestGeneratorStatus m_status;
+ int m_counter;
+ int m_size;
+ Time m_last_transition;
+ Address m_address;
+
+ Instruction *m_instructions;
+ int m_pc;
+ int pc_stack[TESTER_MAX_DEPTH];
+ bool m_transaction;
+ uint8 m_register;
+ uint8 * testArray;
+ //static uint8 dataArray[];
+ bool m_eventPending;
+
+ // for pending aborts
+ bool m_abortPending;
+};
+
+// Output operator declaration
+ostream& operator<<(ostream& out, const XactRequestGenerator& obj);
+
+// ******************* Definitions *******************
+
+// Output operator definition
+extern inline
+ostream& operator<<(ostream& out, const XactRequestGenerator& obj)
+{
+ obj.print(out);
+ out << flush;
+ return out;
+}
+
+#endif //XACTREQUESTGENERATOR_H
+
diff --git a/src/mem/ruby/tester/main.cc b/src/mem/ruby/tester/main.cc
new file mode 100644
index 000000000..35b927f5e
--- /dev/null
+++ b/src/mem/ruby/tester/main.cc
@@ -0,0 +1,51 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ */
+
+#include "main.hh"
+#include "EventQueue.hh"
+#include "RubyConfig.hh"
+#include "test_framework.hh"
+
+// *******************
+// *** tester main ***
+// *******************
+
+int main(int argc, char *argv[])
+{
+ if (g_SIMICS) {
+ ERROR_MSG("g_SIMICS should not be defined.");
+ }
+
+ tester_main(argc, argv);
+}
diff --git a/src/mem/ruby/tester/main.hh b/src/mem/ruby/tester/main.hh
new file mode 100644
index 000000000..05e3a0e8d
--- /dev/null
+++ b/src/mem/ruby/tester/main.hh
@@ -0,0 +1,42 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+#ifndef MAIN_H
+#define MAIN_H
+
+#include "Global.hh"
+
+#endif //MAIN_H
diff --git a/src/mem/ruby/tester/test_framework.cc b/src/mem/ruby/tester/test_framework.cc
new file mode 100644
index 000000000..0f180409e
--- /dev/null
+++ b/src/mem/ruby/tester/test_framework.cc
@@ -0,0 +1,431 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ */
+
+#include "protocol_name.hh"
+#include "test_framework.hh"
+#include "System.hh"
+#include "OpalInterface.hh"
+#include "init.hh"
+#include "Tester.hh"
+#include "EventQueue.hh"
+#include "getopt.hh"
+#include "Network.hh"
+#include "CacheRecorder.hh"
+#include "Tracer.hh"
+
+using namespace std;
+#include <string>
+#include <map>
+
+// Maurice
+// extern "C" {
+// #include "simics/api.hh"
+// };
+
+#include "confio.hh"
+#include "initvar.hh"
+
+// A generated file containing the default tester parameters in string form
+// The defaults are stored in the variables
+// global_default_param and global_default_tester_param
+#include "default_param.hh"
+#include "tester_param.hh"
+
+static void parseOptions(int argc, char **argv);
+static void usageInstructions();
+static void checkArg(char ch);
+static void tester_record_cache();
+static void tester_playback_trace();
+static void tester_initialize(int argc, char **argv);
+static void tester_destroy();
+
+static string trace_filename;
+char * my_default_param;
+initvar_t * my_initvar;
+
+void tester_main(int argc, char **argv)
+{
+ tester_initialize(argc, argv);
+
+ if (trace_filename != "") {
+ // playback a trace (for multicast-mask prediction)
+ tester_playback_trace();
+ } else {
+ // test code to create a trace
+ if (!(g_SYNTHETIC_DRIVER || g_DETERMINISTIC_DRIVER) && trace_filename == "") {
+ g_system_ptr->getTracer()->startTrace("ruby.trace.gz");
+ g_eventQueue_ptr->triggerEvents(g_eventQueue_ptr->getTime() + 10000);
+ g_system_ptr->getTracer()->stopTrace();
+ }
+
+ g_eventQueue_ptr->triggerAllEvents();
+
+ // This call is placed here to make sure the cache dump code doesn't fall victim to code rot
+ if (!(g_SYNTHETIC_DRIVER || g_DETERMINISTIC_DRIVER)) {
+ tester_record_cache();
+ }
+ }
+ tester_destroy();
+}
+
+static void tester_allocate( void )
+{
+ init_simulator();
+}
+
+static void tester_generate_values( void )
+{
+}
+
+void tester_initialize(int argc, char **argv)
+{
+ int param_len = strlen( global_default_param ) + strlen( global_default_tester_param ) + 1;
+ char *default_param = (char *) malloc( sizeof(char) * param_len );
+ my_default_param = default_param;
+ strcpy( default_param, global_default_param );
+ strcat( default_param, global_default_tester_param );
+
+ // when the initvar object is created, it reads the configuration default
+ // -for the tester, the configuration defaults in config/tester.defaults
+
+ /** note: default_param is included twice in the tester:
+ * -once in init.C
+ * -again in this file
+ */
+ initvar_t *ruby_initvar = new initvar_t( "ruby", "../../../ruby/",
+ default_param,
+ &tester_allocate,
+ &tester_generate_values,
+ NULL,
+ NULL );
+ my_initvar = ruby_initvar;
+ ruby_initvar->checkInitialization();
+ parseOptions(argc, argv);
+
+ ruby_initvar->allocate();
+
+ g_system_ptr->printConfig(cout);
+ cout << "Testing clear stats...";
+ g_system_ptr->clearStats();
+ cout << "Done." << endl;
+ //free( default_param );
+ //delete ruby_initvar;
+}
+
+void tester_destroy()
+{
+ g_system_ptr->printStats(cout);
+ g_debug_ptr->closeDebugOutputFile();
+
+ free(my_default_param);
+ delete my_initvar;
+ // Clean up
+ destroy_simulator();
+ cerr << "Success: " << CURRENT_PROTOCOL << endl;
+}
+
+void tester_install_opal(mf_opal_api_t* opal_api, mf_ruby_api_t* ruby_api)
+{
+ // initialize our api interface
+ OpalInterface::installInterface(ruby_api);
+
+ // update the OpalInterface object to point to opal's interface
+ ((OpalInterface *) g_system_ptr->getDriver())->setOpalInterface(opal_api);
+}
+
+void tester_record_cache()
+{
+ cout << "Testing recording of cache contents" << endl;
+ CacheRecorder recorder;
+ g_system_ptr->recordCacheContents(recorder);
+ int written = recorder.dumpRecords("ruby.caches.gz");
+ int read = Tracer::playbackTrace("ruby.caches.gz");
+ assert(read == written);
+ cout << "Testing recording of cache contents completed" << endl;
+}
+
+void tester_playback_trace()
+{
+ assert(trace_filename != "");
+ cout << "Reading trace from file '" << trace_filename << "'..." << endl;
+ int read = Tracer::playbackTrace(trace_filename);
+ cout << "(" << read << " requests read)" << endl;
+ if (read == 0) {
+ ERROR_MSG("Zero items read from tracefile.");
+ }
+}
+
+// ************************************************************************
+// *** Functions for parsing the command line parameters for the tester ***
+// ************************************************************************
+
+static struct option const long_options[] =
+{
+ {"help", no_argument, NULL, 'h'},
+ {"processors", required_argument, NULL, 'p'},
+ {"length", required_argument, NULL, 'l'},
+ {"random", required_argument, NULL, 'r'},
+ {"trace_input", required_argument, NULL, 'z'},
+ {"component", required_argument, NULL, 'c'},
+ {"verbosity", required_argument, NULL, 'v'},
+ {"debug_output_file", required_argument, NULL, 'o'},
+ {"start", required_argument, NULL, 's'},
+ {"bandwidth", required_argument, NULL, 'b'},
+ {"threshold", required_argument, NULL, 't'},
+ {"think_time", required_argument, NULL, 'k'},
+ {"locks", required_argument, NULL, 'q'},
+ {"network", required_argument, NULL, 'n'},
+ {"procs_per_chip", required_argument, NULL, 'a'},
+ {"l2_caches", required_argument, NULL, 'e'},
+ {"memories", required_argument, NULL, 'm'},
+ {NULL, 0, NULL, 0}
+};
+
+static void parseOptions(int argc, char **argv)
+{
+ cout << "Parsing command line arguments:" << endl;
+
+ // construct the short arguments string
+ int counter = 0;
+ string short_options;
+ while (long_options[counter].name != NULL) {
+ short_options += char(long_options[counter].val);
+ if (long_options[counter].has_arg == required_argument) {
+ short_options += char(':');
+ }
+ counter++;
+ }
+
+ char c;
+ /* Parse command line options. */
+ bool error;
+ while ((c = getopt_long (argc, argv, short_options.c_str(), long_options, (int *) 0)) != EOF) {
+ switch (c) {
+ case 0:
+ break;
+
+ case 'c':
+ checkArg(c);
+ cout << " component filter string = " << optarg << endl;
+ error = Debug::checkFilterString( optarg );
+ if (error) {
+ usageInstructions();
+ }
+ DEBUG_FILTER_STRING = strdup( optarg );
+ break;
+
+ case 'h':
+ usageInstructions();
+ break;
+
+ case 'v':
+ checkArg(c);
+ cout << " verbosity string = " << optarg << endl;
+ error = Debug::checkVerbosityString(optarg);
+ if (error) {
+ usageInstructions();
+ }
+ DEBUG_VERBOSITY_STRING = strdup( optarg );
+ break;
+
+ case 'r': {
+ checkArg(c);
+ if (string(optarg) == "random") {
+ g_RANDOM_SEED = time(NULL);
+ } else {
+ g_RANDOM_SEED = atoi(optarg);
+ if (g_RANDOM_SEED == 0) {
+ usageInstructions();
+ }
+ }
+ break;
+ }
+
+ case 'l': {
+ checkArg(c);
+ g_tester_length = atoi(optarg);
+ cout << " length of run = " << g_tester_length << endl;
+ if (g_tester_length == 0) {
+ usageInstructions();
+ }
+ break;
+ }
+
+ case 'q': {
+ checkArg(c);
+ g_synthetic_locks = atoi(optarg);
+ cout << " locks in synthetic workload = " << g_synthetic_locks << endl;
+ if (g_synthetic_locks == 0) {
+ usageInstructions();
+ }
+ break;
+ }
+
+ case 'p': {
+ checkArg(c);
+ g_NUM_PROCESSORS = atoi(optarg);
+ break;
+ }
+
+ case 'a': {
+ checkArg(c);
+ g_PROCS_PER_CHIP = atoi(optarg);
+ cout << " g_PROCS_PER_CHIP: " << g_PROCS_PER_CHIP << endl;
+ break;
+ }
+
+ case 'e': {
+ checkArg(c);
+ g_NUM_L2_BANKS = atoi(optarg);
+ cout << " g_NUM_L2_BANKS: " << g_NUM_L2_BANKS << endl;
+ break;
+ }
+
+ case 'm': {
+ checkArg(c);
+ g_NUM_MEMORIES = atoi(optarg);
+ cout << " g_NUM_MEMORIES: " << g_NUM_MEMORIES << endl;
+ break;
+ }
+
+ case 's': {
+ checkArg(c);
+ long long start_time = atoll(optarg);
+ cout << " debug start cycle = " << start_time << endl;
+ if (start_time == 0) {
+ usageInstructions();
+ }
+ DEBUG_START_TIME = start_time;
+ break;
+ }
+
+ case 'b': {
+ checkArg(c);
+ int bandwidth = atoi(optarg);
+ cout << " bandwidth per link (MB/sec) = " << bandwidth << endl;
+ g_endpoint_bandwidth = bandwidth;
+ if (bandwidth == 0) {
+ usageInstructions();
+ }
+ break;
+ }
+
+ case 't': {
+ checkArg(c);
+ g_bash_bandwidth_adaptive_threshold = atof(optarg);
+ if ((g_bash_bandwidth_adaptive_threshold > 1.1) || (g_bash_bandwidth_adaptive_threshold < -0.1)) {
+ cerr << "Error: Bandwidth adaptive threshold must be between 0.0 and 1.0" << endl;
+ usageInstructions();
+ }
+
+ break;
+ }
+
+ case 'k': {
+ checkArg(c);
+ g_think_time = atoi(optarg);
+ break;
+ }
+
+ case 'o':
+ checkArg(c);
+ cout << " output file = " << optarg << endl;
+ DEBUG_OUTPUT_FILENAME = strdup( optarg );
+ break;
+
+ case 'z':
+ checkArg(c);
+ trace_filename = string(optarg);
+ cout << " tracefile = " << trace_filename << endl;
+ break;
+
+ case 'n':
+ checkArg(c);
+ cout << " topology = " << string(optarg) << endl;
+ g_NETWORK_TOPOLOGY = strdup(optarg);
+ break;
+
+ default:
+ cerr << "parameter '" << c << "' unknown" << endl;
+ usageInstructions();
+ }
+ }
+
+ if ((trace_filename != "") || (g_tester_length != 0)) {
+ if ((trace_filename != "") && (g_tester_length != 0)) {
+ cerr << "Error: both a run length (-l) and a trace file (-z) have been specified." << endl;
+ usageInstructions();
+ }
+ } else {
+ cerr << "Error: either run length (-l) must be > 0 or a trace file (-z) must be specified." << endl;
+ usageInstructions();
+ }
+}
+
+static void usageInstructions()
+{
+ cerr << endl;
+ cerr << "Options:" << endl;
+
+ // print options
+ int counter = 0;
+ while (long_options[counter].name != NULL) {
+ cerr << " -" << char(long_options[counter].val);
+ if (long_options[counter].has_arg == required_argument) {
+ cerr << " <arg>";
+ }
+ cerr << " --" << long_options[counter].name;
+ if (long_options[counter].has_arg == required_argument) {
+ cerr << " <arg>";
+ }
+ cerr << endl;
+ counter++;
+ }
+
+ cerr << "Option --processors (-p) is required." << endl;
+ cerr << "Either option --length (-l) or --trace_input (-z) must be specified." << endl;
+ cerr << endl;
+ g_debug_ptr->usageInstructions();
+ cerr << endl;
+
+ exit(1);
+}
+
+static void checkArg(char ch)
+{
+ if (optarg == NULL) {
+ cerr << "Error: parameter '" << ch << "' missing required argument" << endl;
+ usageInstructions();
+ }
+}
diff --git a/src/mem/ruby/tester/test_framework.hh b/src/mem/ruby/tester/test_framework.hh
new file mode 100644
index 000000000..7464cc274
--- /dev/null
+++ b/src/mem/ruby/tester/test_framework.hh
@@ -0,0 +1,46 @@
+
+/*
+ * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * 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.
+ */
+
+/*
+ * $Id$
+ *
+ * Description:
+ *
+ */
+
+#ifndef TESTFRAMEWORK_H
+#define TESTFRAMEWORK_H
+
+#include "Global.hh"
+#include "mf_api.hh"
+
+void tester_main(int argc, char **argv);
+void tester_install_opal( mf_opal_api_t *opal_api, mf_ruby_api_t *ruby_api );
+
+#endif //TESTFRAMEWORK_H