| /* | |
| * Copyright (c) 2021 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 "dev/amdgpu/memory_manager.hh" | |
| #include <memory> | |
| #include "base/chunk_generator.hh" | |
| #include "debug/AMDGPUMem.hh" | |
| #include "params/AMDGPUMemoryManager.hh" | |
| #include "sim/system.hh" | |
| namespace gem5 | |
| { | |
| AMDGPUMemoryManager::AMDGPUMemoryManager(const AMDGPUMemoryManagerParams &p) | |
| : ClockedObject(p), _gpuMemPort(csprintf("%s-port", name()), *this), | |
| cacheLineSize(p.system->cacheLineSize()), | |
| _requestorId(p.system->getRequestorId(this)) | |
| { | |
| } | |
| void | |
| AMDGPUMemoryManager::writeRequest(Addr addr, uint8_t *data, int size, | |
| Request::Flags flag, Event *callback) | |
| { | |
| assert(data); | |
| // Requests may return out of order, so we should track how many chunks | |
| // are outstanding and if the last chunk was sent. Give each status struct | |
| // a unique ID so that DMAs to the same address may occur at the same time | |
| requestStatus.emplace(std::piecewise_construct, | |
| std::forward_as_tuple(requestId), std::tuple<>{}); | |
| DPRINTF(AMDGPUMem, "Created status for write request %ld\n", requestId); | |
| ChunkGenerator gen(addr, size, cacheLineSize); | |
| for (; !gen.done(); gen.next()) { | |
| RequestPtr req = std::make_shared<Request>(gen.addr(), gen.size(), | |
| flag, _requestorId); | |
| PacketPtr pkt = Packet::createWrite(req); | |
| uint8_t *dataPtr = new uint8_t[gen.size()]; | |
| std::memcpy(dataPtr, data + (gen.complete()/sizeof(uint8_t)), | |
| gen.size()); | |
| pkt->dataDynamic<uint8_t>(dataPtr); | |
| pkt->pushSenderState( | |
| new GPUMemPort::SenderState(callback, addr, requestId)); | |
| requestStatus.at(requestId).outstandingChunks++; | |
| if (gen.last()) { | |
| requestStatus.at(requestId).sentLastChunk = true; | |
| } | |
| if (!_gpuMemPort.sendTimingReq(pkt)) { | |
| DPRINTF(AMDGPUMem, "Request to %#lx needs retry\n", gen.addr()); | |
| _gpuMemPort.retries.push_back(pkt); | |
| } else { | |
| DPRINTF(AMDGPUMem, "Write request to %#lx sent\n", gen.addr()); | |
| } | |
| } | |
| requestId++; | |
| } | |
| void | |
| AMDGPUMemoryManager::readRequest(Addr addr, uint8_t *data, int size, | |
| Request::Flags flag, Event *callback) | |
| { | |
| assert(data); | |
| uint8_t *dataPtr = data; | |
| // Requests may return out of order, so we should track how many chunks | |
| // are outstanding and if the last chunk was sent. Give each status struct | |
| // a unique ID so that DMAs to the same address may occur at the same time | |
| requestStatus.emplace(std::piecewise_construct, | |
| std::forward_as_tuple(requestId), std::tuple<>{}); | |
| DPRINTF(AMDGPUMem, "Created status for read request %ld\n", requestId); | |
| ChunkGenerator gen(addr, size, cacheLineSize); | |
| for (; !gen.done(); gen.next()) { | |
| RequestPtr req = std::make_shared<Request>(gen.addr(), gen.size(), | |
| flag, _requestorId); | |
| PacketPtr pkt = Packet::createRead(req); | |
| pkt->dataStatic<uint8_t>(dataPtr); | |
| dataPtr += gen.size(); | |
| pkt->pushSenderState( | |
| new GPUMemPort::SenderState(callback, addr, requestId)); | |
| requestStatus.at(requestId).outstandingChunks++; | |
| if (gen.last()) { | |
| requestStatus.at(requestId).sentLastChunk = true; | |
| } | |
| if (!_gpuMemPort.sendTimingReq(pkt)) { | |
| DPRINTF(AMDGPUMem, "Request to %#lx needs retry\n", gen.addr()); | |
| _gpuMemPort.retries.push_back(pkt); | |
| } else { | |
| DPRINTF(AMDGPUMem, "Read request to %#lx sent\n", gen.addr()); | |
| } | |
| } | |
| requestId++; | |
| } | |
| bool | |
| AMDGPUMemoryManager::GPUMemPort::recvTimingResp(PacketPtr pkt) | |
| { | |
| // Retrieve sender state | |
| [[maybe_unused]] SenderState *sender_state = | |
| safe_cast<SenderState*>(pkt->senderState); | |
| // Check if all chunks have completed, the last chunk was sent, and there | |
| // is a callback, call the callback now. | |
| assert(gpu_mem.requestStatus.count(sender_state->_requestId)); | |
| auto& status = gpu_mem.requestStatus.at(sender_state->_requestId); | |
| assert(status.outstandingChunks != 0); | |
| status.outstandingChunks--; | |
| DPRINTF(AMDGPUMem, "Received Response for %#x. %d chunks remain, sent " | |
| "last = %d, requestId = %ld\n", sender_state->_addr, | |
| status.outstandingChunks, status.sentLastChunk, | |
| sender_state->_requestId); | |
| if (!status.outstandingChunks && status.sentLastChunk) { | |
| // Call and free the callback if there is one | |
| if (sender_state->_callback) { | |
| DPRINTF(AMDGPUMem, "Calling callback for request %ld\n", | |
| sender_state->_requestId); | |
| sender_state->_callback->process(); | |
| delete sender_state->_callback; | |
| } | |
| DPRINTF(AMDGPUMem, "Deleting status for request %ld\n", | |
| sender_state->_requestId); | |
| gpu_mem.requestStatus.erase(sender_state->_requestId); | |
| } | |
| delete pkt->senderState; | |
| delete pkt; | |
| return true; | |
| } | |
| void | |
| AMDGPUMemoryManager::GPUMemPort::recvReqRetry() | |
| { | |
| for (const auto &pkt : retries) { | |
| if (!sendTimingReq(pkt)) { | |
| break; | |
| } else { | |
| DPRINTF(AMDGPUMem, "Retry for %#lx sent\n", pkt->getAddr()); | |
| retries.pop_front(); | |
| } | |
| } | |
| } | |
| } // namespace gem5 |