| /* | |
| * Copyright (c) 2013-2015 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: Sooraj Puthoor | |
| */ | |
| #include "base/logging.hh" | |
| #include "base/str.hh" | |
| #include "config/the_isa.hh" | |
| #if THE_ISA == X86_ISA | |
| #include "arch/x86/insts/microldstop.hh" | |
| #endif // X86_ISA | |
| #include "mem/ruby/system/VIPERCoalescer.hh" | |
| #include "cpu/testers/rubytest/RubyTester.hh" | |
| #include "debug/GPUCoalescer.hh" | |
| #include "debug/MemoryAccess.hh" | |
| #include "mem/packet.hh" | |
| #include "mem/ruby/common/SubBlock.hh" | |
| #include "mem/ruby/network/MessageBuffer.hh" | |
| #include "mem/ruby/profiler/Profiler.hh" | |
| #include "mem/ruby/slicc_interface/AbstractController.hh" | |
| #include "mem/ruby/slicc_interface/RubyRequest.hh" | |
| #include "mem/ruby/structures/CacheMemory.hh" | |
| #include "mem/ruby/system/GPUCoalescer.hh" | |
| #include "mem/ruby/system/RubySystem.hh" | |
| #include "params/VIPERCoalescer.hh" | |
| using namespace std; | |
| VIPERCoalescer * | |
| VIPERCoalescerParams::create() | |
| { | |
| return new VIPERCoalescer(this); | |
| } | |
| VIPERCoalescer::VIPERCoalescer(const Params *p) | |
| : GPUCoalescer(p) | |
| { | |
| m_max_wb_per_cycle=p->max_wb_per_cycle; | |
| m_max_inv_per_cycle=p->max_inv_per_cycle; | |
| m_outstanding_inv = 0; | |
| m_outstanding_wb = 0; | |
| } | |
| VIPERCoalescer::~VIPERCoalescer() | |
| { | |
| } | |
| // Analyzes the packet to see if this request can be coalesced. | |
| // If request can be coalesced, this request is added to the reqCoalescer table | |
| // and makeRequest returns RequestStatus_Issued; | |
| // If this is the first request to a cacheline, request is added to both | |
| // newRequests queue and to the reqCoalescer table; makeRequest | |
| // returns RequestStatus_Issued. | |
| // If there is a pending request to this cacheline and this request | |
| // can't be coalesced, RequestStatus_Aliased is returned and | |
| // the packet needs to be reissued. | |
| RequestStatus | |
| VIPERCoalescer::makeRequest(PacketPtr pkt) | |
| { | |
| if (m_outstanding_wb | m_outstanding_inv) { | |
| DPRINTF(GPUCoalescer, | |
| "There are %d Writebacks and %d Invalidatons\n", | |
| m_outstanding_wb, m_outstanding_inv); | |
| } | |
| // Are we in the middle of a release | |
| if ((m_outstanding_wb) > 0) { | |
| if (pkt->req->isKernel()) { | |
| // Everythign is fine | |
| // Barriers and Kernel End scan coalesce | |
| // If it is a Kerenl Begin flush the cache | |
| if (pkt->req->isAcquire() && (m_outstanding_inv == 0)) { | |
| invL1(); | |
| } | |
| if (pkt->req->isRelease()) { | |
| insertKernel(pkt->req->contextId(), pkt); | |
| } | |
| return RequestStatus_Issued; | |
| } | |
| // return RequestStatus_Aliased; | |
| } else if (pkt->req->isKernel() && pkt->req->isRelease()) { | |
| // Flush Dirty Data on Kernel End | |
| // isKernel + isRelease | |
| insertKernel(pkt->req->contextId(), pkt); | |
| wbL1(); | |
| if (m_outstanding_wb == 0) { | |
| for (auto it = kernelEndList.begin(); it != kernelEndList.end(); it++) { | |
| newKernelEnds.push_back(it->first); | |
| } | |
| completeIssue(); | |
| } | |
| return RequestStatus_Issued; | |
| } | |
| RequestStatus requestStatus = GPUCoalescer::makeRequest(pkt); | |
| if (requestStatus!=RequestStatus_Issued) { | |
| // Request not isssued | |
| // enqueue Retry | |
| DPRINTF(GPUCoalescer, "Request not issued by GPUCoaleser\n"); | |
| return requestStatus; | |
| } else if (pkt->req->isKernel() && pkt->req->isAcquire()) { | |
| // Invalidate clean Data on Kernel Begin | |
| // isKernel + isAcquire | |
| invL1(); | |
| } else if (pkt->req->isAcquire() && pkt->req->isRelease()) { | |
| // Deschedule the AtomicAcqRel and | |
| // Flush and Invalidate the L1 cache | |
| invwbL1(); | |
| if (m_outstanding_wb > 0 && issueEvent.scheduled()) { | |
| DPRINTF(GPUCoalescer, "issueEvent Descheduled\n"); | |
| deschedule(issueEvent); | |
| } | |
| } else if (pkt->req->isRelease()) { | |
| // Deschedule the StoreRel and | |
| // Flush the L1 cache | |
| wbL1(); | |
| if (m_outstanding_wb > 0 && issueEvent.scheduled()) { | |
| DPRINTF(GPUCoalescer, "issueEvent Descheduled\n"); | |
| deschedule(issueEvent); | |
| } | |
| } else if (pkt->req->isAcquire()) { | |
| // LoadAcq or AtomicAcq | |
| // Invalidate the L1 cache | |
| invL1(); | |
| } | |
| // Request was successful | |
| if (m_outstanding_wb == 0) { | |
| if (!issueEvent.scheduled()) { | |
| DPRINTF(GPUCoalescer, "issueEvent Rescheduled\n"); | |
| schedule(issueEvent, curTick()); | |
| } | |
| } | |
| return RequestStatus_Issued; | |
| } | |
| void | |
| VIPERCoalescer::wbCallback(Addr addr) | |
| { | |
| m_outstanding_wb--; | |
| // if L1 Flush Complete | |
| // attemnpt to schedule issueEvent | |
| assert(((int) m_outstanding_wb) >= 0); | |
| if (m_outstanding_wb == 0) { | |
| for (auto it = kernelEndList.begin(); it != kernelEndList.end(); it++) { | |
| newKernelEnds.push_back(it->first); | |
| } | |
| completeIssue(); | |
| } | |
| trySendRetries(); | |
| } | |
| void | |
| VIPERCoalescer::invCallback(Addr addr) | |
| { | |
| m_outstanding_inv--; | |
| // if L1 Flush Complete | |
| // attemnpt to schedule issueEvent | |
| // This probably won't happen, since | |
| // we dont wait on cache invalidations | |
| if (m_outstanding_wb == 0) { | |
| for (auto it = kernelEndList.begin(); it != kernelEndList.end(); it++) { | |
| newKernelEnds.push_back(it->first); | |
| } | |
| completeIssue(); | |
| } | |
| trySendRetries(); | |
| } | |
| /** | |
| * Invalidate L1 cache (Acquire) | |
| */ | |
| void | |
| VIPERCoalescer::invL1() | |
| { | |
| int size = m_dataCache_ptr->getNumBlocks(); | |
| DPRINTF(GPUCoalescer, | |
| "There are %d Invalidations outstanding before Cache Walk\n", | |
| m_outstanding_inv); | |
| // Walk the cache | |
| for (int i = 0; i < size; i++) { | |
| Addr addr = m_dataCache_ptr->getAddressAtIdx(i); | |
| // Evict Read-only data | |
| std::shared_ptr<RubyRequest> msg = std::make_shared<RubyRequest>( | |
| clockEdge(), addr, (uint8_t*) 0, 0, 0, | |
| RubyRequestType_REPLACEMENT, RubyAccessMode_Supervisor, | |
| nullptr); | |
| assert(m_mandatory_q_ptr != NULL); | |
| m_mandatory_q_ptr->enqueue(msg, clockEdge(), m_data_cache_hit_latency); | |
| m_outstanding_inv++; | |
| } | |
| DPRINTF(GPUCoalescer, | |
| "There are %d Invalidatons outstanding after Cache Walk\n", | |
| m_outstanding_inv); | |
| } | |
| /** | |
| * Writeback L1 cache (Release) | |
| */ | |
| void | |
| VIPERCoalescer::wbL1() | |
| { | |
| int size = m_dataCache_ptr->getNumBlocks(); | |
| DPRINTF(GPUCoalescer, | |
| "There are %d Writebacks outstanding before Cache Walk\n", | |
| m_outstanding_wb); | |
| // Walk the cache | |
| for (int i = 0; i < size; i++) { | |
| Addr addr = m_dataCache_ptr->getAddressAtIdx(i); | |
| // Write dirty data back | |
| std::shared_ptr<RubyRequest> msg = std::make_shared<RubyRequest>( | |
| clockEdge(), addr, (uint8_t*) 0, 0, 0, | |
| RubyRequestType_FLUSH, RubyAccessMode_Supervisor, | |
| nullptr); | |
| assert(m_mandatory_q_ptr != NULL); | |
| m_mandatory_q_ptr->enqueue(msg, clockEdge(), m_data_cache_hit_latency); | |
| m_outstanding_wb++; | |
| } | |
| DPRINTF(GPUCoalescer, | |
| "There are %d Writebacks outstanding after Cache Walk\n", | |
| m_outstanding_wb); | |
| } | |
| /** | |
| * Invalidate and Writeback L1 cache (Acquire&Release) | |
| */ | |
| void | |
| VIPERCoalescer::invwbL1() | |
| { | |
| int size = m_dataCache_ptr->getNumBlocks(); | |
| // Walk the cache | |
| for (int i = 0; i < size; i++) { | |
| Addr addr = m_dataCache_ptr->getAddressAtIdx(i); | |
| // Evict Read-only data | |
| std::shared_ptr<RubyRequest> msg = std::make_shared<RubyRequest>( | |
| clockEdge(), addr, (uint8_t*) 0, 0, 0, | |
| RubyRequestType_REPLACEMENT, RubyAccessMode_Supervisor, | |
| nullptr); | |
| assert(m_mandatory_q_ptr != NULL); | |
| m_mandatory_q_ptr->enqueue(msg, clockEdge(), m_data_cache_hit_latency); | |
| m_outstanding_inv++; | |
| } | |
| // Walk the cache | |
| for (int i = 0; i< size; i++) { | |
| Addr addr = m_dataCache_ptr->getAddressAtIdx(i); | |
| // Write dirty data back | |
| std::shared_ptr<RubyRequest> msg = std::make_shared<RubyRequest>( | |
| clockEdge(), addr, (uint8_t*) 0, 0, 0, | |
| RubyRequestType_FLUSH, RubyAccessMode_Supervisor, | |
| nullptr); | |
| assert(m_mandatory_q_ptr != NULL); | |
| m_mandatory_q_ptr->enqueue(msg, clockEdge(), m_data_cache_hit_latency); | |
| m_outstanding_wb++; | |
| } | |
| } |