From 45230a4f6b82af61c05fcf93b6e1a9319fcb0a2e Mon Sep 17 00:00:00 2001 From: Brad Beckmann Date: Fri, 29 Jan 2010 20:29:23 -0800 Subject: [PATCH] ruby: added the GEMS ruby tester --- configs/example/rubytest.py | 126 ++++++++++ src/cpu/rubytest/Check.cc | 397 +++++++++++++++++++++++++++++++ src/cpu/rubytest/Check.hh | 105 ++++++++ src/cpu/rubytest/CheckTable.cc | 130 ++++++++++ src/cpu/rubytest/CheckTable.hh | 91 +++++++ src/cpu/rubytest/RubyTester.cc | 202 ++++++++++++++++ src/cpu/rubytest/RubyTester.hh | 162 +++++++++++++ src/cpu/rubytest/RubyTester.py | 38 +++ src/cpu/rubytest/SConscript | 47 ++++ src/mem/request.hh | 7 + src/mem/ruby/common/SubBlock.hh | 2 +- src/mem/ruby/system/RubyPort.cc | 10 +- src/mem/ruby/system/RubyPort.hh | 15 +- src/mem/ruby/system/Sequencer.cc | 28 ++- src/mem/ruby/system/Sequencer.hh | 2 + src/mem/ruby/system/Sequencer.py | 1 + 16 files changed, 1355 insertions(+), 8 deletions(-) create mode 100644 configs/example/rubytest.py create mode 100644 src/cpu/rubytest/Check.cc create mode 100644 src/cpu/rubytest/Check.hh create mode 100644 src/cpu/rubytest/CheckTable.cc create mode 100644 src/cpu/rubytest/CheckTable.hh create mode 100644 src/cpu/rubytest/RubyTester.cc create mode 100644 src/cpu/rubytest/RubyTester.hh create mode 100644 src/cpu/rubytest/RubyTester.py create mode 100644 src/cpu/rubytest/SConscript diff --git a/configs/example/rubytest.py b/configs/example/rubytest.py new file mode 100644 index 000000000..13b862756 --- /dev/null +++ b/configs/example/rubytest.py @@ -0,0 +1,126 @@ +# Copyright (c) 2006-2007 The Regents of The University of Michigan +# Copyright (c) 2009 Advanced Micro Devices, Inc. +# 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: Ron Dreslinski +# Brad Beckmann + +import m5 +from m5.objects import * +from m5.defines import buildEnv +from m5.util import addToPath +import os, optparse, sys +addToPath('../common') +addToPath('../ruby') + +import Ruby + +if buildEnv['FULL_SYSTEM']: + panic("This script requires system-emulation mode (*_SE).") + +# Get paths we might need. It's expected this file is in m5/configs/example. +config_path = os.path.dirname(os.path.abspath(__file__)) +config_root = os.path.dirname(config_path) +m5_root = os.path.dirname(config_root) + +parser = optparse.OptionParser() + +parser.add_option("-l", "--checks", metavar="N", default=100, + help="Stop after N checks (loads)") +parser.add_option("-f", "--wakeup_freq", metavar="N", default=10, + help="Wakeup every N cycles") + +# +# Set the default cache size and associativity to be very small to encourage +# races between requests and writebacks. +# +parser.add_option("--l1d_size", type="string", default="256B") +parser.add_option("--l1i_size", type="string", default="256B") +parser.add_option("--l2_size", type="string", default="512B") +parser.add_option("--l1d_assoc", type="int", default=2) +parser.add_option("--l1i_assoc", type="int", default=2) +parser.add_option("--l2_assoc", type="int", default=2) + +execfile(os.path.join(config_root, "common", "Options.py")) + +(options, args) = parser.parse_args() + +if args: + print "Error: script doesn't take any positional arguments" + sys.exit(1) + +# +# Create the ruby random tester +# +tester = RubyTester(checks_to_complete = options.checks, + wakeup_frequency = options.wakeup_freq) + +# +# Create the M5 system. Note that the PhysicalMemory Object isn't +# actually used by the rubytester, but is included to support the +# M5 memory size == Ruby memory size checks +# +system = System(physmem = PhysicalMemory()) + +system.ruby = Ruby.create_system(options, system.physmem) + +assert(options.num_cpus == len(system.ruby.cpu_ruby_ports)) + +# +# The tester is most effective when randomization is turned on and +# artifical delay is randomly inserted on messages +# +system.ruby.randomization = True + +for ruby_port in system.ruby.cpu_ruby_ports: + # + # Tie the ruby tester ports to the ruby cpu ports + # + tester.cpuPort = ruby_port.port + + # + # Tell each sequencer this is the ruby tester so that it + # copies the subblock back to the checker + # + ruby_port.using_ruby_tester = True + +# ----------------------- +# run simulation +# ----------------------- + +root = Root( system = system ) +root.system.mem_mode = 'timing' + +# Not much point in this being higher than the L1 latency +m5.ticks.setGlobalFrequency('1ns') + +# instantiate configuration +m5.instantiate(root) + +# simulate until program terminates +exit_event = m5.simulate(options.maxtick) + +print 'Exiting @ tick', m5.curTick(), 'because', exit_event.getCause() diff --git a/src/cpu/rubytest/Check.cc b/src/cpu/rubytest/Check.cc new file mode 100644 index 000000000..3b358e633 --- /dev/null +++ b/src/cpu/rubytest/Check.cc @@ -0,0 +1,397 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * Copyright (c) 2009 Advanced Micro Devices, Inc. + * 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. + */ + + +#include "cpu/rubytest/Check.hh" +#include "mem/ruby/system/Sequencer.hh" +#include "mem/ruby/system/System.hh" +#include "mem/ruby/common/SubBlock.hh" + +Check::Check(const Address& address, + const Address& pc, + int _num_cpu_sequencers, + RubyTester* _tester) + : m_num_cpu_sequencers(_num_cpu_sequencers), m_tester_ptr(_tester) +{ + 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() +{ + DPRINTF(RubyTest, "initiating\n"); + debugPrint(); + + // + // currently no protocols support prefetches + // + if (false && (random() & 0xf) == 0) { + initiatePrefetch(); // Prefetch from random processor + } + + if(m_status == TesterStatus_Idle) { + initiateAction(); + } else if(m_status == TesterStatus_Ready) { + initiateCheck(); + } else { + // Pending - do nothing + DPRINTF(RubyTest, "initiating action/check - failed: action/check is pending\n"); + } +} + +void Check::initiatePrefetch() +{ + DPRINTF(RubyTest, "initiating prefetch\n"); + + RubyTester::CpuPort* port + = safe_cast \ + (m_tester_ptr->getCpuPort(random() % m_num_cpu_sequencers)); + + Request::Flags flags; + flags.set(Request::PREFETCH); + + // + // Prefetches are assumed to be 0 sized + // + Request *req = new Request(m_address.getAddress(), + 0, + flags, + curTick, + m_pc.getAddress()); + + Packet::Command cmd; + + // + // 1 in 8 chance this will be an exclusive prefetch + // + if ((random() & 0x7) != 0) { + cmd = MemCmd::ReadReq; + // + // 50% chance that the request will be an instruction fetch + // + if ((random() & 0x1) == 0) { + flags.set(Request::INST_FETCH); + } + } else { + cmd = MemCmd::WriteReq; + flags.set(Request::PF_EXCLUSIVE); + } + + PacketPtr pkt = new Packet(req, cmd, port->idx); + + // + // push the subblock onto the sender state. The sequencer will update the + // subblock on the return + // + pkt->senderState = new RubyTester::SenderState(m_address, + req->getSize(), + pkt->senderState); + + if (port->sendTiming(pkt)) { + DPRINTF(RubyTest, "successfully initiated prefetch.\n"); + } else { + // + // If the packet did not issue, must delete + // + RubyTester::SenderState* senderState = + safe_cast(pkt->senderState); + pkt->senderState = senderState->saved; + delete senderState; + delete pkt->req; + delete pkt; + + DPRINTF(RubyTest, "prefetch initiation failed because Port was busy.\n"); + } +} + +void Check::initiateAction() +{ + DPRINTF(RubyTest, "initiating Action\n"); + assert(m_status == TesterStatus_Idle); + + RubyTester::CpuPort* port + = safe_cast \ + (m_tester_ptr->getCpuPort(random() % m_num_cpu_sequencers)); + + Request::Flags flags; + + // + // Create the particular address for the next byte to be written + // + Address writeAddr(m_address.getAddress() + m_store_count); + + // + // Stores are assumed to be 1 byte-sized + // + Request *req = new Request(writeAddr.getAddress(), + 1, + flags, + curTick, + m_pc.getAddress()); + + Packet::Command cmd; + + // + // 1 out of 8 chance, issue an atomic rather than a write + // +// if ((random() & 0x7) == 0) { +// cmd = MemCmd::SwapReq; +// } else { + cmd = MemCmd::WriteReq; +// } + + PacketPtr pkt = new Packet(req, cmd, port->idx); + uint8_t* writeData = new uint8_t; + *writeData = m_value + m_store_count; + pkt->dataDynamic(writeData); + + DPRINTF(RubyTest, + "data 0x%x check 0x%x\n", + *(pkt->getPtr()), + *writeData); + + // + // push the subblock onto the sender state. The sequencer will update the + // subblock on the return + // + pkt->senderState = new RubyTester::SenderState(writeAddr, + req->getSize(), + pkt->senderState); + + if (port->sendTiming(pkt)) { + DPRINTF(RubyTest, "initiating action - successful\n"); + DPRINTF(RubyTest, + "status before action update: %s\n", + (TesterStatus_to_string(m_status)).c_str()); + m_status = TesterStatus_Action_Pending; + } else { + // + // If the packet did not issue, must delete + // Note: No need to delete the data, the packet destructor will delete it + // + RubyTester::SenderState* senderState = + safe_cast(pkt->senderState); + pkt->senderState = senderState->saved; + delete senderState; + delete pkt->req; + delete pkt; + + DPRINTF(RubyTest, "failed to initiate action - sequencer not ready\n"); + } + + DPRINTF(RubyTest, + "status after action update: %s\n", + (TesterStatus_to_string(m_status)).c_str()); +} + +void Check::initiateCheck() +{ + DPRINTF(RubyTest, "Initiating Check\n"); + assert(m_status == TesterStatus_Ready); + + RubyTester::CpuPort* port + = safe_cast \ + (m_tester_ptr->getCpuPort(random() % m_num_cpu_sequencers)); + + Request::Flags flags; + + // + // Checks are sized depending on the number of bytes written + // + Request *req = new Request(m_address.getAddress(), + CHECK_SIZE, + flags, + curTick, + m_pc.getAddress()); + + // + // 50% chance that the request will be an instruction fetch + // + if ((random() & 0x1) == 0) { + flags.set(Request::INST_FETCH); + } + + PacketPtr pkt = new Packet(req, MemCmd::ReadReq, port->idx); + uint8_t* dataArray = new uint8_t[CHECK_SIZE]; + pkt->dataDynamicArray(dataArray); + + // + // push the subblock onto the sender state. The sequencer will update the + // subblock on the return + // + pkt->senderState = new RubyTester::SenderState(m_address, + req->getSize(), + pkt->senderState); + + if (port->sendTiming(pkt)) { + DPRINTF(RubyTest, "initiating check - successful\n"); + DPRINTF(RubyTest, + "status before check update: %s\n", + (TesterStatus_to_string(m_status)).c_str()); + m_status = TesterStatus_Check_Pending; + } else { + // + // If the packet did not issue, must delete + // Note: No need to delete the data, the packet destructor will delete it + // + RubyTester::SenderState* senderState = + safe_cast(pkt->senderState); + pkt->senderState = senderState->saved; + delete senderState; + delete pkt->req; + delete pkt; + + DPRINTF(RubyTest, "failed to initiate check - cpu port not ready\n"); + } + + DPRINTF(RubyTest, + "status after check update: %s\n", + (TesterStatus_to_string(m_status)).c_str()); +} + +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()); + assert(data != NULL); + + DPRINTF(RubyTest, "RubyTester Callback\n"); + debugPrint(); + + if (m_status == TesterStatus_Action_Pending) { + DPRINTF(RubyTest, + "Action callback write value: %d, currently %d\n", + (m_value + m_store_count), + data->getByte(0)); + // + // Perform store one byte at a time + // + data->setByte(0, (m_value + m_store_count)); + m_store_count++; + if (m_store_count == CHECK_SIZE) { + m_status = TesterStatus_Ready; + } else { + m_status = TesterStatus_Idle; + } + DPRINTF(RubyTest, + "Action callback return data now %d\n", + data->getByte(0)); + } else if (m_status == TesterStatus_Check_Pending) { + DPRINTF(RubyTest, "Check callback\n"); + // Perform load/check + for(int byte_number=0; byte_numbergetByte(byte_number)) { + 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"); + } + } + DPRINTF(RubyTest, "Action/check success\n"); + debugPrint(); + + // successful check complete, increment complete + m_tester_ptr->incrementCheckCompletions(); + + 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"); + } + + DPRINTF(RubyTest, "proc: %d, Address: 0x%x\n", proc, getAddress().getLineAddress()); + DPRINTF(RubyTest, "Callback done\n"); + debugPrint(); +} + +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; +} + +void Check::pickValue() +{ + assert(m_status == TesterStatus_Idle); + m_status = TesterStatus_Idle; + m_value = random() & 0xff; // One byte + m_store_count = 0; +} + +void Check::pickInitiatingNode() +{ + assert((m_status == TesterStatus_Idle) || (m_status == TesterStatus_Ready)); + m_status = TesterStatus_Idle; + m_initiatingNode = (random() % m_num_cpu_sequencers); + DPRINTF(RubyTest, "picked initiating node %d\n", 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; +} + +void Check::debugPrint() +{ + DPRINTF(RubyTest, + "[0x%x, value: %d, status: %s, initiating node: %d, store_count: %d]\n", + m_address.getAddress(), + (int)m_value, + (TesterStatus_to_string(m_status)).c_str(), + m_initiatingNode, + m_store_count); +} diff --git a/src/cpu/rubytest/Check.hh b/src/cpu/rubytest/Check.hh new file mode 100644 index 000000000..ce42ed376 --- /dev/null +++ b/src/cpu/rubytest/Check.hh @@ -0,0 +1,105 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * Copyright (c) 2009 Advanced Micro Devices, Inc. + * 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 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" +#include "cpu/rubytest/RubyTester.hh" +class SubBlock; + +const int CHECK_SIZE_BITS = 2; +const int CHECK_SIZE = (1<; + 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; iexist(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()), + m_num_cpu_sequencers, + m_tester_ptr); + for (int i=0; iadd(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/cpu/rubytest/CheckTable.hh b/src/cpu/rubytest/CheckTable.hh new file mode 100644 index 000000000..23726d26b --- /dev/null +++ b/src/cpu/rubytest/CheckTable.hh @@ -0,0 +1,91 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * Copyright (c) 2009 Advanced Micro Devices, Inc. + * 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 CHECKTABLE_H +#define CHECKTABLE_H + +#include "mem/ruby/common/Global.hh" +#include "mem/gems_common/Vector.hh" + +class Address; +class Check; +class RubyTester; +template class Map; + +class CheckTable { +public: + // Constructors + CheckTable(int _num_cpu_sequencers, RubyTester* _tester); + + // 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 m_check_vector; + Map* m_lookup_map_ptr; + + int m_num_cpu_sequencers; + RubyTester* m_tester_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/cpu/rubytest/RubyTester.cc b/src/cpu/rubytest/RubyTester.cc new file mode 100644 index 000000000..547b0eb17 --- /dev/null +++ b/src/cpu/rubytest/RubyTester.cc @@ -0,0 +1,202 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * Copyright (c) 2009 Advanced Micro Devices, Inc. + * 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. + */ + +#include "mem/ruby/common/Global.hh" +#include "mem/ruby/system/System.hh" +#include "cpu/rubytest/RubyTester.hh" +#include "mem/ruby/eventqueue/RubyEventQueue.hh" +#include "mem/ruby/common/SubBlock.hh" +#include "cpu/rubytest/Check.hh" +#include "sim/sim_exit.hh" + +RubyTester::RubyTester(const Params *p) + : MemObject(p), + checkStartEvent(this), + m_checks_to_complete(p->checks_to_complete), + m_deadlock_threshold(p->deadlock_threshold), + m_wakeup_frequency(p->wakeup_frequency) +{ + m_checks_completed = 0; + + // add the check start event to the event queue + schedule(checkStartEvent, 1); + +} + +RubyTester::~RubyTester() +{ + delete m_checkTable_ptr; + for (int i = 0; i < ports.size(); i++) { + delete ports[i]; + } +} + +void RubyTester::init() +{ + assert(ports.size() > 0); + + m_last_progress_vector.setSize(ports.size()); + for (int i = 0; i < m_last_progress_vector.size(); i++) { + m_last_progress_vector[i] = 0; + } + + m_num_cpu_sequencers = ports.size(); + + m_checkTable_ptr = new CheckTable(m_num_cpu_sequencers, this); +} + +Port * +RubyTester::getPort(const std::string &if_name, int idx) +{ + + if (if_name != "cpuPort") { + panic("RubyTester::getPort: unknown port %s requested", if_name); + } + + if (idx >= (int)ports.size()) { + ports.resize(idx + 1); + } + + if (ports[idx] != NULL) { + panic("RubyTester::getPort: port %d already assigned", idx); + } + + CpuPort *port = new CpuPort(csprintf("%s-port%d", name(), idx), this, idx); + + ports[idx] = port; + return port; +} + +Tick +RubyTester::CpuPort::recvAtomic(PacketPtr pkt) +{ + panic("RubyTester::CpuPort::recvAtomic() not implemented!\n"); + return 0; +} + +bool +RubyTester::CpuPort::recvTiming(PacketPtr pkt) +{ + // + // retrieve the subblock and call hitCallback + // + RubyTester::SenderState* senderState = + safe_cast(pkt->senderState); + SubBlock* subblock = senderState->subBlock; + assert(subblock != NULL); + + // pop the sender state from the packet + pkt->senderState = senderState->saved; + + tester->hitCallback(idx, subblock); + + // + // Now that the tester has completed, delete the senderState + // (includes sublock) and the packet, then return + // + delete senderState; + delete pkt->req; + delete pkt; + return true; +} + +Port* +RubyTester::getCpuPort(int idx) +{ + assert(idx >= 0 && idx < ports.size()); + + return ports[idx]; +} + +void RubyTester::hitCallback(NodeID proc, SubBlock* data) +{ + // Mark that we made progress + m_last_progress_vector[proc] = g_eventQueue_ptr->getTime(); + + DPRINTF(RubyTest, "completed request for proc: %d\n", proc); + DPRINTF(RubyTest, + "addr: 0x%x, size: %d, data: ", + data->getAddress(), + data->getSize()); + for (int byte = 0; byte < data->getSize(); byte++) { + DPRINTF(RubyTest, "%d", data->getByte(byte)); + } + DPRINTF(RubyTest, "\n"); + + // + // This tells us our store has 'completed' or for a load gives us + // back the data to make the check + // + Check* check_ptr = m_checkTable_ptr->getCheck(data->getAddress()); + assert(check_ptr != NULL); + check_ptr->performCallback(proc, data); +} + +void RubyTester::wakeup() +{ + if (m_checks_completed < m_checks_to_complete) { + // Try to perform an action or check + Check* check_ptr = m_checkTable_ptr->getRandomCheck(); + assert(check_ptr != NULL); + check_ptr->initiate(); + + checkForDeadlock(); + + schedule(checkStartEvent, curTick + m_wakeup_frequency); + } else { + exitSimLoop("Ruby Tester completed"); + } +} + +void RubyTester::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]) > m_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); + ERROR_MSG("Deadlock detected."); + } + } +} + +void RubyTester::print(ostream& out) const +{ + out << "[RubyTester]" << endl; +} + +RubyTester * +RubyTesterParams::create() +{ + return new RubyTester(this); +} diff --git a/src/cpu/rubytest/RubyTester.hh b/src/cpu/rubytest/RubyTester.hh new file mode 100644 index 000000000..f1ed7e34e --- /dev/null +++ b/src/cpu/rubytest/RubyTester.hh @@ -0,0 +1,162 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * Copyright (c) 2009 Advanced Micro Devices, Inc. + * 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 RUBY_TESTER_H +#define RUBY_TESTER_H + +#include "mem/ruby/common/Global.hh" +#include "mem/mem_object.hh" +#include "cpu/rubytest/CheckTable.hh" +#include "mem/ruby/system/RubyPort.hh" +#include "mem/ruby/common/SubBlock.hh" +#include "mem/ruby/common/DataBlock.hh" +#include "mem/packet.hh" +#include "params/RubyTester.hh" + +class RubyTester : public MemObject +{ + + public: + + class CpuPort : public SimpleTimingPort + { + RubyTester *tester; + + public: + + CpuPort(const std::string &_name, + RubyTester *_tester, + int _idx) + : SimpleTimingPort(_name, _tester), tester(_tester), idx(_idx) + {} + + int idx; + + protected: + + virtual bool recvTiming(PacketPtr pkt); + + virtual Tick recvAtomic(PacketPtr pkt); + + }; + + struct SenderState : public Packet::SenderState + { + SubBlock* subBlock; + Packet::SenderState *saved; + + SenderState(Address addr, + int size, + Packet::SenderState *sender_state = NULL) + : saved(sender_state) + {subBlock = new SubBlock(addr, size);} + + ~SenderState() {delete subBlock;} + }; + + typedef RubyTesterParams Params; + // Constructors + RubyTester(const Params *p); + + // Destructor + ~RubyTester(); + + // Public Methods + + virtual Port *getPort(const std::string &if_name, int idx = -1); + + Port* getCpuPort(int idx); + + void virtual init(); + + void wakeup(); + + void incrementCheckCompletions() { m_checks_completed++; } + + void printStats(ostream& out) const {} + void clearStats() {} + void printConfig(ostream& out) const {} + + void print(ostream& out) const; + + protected: + class CheckStartEvent : public Event + { + private: + RubyTester *tester; + + public: + CheckStartEvent(RubyTester *_tester) : Event(CPU_Tick_Pri), tester(_tester) {} + void process() { tester->wakeup(); } + virtual const char *description() const { return "RubyTester tick"; } + }; + + CheckStartEvent checkStartEvent; + + + private: + // Private Methods + + void hitCallback(NodeID proc, SubBlock* data); + + void checkForDeadlock(); + + // Private copy constructor and assignment operator + RubyTester(const RubyTester& obj); + RubyTester& operator=(const RubyTester& obj); + + // Data Members (m_ prefix) + + CheckTable* m_checkTable_ptr; + Vector