diff options
Diffstat (limited to 'src/mem/ruby/tester')
31 files changed, 4378 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..9dbcf39fd --- /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 "mem/ruby/tester/BarrierGenerator.hh" +#include "mem/ruby/system/Sequencer.hh" +#include "mem/ruby/system/System.hh" +#include "mem/ruby/config/RubyConfig.hh" +#include "mem/ruby/common/SubBlock.hh" +#include "mem/ruby/tester/SyntheticDriver.hh" +#include "mem/protocol/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..e0fa497da --- /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 "mem/ruby/common/Global.hh" +#include "mem/ruby/common/Consumer.hh" +#include "mem/ruby/system/NodeID.hh" +#include "mem/ruby/common/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..7896b572a --- /dev/null +++ b/src/mem/ruby/tester/Check.cc @@ -0,0 +1,310 @@ + +/* + * 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 "mem/ruby/tester/Check.hh" +#include "mem/ruby/system/Sequencer.hh" +#include "mem/ruby/system/System.hh" +#include "mem/ruby/common/SubBlock.hh" +#include "mem/protocol/Chip.hh" +#include "mem/packet.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; + } + + Addr data_addr = m_address.getAddress(); + Addr pc_addr = m_pc.getAddress(); + Request request(0, data_addr, 0, Flags<unsigned int>(Request::PREFETCH), pc_addr, 0, 0); + MemCmd::Command command; + if (type == CacheRequestType_IFETCH) { + command = MemCmd::ReadReq; + request.setFlags(Request::INST_FETCH); + } else if (type == CacheRequestType_LD || type == CacheRequestType_IFETCH) { + command = MemCmd::ReadReq; + } else if (type == CacheRequestType_ST) { + command = MemCmd::WriteReq; + } else if (type == CacheRequestType_ATOMIC) { + command = MemCmd::SwapReq; // TODO -- differentiate between atomic types + } else { + panic("Cannot convert request to packet"); + } + + Packet pkt(&request, command, 0); // TODO -- make dest a real NodeID + + assert(targetSequencer_ptr != NULL); + if (targetSequencer_ptr->isReady(&pkt)) { + targetSequencer_ptr->makeRequest(&pkt); + } +} + +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; + } + + Addr data_addr = m_address.getAddress()+m_store_count; + Addr pc_addr = m_pc.getAddress(); + Request request(0, data_addr, 1, Flags<unsigned int>(), pc_addr, 0, 0); + MemCmd::Command command; + if (type == CacheRequestType_IFETCH) { + command = MemCmd::ReadReq; + request.setFlags(Request::INST_FETCH); + } else if (type == CacheRequestType_LD || type == CacheRequestType_IFETCH) { + command = MemCmd::ReadReq; + } else if (type == CacheRequestType_ST) { + command = MemCmd::WriteReq; + } else if (type == CacheRequestType_ATOMIC) { + command = MemCmd::SwapReq; // TODO -- differentiate between atomic types + } else { + panic("Cannot convert request to packet"); + } + + Packet pkt(&request, command, 0); // TODO -- make dest a real NodeID + + Sequencer* sequencer_ptr = initiatingSequencer(); + if (sequencer_ptr->isReady(&pkt) == 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(&pkt); + } + 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; + } + + + Addr data_addr = m_address.getAddress()+m_store_count; + Addr pc_addr = m_pc.getAddress(); + Request request(0, data_addr, CHECK_SIZE, Flags<unsigned int>(), pc_addr, 0, 0); + MemCmd::Command command; + if (type == CacheRequestType_IFETCH) { + command = MemCmd::ReadReq; + request.setFlags(Request::INST_FETCH); + } else if (type == CacheRequestType_LD || type == CacheRequestType_IFETCH) { + command = MemCmd::ReadReq; + } else if (type == CacheRequestType_ST) { + command = MemCmd::WriteReq; + } else if (type == CacheRequestType_ATOMIC) { + command = MemCmd::SwapReq; // TODO -- differentiate between atomic types + } else { + panic("Cannot convert request to packet"); + } + + Packet pkt(&request, command, 0); // TODO -- make dest a real NodeID + + Sequencer* sequencer_ptr = initiatingSequencer(); + if (sequencer_ptr->isReady(&pkt) == 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(&pkt); + } + 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..8f08b3f40 --- /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 "mem/ruby/common/Global.hh" +#include "mem/ruby/common/Address.hh" +#include "mem/ruby/system/NodeID.hh" +#include "mem/protocol/TesterStatus.hh" +#include "mem/protocol/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..b8e57a646 --- /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 "mem/ruby/tester/CheckTable.hh" +#include "mem/ruby/tester/Check.hh" +#include "mem/gems_common/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..a7f486315 --- /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 "mem/ruby/common/Global.hh" +#include "mem/gems_common/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..e4d8addd2 --- /dev/null +++ b/src/mem/ruby/tester/DetermGETXGenerator.cc @@ -0,0 +1,160 @@ + +/* + * 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 "mem/ruby/tester/DetermGETXGenerator.hh" +#include "mem/protocol/DetermGETXGeneratorStatus.hh" +#include "mem/protocol/LockStatus.hh" +#include "mem/ruby/system/Sequencer.hh" +#include "mem/ruby/system/System.hh" +#include "mem/ruby/config/RubyConfig.hh" +#include "mem/ruby/common/SubBlock.hh" +#include "mem/ruby/tester/DeterministicDriver.hh" +#include "mem/protocol/Chip.hh" +#include "mem/packet.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"); + + Addr data_addr = m_address.getAddress(); + Request request(0, data_addr, 1, Flags<unsigned int>(), 3, 0, 0); + MemCmd::Command command; + command = MemCmd::WriteReq; + + Packet pkt(&request, command, 0); // TODO -- make dest a real NodeID + + sequencer()->makeRequest(&pkt); +} + +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..1f5b67653 --- /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 "mem/ruby/common/Global.hh" +#include "mem/ruby/common/Consumer.hh" +#include "mem/protocol/DetermGETXGeneratorStatus.hh" +#include "mem/ruby/system/NodeID.hh" +#include "mem/ruby/common/Address.hh" +#include "mem/ruby/tester/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..bafaa18ae --- /dev/null +++ b/src/mem/ruby/tester/DetermInvGenerator.cc @@ -0,0 +1,217 @@ + +/* + * 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 "mem/ruby/tester/DetermInvGenerator.hh" +#include "mem/protocol/DetermInvGeneratorStatus.hh" +#include "mem/protocol/LockStatus.hh" +#include "mem/ruby/system/Sequencer.hh" +#include "mem/ruby/system/System.hh" +#include "mem/ruby/config/RubyConfig.hh" +#include "mem/ruby/common/SubBlock.hh" +#include "mem/ruby/tester/DeterministicDriver.hh" +#include "mem/protocol/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); + 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"); + + Addr data_addr = m_address.getAddress(); + Request request(0, data_addr, 1, Flags<unsigned int>(), 1, 0, 0); + MemCmd::Command command; + command = MemCmd::ReadReq; + + Packet pkt(&request, command, 0); // TODO -- make dest a real NodeID + + sequencer()->makeRequest(&pkt); + +} + +void DetermInvGenerator::initiateStore() +{ + DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Store"); + + Addr data_addr = m_address.getAddress(); + Request request(0, data_addr, 1, Flags<unsigned int>(), 3, 0, 0); + MemCmd::Command command; + command = MemCmd::WriteReq; + + Packet pkt(&request, command, 0); // TODO -- make dest a real NodeID + + sequencer()->makeRequest(&pkt); +} + +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..4f0712fbe --- /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 "mem/ruby/common/Global.hh" +#include "mem/ruby/common/Consumer.hh" +#include "mem/protocol/DetermInvGeneratorStatus.hh" +#include "mem/ruby/system/NodeID.hh" +#include "mem/ruby/common/Address.hh" +#include "mem/ruby/tester/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..5adc7aa5c --- /dev/null +++ b/src/mem/ruby/tester/DetermSeriesGETSGenerator.cc @@ -0,0 +1,158 @@ + +/* + * 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 "mem/ruby/tester/DetermSeriesGETSGenerator.hh" +#include "mem/protocol/DetermSeriesGETSGeneratorStatus.hh" +#include "mem/protocol/LockStatus.hh" +#include "mem/ruby/system/Sequencer.hh" +#include "mem/ruby/system/System.hh" +#include "mem/ruby/config/RubyConfig.hh" +#include "mem/ruby/common/SubBlock.hh" +#include "mem/ruby/tester/DeterministicDriver.hh" +#include "mem/protocol/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"); + + Addr data_addr = m_address.getAddress(); + Request request(0, data_addr, 1, Flags<unsigned int>(), 3, 0, 0); + MemCmd::Command command; + command = MemCmd::ReadReq; + request.setFlags(Request::INST_FETCH); + + Packet pkt(&request, command, 0); // TODO -- make dest a real NodeID + + sequencer()->makeRequest(&pkt); +} + +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..1e44dc3bc --- /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 "mem/ruby/common/Global.hh" +#include "mem/ruby/common/Consumer.hh" +#include "mem/protocol/DetermSeriesGETSGeneratorStatus.hh" +#include "mem/ruby/system/NodeID.hh" +#include "mem/ruby/common/Address.hh" +#include "mem/ruby/tester/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..762672118 --- /dev/null +++ b/src/mem/ruby/tester/DeterministicDriver.cc @@ -0,0 +1,285 @@ + +/* + * 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 "mem/ruby/common/Global.hh" +#include "mem/ruby/system/System.hh" +#include "mem/ruby/tester/DeterministicDriver.hh" +#include "mem/ruby/eventqueue/RubyEventQueue.hh" +#include "mem/ruby/tester/SpecifiedGenerator.hh" +#include "mem/ruby/tester/DetermGETXGenerator.hh" +#include "mem/ruby/tester/DetermInvGenerator.hh" +#include "mem/ruby/tester/DetermSeriesGETSGenerator.hh" +#include "mem/ruby/common/SubBlock.hh" +#include "mem/protocol/Chip.hh" +#include "mem/packet.hh" + +DeterministicDriver::DeterministicDriver(RubySystem* sys_ptr) +{ + 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(Packet * pkt) +{ + NodeID proc = pkt->req->contextId(); + SubBlock data(Address(pkt->getAddr()), pkt->req->getSize()); + if (pkt->hasData()) { + for (int i = 0; i < pkt->req->getSize(); i++) { + data.setByte(i, *(pkt->getPtr<uint8>()+i)); + } + } + m_generator_vector[proc]->performCallback(proc, data); + 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); +#ifndef NDEBUG + Sequencer* seq_ptr = g_system_ptr->getChip(processor/RubyConfig::numberOfProcsPerChip())->getSequencer(processor%RubyConfig::numberOfProcsPerChip()); +#endif + 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..710da7922 --- /dev/null +++ b/src/mem/ruby/tester/DeterministicDriver.hh @@ -0,0 +1,126 @@ + +/* + * 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 "mem/ruby/common/Global.hh" +#include "mem/ruby/common/Driver.hh" +#include "mem/ruby/common/Histogram.hh" +#include "mem/protocol/CacheRequestType.hh" + +class RubySystem; +class SpecifiedGenerator; +class Packet; + +class DeterministicDriver : public Driver, public Consumer { +public: + // Constructors + DeterministicDriver(RubySystem* 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(Packet* pkt); + 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..1f4d56fc2 --- /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 "mem/ruby/tester/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..fe5540b00 --- /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 "mem/ruby/common/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..c56557645 --- /dev/null +++ b/src/mem/ruby/tester/RaceyDriver.cc @@ -0,0 +1,135 @@ + +/* + * 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 "mem/ruby/common/Global.hh" +#include "mem/ruby/system/System.hh" +#include "mem/ruby/tester/RaceyDriver.hh" +#include "mem/ruby/eventqueue/RubyEventQueue.hh" +#include "RaceyPseudoThread.hh" +#include "mem/ruby/common/SubBlock.hh" + +RaceyDriver::RaceyDriver() +{ + // 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..a3e35b47c --- /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 "mem/ruby/common/Global.hh" +#include "mem/ruby/common/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..4ee24544f --- /dev/null +++ b/src/mem/ruby/tester/RequestGenerator.cc @@ -0,0 +1,220 @@ + +/* + * 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 "mem/ruby/tester/RequestGenerator.hh" +#include "mem/protocol/RequestGeneratorStatus.hh" +#include "mem/protocol/LockStatus.hh" +#include "mem/ruby/system/Sequencer.hh" +#include "mem/ruby/system/System.hh" +#include "mem/ruby/config/RubyConfig.hh" +#include "mem/ruby/common/SubBlock.hh" +#include "mem/ruby/tester/SyntheticDriver.hh" +#include "mem/protocol/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"); + + Addr data_addr = m_address.getAddress(); + Request request(0, data_addr, 1, Flags<unsigned int>(), 1, 0, 0); + MemCmd::Command command; + command = MemCmd::ReadReq; + + Packet pkt(&request, command, 0); // TODO -- make dest a real NodeID + + sequencer()->makeRequest(&pkt); +} + +void RequestGenerator::initiateSwap() +{ + DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Swap"); + + Addr data_addr = m_address.getAddress(); + Request request(0, data_addr, 1, Flags<unsigned int>(), 2, 0, 0); + MemCmd::Command command; + command = MemCmd::SwapReq; + + Packet pkt(&request, command, 0); // TODO -- make dest a real NodeID + + sequencer()->makeRequest(&pkt); +} + +void RequestGenerator::initiateRelease() +{ + DEBUG_MSG(TESTER_COMP, MedPrio, "initiating Release"); + + Addr data_addr = m_address.getAddress(); + Request request(0, data_addr, 1, Flags<unsigned int>(), 3, 0, 0); + MemCmd::Command command; + command = MemCmd::WriteReq; + + Packet pkt(&request, command, 0); // TODO -- make dest a real NodeID + + sequencer()->makeRequest(&pkt); +} + +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..2859eb436 --- /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 "mem/ruby/common/Global.hh" +#include "mem/ruby/common/Consumer.hh" +#include "mem/protocol/RequestGeneratorStatus.hh" +#include "mem/ruby/system/NodeID.hh" +#include "mem/ruby/common/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/SConscript b/src/mem/ruby/tester/SConscript new file mode 100644 index 000000000..6409b4f50 --- /dev/null +++ b/src/mem/ruby/tester/SConscript @@ -0,0 +1,46 @@ +# -*- mode:python -*- + +# Copyright (c) 2009 The Hewlett-Packard Development Company +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Nathan Binkert + +Import('*') + +if not env['RUBY']: + Return() + +Source('Check.cc') +Source('CheckTable.cc') +Source('DetermGETXGenerator.cc') +Source('DetermInvGenerator.cc') +Source('DetermSeriesGETSGenerator.cc') +Source('DeterministicDriver.cc') +Source('Instruction.cc') +Source('RequestGenerator.cc') +Source('SpecifiedGenerator.cc') +Source('SyntheticDriver.cc') +Source('Tester.cc') diff --git a/src/mem/ruby/tester/SpecifiedGenerator.cc b/src/mem/ruby/tester/SpecifiedGenerator.cc new file mode 100644 index 000000000..17f827ee8 --- /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 "mem/ruby/tester/SpecifiedGenerator.hh" +#include "mem/ruby/system/Sequencer.hh" +#include "mem/ruby/system/System.hh" +#include "mem/ruby/common/SubBlock.hh" +#include "mem/ruby/tester/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..9b4c71623 --- /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 "mem/ruby/common/Global.hh" +#include "mem/ruby/common/Consumer.hh" +#include "mem/ruby/system/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..f19baa202 --- /dev/null +++ b/src/mem/ruby/tester/SyntheticDriver.cc @@ -0,0 +1,286 @@ + +/* + * 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 "mem/ruby/common/Global.hh" +#include "mem/ruby/system/System.hh" +#include "mem/ruby/tester/SyntheticDriver.hh" +#include "mem/ruby/eventqueue/RubyEventQueue.hh" +#include "mem/ruby/tester/RequestGenerator.hh" +#include "mem/ruby/common/SubBlock.hh" +#include "mem/protocol/Chip.hh" + +SyntheticDriver::SyntheticDriver(RubySystem* sys_ptr) +{ + 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(Packet * pkt) +{ + NodeID proc = pkt->req->contextId(); + SubBlock data(Address(pkt->getAddr()), pkt->req->getSize()); + if (pkt->hasData()) { + for (int i = 0; i < pkt->req->getSize(); i++) { + data.setByte(i, *(pkt->getPtr<uint8>()+i)); + } + } + m_request_generator_vector[proc]->performCallback(proc, data); + 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{ + 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 + 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); +#ifndef NDEBUG + Sequencer* seq_ptr = g_system_ptr->getChip(processor/RubyConfig::numberOfProcsPerChip())->getSequencer(processor%RubyConfig::numberOfProcsPerChip()); +#endif + 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..18c463e88 --- /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 "mem/ruby/common/Global.hh" +#include "mem/ruby/common/Driver.hh" +#include "mem/ruby/common/Histogram.hh" +#include "mem/protocol/CacheRequestType.hh" + +class RubySystem; +class RequestGenerator; + +class SyntheticDriver : public Driver, public Consumer { +public: + // Constructors + SyntheticDriver(RubySystem* 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(Packet* pkt); + 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..eafc04a92 --- /dev/null +++ b/src/mem/ruby/tester/Tester.cc @@ -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$ + * + */ + +#include "mem/ruby/common/Global.hh" +#include "mem/ruby/system/System.hh" +#include "mem/ruby/tester/Tester.hh" +#include "mem/ruby/eventqueue/RubyEventQueue.hh" +#include "mem/ruby/common/SubBlock.hh" +#include "mem/ruby/tester/Check.hh" +#include "mem/protocol/Chip.hh" + +Tester::Tester(RubySystem* sys_ptr) +{ + 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..7b721e038 --- /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 "mem/ruby/common/Global.hh" +#include "mem/ruby/common/Driver.hh" +#include "mem/ruby/tester/CheckTable.hh" +#include "mem/protocol/CacheRequestType.hh" + +class RubySystem; + +class Tester : public Driver, public Consumer { +public: + // Constructors + Tester(RubySystem* 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/main.cc b/src/mem/ruby/tester/main.cc new file mode 100644 index 000000000..ba835b488 --- /dev/null +++ b/src/mem/ruby/tester/main.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 "mem/ruby/tester/main.hh" +#include "mem/ruby/eventqueue/RubyEventQueue.hh" +#include "mem/ruby/config/RubyConfig.hh" +//#include "mem/ruby/tester/test_framework.hh" + +// ******************* +// *** tester main *** +// ******************* + +int main(int argc, char *argv[]) +{ + //dsm: PRUNED + //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..3708d770d --- /dev/null +++ b/src/mem/ruby/tester/main.hh @@ -0,0 +1,35 @@ + +/* + * 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. + */ + +#ifndef MAIN_H +#define MAIN_H + +#include "mem/ruby/common/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..9886adc8d --- /dev/null +++ b/src/mem/ruby/tester/test_framework.cc @@ -0,0 +1,426 @@ + +/* + * 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 "mem/protocol/protocol_name.hh" +#include "mem/ruby/tester/test_framework.hh" +#include "mem/ruby/system/System.hh" +#include "mem/ruby/init.hh" +#include "mem/ruby/tester/Tester.hh" +#include "mem/ruby/eventqueue/RubyEventQueue.hh" +#include "getopt.hh" +#include "mem/ruby/network/Network.hh" +#include "mem/ruby/recorder/CacheRecorder.hh" +#include "mem/ruby/recorder/Tracer.hh" + +using namespace std; +#include <string> +#include <map> + +// Maurice +// extern "C" { +// #include "simics/api.hh" +// }; + +#include "mem/gems_common/ioutil/confio.hh" +#include "mem/gems_common/ioutil/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 "mem/ruby/default_param.hh" +#include "mem/ruby/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) +{ + std::cout << __FILE__ << "(" << __LINE__ << "): Not implemented" << std::endl; +} + +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..5e9e9363b --- /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 "mem/ruby/common/Global.hh" +#include "mem/ruby/interfaces/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 |