blob: 6ef52fc5f5c3e9faa0d31415686825339fb3552d [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/AddressManager.hh"
#include <algorithm>
#include "base/intmath.hh"
#include "base/random.hh"
#include "base/trace.hh"
const int AddressManager::INVALID_VALUE = -1;
const int AddressManager::INVALID_LOCATION = -1;
AddressManager::AddressManager(int n_atomic_locs, int n_normal_locs_per_atomic)
: numAtomicLocs(n_atomic_locs),
numLocsPerAtomic(n_normal_locs_per_atomic)
{
assert(numAtomicLocs > 0 && numLocsPerAtomic > 0);
numNormalLocs = numAtomicLocs * numLocsPerAtomic;
// generate random address map
randAddressMap.resize(numAtomicLocs + numNormalLocs);
for (Location i = 0; i < numAtomicLocs + numNormalLocs; ++i) {
// all addresses are sizeof(Value) (i.e., 4-byte) aligned
randAddressMap[i] = (Addr)((i + 128) << floorLog2(sizeof(Value)));
}
// randomly shuffle randAddressMap
std::random_shuffle(randAddressMap.begin(), randAddressMap.end());
// initialize atomic locations
// first and last normal location per atomic location
Location first, last;
for (Location atomic_loc = 0; atomic_loc < numAtomicLocs; ++atomic_loc) {
first = numAtomicLocs + numLocsPerAtomic * atomic_loc;
last = first + numLocsPerAtomic - 1;
atomicStructs.push_back(new AtomicStruct(atomic_loc, first, last));
}
// initialize log table
for (Location loc = 0; loc < numAtomicLocs + numNormalLocs; ++loc) {
logTable.push_back(new LastWriter());
}
}
AddressManager::~AddressManager()
{
for (AtomicStruct* atomic_struct : atomicStructs)
delete atomic_struct;
for (LastWriter* lw : logTable)
delete lw;
}
Addr
AddressManager::getAddress(Location loc)
{
assert(loc < numAtomicLocs + numNormalLocs && loc >= 0);
return randAddressMap[loc];
}
AddressManager::Location
AddressManager::getAtomicLoc()
{
Location ret_atomic_loc = random() % numAtomicLocs;
atomicStructs[ret_atomic_loc]->startLocSelection();
return ret_atomic_loc;
}
AddressManager::Location
AddressManager::getLoadLoc(Location atomic_loc)
{
assert(atomic_loc >= 0 && atomic_loc < numAtomicLocs);
return atomicStructs[atomic_loc]->getLoadLoc();
}
AddressManager::Location
AddressManager::getStoreLoc(Location atomic_loc)
{
assert(atomic_loc >= 0 && atomic_loc < numAtomicLocs);
return atomicStructs[atomic_loc]->getStoreLoc();
}
void
AddressManager::finishLocSelection(Location atomic_loc)
{
assert(atomic_loc >= 0 && atomic_loc < numAtomicLocs);
atomicStructs[atomic_loc]->endLocSelection();
}
void
AddressManager::releaseLocation(Location atomic_loc, Location loc)
{
assert(atomic_loc >= 0 && atomic_loc < numAtomicLocs);
atomicStructs[atomic_loc]->releaseLoc(loc);
}
std::string
AddressManager::printLastWriter(Location loc) const
{
return logTable[loc]->print();
}
// ------------------- AtomicStruct --------------------------
AddressManager::AtomicStruct::AtomicStruct(Location atomic_loc,
Location loc_begin,
Location loc_end)
{
// the location range must have at least 1 location
assert(loc_begin <= loc_end);
atomicLoc = atomic_loc;
arraySize = loc_end - loc_begin + 1;
locationBase = loc_begin;
// allocate an array of arrray_size
locArray = new Location[arraySize];
// initialize locArray & locProps
Location loc;
for (int offset = 0; offset < arraySize; ++offset) {
loc = locationBase + offset;
locArray[offset] = loc;
locProps.push_back(LocProperty(offset, 0));
}
// region (1) and (3) are initially empty
firstMark = 0;
secondMark = arraySize;
// no request made at this location so far
requestCount = 0;
}
AddressManager::AtomicStruct::~AtomicStruct()
{
delete[] locArray;
}
void
AddressManager::AtomicStruct::startLocSelection()
{
assert(firstMark >= 0);
assert(firstMark <= secondMark);
assert(secondMark <= arraySize);
// make sure loadStoreMap has been cleared
assert(loadStoreMap.empty());
// this atomic location is picked for Atomic_ACQ
// and Atomic_REL in an episode
requestCount += 2;
// add two expected values in expectedValues set
expectedValues.insert(requestCount - 1);
expectedValues.insert(requestCount - 2);
}
AddressManager::Location
AddressManager::AtomicStruct::getLoadLoc()
{
assert(firstMark >= 0);
assert(firstMark <= secondMark);
assert(secondMark <= arraySize);
if (firstMark == arraySize) {
// no location can be picked for a LD now, so return an empty location
return INVALID_LOCATION;
} else {
// we can pick any location btw
// locArray [firstMark : arraySize-1]
int range_size = arraySize - firstMark;
Location ret_loc = locArray[firstMark + random() % range_size];
// update loadStoreMap
LdStMap::iterator it = loadStoreMap.find(ret_loc);
if (it == loadStoreMap.end()) {
// insert a new entry to the map b/c the entry is not there yet
// to mark this location has been picked for a LD
loadStoreMap.insert(std::pair<Location, LdStBits>
(ret_loc, LdStBits(true,false)));
} else {
// otherwise, just update the LD bit
(it->second).first = true;
}
return ret_loc;
}
}
AddressManager::Location
AddressManager::AtomicStruct::getStoreLoc()
{
assert(firstMark >= 0);
assert(firstMark <= secondMark);
assert(secondMark <= arraySize);
if (firstMark == secondMark) {
// no location can be picked for a ST now, return an invalid location
return INVALID_LOCATION;
} else {
// we can pick any location btw [firstMark : secondMark-1]
int range_size = secondMark - firstMark;
Location ret_loc = locArray[firstMark + random() % range_size];
// update loadStoreMap
LdStMap::iterator it = loadStoreMap.find(ret_loc);
if (it == loadStoreMap.end()) {
// insert a new entry to the map b/c the entry is not there yet
// to mark this location has been picked for a ST
loadStoreMap.insert(std::pair<Location, LdStBits>
(ret_loc, LdStBits(false,true)));
} else {
// otherwise, just update the ST bit
(it->second).second = true;
}
return ret_loc;
}
}
// for each entry in loadStoreMap,
// if <LD_bit, ST_bit> == <1,0>
// - if the location is in (2), then move it to (3)
// - if the location is in (3), no move
// - otherwise, throw an error
// if <LD_bit, ST_bit> == <0,1> or <1,1>
// - move it from (2) to (1)
void
AddressManager::AtomicStruct::endLocSelection()
{
assert(firstMark >= 0);
assert(firstMark <= secondMark);
assert(secondMark <= arraySize);
for (auto& it : loadStoreMap) {
Location loc = it.first;
LdStBits p = it.second;
assert(loc >= locationBase && loc < locationBase + arraySize);
LocProperty& loc_prop = locProps[loc - locationBase];
if (p.first && !p.second) {
// this location has been picked for LD(s) but not ST
// it must be in either region (2) or (3)
assert(inSecondRegion(loc_prop.first) ||
inThirdRegion(loc_prop.first));
if (inSecondRegion(loc_prop.first)) {
// there is no owner of this location yet
assert(loc_prop.second == 0);
// pick the last location in (2) to swap
Location swapped_loc = locArray[secondMark - 1];
LocProperty& swapped_loc_prop =
locProps[swapped_loc - locationBase];
// swap loc and swapped_loc
swap(loc_prop, swapped_loc_prop);
// then, expand (3)
secondMark--;
}
// increment the location's number of owners
loc_prop.second++;
} else if (p.second) {
// this location has been picked for ST(s) and/or LD(s)
// it must be in region (2)
assert(inSecondRegion(loc_prop.first) && loc_prop.second == 0);
// pick the first location in (2) to swap
Location swapped_loc = locArray[firstMark];
LocProperty& swapped_loc_prop =
locProps[swapped_loc - locationBase];
// swap loc and swapped_loc
swap(loc_prop, swapped_loc_prop);
// then, expand (1)
firstMark++;
// increment the location's number of owners
loc_prop.second++;
} else {
panic("Location in loadStoreMap but wasn't picked "
"in any action\n");
}
}
// clear the ld_st_map
loadStoreMap.clear();
}
void
AddressManager::AtomicStruct::releaseLoc(Location loc)
{
assert(loc >= locationBase && loc < locationBase + arraySize);
LocProperty& loc_prop = locProps[loc - locationBase];
if (inFirstRegion(loc_prop.first)) {
// this location must have exactly 1 owner
assert(loc_prop.second == 1);
// pick the last location in region 1 to swap
Location swapped_loc = locArray[firstMark - 1];
LocProperty& swapped_loc_prop = locProps[swapped_loc - locationBase];
// swap loc and swapped_loc
swap(loc_prop, swapped_loc_prop);
// then shrink (1)
firstMark--;
// reset the location's number of owners
loc_prop.second = 0;
} else if (inThirdRegion(loc_prop.first)) {
// this location must have at least 1 owner
assert(loc_prop.second >= 1);
if (loc_prop.second == 1) {
// pick the first location in region 3 to swap
Location swapped_loc = locArray[secondMark];
LocProperty& swapped_loc_prop =
locProps[swapped_loc - locationBase];
// swap loc and swapped_loc
swap(loc_prop, swapped_loc_prop);
// then shrink (3)
secondMark++;
}
// decrement the loc's number of owners
loc_prop.second--;
} else {
// some one else must already reset this counter
assert(inSecondRegion(loc_prop.first) && loc_prop.second == 0);
}
}
bool
AddressManager::AtomicStruct::isExpectedValue(Value val)
{
ExpectedValueSet::iterator it = expectedValues.find(val);
if (it == expectedValues.end()) {
return false;
}
// erase this value b/c it's done
expectedValues.erase(it);
return true;
}
void
AddressManager::AtomicStruct::swap(LocProperty& prop_1, LocProperty& prop_2)
{
int new_idx_1 = prop_2.first;
int new_idx_2 = prop_1.first;
// swap the two locations in locArray
Location tmp = locArray[prop_1.first];
locArray[prop_1.first] = locArray[prop_2.first];
locArray[prop_2.first] = tmp;
// update their new indices
prop_1.first = new_idx_1;
prop_2.first = new_idx_2;
}
// ------------------ log table ---------------------
void
AddressManager::updateLogTable(Location loc, int thread_id, int episode_id,
Value new_value, Tick cur_tick, int cu_id)
{
assert(loc >= 0 && loc < numAtomicLocs + numNormalLocs);
logTable[loc]->update(thread_id, cu_id, episode_id, new_value, cur_tick);
}
AddressManager::Value
AddressManager::getLoggedValue(Location loc) const
{
assert(loc >= 0 && loc < numAtomicLocs + numNormalLocs);
return logTable[loc]->getLastStoredValue();
}
bool
AddressManager::validateAtomicResp(Location loc, Value ret_val)
{
assert(loc >= 0 && loc < numAtomicLocs);
return atomicStructs[loc]->isExpectedValue(ret_val);
}