dma_device.cc revision 9165
1/*
2 * Copyright (c) 2012 ARM Limited
3 * All rights reserved.
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright (c) 2006 The Regents of The University of Michigan
15 * All rights reserved.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions are
19 * met: redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer;
21 * redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution;
24 * neither the name of the copyright holders nor the names of its
25 * contributors may be used to endorse or promote products derived from
26 * this software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 *
40 * Authors: Ali Saidi
41 *          Nathan Binkert
42 */
43
44#include "base/chunk_generator.hh"
45#include "debug/DMA.hh"
46#include "debug/Drain.hh"
47#include "dev/dma_device.hh"
48#include "sim/system.hh"
49
50DmaPort::DmaPort(MemObject *dev, System *s)
51    : MasterPort(dev->name() + ".dma", dev), device(dev), sys(s),
52      masterId(s->getMasterId(dev->name())),
53      pendingCount(0), drainEvent(NULL),
54      inRetry(false)
55{ }
56
57bool
58DmaPort::recvTimingResp(PacketPtr pkt)
59{
60    if (pkt->senderState) {
61        DmaReqState *state;
62
63        DPRINTF(DMA, "Received response %s addr %#x size %#x\n",
64                pkt->cmdString(), pkt->getAddr(), pkt->req->getSize());
65        state = dynamic_cast<DmaReqState*>(pkt->senderState);
66        pendingCount--;
67
68        assert(pendingCount >= 0);
69        assert(state);
70
71        // We shouldn't ever get a block in ownership state
72        assert(!(pkt->memInhibitAsserted() && !pkt->sharedAsserted()));
73
74        state->numBytes += pkt->req->getSize();
75        assert(state->totBytes >= state->numBytes);
76        if (state->totBytes == state->numBytes) {
77            if (state->completionEvent) {
78                if (state->delay)
79                    device->schedule(state->completionEvent,
80                                     curTick() + state->delay);
81                else
82                    state->completionEvent->process();
83            }
84            delete state;
85        }
86        delete pkt->req;
87        delete pkt;
88
89        if (pendingCount == 0 && transmitList.empty() && drainEvent) {
90            drainEvent->process();
91            drainEvent = NULL;
92        }
93    }  else {
94        panic("Got packet without sender state... huh?\n");
95    }
96
97    return true;
98}
99
100DmaDevice::DmaDevice(const Params *p)
101    : PioDevice(p), dmaPort(this, sys)
102{ }
103
104void
105DmaDevice::init()
106{
107    if (!dmaPort.isConnected())
108        panic("DMA port of %s not connected to anything!", name());
109    PioDevice::init();
110}
111
112unsigned int
113DmaDevice::drain(Event *de)
114{
115    unsigned int count;
116    count = pioPort.drain(de) + dmaPort.drain(de);
117    if (count)
118        changeState(Draining);
119    else
120        changeState(Drained);
121    return count;
122}
123
124unsigned int
125DmaPort::drain(Event *de)
126{
127    if (transmitList.empty() && pendingCount == 0)
128        return 0;
129    drainEvent = de;
130    DPRINTF(Drain, "DmaPort not drained\n");
131    return 1;
132}
133
134void
135DmaPort::recvRetry()
136{
137    assert(transmitList.size());
138    bool result = true;
139    do {
140        PacketPtr pkt = transmitList.front();
141        DPRINTF(DMA, "Retry on %s addr %#x\n",
142                pkt->cmdString(), pkt->getAddr());
143        result = sendTimingReq(pkt);
144        if (result) {
145            DPRINTF(DMA, "-- Done\n");
146            transmitList.pop_front();
147            inRetry = false;
148        } else {
149            inRetry = true;
150            DPRINTF(DMA, "-- Failed, queued\n");
151        }
152    } while (result && transmitList.size());
153
154    DPRINTF(DMA, "TransmitList: %d, inRetry: %d\n",
155            transmitList.size(), inRetry);
156}
157
158void
159DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event,
160                   uint8_t *data, Tick delay, Request::Flags flag)
161{
162    DmaReqState *reqState = new DmaReqState(event, size, delay);
163
164
165    DPRINTF(DMA, "Starting DMA for addr: %#x size: %d sched: %d\n", addr, size,
166            event ? event->scheduled() : -1 );
167    for (ChunkGenerator gen(addr, size, peerBlockSize());
168         !gen.done(); gen.next()) {
169            Request *req = new Request(gen.addr(), gen.size(), flag, masterId);
170            PacketPtr pkt = new Packet(req, cmd);
171
172            // Increment the data pointer on a write
173            if (data)
174                pkt->dataStatic(data + gen.complete());
175
176            pkt->senderState = reqState;
177
178            assert(pendingCount >= 0);
179            pendingCount++;
180            DPRINTF(DMA, "--Queuing DMA for addr: %#x size: %d\n", gen.addr(),
181                    gen.size());
182            queueDma(pkt);
183    }
184
185}
186
187void
188DmaPort::queueDma(PacketPtr pkt, bool front)
189{
190
191    if (front)
192        transmitList.push_front(pkt);
193    else
194        transmitList.push_back(pkt);
195    sendDma();
196}
197
198void
199DmaPort::sendDma()
200{
201    // some kind of selction between access methods
202    // more work is going to have to be done to make
203    // switching actually work
204    assert(transmitList.size());
205    PacketPtr pkt = transmitList.front();
206
207    Enums::MemoryMode state = sys->getMemoryMode();
208    if (state == Enums::timing) {
209        if (inRetry) {
210            DPRINTF(DMA, "Can't send immediately, waiting for retry\n");
211            return;
212        }
213
214        DPRINTF(DMA, "Attempting to send %s addr %#x\n",
215                pkt->cmdString(), pkt->getAddr());
216
217        bool result;
218        do {
219            result = sendTimingReq(pkt);
220            if (result) {
221                transmitList.pop_front();
222                DPRINTF(DMA, "-- Done\n");
223            } else {
224                inRetry = true;
225                DPRINTF(DMA, "-- Failed: queued\n");
226            }
227        } while (result && transmitList.size());
228    } else if (state == Enums::atomic) {
229        transmitList.pop_front();
230
231        Tick lat;
232        DPRINTF(DMA, "--Sending  DMA for addr: %#x size: %d\n",
233                pkt->req->getPaddr(), pkt->req->getSize());
234        lat = sendAtomic(pkt);
235        assert(pkt->senderState);
236        DmaReqState *state = dynamic_cast<DmaReqState*>(pkt->senderState);
237        assert(state);
238        state->numBytes += pkt->req->getSize();
239
240        DPRINTF(DMA, "--Received response for  DMA for addr: %#x size: %d nb: %d, tot: %d sched %d\n",
241                pkt->req->getPaddr(), pkt->req->getSize(), state->numBytes,
242                state->totBytes,
243                state->completionEvent ? state->completionEvent->scheduled() : 0 );
244
245        if (state->totBytes == state->numBytes) {
246            if (state->completionEvent) {
247                assert(!state->completionEvent->scheduled());
248                device->schedule(state->completionEvent,
249                                 curTick() + lat + state->delay);
250            }
251            delete state;
252            delete pkt->req;
253        }
254        pendingCount--;
255        assert(pendingCount >= 0);
256        delete pkt;
257
258        if (pendingCount == 0 && transmitList.empty() && drainEvent) {
259            DPRINTF(Drain, "DmaPort done draining, processing drain event\n");
260            drainEvent->process();
261            drainEvent = NULL;
262        }
263
264   } else
265       panic("Unknown memory command state.");
266}
267
268DmaDevice::~DmaDevice()
269{
270}
271
272MasterPort &
273DmaDevice::getMasterPort(const std::string &if_name, int idx)
274{
275    if (if_name == "dma") {
276        return dmaPort;
277    }
278    return PioDevice::getMasterPort(if_name, idx);
279}
280