DMASequencer.cc revision 10519
12SN/A/* 22188SN/A * Copyright (c) 2008 Mark D. Hill and David A. Wood 32SN/A * All rights reserved. 42SN/A * 52SN/A * Redistribution and use in source and binary forms, with or without 62SN/A * modification, are permitted provided that the following conditions are 72SN/A * met: redistributions of source code must retain the above copyright 82SN/A * notice, this list of conditions and the following disclaimer; 92SN/A * redistributions in binary form must reproduce the above copyright 102SN/A * notice, this list of conditions and the following disclaimer in the 112SN/A * documentation and/or other materials provided with the distribution; 122SN/A * neither the name of the copyright holders nor the names of its 132SN/A * contributors may be used to endorse or promote products derived from 142SN/A * this software without specific prior written permission. 152SN/A * 162SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 172SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 182SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 192SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 202SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 212SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 222SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 232SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 242SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 252SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 262SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 272665SN/A */ 282665SN/A 292665SN/A#include <memory> 302665SN/A 312665SN/A#include "debug/Config.hh" 322SN/A#include "debug/Drain.hh" 332SN/A#include "debug/RubyDma.hh" 342SN/A#include "debug/RubyStats.hh" 352SN/A#include "mem/protocol/SequencerMsg.hh" 362465SN/A#include "mem/ruby/system/DMASequencer.hh" 371717SN/A#include "mem/ruby/system/System.hh" 382683Sktlim@umich.edu#include "sim/system.hh" 392680SN/A 402SN/ADMASequencer::DMASequencer(const Params *p) 411858SN/A : MemObject(p), m_version(p->version), m_controller(NULL), 421917SN/A m_mandatory_q_ptr(NULL), m_usingRubyTester(p->using_ruby_tester), 431070SN/A slave_port(csprintf("%s.slave", name()), this, 0), 441917SN/A drainManager(NULL), system(p->system), retry(false) 452188SN/A{ 461917SN/A assert(m_version != -1); 472290SN/A} 481070SN/A 491070SN/Avoid 501917SN/ADMASequencer::init() 512170SN/A{ 522SN/A MemObject::init(); 53360SN/A assert(m_controller != NULL); 542519SN/A m_mandatory_q_ptr = m_controller->getMandatoryQueue(); 552420SN/A m_mandatory_q_ptr->setSender(this); 562SN/A m_is_busy = false; 572SN/A m_data_block_mask = ~ (~0 << RubySystem::getBlockSizeBits()); 582SN/A 592SN/A slave_port.sendRangeChange(); 602SN/A} 611858SN/A 622683Sktlim@umich.eduBaseSlavePort & 632683Sktlim@umich.eduDMASequencer::getSlavePort(const std::string &if_name, PortID idx) 642683Sktlim@umich.edu{ 652683Sktlim@umich.edu // used by the CPUs to connect the caches to the interconnect, and 662683Sktlim@umich.edu // for the x86 case also the interrupt master 672521SN/A if (if_name != "slave") { 682SN/A // pass it along to our super class 692683Sktlim@umich.edu return MemObject::getSlavePort(if_name, idx); 702190SN/A } else { 712680SN/A return slave_port; 722290SN/A } 732526SN/A} 741917SN/A 751917SN/ADMASequencer::MemSlavePort::MemSlavePort(const std::string &_name, 761982SN/A DMASequencer *_port, PortID id) 771917SN/A : QueuedSlavePort(_name, _port, queue, id), queue(*_port, *this) 782683Sktlim@umich.edu{ 792683Sktlim@umich.edu DPRINTF(RubyDma, "Created slave memport on ruby sequencer %s\n", _name); 801917SN/A} 811917SN/A 821917SN/Abool 831917SN/ADMASequencer::MemSlavePort::recvTimingReq(PacketPtr pkt) 841917SN/A{ 851917SN/A DPRINTF(RubyDma, "Timing request for address %#x on port %d\n", 861917SN/A pkt->getAddr(), id); 871917SN/A DMASequencer *seq = static_cast<DMASequencer *>(&owner); 882521SN/A 892341SN/A if (pkt->memInhibitAsserted()) 902341SN/A panic("DMASequencer should never see an inhibited request\n"); 912341SN/A 922341SN/A assert(isPhysMemAddress(pkt->getAddr())); 932341SN/A assert(Address(pkt->getAddr()).getOffset() + pkt->getSize() <= 942521SN/A RubySystem::getBlockSizeBytes()); 952640SN/A 962683Sktlim@umich.edu // Submit the ruby request 972521SN/A RequestStatus requestStatus = seq->makeRequest(pkt); 982521SN/A 992521SN/A // If the request successfully issued then we should return true. 1002521SN/A // Otherwise, we need to tell the port to retry at a later point 1012640SN/A // and return false. 1022683Sktlim@umich.edu if (requestStatus == RequestStatus_Issued) { 1032521SN/A DPRINTF(RubyDma, "Request %s 0x%x issued\n", pkt->cmdString(), 1042521SN/A pkt->getAddr()); 1052521SN/A return true; 1062SN/A } 1072SN/A 1082683Sktlim@umich.edu // Unless one is using the ruby tester, record the stalled M5 port for 1092520SN/A // later retry when the sequencer becomes free. 1102791Sktlim@umich.edu if (!seq->m_usingRubyTester) { 1112683Sktlim@umich.edu seq->retry = true; 1122SN/A } 1132519SN/A 1142519SN/A DPRINTF(RubyDma, "Request for address %#x did not issued because %s\n", 1152640SN/A pkt->getAddr(), RequestStatus_to_string(requestStatus)); 1162683Sktlim@umich.edu 1172640SN/A return false; 1182520SN/A} 1192519SN/A 1202519SN/Avoid 1212519SN/ADMASequencer::ruby_hit_callback(PacketPtr pkt) 1222526SN/A{ 1232683Sktlim@umich.edu DPRINTF(RubyDma, "Hit callback for %s 0x%x\n", pkt->cmdString(), 1242SN/A pkt->getAddr()); 1252190SN/A 1262862Sktlim@umich.edu // The packet was destined for memory and has not yet been turned 1272862Sktlim@umich.edu // into a response 1282862Sktlim@umich.edu assert(system->isMemAddr(pkt->getAddr())); 1292862Sktlim@umich.edu assert(pkt->isRequest()); 1302862Sktlim@umich.edu slave_port.hitCallback(pkt); 1312862Sktlim@umich.edu 1322862Sktlim@umich.edu // If we had to stall the slave ports, wake it up because 1332862Sktlim@umich.edu // the sequencer likely has free resources now. 1342190SN/A if (retry) { 1352683Sktlim@umich.edu retry = false; 1362862Sktlim@umich.edu DPRINTF(RubyDma,"Sequencer may now be free. SendRetry to port %s\n", 1372862Sktlim@umich.edu slave_port.name()); 1382862Sktlim@umich.edu slave_port.sendRetry(); 1392862Sktlim@umich.edu } 1402862Sktlim@umich.edu 1412862Sktlim@umich.edu testDrainComplete(); 1422862Sktlim@umich.edu} 1432862Sktlim@umich.edu 1442862Sktlim@umich.eduvoid 1452862Sktlim@umich.eduDMASequencer::testDrainComplete() 1462862Sktlim@umich.edu{ 1472862Sktlim@umich.edu //If we weren't able to drain before, we might be able to now. 1482862Sktlim@umich.edu if (drainManager != NULL) { 1492862Sktlim@umich.edu unsigned int drainCount = outstandingCount(); 1502190SN/A DPRINTF(Drain, "Drain count: %u\n", drainCount); 1512190SN/A if (drainCount == 0) { 1522683Sktlim@umich.edu DPRINTF(Drain, "DMASequencer done draining, signaling drain done\n"); 1531070SN/A drainManager->signalDrainDone(); 1542680SN/A // Clear the drain manager once we're done with it. 1551070SN/A drainManager = NULL; 1561070SN/A } 1571917SN/A } 1582683Sktlim@umich.edu} 159180SN/A 160180SN/Aunsigned int 1611858SN/ADMASequencer::getChildDrainCount(DrainManager *dm) 1622235SN/A{ 163180SN/A int count = 0; 1642235SN/A count += slave_port.drain(dm); 165180SN/A DPRINTF(Config, "count after slave port check %d\n", count); 166180SN/A return count; 1672862Sktlim@umich.edu} 1682862Sktlim@umich.edu 1692313SN/Aunsigned int 1702313SN/ADMASequencer::drain(DrainManager *dm) 1712680SN/A{ 1722313SN/A if (isDeadlockEventScheduled()) { 1732680SN/A descheduleDeadlockEvent(); 1742313SN/A } 1752313SN/A 1762680SN/A // If the DMASequencer is not empty, then it needs to clear all outstanding 1772313SN/A // requests before it should call drainManager->signalDrainDone() 1782235SN/A DPRINTF(Config, "outstanding count %d\n", outstandingCount()); 179180SN/A bool need_drain = outstandingCount() > 0; 180180SN/A 181180SN/A // 1822680SN/A // Also, get the number of child ports that will also need to clear 183180SN/A // their buffered requests before they call drainManager->signalDrainDone() 184180SN/A // 1852SN/A unsigned int child_drain_count = getChildDrainCount(dm); 1862862Sktlim@umich.edu 1872862Sktlim@umich.edu // Set status 1882862Sktlim@umich.edu if (need_drain) { 1892862Sktlim@umich.edu drainManager = dm; 1902862Sktlim@umich.edu 1912862Sktlim@umich.edu DPRINTF(Drain, "DMASequencer not drained\n"); 1922862Sktlim@umich.edu setDrainState(Drainable::Draining); 1932862Sktlim@umich.edu return child_drain_count + 1; 1942862Sktlim@umich.edu } 1952862Sktlim@umich.edu 1962862Sktlim@umich.edu drainManager = NULL; 1972862Sktlim@umich.edu setDrainState(Drainable::Drained); 1982683Sktlim@umich.edu return child_drain_count; 199217SN/A} 2002862Sktlim@umich.edu 201223SN/Avoid 202223SN/ADMASequencer::MemSlavePort::hitCallback(PacketPtr pkt) 203217SN/A{ 204217SN/A bool needsResponse = pkt->needsResponse(); 205217SN/A assert(!pkt->isLLSC()); 206217SN/A assert(!pkt->isFlush()); 2072683Sktlim@umich.edu 208217SN/A DPRINTF(RubyDma, "Hit callback needs response %d\n", needsResponse); 2092862Sktlim@umich.edu 210237SN/A // turn packet around to go back to requester if response expected 211223SN/A if (needsResponse) { 212217SN/A pkt->makeResponse(); 213217SN/A DPRINTF(RubyDma, "Sending packet back over port\n"); 2142683Sktlim@umich.edu // send next cycle 2152683Sktlim@umich.edu schedTimingResp(pkt, curTick() + g_system_ptr->clockPeriod()); 2162683Sktlim@umich.edu } else { 2172683Sktlim@umich.edu delete pkt; 2182683Sktlim@umich.edu } 2192683Sktlim@umich.edu 2202683Sktlim@umich.edu DPRINTF(RubyDma, "Hit callback done!\n"); 2212683Sktlim@umich.edu} 222217SN/A 223217SN/Abool 2242683Sktlim@umich.eduDMASequencer::MemSlavePort::isPhysMemAddress(Addr addr) const 2252SN/A{ 2262680SN/A DMASequencer *seq = static_cast<DMASequencer *>(&owner); 2272SN/A return seq->system->isMemAddr(addr); 2282SN/A} 2292188SN/A 2302188SN/ARequestStatus 2312680SN/ADMASequencer::makeRequest(PacketPtr pkt) 2322683Sktlim@umich.edu{ 2332290SN/A if (m_is_busy) { 2342290SN/A return RequestStatus_BufferFull; 2352290SN/A } 2362680SN/A 2372290SN/A uint64_t paddr = pkt->getAddr(); 2382290SN/A uint8_t* data = pkt->getPtr<uint8_t>(true); 2392683Sktlim@umich.edu int len = pkt->getSize(); 240393SN/A bool write = pkt->isWrite(); 241393SN/A 242393SN/A assert(!m_is_busy); // only support one outstanding DMA request 2432683Sktlim@umich.edu m_is_busy = true; 244393SN/A 2452680SN/A active_request.start_paddr = paddr; 246393SN/A active_request.write = write; 247393SN/A active_request.data = data; 2482188SN/A active_request.len = len; 2492188SN/A active_request.bytes_completed = 0; 2502188SN/A active_request.bytes_issued = 0; 2511858SN/A active_request.pkt = pkt; 2522SN/A 253393SN/A std::shared_ptr<SequencerMsg> msg = 2542680SN/A std::make_shared<SequencerMsg>(clockEdge()); 2552SN/A msg->getPhysicalAddress() = Address(paddr); 2562SN/A msg->getLineAddress() = line_address(msg->getPhysicalAddress()); 2572SN/A msg->getType() = write ? SequencerRequestType_ST : SequencerRequestType_LD; 2582188SN/A int offset = paddr & m_data_block_mask; 2592680SN/A 2602683Sktlim@umich.edu msg->getLen() = (offset + len) <= RubySystem::getBlockSizeBytes() ? 2612SN/A len : RubySystem::getBlockSizeBytes() - offset; 2622SN/A 2632SN/A if (write && (data != NULL)) { 2642683Sktlim@umich.edu if (active_request.data != NULL) { 265393SN/A msg->getDataBlk().setData(data, offset, msg->getLen()); 2662680SN/A } 267393SN/A } 268393SN/A 2692680SN/A assert(m_mandatory_q_ptr != NULL); 2702683Sktlim@umich.edu m_mandatory_q_ptr->enqueue(msg); 271393SN/A active_request.bytes_issued += msg->getLen(); 272393SN/A 273393SN/A return RequestStatus_Issued; 2742683Sktlim@umich.edu} 275393SN/A 2762680SN/Avoid 277393SN/ADMASequencer::issueNext() 278393SN/A{ 2792680SN/A assert(m_is_busy); 2802683Sktlim@umich.edu active_request.bytes_completed = active_request.bytes_issued; 281393SN/A if (active_request.len == active_request.bytes_completed) { 282393SN/A // 283393SN/A // Must unset the busy flag before calling back the dma port because 284393SN/A // the callback may cause a previously nacked request to be reissued 2852683Sktlim@umich.edu // 2862SN/A DPRINTF(RubyDma, "DMA request completed\n"); 2872330SN/A m_is_busy = false; 2882341SN/A ruby_hit_callback(active_request.pkt); 2892341SN/A return; 2902330SN/A } 2912SN/A 292716SN/A std::shared_ptr<SequencerMsg> msg = 293716SN/A std::make_shared<SequencerMsg>(clockEdge()); 2942683Sktlim@umich.edu msg->getPhysicalAddress() = Address(active_request.start_paddr + 2952190SN/A active_request.bytes_completed); 2962680SN/A 2972190SN/A assert((msg->getPhysicalAddress().getAddress() & m_data_block_mask) == 0); 2982190SN/A msg->getLineAddress() = line_address(msg->getPhysicalAddress()); 2992521SN/A 3002521SN/A msg->getType() = (active_request.write ? SequencerRequestType_ST : 3012683Sktlim@umich.edu SequencerRequestType_LD); 3022521SN/A 3032680SN/A msg->getLen() = 3042521SN/A (active_request.len - 3052521SN/A active_request.bytes_completed < RubySystem::getBlockSizeBytes() ? 3062521SN/A active_request.len - active_request.bytes_completed : 3072521SN/A RubySystem::getBlockSizeBytes()); 3082521SN/A 3092680SN/A if (active_request.write) { 3102521SN/A msg->getDataBlk(). 3112521SN/A setData(&active_request.data[active_request.bytes_completed], 3122521SN/A 0, msg->getLen()); 3132521SN/A msg->getType() = SequencerRequestType_ST; 3142521SN/A } else { 3152521SN/A msg->getType() = SequencerRequestType_LD; 3162521SN/A } 3172683Sktlim@umich.edu 3182521SN/A assert(m_mandatory_q_ptr != NULL); 3192684Ssaidi@eecs.umich.edu m_mandatory_q_ptr->enqueue(msg); 3202684Ssaidi@eecs.umich.edu active_request.bytes_issued += msg->getLen(); 3212684Ssaidi@eecs.umich.edu DPRINTF(RubyDma, 3222684Ssaidi@eecs.umich.edu "DMA request bytes issued %d, bytes completed %d, total len %d\n", 3232521SN/A active_request.bytes_issued, active_request.bytes_completed, 3242521SN/A active_request.len); 3252521SN/A} 3262521SN/A 3272521SN/Avoid 328DMASequencer::dataCallback(const DataBlock & dblk) 329{ 330 assert(m_is_busy); 331 int len = active_request.bytes_issued - active_request.bytes_completed; 332 int offset = 0; 333 if (active_request.bytes_completed == 0) 334 offset = active_request.start_paddr & m_data_block_mask; 335 assert(!active_request.write); 336 if (active_request.data != NULL) { 337 memcpy(&active_request.data[active_request.bytes_completed], 338 dblk.getData(offset, len), len); 339 } 340 issueNext(); 341} 342 343void 344DMASequencer::ackCallback() 345{ 346 issueNext(); 347} 348 349void 350DMASequencer::recordRequestType(DMASequencerRequestType requestType) 351{ 352 DPRINTF(RubyStats, "Recorded statistic: %s\n", 353 DMASequencerRequestType_to_string(requestType)); 354} 355 356DMASequencer * 357DMASequencerParams::create() 358{ 359 return new DMASequencer(this); 360} 361