diff options
Diffstat (limited to 'src/mem/ruby/tester')
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 |