dma_device.cc revision 8922
1/*
2 * Copyright (c) 2006 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Ali Saidi
29 *          Nathan Binkert
30 */
31
32#include "base/chunk_generator.hh"
33#include "base/trace.hh"
34#include "debug/BusAddrRanges.hh"
35#include "debug/DMA.hh"
36#include "dev/io_device.hh"
37#include "sim/system.hh"
38
39PioPort::PioPort(PioDevice *dev)
40    : SimpleTimingPort(dev->name() + "-pioport", dev), device(dev)
41{
42}
43
44
45Tick
46PioPort::recvAtomic(PacketPtr pkt)
47{
48    return pkt->isRead() ? device->read(pkt) : device->write(pkt);
49}
50
51AddrRangeList
52PioPort::getAddrRanges()
53{
54    return device->getAddrRanges();
55}
56
57
58PioDevice::PioDevice(const Params *p)
59    : MemObject(p), sys(p->system), pioPort(this)
60{}
61
62PioDevice::~PioDevice()
63{
64}
65
66void
67PioDevice::init()
68{
69    if (!pioPort.isConnected())
70        panic("Pio port of %s not connected to anything!", name());
71    pioPort.sendRangeChange();
72}
73
74SlavePort &
75PioDevice::getSlavePort(const std::string &if_name, int idx)
76{
77    if (if_name == "pio") {
78        return pioPort;
79    }
80    return MemObject::getSlavePort(if_name, idx);
81}
82
83unsigned int
84PioDevice::drain(Event *de)
85{
86    unsigned int count;
87    count = pioPort.drain(de);
88    if (count)
89        changeState(Draining);
90    else
91        changeState(Drained);
92    return count;
93}
94
95BasicPioDevice::BasicPioDevice(const Params *p)
96    : PioDevice(p), pioAddr(p->pio_addr), pioSize(0),
97      pioDelay(p->pio_latency)
98{}
99
100AddrRangeList
101BasicPioDevice::getAddrRanges()
102{
103    assert(pioSize != 0);
104    AddrRangeList ranges;
105    DPRINTF(BusAddrRanges, "registering range: %#x-%#x\n", pioAddr, pioSize);
106    ranges.push_back(RangeSize(pioAddr, pioSize));
107    return ranges;
108}
109
110
111DmaPort::DmaPort(MemObject *dev, System *s, Tick min_backoff, Tick max_backoff,
112                 bool recv_snoops)
113    : MasterPort(dev->name() + "-dmaport", dev), device(dev), sys(s),
114      masterId(s->getMasterId(dev->name())),
115      pendingCount(0), actionInProgress(0), drainEvent(NULL),
116      backoffTime(0), minBackoffDelay(min_backoff),
117      maxBackoffDelay(max_backoff), inRetry(false), recvSnoops(recv_snoops),
118      backoffEvent(this)
119{ }
120
121bool
122DmaPort::recvTiming(PacketPtr pkt)
123{
124    if (pkt->wasNacked()) {
125        DPRINTF(DMA, "Received nacked %s addr %#x\n",
126                pkt->cmdString(), pkt->getAddr());
127
128        if (backoffTime < minBackoffDelay)
129            backoffTime = minBackoffDelay;
130        else if (backoffTime < maxBackoffDelay)
131            backoffTime <<= 1;
132
133        device->reschedule(backoffEvent, curTick() + backoffTime, true);
134
135        DPRINTF(DMA, "Backoff time set to %d ticks\n", backoffTime);
136
137        pkt->reinitNacked();
138        queueDma(pkt, true);
139    } else if (pkt->isRequest() && recvSnoops) {
140        return true;
141    } else if (pkt->senderState) {
142        DmaReqState *state;
143        backoffTime >>= 2;
144
145        DPRINTF(DMA, "Received response %s addr %#x size %#x\n",
146                pkt->cmdString(), pkt->getAddr(), pkt->req->getSize());
147        state = dynamic_cast<DmaReqState*>(pkt->senderState);
148        pendingCount--;
149
150        assert(pendingCount >= 0);
151        assert(state);
152
153        // We shouldn't ever get a block in ownership state
154        assert(!(pkt->memInhibitAsserted() && !pkt->sharedAsserted()));
155
156        state->numBytes += pkt->req->getSize();
157        assert(state->totBytes >= state->numBytes);
158        if (state->totBytes == state->numBytes) {
159            if (state->completionEvent) {
160                if (state->delay)
161                    device->schedule(state->completionEvent,
162                                     curTick() + state->delay);
163                else
164                    state->completionEvent->process();
165            }
166            delete state;
167        }
168        delete pkt->req;
169        delete pkt;
170
171        if (pendingCount == 0 && drainEvent) {
172            drainEvent->process();
173            drainEvent = NULL;
174        }
175    }  else {
176        panic("Got packet without sender state... huh?\n");
177    }
178
179    return true;
180}
181
182DmaDevice::DmaDevice(const Params *p)
183    : PioDevice(p), dmaPort(this, sys, params()->min_backoff_delay,
184                            params()->max_backoff_delay)
185{ }
186
187void
188DmaDevice::init()
189{
190    if (!dmaPort.isConnected())
191        panic("DMA port of %s not connected to anything!", name());
192    PioDevice::init();
193}
194
195unsigned int
196DmaDevice::drain(Event *de)
197{
198    unsigned int count;
199    count = pioPort.drain(de) + dmaPort.drain(de);
200    if (count)
201        changeState(Draining);
202    else
203        changeState(Drained);
204    return count;
205}
206
207unsigned int
208DmaPort::drain(Event *de)
209{
210    if (pendingCount == 0)
211        return 0;
212    drainEvent = de;
213    return 1;
214}
215
216
217void
218DmaPort::recvRetry()
219{
220    assert(transmitList.size());
221    bool result = true;
222    do {
223        PacketPtr pkt = transmitList.front();
224        DPRINTF(DMA, "Retry on %s addr %#x\n",
225                pkt->cmdString(), pkt->getAddr());
226        result = sendTiming(pkt);
227        if (result) {
228            DPRINTF(DMA, "-- Done\n");
229            transmitList.pop_front();
230            inRetry = false;
231        } else {
232            inRetry = true;
233            DPRINTF(DMA, "-- Failed, queued\n");
234        }
235    } while (!backoffTime &&  result && transmitList.size());
236
237    if (transmitList.size() && backoffTime && !inRetry) {
238        DPRINTF(DMA, "Scheduling backoff for %d\n", curTick()+backoffTime);
239        if (!backoffEvent.scheduled())
240            device->schedule(backoffEvent, backoffTime + curTick());
241    }
242    DPRINTF(DMA, "TransmitList: %d, backoffTime: %d inRetry: %d es: %d\n",
243            transmitList.size(), backoffTime, inRetry,
244            backoffEvent.scheduled());
245}
246
247
248void
249DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event,
250                   uint8_t *data, Tick delay, Request::Flags flag)
251{
252    assert(device->getState() == SimObject::Running);
253
254    DmaReqState *reqState = new DmaReqState(event, this, size, delay);
255
256
257    DPRINTF(DMA, "Starting DMA for addr: %#x size: %d sched: %d\n", addr, size,
258            event ? event->scheduled() : -1 );
259    for (ChunkGenerator gen(addr, size, peerBlockSize());
260         !gen.done(); gen.next()) {
261            Request *req = new Request(gen.addr(), gen.size(), flag, masterId);
262            PacketPtr pkt = new Packet(req, cmd, Packet::Broadcast);
263
264            // Increment the data pointer on a write
265            if (data)
266                pkt->dataStatic(data + gen.complete());
267
268            pkt->senderState = reqState;
269
270            assert(pendingCount >= 0);
271            pendingCount++;
272            DPRINTF(DMA, "--Queuing DMA for addr: %#x size: %d\n", gen.addr(),
273                    gen.size());
274            queueDma(pkt);
275    }
276
277}
278
279void
280DmaPort::queueDma(PacketPtr pkt, bool front)
281{
282
283    if (front)
284        transmitList.push_front(pkt);
285    else
286        transmitList.push_back(pkt);
287    sendDma();
288}
289
290
291void
292DmaPort::sendDma()
293{
294    // some kind of selction between access methods
295    // more work is going to have to be done to make
296    // switching actually work
297    assert(transmitList.size());
298    PacketPtr pkt = transmitList.front();
299
300    Enums::MemoryMode state = sys->getMemoryMode();
301    if (state == Enums::timing) {
302        if (backoffEvent.scheduled() || inRetry) {
303            DPRINTF(DMA, "Can't send immediately, waiting for retry or backoff timer\n");
304            return;
305        }
306
307        DPRINTF(DMA, "Attempting to send %s addr %#x\n",
308                pkt->cmdString(), pkt->getAddr());
309
310        bool result;
311        do {
312            result = sendTiming(pkt);
313            if (result) {
314                transmitList.pop_front();
315                DPRINTF(DMA, "-- Done\n");
316            } else {
317                inRetry = true;
318                DPRINTF(DMA, "-- Failed: queued\n");
319            }
320        } while (result && !backoffTime && transmitList.size());
321
322        if (transmitList.size() && backoffTime && !inRetry &&
323                !backoffEvent.scheduled()) {
324            DPRINTF(DMA, "-- Scheduling backoff timer for %d\n",
325                    backoffTime+curTick());
326            device->schedule(backoffEvent, backoffTime + curTick());
327        }
328    } else if (state == Enums::atomic) {
329        transmitList.pop_front();
330
331        Tick lat;
332        DPRINTF(DMA, "--Sending  DMA for addr: %#x size: %d\n",
333                pkt->req->getPaddr(), pkt->req->getSize());
334        lat = sendAtomic(pkt);
335        assert(pkt->senderState);
336        DmaReqState *state = dynamic_cast<DmaReqState*>(pkt->senderState);
337        assert(state);
338        state->numBytes += pkt->req->getSize();
339
340        DPRINTF(DMA, "--Received response for  DMA for addr: %#x size: %d nb: %d, tot: %d sched %d\n",
341                pkt->req->getPaddr(), pkt->req->getSize(), state->numBytes,
342                state->totBytes,
343                state->completionEvent ? state->completionEvent->scheduled() : 0 );
344
345        if (state->totBytes == state->numBytes) {
346            if (state->completionEvent) {
347                assert(!state->completionEvent->scheduled());
348                device->schedule(state->completionEvent,
349                                 curTick() + lat + state->delay);
350            }
351            delete state;
352            delete pkt->req;
353        }
354        pendingCount--;
355        assert(pendingCount >= 0);
356        delete pkt;
357
358        if (pendingCount == 0 && drainEvent) {
359            drainEvent->process();
360            drainEvent = NULL;
361        }
362
363   } else
364       panic("Unknown memory command state.");
365}
366
367DmaDevice::~DmaDevice()
368{
369}
370
371
372MasterPort &
373DmaDevice::getMasterPort(const std::string &if_name, int idx)
374{
375    if (if_name == "dma") {
376        return dmaPort;
377    }
378    return PioDevice::getMasterPort(if_name, idx);
379}
380
381