blob: 7e4b36f7e594c8f081ab309e659044db14ec053f [file] [log] [blame]
/*
* Copyright (c) 2011-2017 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 "gpu-compute/wavefront.hh"
#include "base/bitfield.hh"
#include "debug/GPUExec.hh"
#include "debug/GPUInitAbi.hh"
#include "debug/WavefrontStack.hh"
#include "gpu-compute/compute_unit.hh"
#include "gpu-compute/gpu_dyn_inst.hh"
#include "gpu-compute/scalar_register_file.hh"
#include "gpu-compute/shader.hh"
#include "gpu-compute/simple_pool_manager.hh"
#include "gpu-compute/vector_register_file.hh"
namespace gem5
{
Wavefront::Wavefront(const Params &p)
: SimObject(p), wfSlotId(p.wf_slot_id), simdId(p.simdId),
maxIbSize(p.max_ib_size), _gpuISA(*this),
vmWaitCnt(-1), expWaitCnt(-1), lgkmWaitCnt(-1),
vmemInstsIssued(0), expInstsIssued(0), lgkmInstsIssued(0),
sleepCnt(0), barId(WFBarrier::InvalidID), stats(this)
{
lastTrace = 0;
execUnitId = -1;
status = S_STOPPED;
reservedVectorRegs = 0;
reservedScalarRegs = 0;
startVgprIndex = 0;
startSgprIndex = 0;
outstandingReqs = 0;
outstandingReqsWrGm = 0;
outstandingReqsWrLm = 0;
outstandingReqsRdGm = 0;
outstandingReqsRdLm = 0;
rdLmReqsInPipe = 0;
rdGmReqsInPipe = 0;
wrLmReqsInPipe = 0;
wrGmReqsInPipe = 0;
scalarRdGmReqsInPipe = 0;
scalarWrGmReqsInPipe = 0;
scalarOutstandingReqsRdGm = 0;
scalarOutstandingReqsWrGm = 0;
lastNonIdleTick = 0;
ldsChunk = nullptr;
memTraceBusy = 0;
oldVgprTcnt = 0xffffffffffffffffll;
oldDgprTcnt = 0xffffffffffffffffll;
oldVgpr.resize(p.wf_size);
pendingFetch = false;
dropFetch = false;
maxVgprs = 0;
maxSgprs = 0;
lastAddr.resize(p.wf_size);
workItemFlatId.resize(p.wf_size);
oldDgpr.resize(p.wf_size);
for (int i = 0; i < 3; ++i) {
workItemId[i].resize(p.wf_size);
}
_execMask.set();
rawDist.clear();
lastInstExec = 0;
vecReads.clear();
}
void
Wavefront::init()
{
reservedVectorRegs = 0;
reservedScalarRegs = 0;
startVgprIndex = 0;
startSgprIndex = 0;
scalarAlu = computeUnit->mapWaveToScalarAlu(this);
scalarAluGlobalIdx = computeUnit->mapWaveToScalarAluGlobalIdx(this);
globalMem = computeUnit->mapWaveToGlobalMem(this);
localMem = computeUnit->mapWaveToLocalMem(this);
scalarMem = computeUnit->mapWaveToScalarMem(this);
}
void
Wavefront::initRegState(HSAQueueEntry *task, int wgSizeInWorkItems)
{
int regInitIdx = 0;
// iterate over all the init fields and check which
// bits are enabled
for (int en_bit = 0; en_bit < NumScalarInitFields; ++en_bit) {
if (task->sgprBitEnabled(en_bit)) {
int physSgprIdx = 0;
uint32_t wiCount = 0;
uint32_t firstWave = 0;
int orderedAppendTerm = 0;
int numWfsInWg = 0;
uint32_t finalValue = 0;
Addr host_disp_pkt_addr = task->hostDispPktAddr();
Addr kernarg_addr = task->kernargAddr();
Addr hidden_priv_base(0);
switch (en_bit) {
case PrivateSegBuf:
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
task->amdQueue.scratch_resource_descriptor[0]);
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting PrivateSegBuffer: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
task->amdQueue.scratch_resource_descriptor[0]);
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
task->amdQueue.scratch_resource_descriptor[1]);
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting PrivateSegBuffer: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
task->amdQueue.scratch_resource_descriptor[1]);
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
task->amdQueue.scratch_resource_descriptor[2]);
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting PrivateSegBuffer: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
task->amdQueue.scratch_resource_descriptor[2]);
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
task->amdQueue.scratch_resource_descriptor[3]);
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting PrivateSegBuffer: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
task->amdQueue.scratch_resource_descriptor[3]);
break;
case DispatchPtr:
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
bits(host_disp_pkt_addr, 31, 0));
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting DispatchPtr: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
bits(host_disp_pkt_addr, 31, 0));
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
bits(host_disp_pkt_addr, 63, 32));
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting DispatchPtr: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
bits(host_disp_pkt_addr, 63, 32));
++regInitIdx;
break;
case QueuePtr:
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
bits(task->hostAMDQueueAddr, 31, 0));
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting QueuePtr: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
bits(task->hostAMDQueueAddr, 31, 0));
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
bits(task->hostAMDQueueAddr, 63, 32));
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting QueuePtr: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
bits(task->hostAMDQueueAddr, 63, 32));
++regInitIdx;
break;
case KernargSegPtr:
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
bits(kernarg_addr, 31, 0));
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting KernargSegPtr: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
bits(kernarg_addr, 31, 0));
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
bits(kernarg_addr, 63, 32));
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting KernargSegPtr: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
bits(kernarg_addr, 63, 32));
++regInitIdx;
break;
case DispatchId:
physSgprIdx
= computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
task->dispatchId());
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting DispatchId: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
task->dispatchId());
break;
case FlatScratchInit:
physSgprIdx
= computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
(TheGpuISA::ScalarRegU32)(task->amdQueue
.scratch_backing_memory_location & 0xffffffff));
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting FlatScratch Addr: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
(TheGpuISA::ScalarRegU32)(task->amdQueue
.scratch_backing_memory_location & 0xffffffff));
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
// This vallue should be sizeof(DWORD) aligned, that is
// 4 byte aligned
computeUnit->srf[simdId]->write(physSgprIdx,
task->amdQueue.scratch_workitem_byte_size);
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting FlatScratch size: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
task->amdQueue.scratch_workitem_byte_size);
/**
* Since flat scratch init is needed for this kernel, this
* kernel is going to have flat memory instructions and we
* need to initialize the hidden private base for this queue.
* scratch_resource_descriptor[0] has this queue's scratch
* base address. scratch_backing_memory_location has the
* offset to this queue's scratch base address from the
* SH_HIDDEN_PRIVATE_BASE_VMID. Ideally, we only require this
* queue's scratch base address for address calculation
* (stored in scratch_resource_descriptor[0]). But that
* address calculation shoule be done by first finding the
* queue's scratch base address using the calculation
* "SH_HIDDEN_PRIVATE_BASE_VMID + offset". So, we initialize
* SH_HIDDEN_PRIVATE_BASE_VMID.
*
* For more details see:
* http://rocm-documentation.readthedocs.io/en/latest/
* ROCm_Compiler_SDK/ROCm-Native-ISA.html#flat-scratch
*
* https://github.com/ROCm-Developer-Tools/
* ROCm-ComputeABI-Doc/blob/master/AMDGPU-ABI.md
* #flat-addressing
*/
hidden_priv_base =
(uint64_t)task->amdQueue.scratch_resource_descriptor[0] |
(((uint64_t)task->amdQueue.scratch_resource_descriptor[1]
& 0x000000000000ffff) << 32);
computeUnit->shader->initShHiddenPrivateBase(
hidden_priv_base,
task->amdQueue.scratch_backing_memory_location);
break;
case PrivateSegSize:
physSgprIdx
= computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
task->privMemPerItem());
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting private segment size: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
task->privMemPerItem());
break;
case GridWorkgroupCountX:
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
wiCount = ((task->gridSize(0) +
task->wgSize(0) - 1) /
task->wgSize(0));
computeUnit->srf[simdId]->write(physSgprIdx, wiCount);
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting num WG X: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx, wiCount);
break;
case GridWorkgroupCountY:
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
wiCount = ((task->gridSize(1) +
task->wgSize(1) - 1) /
task->wgSize(1));
computeUnit->srf[simdId]->write(physSgprIdx, wiCount);
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting num WG Y: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx, wiCount);
break;
case GridWorkgroupCountZ:
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
wiCount = ((task->gridSize(2) +
task->wgSize(2) - 1) /
task->wgSize(2));
computeUnit->srf[simdId]->write(physSgprIdx, wiCount);
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting num WG Z: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx, wiCount);
break;
case WorkgroupIdX:
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
workGroupId[0]);
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting WG ID X: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx, workGroupId[0]);
break;
case WorkgroupIdY:
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
workGroupId[1]);
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting WG ID Y: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx, workGroupId[1]);
break;
case WorkgroupIdZ:
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->write(physSgprIdx,
workGroupId[2]);
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting WG ID Z: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx, workGroupId[2]);
break;
case PrivSegWaveByteOffset:
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
/**
* the compute_tmpring_size_wavesize specifies the number of
* kB allocated per wavefront, hence the multiplication by
* 1024.
*
* to get the per wavefront offset into the scratch
* memory, we also multiply this by the wfId. the wfId stored
* in the Wavefront class, however, is the wave ID within the
* WG, whereas here we need the global WFID because the
* scratch space will be divided amongst all waves in the
* kernel. to get the global ID we multiply the WGID by
* the WG size, then add the WFID of the wave within its WG.
*/
computeUnit->srf[simdId]->write(physSgprIdx, 1024 *
(wgId * (wgSz / 64) + wfId) *
task->amdQueue.compute_tmpring_size_wavesize);
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting Private Seg Offset: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx,
1024 * (wgId * (wgSz / 64) + wfId) *
task->amdQueue.compute_tmpring_size_wavesize);
break;
case WorkgroupInfo:
firstWave = (wfId == 0) ? 1 : 0;
numWfsInWg = divCeil(wgSizeInWorkItems,
computeUnit->wfSize());
finalValue = firstWave << ((sizeof(uint32_t) * 8) - 1);
finalValue |= (orderedAppendTerm << 6);
finalValue |= numWfsInWg;
physSgprIdx =
computeUnit->registerManager->mapSgpr(this, regInitIdx);
computeUnit->srf[simdId]->
write(physSgprIdx, finalValue);
++regInitIdx;
DPRINTF(GPUInitAbi, "CU%d: WF[%d][%d]: wave[%d] "
"Setting WG Info: s[%d] = %x\n",
computeUnit->cu_id, simdId,
wfSlotId, wfDynId, physSgprIdx, finalValue);
break;
default:
fatal("SGPR enable bit %i not supported\n", en_bit);
break;
}
}
}
regInitIdx = 0;
// iterate over all the init fields and check which
// bits are enabled
for (int en_bit = 0; en_bit < NumVectorInitFields; ++en_bit) {
if (task->vgprBitEnabled(en_bit)) {
uint32_t physVgprIdx = 0;
TheGpuISA::VecRegContainerU32 raw_vgpr;
switch (en_bit) {
case WorkitemIdX:
{
physVgprIdx = computeUnit->registerManager
->mapVgpr(this, regInitIdx);
TheGpuISA::VecElemU32 *vgpr_x
= raw_vgpr.as<TheGpuISA::VecElemU32>();
for (int lane = 0; lane < workItemId[0].size(); ++lane) {
vgpr_x[lane] = workItemId[0][lane];
}
computeUnit->vrf[simdId]->write(physVgprIdx, raw_vgpr);
rawDist[regInitIdx] = 0;
++regInitIdx;
}
break;
case WorkitemIdY:
{
physVgprIdx = computeUnit->registerManager
->mapVgpr(this, regInitIdx);
TheGpuISA::VecElemU32 *vgpr_y
= raw_vgpr.as<TheGpuISA::VecElemU32>();
for (int lane = 0; lane < workItemId[1].size(); ++lane) {
vgpr_y[lane] = workItemId[1][lane];
}
computeUnit->vrf[simdId]->write(physVgprIdx, raw_vgpr);
rawDist[regInitIdx] = 0;
++regInitIdx;
}
break;
case WorkitemIdZ:
{
physVgprIdx = computeUnit->registerManager->
mapVgpr(this, regInitIdx);
TheGpuISA::VecElemU32 *vgpr_z
= raw_vgpr.as<TheGpuISA::VecElemU32>();
for (int lane = 0; lane < workItemId[2].size(); ++lane) {
vgpr_z[lane] = workItemId[2][lane];
}
computeUnit->vrf[simdId]->write(physVgprIdx, raw_vgpr);
rawDist[regInitIdx] = 0;
++regInitIdx;
}
break;
}
}
}
}
void
Wavefront::resizeRegFiles(int num_vregs, int num_sregs)
{
maxVgprs = num_vregs;
maxSgprs = num_sregs;
}
Wavefront::~Wavefront()
{
}
void
Wavefront::setStatus(status_e newStatus)
{
if (computeUnit->idleCUTimeout > 0) {
// Wavefront's status transitions to stalled or stopped
if ((newStatus == S_STOPPED || newStatus == S_STALLED ||
newStatus == S_WAITCNT || newStatus == S_BARRIER) &&
(status != newStatus)) {
computeUnit->idleWfs++;
assert(computeUnit->idleWfs <=
(computeUnit->shader->n_wf * computeUnit->numVectorALUs));
if (computeUnit->idleWfs ==
(computeUnit->shader->n_wf * computeUnit->numVectorALUs)) {
lastNonIdleTick = curTick();
}
// Wavefront's status transitions to an active state (from
// a stopped or stalled state)
} else if ((status == S_STOPPED || status == S_STALLED ||
status == S_WAITCNT || status == S_BARRIER) &&
(status != newStatus)) {
// if all WFs in the CU were idle then check if the idleness
// period exceeded the timeout threshold
if (computeUnit->idleWfs ==
(computeUnit->shader->n_wf * computeUnit->numVectorALUs)) {
panic_if((curTick() - lastNonIdleTick) >=
computeUnit->idleCUTimeout,
"CU%d has been idle for %d ticks at tick %d",
computeUnit->cu_id, computeUnit->idleCUTimeout,
curTick());
}
computeUnit->idleWfs--;
assert(computeUnit->idleWfs >= 0);
}
}
status = newStatus;
}
void
Wavefront::start(uint64_t _wf_dyn_id, Addr init_pc)
{
wfDynId = _wf_dyn_id;
_pc = init_pc;
status = S_RUNNING;
vecReads.resize(maxVgprs, 0);
}
bool
Wavefront::isGmInstruction(GPUDynInstPtr ii)
{
if (ii->isGlobalMem() ||
(ii->isFlat() && ii->executedAs() == enums::SC_GLOBAL)) {
return true;
}
return false;
}
bool
Wavefront::isLmInstruction(GPUDynInstPtr ii)
{
if (ii->isLocalMem() ||
(ii->isFlat() && ii->executedAs() == enums::SC_GROUP)) {
return true;
}
return false;
}
bool
Wavefront::isOldestInstSleep()
{
if (instructionBuffer.empty())
return false;
GPUDynInstPtr ii = instructionBuffer.front();
if (ii->isSleep()) {
return true;
}
return false;
}
bool
Wavefront::isOldestInstWaitcnt()
{
if (instructionBuffer.empty())
return false;
GPUDynInstPtr ii = instructionBuffer.front();
if (ii->isWaitcnt()) {
// waitcnt is a scalar
assert(ii->isScalar());
return true;
}
return false;
}
bool
Wavefront::isOldestInstScalarALU()
{
assert(!instructionBuffer.empty());
GPUDynInstPtr ii = instructionBuffer.front();
if (status != S_STOPPED && ii->isScalar() && (ii->isNop() || ii->isReturn()
|| ii->isEndOfKernel() || ii->isBranch() || ii->isALU() ||
(ii->isKernArgSeg() && ii->isLoad()))) {
return true;
}
return false;
}
bool
Wavefront::isOldestInstVectorALU()
{
assert(!instructionBuffer.empty());
GPUDynInstPtr ii = instructionBuffer.front();
if (status != S_STOPPED && !ii->isScalar() && (ii->isNop() ||
ii->isReturn() || ii->isBranch() || ii->isALU() || ii->isEndOfKernel()
|| (ii->isKernArgSeg() && ii->isLoad()))) {
return true;
}
return false;
}
bool
Wavefront::isOldestInstBarrier()
{
assert(!instructionBuffer.empty());
GPUDynInstPtr ii = instructionBuffer.front();
if (status != S_STOPPED && ii->isBarrier()) {
return true;
}
return false;
}
bool
Wavefront::isOldestInstGMem()
{
assert(!instructionBuffer.empty());
GPUDynInstPtr ii = instructionBuffer.front();
if (status != S_STOPPED && !ii->isScalar() && ii->isGlobalMem()) {
return true;
}
return false;
}
bool
Wavefront::isOldestInstScalarMem()
{
assert(!instructionBuffer.empty());
GPUDynInstPtr ii = instructionBuffer.front();
if (status != S_STOPPED && ii->isScalar() && ii->isGlobalMem()) {
return true;
}
return false;
}
bool
Wavefront::isOldestInstLMem()
{
assert(!instructionBuffer.empty());
GPUDynInstPtr ii = instructionBuffer.front();
if (status != S_STOPPED && ii->isLocalMem()) {
return true;
}
return false;
}
bool
Wavefront::isOldestInstPrivMem()
{
assert(!instructionBuffer.empty());
GPUDynInstPtr ii = instructionBuffer.front();
if (status != S_STOPPED && ii->isPrivateSeg()) {
return true;
}
return false;
}
bool
Wavefront::isOldestInstFlatMem()
{
assert(!instructionBuffer.empty());
GPUDynInstPtr ii = instructionBuffer.front();
if (status != S_STOPPED && ii->isFlat()) {
return true;
}
return false;
}
bool
Wavefront::stopFetch()
{
for (auto it : instructionBuffer) {
GPUDynInstPtr ii = it;
if (ii->isReturn() || ii->isBranch() ||
ii->isEndOfKernel()) {
return true;
}
}
return false;
}
void
Wavefront::freeResources()
{
execUnitId = -1;
}
void Wavefront::validateRequestCounters()
{
panic_if(wrGmReqsInPipe < 0 || rdGmReqsInPipe < 0 ||
wrLmReqsInPipe < 0 || rdLmReqsInPipe < 0 ||
outstandingReqs < 0,
"Negative requests in pipe for WF%d for slot%d"
" and SIMD%d: Rd GlobalMem Reqs=%d, Wr GlobalMem Reqs=%d,"
" Rd LocalMem Reqs=%d, Wr LocalMem Reqs=%d,"
" Outstanding Reqs=%d\n",
wfDynId, wfSlotId, simdId, rdGmReqsInPipe, wrGmReqsInPipe,
rdLmReqsInPipe, wrLmReqsInPipe, outstandingReqs);
}
void
Wavefront::reserveGmResource(GPUDynInstPtr ii)
{
if (!ii->isScalar()) {
if (ii->isLoad()) {
rdGmReqsInPipe++;
} else if (ii->isStore()) {
wrGmReqsInPipe++;
} else if (ii->isAtomic() || ii->isMemSync()) {
rdGmReqsInPipe++;
wrGmReqsInPipe++;
} else {
panic("Invalid memory operation!\n");
}
execUnitId = globalMem;
} else {
if (ii->isLoad()) {
scalarRdGmReqsInPipe++;
} else if (ii->isStore()) {
scalarWrGmReqsInPipe++;
} else if (ii->isAtomic() || ii->isMemSync()) {
scalarWrGmReqsInPipe++;
scalarRdGmReqsInPipe++;
} else {
panic("Invalid memory operation!\n");
}
execUnitId = scalarMem;
}
}
void
Wavefront::reserveLmResource(GPUDynInstPtr ii)
{
fatal_if(ii->isScalar(),
"Scalar instructions can not access Shared memory!!!");
if (ii->isLoad()) {
rdLmReqsInPipe++;
} else if (ii->isStore()) {
wrLmReqsInPipe++;
} else if (ii->isAtomic() || ii->isMemSync()) {
wrLmReqsInPipe++;
rdLmReqsInPipe++;
} else {
panic("Invalid memory operation!\n");
}
execUnitId = localMem;
}
std::vector<int>
Wavefront::reserveResources()
{
// vector of execution unit IDs to return to schedule stage
// this return is only used for debugging and an assertion...
std::vector<int> execUnitIds;
// Get current instruction
GPUDynInstPtr ii = instructionBuffer.front();
assert(ii);
// Single precision ALU or Branch or Return or Special instruction
if (ii->isALU() || ii->isSpecialOp() ||
ii->isBranch() || ii->isNop() ||
(ii->isKernArgSeg() && ii->isLoad()) || ii->isArgSeg() ||
ii->isReturn() || ii->isEndOfKernel()) {
if (!ii->isScalar()) {
execUnitId = simdId;
} else {
execUnitId = scalarAluGlobalIdx;
}
// this is to enforce a fixed number of cycles per issue slot per SIMD
} else if (ii->isBarrier()) {
execUnitId = ii->isScalar() ? scalarAluGlobalIdx : simdId;
} else if (ii->isFlat()) {
assert(!ii->isScalar());
reserveLmResource(ii);
// add execUnitId, reserved by reserveLmResource, list before it is
// overwriten by reserveGmResource
execUnitIds.push_back(execUnitId);
flatLmUnitId = execUnitId;
reserveGmResource(ii);
flatGmUnitId = execUnitId;
execUnitIds.push_back(flatGmUnitId);
execUnitId = -1;
} else if (ii->isGlobalMem()) {
reserveGmResource(ii);
} else if (ii->isLocalMem()) {
reserveLmResource(ii);
} else if (ii->isPrivateSeg()) {
fatal_if(ii->isScalar(),
"Scalar instructions can not access Private memory!!!");
reserveGmResource(ii);
} else {
panic("reserveResources -> Couldn't process op!\n");
}
if (execUnitId != -1) {
execUnitIds.push_back(execUnitId);
}
assert(execUnitIds.size());
return execUnitIds;
}
void
Wavefront::exec()
{
// ---- Exit if wavefront is inactive ----------------------------- //
if (status == S_STOPPED || status == S_RETURNING ||
status==S_STALLED || instructionBuffer.empty()) {
return;
}
if (status == S_WAITCNT) {
/**
* if this wave is in S_WAITCNT state, then
* it should enter exec() precisely one time
* before the waitcnts are satisfied, in order
* to execute the waitcnt instruction itself
* thus we assert that the waitcnt is the
* oldest instruction. if we enter exec() with
* active waitcnts, and we're not executing
* the waitcnt instruction, something must be
* wrong
*/
assert(isOldestInstWaitcnt());
}
// Get current instruction
GPUDynInstPtr ii = instructionBuffer.front();
const Addr old_pc = pc();
DPRINTF(GPUExec, "CU%d: WF[%d][%d]: wave[%d] Executing inst: %s "
"(pc: %#x; seqNum: %d)\n", computeUnit->cu_id, simdId, wfSlotId,
wfDynId, ii->disassemble(), old_pc, ii->seqNum());
ii->execute(ii);
// delete the dynamic instruction from the pipeline map
computeUnit->deleteFromPipeMap(this);
// update the instruction stats in the CU
computeUnit->updateInstStats(ii);
// inform VRF of instruction execution to schedule write-back
// and scoreboard ready for registers
if (!ii->isScalar()) {
computeUnit->vrf[simdId]->waveExecuteInst(this, ii);
}
computeUnit->srf[simdId]->waveExecuteInst(this, ii);
computeUnit->shader->incVectorInstSrcOperand(ii->numSrcVecRegOperands());
computeUnit->shader->incVectorInstDstOperand(ii->numDstVecRegOperands());
computeUnit->stats.numInstrExecuted++;
stats.numInstrExecuted++;
computeUnit->instExecPerSimd[simdId]++;
computeUnit->stats.execRateDist.sample(
computeUnit->stats.totalCycles.value() -
computeUnit->lastExecCycle[simdId]);
computeUnit->lastExecCycle[simdId] =
computeUnit->stats.totalCycles.value();
if (lastInstExec) {
computeUnit->stats.instInterleave[simdId].
sample(computeUnit->instExecPerSimd[simdId] - lastInstExec);
}
lastInstExec = computeUnit->instExecPerSimd[simdId];
// want to track:
// number of reads that occur per value written
// vector RAW dependency tracking
for (const auto& srcVecOp : ii->srcVecRegOperands()) {
for (const auto& virtIdx : srcVecOp.virtIndices()) {
// This check should never fail, but to be safe we check
if (rawDist.find(virtIdx) != rawDist.end()) {
stats.vecRawDistance.sample(stats.numInstrExecuted.value() -
rawDist[virtIdx]);
}
// increment number of reads to this register
vecReads[virtIdx]++;
}
}
for (const auto& dstVecOp : ii->dstVecRegOperands()) {
for (const auto& virtIdx : dstVecOp.virtIndices()) {
// rawDist is set on writes, but will not be set for the first
// write to each physical register
if (rawDist.find(virtIdx) != rawDist.end()) {
// Sample the number of reads that were performed
stats.readsPerWrite.sample(vecReads[virtIdx]);
}
// on a write, reset count of reads to 0
vecReads[virtIdx] = 0;
rawDist[virtIdx] = stats.numInstrExecuted.value();
}
}
if (pc() == old_pc) {
// PC not modified by instruction, proceed to next
_gpuISA.advancePC(ii);
instructionBuffer.pop_front();
} else {
DPRINTF(GPUExec, "CU%d: WF[%d][%d]: wave%d %s taken branch\n",
computeUnit->cu_id, simdId, wfSlotId, wfDynId,
ii->disassemble());
discardFetch();
}
DPRINTF(GPUExec, "CU%d: WF[%d][%d]: wave[%d] (pc: %#x)\n",
computeUnit->cu_id, simdId, wfSlotId, wfDynId, pc());
if (computeUnit->shader->hsail_mode==Shader::SIMT) {
const int num_active_lanes = execMask().count();
computeUnit->stats.controlFlowDivergenceDist.sample(num_active_lanes);
computeUnit->stats.numVecOpsExecuted += num_active_lanes;
if (ii->isF16() && ii->isALU()) {
if (ii->isF32() || ii->isF64()) {
fatal("Instruction is tagged as both (1) F16, and (2)"
"either F32 or F64.");
}
computeUnit->stats.numVecOpsExecutedF16 += num_active_lanes;
if (ii->isFMA()) {
computeUnit->stats.numVecOpsExecutedFMA16 += num_active_lanes;
computeUnit->stats.numVecOpsExecutedTwoOpFP
+= num_active_lanes;
}
else if (ii->isMAC()) {
computeUnit->stats.numVecOpsExecutedMAC16 += num_active_lanes;
computeUnit->stats.numVecOpsExecutedTwoOpFP
+= num_active_lanes;
}
else if (ii->isMAD()) {
computeUnit->stats.numVecOpsExecutedMAD16 += num_active_lanes;
computeUnit->stats.numVecOpsExecutedTwoOpFP
+= num_active_lanes;
}
}
if (ii->isF32() && ii->isALU()) {
if (ii->isF16() || ii->isF64()) {
fatal("Instruction is tagged as both (1) F32, and (2)"
"either F16 or F64.");
}
computeUnit->stats.numVecOpsExecutedF32 += num_active_lanes;
if (ii->isFMA()) {
computeUnit->stats.numVecOpsExecutedFMA32 += num_active_lanes;
computeUnit->stats.numVecOpsExecutedTwoOpFP
+= num_active_lanes;
}
else if (ii->isMAC()) {
computeUnit->stats.numVecOpsExecutedMAC32 += num_active_lanes;
computeUnit->stats.numVecOpsExecutedTwoOpFP
+= num_active_lanes;
}
else if (ii->isMAD()) {
computeUnit->stats.numVecOpsExecutedMAD32 += num_active_lanes;
computeUnit->stats.numVecOpsExecutedTwoOpFP
+= num_active_lanes;
}
}
if (ii->isF64() && ii->isALU()) {
if (ii->isF16() || ii->isF32()) {
fatal("Instruction is tagged as both (1) F64, and (2)"
"either F16 or F32.");
}
computeUnit->stats.numVecOpsExecutedF64 += num_active_lanes;
if (ii->isFMA()) {
computeUnit->stats.numVecOpsExecutedFMA64 += num_active_lanes;
computeUnit->stats.numVecOpsExecutedTwoOpFP
+= num_active_lanes;
}
else if (ii->isMAC()) {
computeUnit->stats.numVecOpsExecutedMAC64 += num_active_lanes;
computeUnit->stats.numVecOpsExecutedTwoOpFP
+= num_active_lanes;
}
else if (ii->isMAD()) {
computeUnit->stats.numVecOpsExecutedMAD64 += num_active_lanes;
computeUnit->stats.numVecOpsExecutedTwoOpFP
+= num_active_lanes;
}
}
if (isGmInstruction(ii)) {
computeUnit->stats.activeLanesPerGMemInstrDist.sample(
num_active_lanes);
} else if (isLmInstruction(ii)) {
computeUnit->stats.activeLanesPerLMemInstrDist.sample(
num_active_lanes);
}
}
/**
* we return here to avoid spurious errors related to flat insts
* and their address segment resolution.
*/
if (execMask().none() && ii->isFlat()) {
computeUnit->getTokenManager()->recvTokens(1);
return;
}
// Update Vector ALU pipeline and other resources
bool flat_as_gm = false;
bool flat_as_lm = false;
if (ii->isFlat()) {
flat_as_gm = (ii->executedAs() == enums::SC_GLOBAL) ||
(ii->executedAs() == enums::SC_PRIVATE);
flat_as_lm = (ii->executedAs() == enums::SC_GROUP);
}
// Single precision ALU or Branch or Return or Special instruction
// Note, we use the same timing regardless of SP or DP ALU operation.
if (ii->isALU() || ii->isSpecialOp() ||
ii->isBranch() || ii->isNop() ||
(ii->isKernArgSeg() && ii->isLoad()) ||
ii->isArgSeg() || ii->isEndOfKernel() || ii->isReturn()) {
// this is to enforce a fixed number of cycles per issue slot per SIMD
if (!ii->isScalar()) {
computeUnit->vectorALUs[simdId].set(computeUnit->
cyclesToTicks(computeUnit->issuePeriod));
} else {
computeUnit->scalarALUs[scalarAlu].set(computeUnit->
cyclesToTicks(computeUnit->issuePeriod));
}
// Barrier on Scalar ALU
} else if (ii->isBarrier()) {
computeUnit->scalarALUs[scalarAlu].set(computeUnit->
cyclesToTicks(computeUnit->issuePeriod));
// GM or Flat as GM Load
} else if (ii->isLoad() && (ii->isGlobalMem() || flat_as_gm)) {
if (!ii->isScalar()) {
computeUnit->vrfToGlobalMemPipeBus.set(
computeUnit->cyclesToTicks(computeUnit->vrf_gm_bus_latency));
computeUnit->vectorGlobalMemUnit.
set(computeUnit->cyclesToTicks(computeUnit->issuePeriod));
computeUnit->stats.instCyclesVMemPerSimd[simdId] +=
computeUnit->vrf_gm_bus_latency;
} else {
computeUnit->srfToScalarMemPipeBus.set(computeUnit->
cyclesToTicks(computeUnit->srf_scm_bus_latency));
computeUnit->scalarMemUnit.
set(computeUnit->cyclesToTicks(computeUnit->issuePeriod));
computeUnit->stats.instCyclesScMemPerSimd[simdId] +=
computeUnit->srf_scm_bus_latency;
}
// GM or Flat as GM Store
} else if (ii->isStore() && (ii->isGlobalMem() || flat_as_gm)) {
if (!ii->isScalar()) {
computeUnit->vrfToGlobalMemPipeBus.set(computeUnit->
cyclesToTicks(Cycles(2 * computeUnit->vrf_gm_bus_latency)));
computeUnit->vectorGlobalMemUnit.
set(computeUnit->cyclesToTicks(computeUnit->issuePeriod));
computeUnit->stats.instCyclesVMemPerSimd[simdId] +=
(2 * computeUnit->vrf_gm_bus_latency);
} else {
computeUnit->srfToScalarMemPipeBus.set(computeUnit->
cyclesToTicks(Cycles(2 * computeUnit->srf_scm_bus_latency)));
computeUnit->scalarMemUnit.
set(computeUnit->cyclesToTicks(computeUnit->issuePeriod));
computeUnit->stats.instCyclesScMemPerSimd[simdId] +=
(2 * computeUnit->srf_scm_bus_latency);
}
} else if ((ii->isAtomic() || ii->isMemSync()) &&
(ii->isGlobalMem() || flat_as_gm)) {
if (!ii->isScalar()) {
computeUnit->vrfToGlobalMemPipeBus.set(computeUnit->
cyclesToTicks(Cycles(2 * computeUnit->vrf_gm_bus_latency)));
computeUnit->vectorGlobalMemUnit.
set(computeUnit->cyclesToTicks(computeUnit->issuePeriod));
computeUnit->stats.instCyclesVMemPerSimd[simdId] +=
(2 * computeUnit->vrf_gm_bus_latency);
} else {
computeUnit->srfToScalarMemPipeBus.set(computeUnit->
cyclesToTicks(Cycles(2 * computeUnit->srf_scm_bus_latency)));
computeUnit->scalarMemUnit.
set(computeUnit->cyclesToTicks(computeUnit->issuePeriod));
computeUnit->stats.instCyclesScMemPerSimd[simdId] +=
(2 * computeUnit->srf_scm_bus_latency);
}
// LM or Flat as LM Load
} else if (ii->isLoad() && (ii->isLocalMem() || flat_as_lm)) {
computeUnit->vrfToLocalMemPipeBus.set(computeUnit->
cyclesToTicks(computeUnit->vrf_lm_bus_latency));
computeUnit->vectorSharedMemUnit.
set(computeUnit->shader->cyclesToTicks(computeUnit->issuePeriod));
computeUnit->stats.instCyclesLdsPerSimd[simdId] +=
computeUnit->vrf_lm_bus_latency;
// LM or Flat as LM Store
} else if (ii->isStore() && (ii->isLocalMem() || flat_as_lm)) {
computeUnit->vrfToLocalMemPipeBus.set(computeUnit->
cyclesToTicks(Cycles(2 * computeUnit->vrf_lm_bus_latency)));
computeUnit->vectorSharedMemUnit.
set(computeUnit->cyclesToTicks(computeUnit->issuePeriod));
computeUnit->stats.instCyclesLdsPerSimd[simdId] +=
(2 * computeUnit->vrf_lm_bus_latency);
// LM or Flat as LM, Atomic or MemFence
} else if ((ii->isAtomic() || ii->isMemSync()) &&
(ii->isLocalMem() || flat_as_lm)) {
computeUnit->vrfToLocalMemPipeBus.set(computeUnit->
cyclesToTicks(Cycles(2 * computeUnit->vrf_lm_bus_latency)));
computeUnit->vectorSharedMemUnit.
set(computeUnit->cyclesToTicks(computeUnit->issuePeriod));
computeUnit->stats.instCyclesLdsPerSimd[simdId] +=
(2 * computeUnit->vrf_lm_bus_latency);
} else {
panic("Bad instruction type!\n");
}
}
GPUDynInstPtr
Wavefront::nextInstr()
{
// Read next instruction from instruction buffer
GPUDynInstPtr ii = instructionBuffer.front();
// if the WF has been dispatched in the schedule stage then
// check the next oldest instruction for readiness
if (computeUnit->pipeMap.find(ii->seqNum()) !=
computeUnit->pipeMap.end()) {
if (instructionBuffer.size() > 1) {
auto it = instructionBuffer.begin() + 1;
return *it;
} else { // No new instructions to check
return nullptr;
}
}
return ii;
}
void
Wavefront::discardFetch()
{
instructionBuffer.clear();
dropFetch |= pendingFetch;
/**
* clear the fetch buffer for this wave in order to
* remove any stale inst data
*/
computeUnit->fetchStage.fetchUnit(simdId).flushBuf(wfSlotId);
}
bool
Wavefront::waitCntsSatisfied()
{
// Both vmWaitCnt && lgkmWaitCnt uninitialized means
// waitCnt instruction has been dispatched but not executed yet: next
// instruction should be blocked until waitCnt is executed.
if (vmWaitCnt == -1 && expWaitCnt == -1 && lgkmWaitCnt == -1) {
return false;
}
/**
* If we reach here, that means an s_waitcnt instruction was executed
* and the waitcnts are set by the execute method. Check if waitcnts
* are satisfied.
*/
if (vmWaitCnt != -1) {
if (vmemInstsIssued > vmWaitCnt) {
// vmWaitCnt not satisfied
return false;
}
}
if (expWaitCnt != -1) {
if (expInstsIssued > expWaitCnt) {
// expWaitCnt not satisfied
return false;
}
}
if (lgkmWaitCnt != -1) {
if (lgkmInstsIssued > lgkmWaitCnt) {
// lgkmWaitCnt not satisfied
return false;
}
}
// if we get here all outstanding waitcnts must
// be satisfied, so we resume normal operation
clearWaitCnts();
return true;
}
bool
Wavefront::sleepDone()
{
assert(status == S_STALLED_SLEEP);
// if the sleep count has not been set, then the sleep instruction has not
// been executed yet, so we will return true without setting the wavefront
// status
if (sleepCnt == 0)
return false;
sleepCnt--;
if (sleepCnt != 0)
return false;
status = S_RUNNING;
return true;
}
void
Wavefront::setSleepTime(int sleep_time)
{
assert(sleepCnt == 0);
sleepCnt = sleep_time;
}
void
Wavefront::setWaitCnts(int vm_wait_cnt, int exp_wait_cnt, int lgkm_wait_cnt)
{
// the scoreboard should have set the status
// to S_WAITCNT once a waitcnt instruction
// was marked as ready
assert(status == S_WAITCNT);
// waitcnt instruction shouldn't be sending
// negative counts
assert(vm_wait_cnt >= 0);
assert(exp_wait_cnt >= 0);
assert(lgkm_wait_cnt >= 0);
// waitcnts are a max of 15 because we have
// only 1 nibble (4 bits) to set the counts
assert(vm_wait_cnt <= 0xf);
assert(exp_wait_cnt <= 0x7);
assert(lgkm_wait_cnt <= 0x1f);
/**
* prior waitcnts should be satisfied,
* at which time the WF resets them
* back to -1, indicating they are no
* longer active
*/
assert(vmWaitCnt == -1);
assert(expWaitCnt == -1);
assert(lgkmWaitCnt == -1);
/**
* if the instruction encoding
* indicates a waitcnt of 0xf,
* that means the waitcnt is
* not being used
*/
if (vm_wait_cnt != 0xf)
vmWaitCnt = vm_wait_cnt;
if (exp_wait_cnt != 0x7)
expWaitCnt = exp_wait_cnt;
if (lgkm_wait_cnt != 0x1f)
lgkmWaitCnt = lgkm_wait_cnt;
}
void
Wavefront::clearWaitCnts()
{
// reset the waitcnts back to
// -1, indicating they are no
// longer valid
vmWaitCnt = -1;
expWaitCnt = -1;
lgkmWaitCnt = -1;
// resume running normally
status = S_RUNNING;
}
void
Wavefront::incVMemInstsIssued()
{
++vmemInstsIssued;
}
void
Wavefront::incExpInstsIssued()
{
++expInstsIssued;
}
void
Wavefront::incLGKMInstsIssued()
{
++lgkmInstsIssued;
}
void
Wavefront::decVMemInstsIssued()
{
--vmemInstsIssued;
}
void
Wavefront::decExpInstsIssued()
{
--expInstsIssued;
}
void
Wavefront::decLGKMInstsIssued()
{
--lgkmInstsIssued;
}
Addr
Wavefront::pc() const
{
return _pc;
}
void
Wavefront::pc(Addr new_pc)
{
_pc = new_pc;
}
VectorMask&
Wavefront::execMask()
{
return _execMask;
}
bool
Wavefront::execMask(int lane) const
{
return _execMask[lane];
}
void
Wavefront::freeRegisterFile()
{
/* clear busy registers */
for (int i=0; i < maxVgprs; i++) {
int vgprIdx = computeUnit->registerManager->mapVgpr(this, i);
computeUnit->vrf[simdId]->markReg(vgprIdx, false);
}
/* Free registers used by this wavefront */
uint32_t endIndex = (startVgprIndex + reservedVectorRegs - 1) %
computeUnit->vrf[simdId]->numRegs();
computeUnit->registerManager->vrfPoolMgrs[simdId]->
freeRegion(startVgprIndex, endIndex);
}
void
Wavefront::computeActualWgSz(HSAQueueEntry *task)
{
actualWgSzTotal = 1;
for (int d = 0; d < HSAQueueEntry::MAX_DIM; ++d) {
actualWgSz[d] = std::min(workGroupSz[d], gridSz[d]
- task->wgId(d) * workGroupSz[d]);
actualWgSzTotal *= actualWgSz[d];
}
}
void
Wavefront::barrierId(int bar_id)
{
assert(bar_id >= WFBarrier::InvalidID);
assert(bar_id < computeUnit->numBarrierSlots());
barId = bar_id;
}
int
Wavefront::barrierId() const
{
return barId;
}
bool
Wavefront::hasBarrier() const
{
return barId > WFBarrier::InvalidID;
}
void
Wavefront::releaseBarrier()
{
barId = WFBarrier::InvalidID;
}
Wavefront::WavefrontStats::WavefrontStats(statistics::Group *parent)
: statistics::Group(parent),
ADD_STAT(numInstrExecuted,
"number of instructions executed by this WF slot"),
ADD_STAT(schCycles, "number of cycles spent in schedule stage"),
ADD_STAT(schStalls, "number of cycles WF is stalled in SCH stage"),
ADD_STAT(schRfAccessStalls, "number of cycles wave selected in SCH but "
"RF denied adding instruction"),
ADD_STAT(schResourceStalls, "number of cycles stalled in sch by resource"
" not available"),
ADD_STAT(schOpdNrdyStalls, "number of cycles stalled in sch waiting for "
"RF reads to complete"),
ADD_STAT(schLdsArbStalls,
"number of cycles wave stalled due to LDS-VRF arbitration"),
// FIXME: the name of the WF needs to be unique
ADD_STAT(numTimesBlockedDueWAXDependencies, "number of times the wf's "
"instructions are blocked due to WAW or WAR dependencies"),
// FIXME: the name of the WF needs to be unique
ADD_STAT(numTimesBlockedDueRAWDependencies, "number of times the wf's "
"instructions are blocked due to RAW dependencies"),
ADD_STAT(vecRawDistance,
"Count of RAW distance in dynamic instructions for this WF"),
ADD_STAT(readsPerWrite, "Count of Vector reads per write for this WF")
{
vecRawDistance.init(0, 20, 1);
readsPerWrite.init(0, 4, 1);
}
} // namespace gem5