blob: 5184df730a66b2dc81216f47145c9a27b45b3dbb [file] [log] [blame]
/*
* Copyright (c) 2017 Advanced Micro Devices, Inc.
* All rights reserved.
*
* For use for simulation and test purposes only
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the copyright holder 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 HOLDER 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: Tuan Ta
*/
#include "cpu/testers/gpu_ruby_test/ProtocolTester.hh"
#include <algorithm>
#include <ctime>
#include <fstream>
#include <random>
#include "cpu/testers/gpu_ruby_test/CpuThread.hh"
#include "cpu/testers/gpu_ruby_test/GpuWavefront.hh"
#include "cpu/testers/gpu_ruby_test/Thread.hh"
#include "debug/ProtocolTest.hh"
#include "mem/request.hh"
#include "sim/sim_exit.hh"
#include "sim/system.hh"
ProtocolTester::ProtocolTester(const Params *p)
: MemObject(p),
_masterId(p->system->getMasterId(name())),
numCpuPorts(p->port_cpu_ports_connection_count),
numVectorPorts(p->port_cu_vector_ports_connection_count),
numSqcPorts(p->port_cu_sqc_ports_connection_count),
numScalarPorts(p->port_cu_scalar_ports_connection_count),
numCusPerSqc(p->cus_per_sqc),
numCusPerScalar(p->cus_per_scalar),
numWfsPerCu(p->wavefronts_per_cu),
numWisPerWf(p->workitems_per_wavefront),
numAtomicLocs(p->num_atomic_locations),
numNormalLocsPerAtomic(p->num_normal_locs_per_atomic),
episodeLength(p->episode_length),
maxNumEpisodes(p->max_num_episodes),
debugTester(p->debug_tester),
cpuThreads(p->cpu_threads),
wfs(p->wavefronts)
{
int idx = 0; // global port index
numCpus = numCpuPorts; // 1 cpu port per CPU
numCus = numVectorPorts; // 1 vector port per CU
// create all physical cpu's data ports
for (int i = 0; i < numCpuPorts; ++i) {
DPRINTF(ProtocolTest, "Creating %s\n",
csprintf("%s-cpuPort%d", name(), i));
cpuPorts.push_back(new SeqPort(csprintf("%s-cpuPort%d", name(), i),
this, i, idx));
idx++;
}
// create all physical gpu's data ports
for (int i = 0; i < numVectorPorts; ++i) {
DPRINTF(ProtocolTest, "Creating %s\n",
csprintf("%s-cuVectorPort%d", name(), i));
cuVectorPorts.push_back(new SeqPort(csprintf("%s-cuVectorPort%d",
name(), i),
this, i, idx));
idx++;
}
for (int i = 0; i < numScalarPorts; ++i) {
DPRINTF(ProtocolTest, "Creating %s\n",
csprintf("%s-cuScalarPort%d", name(), i));
cuScalarPorts.push_back(new SeqPort(csprintf("%s-cuScalarPort%d",
name(), i),
this, i, idx));
idx++;
}
for (int i = 0; i < numSqcPorts; ++i) {
DPRINTF(ProtocolTest, "Creating %s\n",
csprintf("%s-cuSqcPort%d", name(), i));
cuSqcPorts.push_back(new SeqPort(csprintf("%s-cuSqcPort%d",
name(), i),
this, i, idx));
idx++;
}
// create an address manager
addrManager = new AddressManager(numAtomicLocs,
numNormalLocsPerAtomic);
nextEpisodeId = 0;
if (!debugTester)
warn("Data race check is not enabled\n");
sentExitSignal = false;
// set random seed number
if (p->random_seed != 0) {
srand(p->random_seed);
} else {
srand(time(NULL));
}
actionCount = 0;
// create a new log file
logFile = simout.create(p->log_file);
assert(logFile);
// print test configs
std::stringstream ss;
ss << "GPU Ruby test's configurations" << std::endl
<< "\tNumber of CPUs: " << numCpus << std::endl
<< "\tNumber of CUs: " << numCus << std::endl
<< "\tNumber of wavefronts per CU: " << numWfsPerCu << std::endl
<< "\tWavefront size: " << numWisPerWf << std::endl
<< "\tNumber of atomic locations: " << numAtomicLocs << std::endl
<< "\tNumber of non-atomic locations: "
<< numNormalLocsPerAtomic * numAtomicLocs << std::endl
<< "\tEpisode length: " << episodeLength << std::endl
<< "\tTest length (max number of episodes): " << maxNumEpisodes
<< std::endl
<< "\tRandom seed: " << p->random_seed
<< std::endl;
ccprintf(*(logFile->stream()), "%s", ss.str());
logFile->stream()->flush();
}
ProtocolTester::~ProtocolTester()
{
for (int i = 0; i < cpuPorts.size(); ++i)
delete cpuPorts[i];
for (int i = 0; i < cuVectorPorts.size(); ++i)
delete cuVectorPorts[i];
for (int i = 0; i < cuScalarPorts.size(); ++i)
delete cuScalarPorts[i];
for (int i = 0; i < cuSqcPorts.size(); ++i)
delete cuSqcPorts[i];
delete addrManager;
// close the log file
simout.close(logFile);
}
void
ProtocolTester::init()
{
DPRINTF(ProtocolTest, "Attach threads to ports\n");
// connect cpu threads to cpu's ports
for (int cpu_id = 0; cpu_id < numCpus; ++cpu_id) {
cpuThreads[cpu_id]->attachThreadToPorts(this,
static_cast<SeqPort*>(cpuPorts[cpu_id]));
cpuThreads[cpu_id]->scheduleWakeup();
cpuThreads[cpu_id]->scheduleDeadlockCheckEvent();
}
// connect gpu wavefronts to gpu's ports
int wfId = 0;
int vectorPortId = 0;
int sqcPortId = 0;
int scalarPortId = 0;
for (int cu_id = 0; cu_id < numCus; ++cu_id) {
vectorPortId = cu_id;
sqcPortId = cu_id/numCusPerSqc;
// no scalar port if 'numCusPerScalar' is '0'
if (numCusPerScalar != 0)
scalarPortId = cu_id/numCusPerScalar;
for (int i = 0; i < numWfsPerCu; ++i) {
wfId = cu_id * numWfsPerCu + i;
wfs[wfId]->attachThreadToPorts(this,
static_cast<SeqPort*>(cuVectorPorts[vectorPortId]),
static_cast<SeqPort*>(cuSqcPorts[sqcPortId]),
!numCusPerScalar ? nullptr :
static_cast<SeqPort*>(cuScalarPorts[scalarPortId]));
wfs[wfId]->scheduleWakeup();
wfs[wfId]->scheduleDeadlockCheckEvent();
}
}
}
BaseMasterPort &
ProtocolTester::getMasterPort(const std::string & if_name, PortID idx)
{
if (if_name != "cpu_ports" && if_name != "cu_vector_ports" &&
if_name != "cu_sqc_ports" && if_name != "cu_scalar_ports") {
// pass along to super class
return MemObject::getMasterPort(if_name, idx);
} else {
if (if_name == "cpu_ports") {
if (idx > numCpuPorts)
panic("ProtocolTester: unknown cpu port %d\n", idx);
return *cpuPorts[idx];
} else if (if_name == "cu_vector_ports") {
if (idx > numVectorPorts)
panic("ProtocolTester: unknown cu vect port %d\n", idx);
return *cuVectorPorts[idx];
} else if (if_name == "cu_sqc_ports") {
if (idx > numSqcPorts)
panic("ProtocolTester: unknown cu sqc port %d\n", idx);
return *cuSqcPorts[idx];
} else {
assert(if_name == "cu_scalar_ports");
if (idx > numScalarPorts)
panic("ProtocolTester: unknown cu scal port %d\n", idx);
return *cuScalarPorts[idx];
}
}
assert(false);
}
bool
ProtocolTester::checkExit()
{
if (nextEpisodeId > maxNumEpisodes) {
if (!sentExitSignal) {
// all done
inform("Total completed episodes: %d\n", nextEpisodeId - 1);
inform("Protocol Test: Passed!\n");
exitSimLoop("ProtocolTester completed!");
sentExitSignal = true;
}
return true;
}
return false;
}
bool
ProtocolTester::checkDRF(Location atomic_loc,
Location loc, bool isStore) const
{
if (debugTester) {
// go through all active episodes in all threads
for (const Thread* th : wfs) {
if (!th->checkDRF(atomic_loc, loc, isStore))
return false;
}
for (const Thread* th : cpuThreads) {
if (!th->checkDRF(atomic_loc, loc, isStore))
return false;
}
}
return true;
}
void
ProtocolTester::dumpErrorLog(std::stringstream& ss)
{
if (!sentExitSignal) {
// go through all threads and dump their outstanding requests
for (auto t : cpuThreads) {
t->printAllOutstandingReqs(ss);
}
for (auto t : wfs) {
t->printAllOutstandingReqs(ss);
}
// dump error log into a file
assert(logFile);
ccprintf(*(logFile->stream()), "%s", ss.str());
logFile->stream()->flush();
// exit the sim loop
exitSimLoop("GPU Ruby Tester: Failed!", -1);
sentExitSignal = true;
}
}
bool
ProtocolTester::SeqPort::recvTimingResp(PacketPtr pkt)
{
// get the requesting thread from the original sender state
ProtocolTester::SenderState* senderState =
safe_cast<ProtocolTester::SenderState*>(pkt->senderState);
Thread *th = senderState->th;
th->hitCallback(pkt);
return true;
}
ProtocolTester*
ProtocolTesterParams::create()
{
return new ProtocolTester(this);
}