summaryrefslogtreecommitdiff
path: root/src/mem/ruby/tester
diff options
context:
space:
mode:
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