blob: 0029ee37fbe2348e7ebe076668913254495e7416 [file] [log] [blame]
/*
* Copyright (c) 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/dma_thread.hh"
#include "debug/ProtocolTest.hh"
namespace gem5
{
DmaThread::DmaThread(const Params& _params)
: TesterThread(_params)
{
threadName = "DmaThread(Thread ID " + std::to_string(threadId) + ")";
threadEvent.setDesc("DmaThread tick");
assert(numLanes == 1);
}
DmaThread::~DmaThread()
{
}
void
DmaThread::issueLoadOps()
{
assert(curAction);
assert(curAction->getType() == Episode::Action::Type::LOAD);
// we should not have any outstanding fence or atomic op at this point
assert(pendingFenceCount == 0);
assert(pendingAtomicCount == 0);
// DMA thread is a scalar thread so always set lane to zero. This allows
// us to reuse the API for GPU threads rather than have a specific API
// for scalar tester threads
int lane = 0;
Location location = curAction->getLocation(lane);
assert(location >= AddressManager::INVALID_LOCATION);
if (location >= 0) {
Addr address = addrManager->getAddress(location);
DPRINTF(ProtocolTest, "%s Episode %d: Issuing Load - Addr %s\n",
this->getName(), curEpisode->getEpisodeId(),
ruby::printAddress(address));
int load_size = sizeof(Value);
// for now, assert address is 4-byte aligned
assert(address % load_size == 0);
auto req = std::make_shared<Request>(address, load_size,
0, tester->requestorId(),
0, threadId, nullptr);
req->setPaddr(address);
req->setReqInstSeqNum(tester->getActionSeqNum());
PacketPtr pkt = new Packet(req, MemCmd::ReadReq);
uint8_t* data = new uint8_t[load_size];
pkt->dataDynamic(data);
pkt->senderState = new ProtocolTester::SenderState(this);
if (!port->sendTimingReq(pkt)) {
panic("Not expected failed sendTimingReq\n");
}
// insert an outstanding load
addOutstandingReqs(outstandingLoads, address, lane, location);
// increment the number of outstanding ld_st requests
pendingLdStCount++;
}
}
void
DmaThread::issueStoreOps()
{
assert(curAction);
assert(curAction->getType() == Episode::Action::Type::STORE);
// we should not have any outstanding fence or atomic op at this point
assert(pendingFenceCount == 0);
assert(pendingAtomicCount == 0);
// DMA thread is a scalar thread so always set lane to zero. This allows
// us to reuse the API for GPU threads rather than have a specific API
// for scalar tester threads
int lane = 0;
Location location = curAction->getLocation(lane);
assert(location >= AddressManager::INVALID_LOCATION);
if (location >= 0) {
// prepare the next value to store
Value new_value = addrManager->getLoggedValue(location) + 1;
Addr address = addrManager->getAddress(location);
// must be aligned with store size
assert(address % sizeof(Value) == 0);
DPRINTF(ProtocolTest, "%s Episode %d: Issuing Store - Addr %s - "
"Value %d\n", this->getName(),
curEpisode->getEpisodeId(), ruby::printAddress(address),
new_value);
auto req = std::make_shared<Request>(address, sizeof(Value),
0, tester->requestorId(), 0,
threadId, nullptr);
req->setPaddr(address);
req->setReqInstSeqNum(tester->getActionSeqNum());
PacketPtr pkt = new Packet(req, MemCmd::WriteReq);
uint8_t *writeData = new uint8_t[sizeof(Value)];
for (int j = 0; j < sizeof(Value); ++j) {
writeData[j] = ((uint8_t*)&new_value)[j];
}
pkt->dataDynamic(writeData);
pkt->senderState = new ProtocolTester::SenderState(this);
if (!port->sendTimingReq(pkt)) {
panic("Not expecting a failed sendTimingReq\n");
}
// add an outstanding store
addOutstandingReqs(outstandingStores, address, lane, location,
new_value);
// increment the number of outstanding ld_st requests
pendingLdStCount++;
}
}
void
DmaThread::issueAtomicOps()
{
DPRINTF(ProtocolTest, "Issuing Atomic Op ...\n");
assert(curAction);
assert(curAction->getType() == Episode::Action::Type::ATOMIC);
// we should not have any outstanding ops at this point
assert(pendingFenceCount == 0);
assert(pendingLdStCount == 0);
assert(pendingAtomicCount == 0);
// no-op: No DMA protocol exists with Atomics
}
void
DmaThread::issueAcquireOp()
{
DPRINTF(ProtocolTest, "Issuing Acquire Op ...\n");
assert(curAction);
assert(curAction->getType() == Episode::Action::Type::ACQUIRE);
// we should not have any outstanding ops at this point
assert(pendingFenceCount == 0);
assert(pendingLdStCount == 0);
assert(pendingAtomicCount == 0);
// no-op: Acquire does not apply to DMA threads
}
void
DmaThread::issueReleaseOp()
{
DPRINTF(ProtocolTest, "Issuing Release Op ...\n");
assert(curAction);
assert(curAction->getType() == Episode::Action::Type::RELEASE);
// we should not have any outstanding ops at this point
assert(pendingFenceCount == 0);
assert(pendingLdStCount == 0);
assert(pendingAtomicCount == 0);
// no-op: Release does not apply to DMA threads
}
void
DmaThread::hitCallback(PacketPtr pkt)
{
assert(pkt);
MemCmd resp_cmd = pkt->cmd;
Addr addr = pkt->getAddr();
DPRINTF(ProtocolTest, "%s Episode %d: hitCallback - Command %s -"
" Addr %s\n", this->getName(), curEpisode->getEpisodeId(),
resp_cmd.toString(), ruby::printAddress(addr));
if (resp_cmd == MemCmd::SwapResp) {
// response to a pending atomic
assert(pendingAtomicCount > 0);
assert(pendingLdStCount == 0);
assert(outstandingAtomics.count(addr) > 0);
// get return data
Value value = *(pkt->getPtr<Value>());
// validate atomic op return
OutstandingReq req = popOutstandingReq(outstandingAtomics, addr);
assert(req.lane == 0);
validateAtomicResp(req.origLoc, req.lane, value);
// update log table
addrManager->updateLogTable(req.origLoc, threadId,
curEpisode->getEpisodeId(), value,
curTick(),
0);
// this Atomic is done
pendingAtomicCount--;
} else if (resp_cmd == MemCmd::ReadResp) {
// response to a pending read
assert(pendingLdStCount > 0);
assert(pendingAtomicCount == 0);
assert(outstandingLoads.count(addr) > 0);
// get return data
Value value = *(pkt->getPtr<Value>());
OutstandingReq req = popOutstandingReq(outstandingLoads, addr);
assert(req.lane == 0);
validateLoadResp(req.origLoc, req.lane, value);
// this Read is done
pendingLdStCount--;
} else if (resp_cmd == MemCmd::WriteResp) {
// response to a pending write
assert(pendingLdStCount > 0);
assert(pendingAtomicCount == 0);
// no need to validate Write response
// just pop it from the outstanding req table so that subsequent
// requests dependent on this write can proceed
// note that unlike GpuWavefront we do decrement pendingLdStCount here
// since the write is guaranteed to be completed in downstream memory.
assert(outstandingStores.count(addr) > 0);
OutstandingReq req = popOutstandingReq(outstandingStores, addr);
assert(req.storedValue != AddressManager::INVALID_VALUE);
// update log table
addrManager->updateLogTable(req.origLoc, threadId,
curEpisode->getEpisodeId(),
req.storedValue,
curTick(),
0);
// the Write is now done
pendingLdStCount--;
} else {
panic("UnsupportedMemCmd response type: %s",
resp_cmd.toString().c_str());
}
delete pkt->senderState;
delete pkt;
// record the last active cycle to check for deadlock
lastActiveCycle = curCycle();
// we may be able to issue an action. Let's check
if (!threadEvent.scheduled()) {
scheduleWakeup();
}
}
} // namespace gem5