In "src/cpu/testers/gpu_ruby_test" a random number generator was used. This was using the CPP "<random>" library. This patch changes it to the gem5 random class (that declared in "base/random.hh"). In addition to this, undeterministic behavior has been removed. Via "protocol_tester.cc" the RNG is either seeded with a seed specified by the user, or goes with the gem5 default seed. This ensures reproducable runs. Prior to this patch the RNG was seeded with `time(NULL)`. This made finding faults difficult. Change-Id: Ia8e9f7b87e91323f828e0b7f6c3906c0c5793b2c
326 lines
11 KiB
C++
326 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2017-2021 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:
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "cpu/testers/gpu_ruby_test/episode.hh"
|
|
|
|
#include <fstream>
|
|
#include <unordered_set>
|
|
|
|
#include "base/random.hh"
|
|
#include "cpu/testers/gpu_ruby_test/protocol_tester.hh"
|
|
#include "cpu/testers/gpu_ruby_test/tester_thread.hh"
|
|
|
|
namespace gem5
|
|
{
|
|
|
|
Episode::Episode(ProtocolTester* _tester, TesterThread* _thread, int num_loads,
|
|
int num_stores)
|
|
: tester(_tester),
|
|
thread(_thread),
|
|
numLoads(num_loads),
|
|
numStores(num_stores),
|
|
nextActionIdx(0)
|
|
{
|
|
assert(tester && thread);
|
|
|
|
episodeId = tester->getNextEpisodeID();
|
|
numLanes = thread->getNumLanes();
|
|
assert(numLanes > 0);
|
|
|
|
addrManager = tester->getAddressManager();
|
|
assert(addrManager);
|
|
|
|
atomicLocs.resize(numLanes, AddressManager::INVALID_LOCATION);
|
|
// generate a sequence of actions
|
|
initActions();
|
|
isActive = true;
|
|
|
|
DPRINTFN("Episode %d\n", episodeId);
|
|
}
|
|
|
|
Episode::~Episode()
|
|
{
|
|
for (Episode::Action* action : actions) {
|
|
assert(action);
|
|
delete action;
|
|
}
|
|
}
|
|
|
|
const Episode::Action*
|
|
Episode::peekCurAction() const
|
|
{
|
|
if (nextActionIdx < actions.size())
|
|
return actions[nextActionIdx];
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
void
|
|
Episode::popAction()
|
|
{
|
|
assert(nextActionIdx < actions.size());
|
|
nextActionIdx++;
|
|
}
|
|
|
|
void
|
|
Episode::initActions()
|
|
{
|
|
// first, push Atomic & then Acquire action
|
|
actions.push_back(new Action(Action::Type::ATOMIC, numLanes));
|
|
actions.push_back(new Action(Action::Type::ACQUIRE, numLanes));
|
|
|
|
// second, push a number of LD/ST actions
|
|
int num_loads = numLoads;
|
|
int num_stores = numStores;
|
|
while ((num_loads + num_stores) > 0) {
|
|
switch (random_mt.random<unsigned int>() % 2) {
|
|
case 0: // Load
|
|
if (num_loads > 0) {
|
|
actions.push_back(new Action(Action::Type::LOAD,
|
|
numLanes));
|
|
num_loads--;
|
|
}
|
|
break;
|
|
case 1: // Store
|
|
if (num_stores > 0) {
|
|
actions.push_back(new Action(Action::Type::STORE,
|
|
numLanes));
|
|
num_stores--;
|
|
}
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
// last, push an Release & then Atomic action
|
|
actions.push_back(new Action(Action::Type::RELEASE, numLanes));
|
|
actions.push_back(new Action(Action::Type::ATOMIC, numLanes));
|
|
|
|
// for each lane, pick a list of locations
|
|
Location normal_loc;
|
|
|
|
for (int lane = 0; lane < numLanes; ++lane) {
|
|
normal_loc = AddressManager::INVALID_LOCATION;
|
|
|
|
// first, we select atomic loc for this lane
|
|
// atomic loc for this lane should not have been picked yet
|
|
assert(atomicLocs[lane] == AddressManager::INVALID_LOCATION);
|
|
// pick randomly an atomic location
|
|
atomicLocs[lane] = addrManager->getAtomicLoc();
|
|
assert(atomicLocs[lane] >= 0);
|
|
|
|
// go through each action in this lane and set its location
|
|
for (Action* action : actions) {
|
|
assert(action);
|
|
|
|
switch (action->getType()) {
|
|
case Action::Type::ATOMIC:
|
|
action->setLocation(lane, atomicLocs[lane]);
|
|
break;
|
|
case Action::Type::LOAD:
|
|
// pick randomly a normal location
|
|
normal_loc = addrManager->
|
|
getLoadLoc(atomicLocs[lane]);
|
|
assert(normal_loc >= AddressManager::INVALID_LOCATION);
|
|
|
|
if (normal_loc != AddressManager::INVALID_LOCATION) {
|
|
// check DRF
|
|
if (!tester->checkDRF(atomicLocs[lane],
|
|
normal_loc, false) ||
|
|
!this->checkDRF(atomicLocs[lane], normal_loc,
|
|
false, lane)) {
|
|
panic("TestTh %d - Data race detected. STOPPED!\n",
|
|
thread->getTesterThreadId());
|
|
}
|
|
}
|
|
|
|
action->setLocation(lane, normal_loc);
|
|
break;
|
|
case Action::Type::STORE:
|
|
// pick randomly a normal location
|
|
normal_loc = addrManager->
|
|
getStoreLoc(atomicLocs[lane]);
|
|
assert(normal_loc >= AddressManager::INVALID_LOCATION);
|
|
|
|
if (normal_loc != AddressManager::INVALID_LOCATION) {
|
|
// check DRF
|
|
if (!tester->checkDRF(atomicLocs[lane],
|
|
normal_loc, true) ||
|
|
!this->checkDRF(atomicLocs[lane], normal_loc,
|
|
true, lane)) {
|
|
panic("TestTh %d - Data race detected. STOPPED!\n",
|
|
thread->getTesterThreadId());
|
|
}
|
|
}
|
|
|
|
action->setLocation(lane, normal_loc);
|
|
break;
|
|
case Action::Type::ACQUIRE:
|
|
case Action::Type::RELEASE:
|
|
// no op
|
|
break;
|
|
default:
|
|
panic("Invalid action type\n");
|
|
}
|
|
}
|
|
|
|
addrManager->finishLocSelection(atomicLocs[lane]);
|
|
}
|
|
}
|
|
|
|
void
|
|
Episode::completeEpisode()
|
|
{
|
|
// release all locations this episode has picked and used
|
|
Location atomic_loc, normal_loc;
|
|
for (int lane = 0; lane < numLanes; ++lane) {
|
|
atomic_loc = AddressManager::INVALID_LOCATION;
|
|
normal_loc = AddressManager::INVALID_LOCATION;
|
|
|
|
std::unordered_set<Location> unique_loc_set;
|
|
|
|
for (Action* action : actions) {
|
|
assert(action);
|
|
|
|
if (action->isAtomicAction()) {
|
|
if (atomic_loc == AddressManager::INVALID_LOCATION) {
|
|
atomic_loc = action->getLocation(lane);
|
|
} else {
|
|
// both atomic ops in the same lane must be
|
|
// at the same location
|
|
assert(atomic_loc == action->getLocation(lane));
|
|
}
|
|
} else if (!action->isMemFenceAction()) {
|
|
assert(atomic_loc >= 0);
|
|
normal_loc = action->getLocation(lane);
|
|
|
|
if (normal_loc >= 0)
|
|
unique_loc_set.insert(normal_loc);
|
|
}
|
|
}
|
|
|
|
// each unique loc can be released only once
|
|
for (Location loc : unique_loc_set)
|
|
addrManager->releaseLocation(atomic_loc, loc);
|
|
}
|
|
|
|
// this episode is no longer active
|
|
isActive = false;
|
|
}
|
|
|
|
bool
|
|
Episode::checkDRF(Location atomic_loc, Location loc, bool isStore,
|
|
int max_lane) const
|
|
{
|
|
assert(atomic_loc != AddressManager::INVALID_LOCATION);
|
|
assert(loc != AddressManager::INVALID_LOCATION);
|
|
assert(max_lane <= numLanes);
|
|
|
|
for (int lane = 0; lane < max_lane; ++lane) {
|
|
if (atomic_loc == atomicLocs[lane]) {
|
|
for (const Action* action : actions) {
|
|
if (!action->isAtomicAction() &&
|
|
!action->isMemFenceAction()) {
|
|
if (isStore && loc == action->getLocation(lane)) {
|
|
warn("ST at location %d races against thread %d\n",
|
|
loc, thread->getTesterThreadId());
|
|
return false;
|
|
} else if (!isStore &&
|
|
action->getType() == Action::Type::STORE &&
|
|
loc == action->getLocation(lane)) {
|
|
warn("LD at location %d races against thread %d\n",
|
|
loc, thread->getTesterThreadId());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// -------------------- Action class ----------------------------
|
|
Episode::Action::Action(Type t, int num_lanes)
|
|
: type(t),
|
|
numLanes(num_lanes)
|
|
{
|
|
assert(numLanes > 0);
|
|
locations.resize(numLanes);
|
|
for (Location &loc : locations) loc = AddressManager::INVALID_LOCATION;
|
|
}
|
|
|
|
void
|
|
Episode::Action::setLocation(int lane, Location loc)
|
|
{
|
|
assert(lane >= 0 && lane < numLanes);
|
|
locations[lane] = loc;
|
|
}
|
|
|
|
AddressManager::Location
|
|
Episode::Action::getLocation(int lane) const
|
|
{
|
|
assert(lane >= 0 && lane < numLanes);
|
|
return locations[lane];
|
|
}
|
|
|
|
bool
|
|
Episode::Action::isAtomicAction() const
|
|
{
|
|
return (type == Type::ATOMIC);
|
|
}
|
|
|
|
bool
|
|
Episode::Action::isMemFenceAction() const
|
|
{
|
|
return (type == Type::ACQUIRE || type == Type::RELEASE);
|
|
}
|
|
|
|
const std::string
|
|
Episode::Action::printType() const
|
|
{
|
|
if (type == Type::ACQUIRE)
|
|
return "ACQUIRE";
|
|
else if (type == Type::RELEASE)
|
|
return "RELEASE";
|
|
else if (type == Type::ATOMIC)
|
|
return "ATOMIC";
|
|
else if (type == Type::LOAD)
|
|
return "LOAD";
|
|
else if (type == Type::STORE)
|
|
return "STORE";
|
|
else
|
|
panic("Invalid action type\n");
|
|
}
|
|
|
|
} // namespace gem5
|