| /* |
| * Copyright (c) 2017-2021 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. |
| */ |
| |
| #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 |