blob: 5a83d9ca27fdd70f537b916c15adeed23c59eace [file] [log] [blame]
/*
* 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/testers/rubytest/Check.hh"
#include "base/random.hh"
#include "base/trace.hh"
#include "debug/RubyTest.hh"
#include "mem/ruby/common/SubBlock.hh"
namespace gem5
{
typedef RubyTester::SenderState SenderState;
Check::Check(Addr address, Addr pc, int _num_writers, int _num_readers,
RubyTester* _tester)
: m_num_writers(_num_writers), m_num_readers(_num_readers),
m_tester_ptr(_tester)
{
m_status = ruby::TesterStatus_Idle;
pickValue();
pickInitiatingNode();
changeAddress(address);
m_pc = pc;
m_access_mode = ruby::RubyAccessMode(
random_mt.random(0, ruby::RubyAccessMode_NUM - 1));
m_store_count = 0;
}
void
Check::initiate()
{
DPRINTF(RubyTest, "initiating\n");
debugPrint();
// currently no protocols support prefetches
if (false && (random_mt.random(0, 0xf) == 0)) {
initiatePrefetch(); // Prefetch from random processor
}
if (m_tester_ptr->getCheckFlush() && (random_mt.random(0, 0xff) == 0)) {
initiateFlush(); // issue a Flush request from random processor
}
if (m_status == ruby::TesterStatus_Idle) {
initiateAction();
} else if (m_status == ruby::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");
int index = random_mt.random(0, m_num_readers - 1);
RequestPort* port = m_tester_ptr->getReadableCpuPort(index);
Request::Flags flags;
flags.set(Request::PREFETCH);
Packet::Command cmd;
// 1 in 8 chance this will be an exclusive prefetch
if (random_mt.random(0, 0x7) != 0) {
cmd = MemCmd::ReadReq;
// if necessary, make the request an instruction fetch
if (m_tester_ptr->isInstOnlyCpuPort(index) ||
(m_tester_ptr->isInstDataCpuPort(index) &&
(random_mt.random(0, 0x1)))) {
flags.set(Request::INST_FETCH);
}
} else {
cmd = MemCmd::WriteReq;
flags.set(Request::PF_EXCLUSIVE);
}
// Prefetches are assumed to be 0 sized
RequestPtr req = std::make_shared<Request>(
m_address, 0, flags, m_tester_ptr->requestorId());
req->setPC(m_pc);
req->setContext(index);
PacketPtr pkt = new Packet(req, cmd);
// despite the oddity of the 0 size (questionable if this should
// even be allowed), a prefetch is still a read and as such needs
// a place to store the result
uint8_t *data = new uint8_t[1];
pkt->dataDynamic(data);
// push the subblock onto the sender state. The sequencer will
// update the subblock on the return
pkt->senderState = new SenderState(m_address, req->getSize());
if (port->sendTimingReq(pkt)) {
DPRINTF(RubyTest, "successfully initiated prefetch.\n");
} else {
// If the packet did not issue, must delete
delete pkt->senderState;
delete pkt;
DPRINTF(RubyTest,
"prefetch initiation failed because Port was busy.\n");
}
}
void
Check::initiateFlush()
{
DPRINTF(RubyTest, "initiating Flush\n");
int index = random_mt.random(0, m_num_writers - 1);
RequestPort* port = m_tester_ptr->getWritableCpuPort(index);
Request::Flags flags;
RequestPtr req = std::make_shared<Request>(
m_address, CHECK_SIZE, flags, m_tester_ptr->requestorId());
req->setPC(m_pc);
Packet::Command cmd;
cmd = MemCmd::FlushReq;
PacketPtr pkt = new Packet(req, cmd);
// push the subblock onto the sender state. The sequencer will
// update the subblock on the return
pkt->senderState = new SenderState(m_address, req->getSize());
if (port->sendTimingReq(pkt)) {
DPRINTF(RubyTest, "initiating Flush - successful\n");
}
}
void
Check::initiateAction()
{
DPRINTF(RubyTest, "initiating Action\n");
assert(m_status == ruby::TesterStatus_Idle);
int index = random_mt.random(0, m_num_writers - 1);
RequestPort* port = m_tester_ptr->getWritableCpuPort(index);
Request::Flags flags;
// Create the particular address for the next byte to be written
Addr writeAddr(m_address + m_store_count);
// Stores are assumed to be 1 byte-sized
RequestPtr req = std::make_shared<Request>(
writeAddr, 1, flags, m_tester_ptr->requestorId());
req->setPC(m_pc);
req->setContext(index);
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);
uint8_t *writeData = new uint8_t[1];
*writeData = m_value + m_store_count;
pkt->dataDynamic(writeData);
DPRINTF(RubyTest, "Seq write: index %d data 0x%x check 0x%x\n", index,
*(pkt->getConstPtr<uint8_t>()), *writeData);
// push the subblock onto the sender state. The sequencer will
// update the subblock on the return
pkt->senderState = new SenderState(writeAddr, req->getSize());
if (port->sendTimingReq(pkt)) {
DPRINTF(RubyTest, "initiating action - successful\n");
DPRINTF(RubyTest, "status before action update: %s\n",
(ruby::TesterStatus_to_string(m_status)).c_str());
m_status = ruby::TesterStatus_Action_Pending;
DPRINTF(RubyTest, "Check %#x, State=Action_Pending\n", m_address);
} else {
// If the packet did not issue, must delete
// Note: No need to delete the data, the packet destructor
// will delete it
delete pkt->senderState;
delete pkt;
DPRINTF(RubyTest, "failed to initiate action - sequencer not ready\n");
}
DPRINTF(RubyTest, "status after action update: %s\n",
(ruby::TesterStatus_to_string(m_status)).c_str());
}
void
Check::initiateCheck()
{
DPRINTF(RubyTest, "Initiating Check\n");
assert(m_status == ruby::TesterStatus_Ready);
int index = random_mt.random(0, m_num_readers - 1);
RequestPort* port = m_tester_ptr->getReadableCpuPort(index);
Request::Flags flags;
// If necessary, make the request an instruction fetch
if (m_tester_ptr->isInstOnlyCpuPort(index) ||
(m_tester_ptr->isInstDataCpuPort(index) &&
(random_mt.random(0, 0x1)))) {
flags.set(Request::INST_FETCH);
}
// Checks are sized depending on the number of bytes written
RequestPtr req = std::make_shared<Request>(
m_address, CHECK_SIZE, flags, m_tester_ptr->requestorId());
req->setPC(m_pc);
req->setContext(index);
PacketPtr pkt = new Packet(req, MemCmd::ReadReq);
uint8_t *dataArray = new uint8_t[CHECK_SIZE];
pkt->dataDynamic(dataArray);
DPRINTF(RubyTest, "Seq read: index %d\n", index);
// push the subblock onto the sender state. The sequencer will
// update the subblock on the return
pkt->senderState = new SenderState(m_address, req->getSize());
if (port->sendTimingReq(pkt)) {
DPRINTF(RubyTest, "initiating check - successful\n");
DPRINTF(RubyTest, "status before check update: %s\n",
ruby::TesterStatus_to_string(m_status).c_str());
m_status = ruby::TesterStatus_Check_Pending;
DPRINTF(RubyTest, "Check %#x, State=Check_Pending\n", m_address);
} else {
// If the packet did not issue, must delete
// Note: No need to delete the data, the packet destructor
// will delete it
delete pkt->senderState;
delete pkt;
DPRINTF(RubyTest, "failed to initiate check - cpu port not ready\n");
}
DPRINTF(RubyTest, "status after check update: %s\n",
ruby::TesterStatus_to_string(m_status).c_str());
}
void
Check::performCallback(ruby::NodeID proc, ruby::SubBlock* data, Cycles curTime)
{
Addr address = data->getAddress();
// This isn't exactly right since we now have multi-byte checks
// assert(getAddress() == address);
assert(ruby::makeLineAddress(m_address) == ruby::makeLineAddress(address));
assert(data != NULL);
DPRINTF(RubyTest, "RubyTester Callback\n");
debugPrint();
if (m_status == ruby::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 = ruby::TesterStatus_Ready;
DPRINTF(RubyTest, "Check %#x, State=Ready\n", m_address);
} else {
m_status = ruby::TesterStatus_Idle;
DPRINTF(RubyTest, "Check %#x, State=Idle store_count: %d\n",
m_address, m_store_count);
}
DPRINTF(RubyTest, "Action callback return data now %d\n",
data->getByte(0));
} else if (m_status == ruby::TesterStatus_Check_Pending) {
DPRINTF(RubyTest, "Check callback\n");
// Perform load/check
for (int byte_number=0; byte_number<CHECK_SIZE; byte_number++) {
if (uint8_t(m_value + byte_number) != data->getByte(byte_number)) {
panic("Action/check failure: proc: %d address: %#x data: %s "
"byte_number: %d m_value+byte_number: %d byte: %d %s"
"Time: %d\n",
proc, address, data, byte_number,
(int)m_value + byte_number,
(int)data->getByte(byte_number), *this, curTime);
}
}
DPRINTF(RubyTest, "Action/check success\n");
debugPrint();
// successful check complete, increment complete
m_tester_ptr->incrementCheckCompletions();
m_status = ruby::TesterStatus_Idle;
DPRINTF(RubyTest, "Check %#x, State=Idle\n", m_address);
pickValue();
} else {
panic("Unexpected TesterStatus: %s proc: %d data: %s m_status: %s "
"time: %d\n", *this, proc, data, m_status, curTime);
}
DPRINTF(RubyTest, "proc: %d, Address: 0x%x\n", proc,
ruby::makeLineAddress(m_address));
DPRINTF(RubyTest, "Callback done\n");
debugPrint();
}
void
Check::changeAddress(Addr address)
{
assert(m_status == ruby::TesterStatus_Idle ||
m_status == ruby::TesterStatus_Ready);
m_status = ruby::TesterStatus_Idle;
m_address = address;
DPRINTF(RubyTest, "Check %#x, State=Idle\n", m_address);
m_store_count = 0;
}
void
Check::pickValue()
{
assert(m_status == ruby::TesterStatus_Idle);
m_value = random_mt.random(0, 0xff); // One byte
m_store_count = 0;
}
void
Check::pickInitiatingNode()
{
assert(m_status == ruby::TesterStatus_Idle ||
m_status == ruby::TesterStatus_Ready);
m_status = ruby::TesterStatus_Idle;
m_initiatingNode = (random_mt.random(0, m_num_writers - 1));
DPRINTF(RubyTest, "Check %#x, State=Idle, picked initiating node %d\n",
m_address, m_initiatingNode);
m_store_count = 0;
}
void
Check::print(std::ostream& out) const
{
out << "["
<< m_address << ", value: "
<< (int)m_value << ", status: "
<< m_status << ", initiating node: "
<< m_initiatingNode << ", store_count: "
<< m_store_count
<< "]" << std::flush;
}
void
Check::debugPrint()
{
DPRINTF(RubyTest,
"[%#x, value: %d, status: %s, initiating node: %d, store_count: %d]\n",
m_address, (int)m_value,
ruby::TesterStatus_to_string(m_status).c_str(),
m_initiatingNode, m_store_count);
}
} // namespace gem5