| /* | |
| * 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 "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() % 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 |