DMASequencer.cc revision 10519
15222Sksewell@umich.edu/*
25254Sksewell@umich.edu * Copyright (c) 2008 Mark D. Hill and David A. Wood
35254Sksewell@umich.edu * All rights reserved.
45254Sksewell@umich.edu *
55222Sksewell@umich.edu * Redistribution and use in source and binary forms, with or without
65254Sksewell@umich.edu * modification, are permitted provided that the following conditions are
75254Sksewell@umich.edu * met: redistributions of source code must retain the above copyright
85254Sksewell@umich.edu * notice, this list of conditions and the following disclaimer;
95254Sksewell@umich.edu * redistributions in binary form must reproduce the above copyright
105254Sksewell@umich.edu * notice, this list of conditions and the following disclaimer in the
115254Sksewell@umich.edu * documentation and/or other materials provided with the distribution;
125254Sksewell@umich.edu * neither the name of the copyright holders nor the names of its
135254Sksewell@umich.edu * contributors may be used to endorse or promote products derived from
145254Sksewell@umich.edu * this software without specific prior written permission.
155254Sksewell@umich.edu *
165222Sksewell@umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
175254Sksewell@umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
185254Sksewell@umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
195254Sksewell@umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
205254Sksewell@umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
215254Sksewell@umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
225254Sksewell@umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
235254Sksewell@umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
245254Sksewell@umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
255254Sksewell@umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
265254Sksewell@umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
275254Sksewell@umich.edu */
285222Sksewell@umich.edu
295254Sksewell@umich.edu#include <memory>
305254Sksewell@umich.edu
315254Sksewell@umich.edu#include "debug/Config.hh"
325222Sksewell@umich.edu#include "debug/Drain.hh"
335222Sksewell@umich.edu#include "debug/RubyDma.hh"
345222Sksewell@umich.edu#include "debug/RubyStats.hh"
355222Sksewell@umich.edu#include "mem/protocol/SequencerMsg.hh"
365222Sksewell@umich.edu#include "mem/ruby/system/DMASequencer.hh"
375222Sksewell@umich.edu#include "mem/ruby/system/System.hh"
385222Sksewell@umich.edu#include "sim/system.hh"
395222Sksewell@umich.edu
405222Sksewell@umich.eduDMASequencer::DMASequencer(const Params *p)
415222Sksewell@umich.edu    : MemObject(p), m_version(p->version), m_controller(NULL),
425222Sksewell@umich.edu      m_mandatory_q_ptr(NULL), m_usingRubyTester(p->using_ruby_tester),
435222Sksewell@umich.edu      slave_port(csprintf("%s.slave", name()), this, 0),
445222Sksewell@umich.edu      drainManager(NULL), system(p->system), retry(false)
455222Sksewell@umich.edu{
465222Sksewell@umich.edu    assert(m_version != -1);
475222Sksewell@umich.edu}
485222Sksewell@umich.edu
495222Sksewell@umich.eduvoid
505222Sksewell@umich.eduDMASequencer::init()
515222Sksewell@umich.edu{
525222Sksewell@umich.edu    MemObject::init();
535222Sksewell@umich.edu    assert(m_controller != NULL);
545222Sksewell@umich.edu    m_mandatory_q_ptr = m_controller->getMandatoryQueue();
555222Sksewell@umich.edu    m_mandatory_q_ptr->setSender(this);
565222Sksewell@umich.edu    m_is_busy = false;
575222Sksewell@umich.edu    m_data_block_mask = ~ (~0 << RubySystem::getBlockSizeBits());
585222Sksewell@umich.edu
595222Sksewell@umich.edu    slave_port.sendRangeChange();
605222Sksewell@umich.edu}
615222Sksewell@umich.edu
625222Sksewell@umich.eduBaseSlavePort &
635222Sksewell@umich.eduDMASequencer::getSlavePort(const std::string &if_name, PortID idx)
645222Sksewell@umich.edu{
655222Sksewell@umich.edu    // used by the CPUs to connect the caches to the interconnect, and
665222Sksewell@umich.edu    // for the x86 case also the interrupt master
675222Sksewell@umich.edu    if (if_name != "slave") {
685222Sksewell@umich.edu        // pass it along to our super class
695222Sksewell@umich.edu        return MemObject::getSlavePort(if_name, idx);
705222Sksewell@umich.edu    } else {
715222Sksewell@umich.edu        return slave_port;
725222Sksewell@umich.edu    }
735222Sksewell@umich.edu}
745222Sksewell@umich.edu
755222Sksewell@umich.eduDMASequencer::MemSlavePort::MemSlavePort(const std::string &_name,
765222Sksewell@umich.edu    DMASequencer *_port, PortID id)
775222Sksewell@umich.edu    : QueuedSlavePort(_name, _port, queue, id), queue(*_port, *this)
785222Sksewell@umich.edu{
795704Snate@binkert.org    DPRINTF(RubyDma, "Created slave memport on ruby sequencer %s\n", _name);
805222Sksewell@umich.edu}
815222Sksewell@umich.edu
825222Sksewell@umich.edubool
835222Sksewell@umich.eduDMASequencer::MemSlavePort::recvTimingReq(PacketPtr pkt)
845222Sksewell@umich.edu{
855222Sksewell@umich.edu    DPRINTF(RubyDma, "Timing request for address %#x on port %d\n",
865222Sksewell@umich.edu            pkt->getAddr(), id);
875222Sksewell@umich.edu    DMASequencer *seq = static_cast<DMASequencer *>(&owner);
885222Sksewell@umich.edu
895222Sksewell@umich.edu    if (pkt->memInhibitAsserted())
905222Sksewell@umich.edu        panic("DMASequencer should never see an inhibited request\n");
915222Sksewell@umich.edu
925222Sksewell@umich.edu    assert(isPhysMemAddress(pkt->getAddr()));
935222Sksewell@umich.edu    assert(Address(pkt->getAddr()).getOffset() + pkt->getSize() <=
945222Sksewell@umich.edu           RubySystem::getBlockSizeBytes());
955222Sksewell@umich.edu
965222Sksewell@umich.edu    // Submit the ruby request
975222Sksewell@umich.edu    RequestStatus requestStatus = seq->makeRequest(pkt);
985222Sksewell@umich.edu
995222Sksewell@umich.edu    // If the request successfully issued then we should return true.
1005222Sksewell@umich.edu    // Otherwise, we need to tell the port to retry at a later point
1015222Sksewell@umich.edu    // and return false.
1025222Sksewell@umich.edu    if (requestStatus == RequestStatus_Issued) {
1035222Sksewell@umich.edu        DPRINTF(RubyDma, "Request %s 0x%x issued\n", pkt->cmdString(),
1045222Sksewell@umich.edu                pkt->getAddr());
1055222Sksewell@umich.edu        return true;
1065222Sksewell@umich.edu    }
1075222Sksewell@umich.edu
1085222Sksewell@umich.edu    // Unless one is using the ruby tester, record the stalled M5 port for
1095222Sksewell@umich.edu    // later retry when the sequencer becomes free.
1105222Sksewell@umich.edu    if (!seq->m_usingRubyTester) {
1115222Sksewell@umich.edu        seq->retry = true;
1125222Sksewell@umich.edu    }
1135222Sksewell@umich.edu
1145222Sksewell@umich.edu    DPRINTF(RubyDma, "Request for address %#x did not issued because %s\n",
1155222Sksewell@umich.edu            pkt->getAddr(), RequestStatus_to_string(requestStatus));
1165222Sksewell@umich.edu
1175222Sksewell@umich.edu    return false;
1185222Sksewell@umich.edu}
1195222Sksewell@umich.edu
1205222Sksewell@umich.eduvoid
1215222Sksewell@umich.eduDMASequencer::ruby_hit_callback(PacketPtr pkt)
1225222Sksewell@umich.edu{
1235222Sksewell@umich.edu    DPRINTF(RubyDma, "Hit callback for %s 0x%x\n", pkt->cmdString(),
1245222Sksewell@umich.edu            pkt->getAddr());
1255222Sksewell@umich.edu
1265222Sksewell@umich.edu    // The packet was destined for memory and has not yet been turned
1275222Sksewell@umich.edu    // into a response
1285222Sksewell@umich.edu    assert(system->isMemAddr(pkt->getAddr()));
1295222Sksewell@umich.edu    assert(pkt->isRequest());
1305222Sksewell@umich.edu    slave_port.hitCallback(pkt);
1315222Sksewell@umich.edu
1325222Sksewell@umich.edu    // If we had to stall the slave ports, wake it up because
1335222Sksewell@umich.edu    // the sequencer likely has free resources now.
1345222Sksewell@umich.edu    if (retry) {
1355222Sksewell@umich.edu        retry = false;
1365222Sksewell@umich.edu        DPRINTF(RubyDma,"Sequencer may now be free.  SendRetry to port %s\n",
1375222Sksewell@umich.edu                slave_port.name());
1385222Sksewell@umich.edu        slave_port.sendRetry();
1395222Sksewell@umich.edu    }
1405222Sksewell@umich.edu
1415222Sksewell@umich.edu    testDrainComplete();
1425222Sksewell@umich.edu}
1435222Sksewell@umich.edu
1445222Sksewell@umich.eduvoid
1455222Sksewell@umich.eduDMASequencer::testDrainComplete()
1465222Sksewell@umich.edu{
1475222Sksewell@umich.edu    //If we weren't able to drain before, we might be able to now.
1485222Sksewell@umich.edu    if (drainManager != NULL) {
1495222Sksewell@umich.edu        unsigned int drainCount = outstandingCount();
1505222Sksewell@umich.edu        DPRINTF(Drain, "Drain count: %u\n", drainCount);
1515222Sksewell@umich.edu        if (drainCount == 0) {
1525222Sksewell@umich.edu            DPRINTF(Drain, "DMASequencer done draining, signaling drain done\n");
1535222Sksewell@umich.edu            drainManager->signalDrainDone();
1545222Sksewell@umich.edu            // Clear the drain manager once we're done with it.
1555222Sksewell@umich.edu            drainManager = NULL;
1565222Sksewell@umich.edu        }
1575222Sksewell@umich.edu    }
1585222Sksewell@umich.edu}
1595222Sksewell@umich.edu
1605222Sksewell@umich.eduunsigned int
1615222Sksewell@umich.eduDMASequencer::getChildDrainCount(DrainManager *dm)
1625222Sksewell@umich.edu{
1635222Sksewell@umich.edu    int count = 0;
1645222Sksewell@umich.edu    count += slave_port.drain(dm);
1655222Sksewell@umich.edu    DPRINTF(Config, "count after slave port check %d\n", count);
1665222Sksewell@umich.edu    return count;
1675222Sksewell@umich.edu}
1685222Sksewell@umich.edu
1695222Sksewell@umich.eduunsigned int
1705222Sksewell@umich.eduDMASequencer::drain(DrainManager *dm)
1715222Sksewell@umich.edu{
1725222Sksewell@umich.edu    if (isDeadlockEventScheduled()) {
1735222Sksewell@umich.edu        descheduleDeadlockEvent();
1745222Sksewell@umich.edu    }
1755222Sksewell@umich.edu
1765222Sksewell@umich.edu    // If the DMASequencer is not empty, then it needs to clear all outstanding
1775222Sksewell@umich.edu    // requests before it should call drainManager->signalDrainDone()
1785222Sksewell@umich.edu    DPRINTF(Config, "outstanding count %d\n", outstandingCount());
1795222Sksewell@umich.edu    bool need_drain = outstandingCount() > 0;
1805222Sksewell@umich.edu
1815222Sksewell@umich.edu    //
1825222Sksewell@umich.edu    // Also, get the number of child ports that will also need to clear
1835222Sksewell@umich.edu    // their buffered requests before they call drainManager->signalDrainDone()
1845222Sksewell@umich.edu    //
1855222Sksewell@umich.edu    unsigned int child_drain_count = getChildDrainCount(dm);
1865222Sksewell@umich.edu
1875222Sksewell@umich.edu    // Set status
1885222Sksewell@umich.edu    if (need_drain) {
1895222Sksewell@umich.edu        drainManager = dm;
1905222Sksewell@umich.edu
1915222Sksewell@umich.edu        DPRINTF(Drain, "DMASequencer not drained\n");
1925704Snate@binkert.org        setDrainState(Drainable::Draining);
1935222Sksewell@umich.edu        return child_drain_count + 1;
1945222Sksewell@umich.edu    }
1955222Sksewell@umich.edu
1965222Sksewell@umich.edu    drainManager = NULL;
1975222Sksewell@umich.edu    setDrainState(Drainable::Drained);
1985222Sksewell@umich.edu    return child_drain_count;
1995704Snate@binkert.org}
2005222Sksewell@umich.edu
2015222Sksewell@umich.eduvoid
2025222Sksewell@umich.eduDMASequencer::MemSlavePort::hitCallback(PacketPtr pkt)
2035222Sksewell@umich.edu{
2045222Sksewell@umich.edu    bool needsResponse = pkt->needsResponse();
2055222Sksewell@umich.edu    assert(!pkt->isLLSC());
2065222Sksewell@umich.edu    assert(!pkt->isFlush());
2075222Sksewell@umich.edu
2085222Sksewell@umich.edu    DPRINTF(RubyDma, "Hit callback needs response %d\n", needsResponse);
2095222Sksewell@umich.edu
2105222Sksewell@umich.edu    // turn packet around to go back to requester if response expected
2115222Sksewell@umich.edu    if (needsResponse) {
2125222Sksewell@umich.edu        pkt->makeResponse();
2135222Sksewell@umich.edu        DPRINTF(RubyDma, "Sending packet back over port\n");
2145222Sksewell@umich.edu        // send next cycle
2155222Sksewell@umich.edu        schedTimingResp(pkt, curTick() + g_system_ptr->clockPeriod());
2165222Sksewell@umich.edu    } else {
2175222Sksewell@umich.edu        delete pkt;
2185222Sksewell@umich.edu    }
2195222Sksewell@umich.edu
2205222Sksewell@umich.edu    DPRINTF(RubyDma, "Hit callback done!\n");
2215222Sksewell@umich.edu}
2225222Sksewell@umich.edu
2235222Sksewell@umich.edubool
2245222Sksewell@umich.eduDMASequencer::MemSlavePort::isPhysMemAddress(Addr addr) const
2255222Sksewell@umich.edu{
2265222Sksewell@umich.edu    DMASequencer *seq = static_cast<DMASequencer *>(&owner);
2275222Sksewell@umich.edu    return seq->system->isMemAddr(addr);
2285222Sksewell@umich.edu}
2295222Sksewell@umich.edu
2305222Sksewell@umich.eduRequestStatus
2315222Sksewell@umich.eduDMASequencer::makeRequest(PacketPtr pkt)
2325222Sksewell@umich.edu{
2335222Sksewell@umich.edu    if (m_is_busy) {
2345222Sksewell@umich.edu        return RequestStatus_BufferFull;
2355222Sksewell@umich.edu    }
2365222Sksewell@umich.edu
2375222Sksewell@umich.edu    uint64_t paddr = pkt->getAddr();
2385222Sksewell@umich.edu    uint8_t* data =  pkt->getPtr<uint8_t>(true);
2395222Sksewell@umich.edu    int len = pkt->getSize();
2405222Sksewell@umich.edu    bool write = pkt->isWrite();
2415222Sksewell@umich.edu
2425222Sksewell@umich.edu    assert(!m_is_busy);  // only support one outstanding DMA request
2435222Sksewell@umich.edu    m_is_busy = true;
2445222Sksewell@umich.edu
2455222Sksewell@umich.edu    active_request.start_paddr = paddr;
2465222Sksewell@umich.edu    active_request.write = write;
2475222Sksewell@umich.edu    active_request.data = data;
2485222Sksewell@umich.edu    active_request.len = len;
2495222Sksewell@umich.edu    active_request.bytes_completed = 0;
2505222Sksewell@umich.edu    active_request.bytes_issued = 0;
2515222Sksewell@umich.edu    active_request.pkt = pkt;
2525222Sksewell@umich.edu
2535222Sksewell@umich.edu    std::shared_ptr<SequencerMsg> msg =
2545222Sksewell@umich.edu        std::make_shared<SequencerMsg>(clockEdge());
2555222Sksewell@umich.edu    msg->getPhysicalAddress() = Address(paddr);
2565222Sksewell@umich.edu    msg->getLineAddress() = line_address(msg->getPhysicalAddress());
2575222Sksewell@umich.edu    msg->getType() = write ? SequencerRequestType_ST : SequencerRequestType_LD;
2585222Sksewell@umich.edu    int offset = paddr & m_data_block_mask;
2595222Sksewell@umich.edu
2605222Sksewell@umich.edu    msg->getLen() = (offset + len) <= RubySystem::getBlockSizeBytes() ?
2615222Sksewell@umich.edu        len : RubySystem::getBlockSizeBytes() - offset;
2625222Sksewell@umich.edu
2635222Sksewell@umich.edu    if (write && (data != NULL)) {
2645222Sksewell@umich.edu        if (active_request.data != NULL) {
2655222Sksewell@umich.edu            msg->getDataBlk().setData(data, offset, msg->getLen());
2665222Sksewell@umich.edu        }
2675222Sksewell@umich.edu    }
2685222Sksewell@umich.edu
2695222Sksewell@umich.edu    assert(m_mandatory_q_ptr != NULL);
2705222Sksewell@umich.edu    m_mandatory_q_ptr->enqueue(msg);
2715222Sksewell@umich.edu    active_request.bytes_issued += msg->getLen();
2725222Sksewell@umich.edu
2735222Sksewell@umich.edu    return RequestStatus_Issued;
274}
275
276void
277DMASequencer::issueNext()
278{
279    assert(m_is_busy);
280    active_request.bytes_completed = active_request.bytes_issued;
281    if (active_request.len == active_request.bytes_completed) {
282        //
283        // Must unset the busy flag before calling back the dma port because
284        // the callback may cause a previously nacked request to be reissued
285        //
286        DPRINTF(RubyDma, "DMA request completed\n");
287        m_is_busy = false;
288        ruby_hit_callback(active_request.pkt);
289        return;
290    }
291
292    std::shared_ptr<SequencerMsg> msg =
293        std::make_shared<SequencerMsg>(clockEdge());
294    msg->getPhysicalAddress() = Address(active_request.start_paddr +
295                                       active_request.bytes_completed);
296
297    assert((msg->getPhysicalAddress().getAddress() & m_data_block_mask) == 0);
298    msg->getLineAddress() = line_address(msg->getPhysicalAddress());
299
300    msg->getType() = (active_request.write ? SequencerRequestType_ST :
301                     SequencerRequestType_LD);
302
303    msg->getLen() =
304        (active_request.len -
305         active_request.bytes_completed < RubySystem::getBlockSizeBytes() ?
306         active_request.len - active_request.bytes_completed :
307         RubySystem::getBlockSizeBytes());
308
309    if (active_request.write) {
310        msg->getDataBlk().
311            setData(&active_request.data[active_request.bytes_completed],
312                    0, msg->getLen());
313        msg->getType() = SequencerRequestType_ST;
314    } else {
315        msg->getType() = SequencerRequestType_LD;
316    }
317
318    assert(m_mandatory_q_ptr != NULL);
319    m_mandatory_q_ptr->enqueue(msg);
320    active_request.bytes_issued += msg->getLen();
321    DPRINTF(RubyDma,
322            "DMA request bytes issued %d, bytes completed %d, total len %d\n",
323            active_request.bytes_issued, active_request.bytes_completed,
324            active_request.len);
325}
326
327void
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