dma_device.cc revision 10913:38dbdeea7f1f
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 *          Andreas Hansson
43 */
44
45#include "base/chunk_generator.hh"
46#include "debug/DMA.hh"
47#include "debug/Drain.hh"
48#include "dev/dma_device.hh"
49#include "sim/system.hh"
50
51DmaPort::DmaPort(MemObject *dev, System *s)
52    : MasterPort(dev->name() + ".dma", dev), device(dev), sendEvent(this),
53      sys(s), masterId(s->getMasterId(dev->name())),
54      pendingCount(0), inRetry(false)
55{ }
56
57void
58DmaPort::handleResp(PacketPtr pkt, Tick delay)
59{
60    // should always see a response with a sender state
61    assert(pkt->isResponse());
62
63    // get the DMA sender state
64    DmaReqState *state = dynamic_cast<DmaReqState*>(pkt->senderState);
65    assert(state);
66
67    DPRINTF(DMA, "Received response %s for addr: %#x size: %d nb: %d,"  \
68            " tot: %d sched %d\n",
69            pkt->cmdString(), pkt->getAddr(), pkt->req->getSize(),
70            state->numBytes, state->totBytes,
71            state->completionEvent ?
72            state->completionEvent->scheduled() : 0);
73
74    assert(pendingCount != 0);
75    pendingCount--;
76
77    // update the number of bytes received based on the request rather
78    // than the packet as the latter could be rounded up to line sizes
79    state->numBytes += pkt->req->getSize();
80    assert(state->totBytes >= state->numBytes);
81
82    // if we have reached the total number of bytes for this DMA
83    // request, then signal the completion and delete the sate
84    if (state->totBytes == state->numBytes) {
85        if (state->completionEvent) {
86            delay += state->delay;
87            device->schedule(state->completionEvent, curTick() + delay);
88        }
89        delete state;
90    }
91
92    // delete the request that we created and also the packet
93    delete pkt->req;
94    delete pkt;
95
96    // we might be drained at this point, if so signal the drain event
97    if (pendingCount == 0)
98        signalDrainDone();
99}
100
101bool
102DmaPort::recvTimingResp(PacketPtr pkt)
103{
104    // We shouldn't ever get a cacheable block in ownership state
105    assert(pkt->req->isUncacheable() ||
106           !(pkt->memInhibitAsserted() && !pkt->sharedAsserted()));
107
108    handleResp(pkt);
109
110    return true;
111}
112
113DmaDevice::DmaDevice(const Params *p)
114    : PioDevice(p), dmaPort(this, sys)
115{ }
116
117void
118DmaDevice::init()
119{
120    if (!dmaPort.isConnected())
121        panic("DMA port of %s not connected to anything!", name());
122    PioDevice::init();
123}
124
125DrainState
126DmaPort::drain()
127{
128    if (pendingCount == 0) {
129        return DrainState::Drained;
130    } else {
131        DPRINTF(Drain, "DmaPort not drained\n");
132        return DrainState::Draining;
133    }
134}
135
136void
137DmaPort::recvReqRetry()
138{
139    assert(transmitList.size());
140    trySendTimingReq();
141}
142
143RequestPtr
144DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event,
145                   uint8_t *data, Tick delay, Request::Flags flag)
146{
147    // one DMA request sender state for every action, that is then
148    // split into many requests and packets based on the block size,
149    // i.e. cache line size
150    DmaReqState *reqState = new DmaReqState(event, size, delay);
151
152    // (functionality added for Table Walker statistics)
153    // We're only interested in this when there will only be one request.
154    // For simplicity, we return the last request, which would also be
155    // the only request in that case.
156    RequestPtr req = NULL;
157
158    DPRINTF(DMA, "Starting DMA for addr: %#x size: %d sched: %d\n", addr, size,
159            event ? event->scheduled() : -1);
160    for (ChunkGenerator gen(addr, size, sys->cacheLineSize());
161         !gen.done(); gen.next()) {
162        req = new Request(gen.addr(), gen.size(), flag, masterId);
163        req->taskId(ContextSwitchTaskId::DMA);
164        PacketPtr pkt = new Packet(req, cmd);
165
166        // Increment the data pointer on a write
167        if (data)
168            pkt->dataStatic(data + gen.complete());
169
170        pkt->senderState = reqState;
171
172        DPRINTF(DMA, "--Queuing DMA for addr: %#x size: %d\n", gen.addr(),
173                gen.size());
174        queueDma(pkt);
175    }
176
177    // in zero time also initiate the sending of the packets we have
178    // just created, for atomic this involves actually completing all
179    // the requests
180    sendDma();
181
182    return req;
183}
184
185void
186DmaPort::queueDma(PacketPtr pkt)
187{
188    transmitList.push_back(pkt);
189
190    // remember that we have another packet pending, this will only be
191    // decremented once a response comes back
192    pendingCount++;
193}
194
195void
196DmaPort::trySendTimingReq()
197{
198    // send the first packet on the transmit list and schedule the
199    // following send if it is successful
200    PacketPtr pkt = transmitList.front();
201
202    DPRINTF(DMA, "Trying to send %s addr %#x\n", pkt->cmdString(),
203            pkt->getAddr());
204
205    inRetry = !sendTimingReq(pkt);
206    if (!inRetry) {
207        transmitList.pop_front();
208        DPRINTF(DMA, "-- Done\n");
209        // if there is more to do, then do so
210        if (!transmitList.empty())
211            // this should ultimately wait for as many cycles as the
212            // device needs to send the packet, but currently the port
213            // does not have any known width so simply wait a single
214            // cycle
215            device->schedule(sendEvent, device->clockEdge(Cycles(1)));
216    } else {
217        DPRINTF(DMA, "-- Failed, waiting for retry\n");
218    }
219
220    DPRINTF(DMA, "TransmitList: %d, inRetry: %d\n",
221            transmitList.size(), inRetry);
222}
223
224void
225DmaPort::sendDma()
226{
227    // some kind of selcetion between access methods
228    // more work is going to have to be done to make
229    // switching actually work
230    assert(transmitList.size());
231
232    if (sys->isTimingMode()) {
233        // if we are either waiting for a retry or are still waiting
234        // after sending the last packet, then do not proceed
235        if (inRetry || sendEvent.scheduled()) {
236            DPRINTF(DMA, "Can't send immediately, waiting to send\n");
237            return;
238        }
239
240        trySendTimingReq();
241    } else if (sys->isAtomicMode()) {
242        // send everything there is to send in zero time
243        while (!transmitList.empty()) {
244            PacketPtr pkt = transmitList.front();
245            transmitList.pop_front();
246
247            DPRINTF(DMA, "Sending  DMA for addr: %#x size: %d\n",
248                    pkt->req->getPaddr(), pkt->req->getSize());
249            Tick lat = sendAtomic(pkt);
250
251            handleResp(pkt, lat);
252        }
253    } else
254        panic("Unknown memory mode.");
255}
256
257BaseMasterPort &
258DmaDevice::getMasterPort(const std::string &if_name, PortID idx)
259{
260    if (if_name == "dma") {
261        return dmaPort;
262    }
263    return PioDevice::getMasterPort(if_name, idx);
264}
265