| /* | |
| * Copyright (c) 2014-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: Anthony Gutierrez, | |
| * Brad Beckmann, | |
| * Sooraj Puthoor | |
| */ | |
| #include "gpu-compute/fetch_unit.hh" | |
| #include "debug/GPUFetch.hh" | |
| #include "debug/GPUPort.hh" | |
| #include "debug/GPUTLB.hh" | |
| #include "gpu-compute/compute_unit.hh" | |
| #include "gpu-compute/gpu_dyn_inst.hh" | |
| #include "gpu-compute/gpu_static_inst.hh" | |
| #include "gpu-compute/shader.hh" | |
| #include "gpu-compute/wavefront.hh" | |
| #include "mem/ruby/system/RubySystem.hh" | |
| uint32_t FetchUnit::globalFetchUnitID; | |
| FetchUnit::FetchUnit(const ComputeUnitParams* params) : | |
| timingSim(true), | |
| computeUnit(nullptr), | |
| fetchScheduler(params), | |
| waveList(nullptr) | |
| { | |
| } | |
| FetchUnit::~FetchUnit() | |
| { | |
| fetchQueue.clear(); | |
| fetchStatusQueue.clear(); | |
| } | |
| void | |
| FetchUnit::init(ComputeUnit *cu) | |
| { | |
| computeUnit = cu; | |
| timingSim = computeUnit->shader->timingSim; | |
| fetchQueue.clear(); | |
| fetchStatusQueue.resize(computeUnit->shader->n_wf); | |
| for (int j = 0; j < computeUnit->shader->n_wf; ++j) { | |
| fetchStatusQueue[j] = std::make_pair(waveList->at(j), false); | |
| } | |
| fetchScheduler.bindList(&fetchQueue); | |
| } | |
| void | |
| FetchUnit::exec() | |
| { | |
| // re-evaluate waves which are marked as not ready for fetch | |
| for (int j = 0; j < computeUnit->shader->n_wf; ++j) { | |
| // Following code assumes 64-bit opertaion and all insts are | |
| // represented by 64-bit pointers to inst objects. | |
| Wavefront *curWave = fetchStatusQueue[j].first; | |
| assert (curWave); | |
| // The wavefront has to be active, the IB occupancy has to be | |
| // 4 or less instructions and it can not have any branches to | |
| // prevent speculative instruction fetches | |
| if (!fetchStatusQueue[j].second) { | |
| if (curWave->status == Wavefront::S_RUNNING && | |
| curWave->instructionBuffer.size() <= 4 && | |
| !curWave->instructionBufferHasBranch() && | |
| !curWave->pendingFetch) { | |
| fetchQueue.push_back(curWave); | |
| fetchStatusQueue[j].second = true; | |
| } | |
| } | |
| } | |
| // Fetch only if there is some wave ready to be fetched | |
| // An empty fetchQueue will cause the schedular to panic | |
| if (fetchQueue.size()) { | |
| Wavefront *waveToBeFetched = fetchScheduler.chooseWave(); | |
| waveToBeFetched->pendingFetch = true; | |
| fetchStatusQueue[waveToBeFetched->wfSlotId].second = false; | |
| initiateFetch(waveToBeFetched); | |
| } | |
| } | |
| void | |
| FetchUnit::initiateFetch(Wavefront *wavefront) | |
| { | |
| // calculate the virtual address to fetch from the SQC | |
| Addr vaddr = wavefront->pc(); | |
| /** | |
| * the instruction buffer holds one instruction per entry, regardless | |
| * of the underlying instruction's size. the PC, however, addresses | |
| * instrutions on a 32b granularity so we must account for that here. | |
| */ | |
| for (int i = 0; i < wavefront->instructionBuffer.size(); ++i) { | |
| vaddr += | |
| wavefront->instructionBuffer.at(i)->staticInstruction()->instSize(); | |
| } | |
| vaddr = wavefront->basePtr + vaddr; | |
| DPRINTF(GPUTLB, "CU%d: WF[%d][%d]: Initiating fetch translation: %#x\n", | |
| computeUnit->cu_id, wavefront->simdId, wavefront->wfSlotId, vaddr); | |
| // Since this is an instruction prefetch, if you're split then just finish | |
| // out the current line. | |
| int block_size = computeUnit->cacheLineSize(); | |
| // check for split accesses | |
| Addr split_addr = roundDown(vaddr + block_size - 1, block_size); | |
| int size = block_size; | |
| if (split_addr > vaddr) { | |
| // misaligned access, just grab the rest of the line | |
| size = split_addr - vaddr; | |
| } | |
| // set up virtual request | |
| RequestPtr req = std::make_shared<Request>( | |
| 0, vaddr, size, Request::INST_FETCH, | |
| computeUnit->masterId(), 0, 0, nullptr); | |
| PacketPtr pkt = new Packet(req, MemCmd::ReadReq); | |
| // This fetchBlock is kind of faux right now - because the translations so | |
| // far don't actually return Data | |
| uint64_t fetchBlock; | |
| pkt->dataStatic(&fetchBlock); | |
| if (timingSim) { | |
| // SenderState needed on Return | |
| pkt->senderState = new ComputeUnit::ITLBPort::SenderState(wavefront); | |
| // Sender State needed by TLB hierarchy | |
| pkt->senderState = | |
| new TheISA::GpuTLB::TranslationState(BaseTLB::Execute, | |
| computeUnit->shader->gpuTc, | |
| false, pkt->senderState); | |
| if (computeUnit->sqcTLBPort->isStalled()) { | |
| assert(computeUnit->sqcTLBPort->retries.size() > 0); | |
| DPRINTF(GPUTLB, "Failed to send TLB req for FETCH addr %#x\n", | |
| vaddr); | |
| computeUnit->sqcTLBPort->retries.push_back(pkt); | |
| } else if (!computeUnit->sqcTLBPort->sendTimingReq(pkt)) { | |
| // Stall the data port; | |
| // No more packet is issued till | |
| // ruby indicates resources are freed by | |
| // a recvReqRetry() call back on this port. | |
| computeUnit->sqcTLBPort->stallPort(); | |
| DPRINTF(GPUTLB, "Failed to send TLB req for FETCH addr %#x\n", | |
| vaddr); | |
| computeUnit->sqcTLBPort->retries.push_back(pkt); | |
| } else { | |
| DPRINTF(GPUTLB, "sent FETCH translation request for %#x\n", vaddr); | |
| } | |
| } else { | |
| pkt->senderState = | |
| new TheISA::GpuTLB::TranslationState(BaseTLB::Execute, | |
| computeUnit->shader->gpuTc); | |
| computeUnit->sqcTLBPort->sendFunctional(pkt); | |
| TheISA::GpuTLB::TranslationState *sender_state = | |
| safe_cast<TheISA::GpuTLB::TranslationState*>(pkt->senderState); | |
| delete sender_state->tlbEntry; | |
| delete sender_state; | |
| // fetch the instructions from the SQC when we operate in | |
| // functional mode only | |
| fetch(pkt, wavefront); | |
| } | |
| } | |
| void | |
| FetchUnit::fetch(PacketPtr pkt, Wavefront *wavefront) | |
| { | |
| assert(pkt->req->hasPaddr()); | |
| assert(pkt->req->hasSize()); | |
| DPRINTF(GPUFetch, "CU%d: WF[%d][%d]: Fetch Access: %#x\n", | |
| computeUnit->cu_id, wavefront->simdId, wavefront->wfSlotId, | |
| pkt->req->getPaddr()); | |
| // this is necessary because the GPU TLB receives packets instead of | |
| // requests. when the translation is complete, all relevent fields in the | |
| // request will be populated, but not in the packet. here we create the | |
| // new packet so we can set the size, addr, and proper flags. | |
| PacketPtr oldPkt = pkt; | |
| pkt = new Packet(oldPkt->req, oldPkt->cmd); | |
| delete oldPkt; | |
| TheGpuISA::RawMachInst *data = | |
| new TheGpuISA::RawMachInst[pkt->req->getSize() / | |
| sizeof(TheGpuISA::RawMachInst)]; | |
| pkt->dataDynamic<TheGpuISA::RawMachInst>(data); | |
| // New SenderState for the memory access | |
| pkt->senderState = new ComputeUnit::SQCPort::SenderState(wavefront); | |
| if (timingSim) { | |
| // translation is done. Send the appropriate timing memory request. | |
| if (!computeUnit->sqcPort->sendTimingReq(pkt)) { | |
| computeUnit->sqcPort->retries.push_back(std::make_pair(pkt, | |
| wavefront)); | |
| DPRINTF(GPUPort, "CU%d: WF[%d][%d]: Fetch addr %#x failed!\n", | |
| computeUnit->cu_id, wavefront->simdId, wavefront->wfSlotId, | |
| pkt->req->getPaddr()); | |
| } else { | |
| DPRINTF(GPUPort, "CU%d: WF[%d][%d]: Fetch addr %#x sent!\n", | |
| computeUnit->cu_id, wavefront->simdId, wavefront->wfSlotId, | |
| pkt->req->getPaddr()); | |
| } | |
| } else { | |
| computeUnit->sqcPort->sendFunctional(pkt); | |
| processFetchReturn(pkt); | |
| } | |
| } | |
| void | |
| FetchUnit::processFetchReturn(PacketPtr pkt) | |
| { | |
| ComputeUnit::SQCPort::SenderState *sender_state = | |
| safe_cast<ComputeUnit::SQCPort::SenderState*>(pkt->senderState); | |
| Wavefront *wavefront = sender_state->wavefront; | |
| DPRINTF(GPUFetch, "CU%d: WF[%d][%d]: Fetch addr %#x returned " | |
| "%d bytes, %d instructions!\n", computeUnit->cu_id, | |
| wavefront->simdId, wavefront->wfSlotId, pkt->req->getPaddr(), | |
| pkt->req->getSize(), pkt->req->getSize() / | |
| sizeof(TheGpuISA::RawMachInst)); | |
| if (wavefront->dropFetch) { | |
| assert(wavefront->instructionBuffer.empty()); | |
| wavefront->dropFetch = false; | |
| } else { | |
| TheGpuISA::RawMachInst *inst_index_ptr = | |
| (TheGpuISA::RawMachInst*)pkt->getPtr<uint8_t>(); | |
| assert(wavefront->instructionBuffer.size() <= 4); | |
| for (int i = 0; i < pkt->req->getSize() / | |
| sizeof(TheGpuISA::RawMachInst); ++i) { | |
| GPUStaticInst *inst_ptr = decoder.decode(inst_index_ptr[i]); | |
| assert(inst_ptr); | |
| if (inst_ptr->instSize() == 8) { | |
| /** | |
| * this instruction occupies 2 consecutive | |
| * entries in the instruction array, the | |
| * second of which contains a nullptr. so if | |
| * this inst is 8 bytes we advance two entries | |
| * instead of 1 | |
| */ | |
| ++i; | |
| } | |
| DPRINTF(GPUFetch, "CU%d: WF[%d][%d]: added %s\n", | |
| computeUnit->cu_id, wavefront->simdId, | |
| wavefront->wfSlotId, inst_ptr->disassemble()); | |
| GPUDynInstPtr gpuDynInst = | |
| std::make_shared<GPUDynInst>(computeUnit, wavefront, inst_ptr, | |
| computeUnit->getAndIncSeqNum()); | |
| wavefront->instructionBuffer.push_back(gpuDynInst); | |
| } | |
| } | |
| wavefront->pendingFetch = false; | |
| delete pkt->senderState; | |
| delete pkt; | |
| } | |
| void | |
| FetchUnit::bindWaveList(std::vector<Wavefront*> *wave_list) | |
| { | |
| waveList = wave_list; | |
| } |