copy_engine.cc revision 8851
12199SN/A/*
22199SN/A * Copyright (c) 2012 ARM Limited
32199SN/A * All rights reserved
42199SN/A *
52199SN/A * The license below extends only to copyright in the software and shall
62199SN/A * not be construed as granting a license to any other intellectual
72199SN/A * property including but not limited to intellectual property relating
82199SN/A * to a hardware implementation of the functionality of the software
92199SN/A * licensed hereunder.  You may use the software subject to the license
102199SN/A * terms below provided that you ensure that this notice is replicated
112199SN/A * unmodified and in its entirety in all distributions of the software,
122199SN/A * modified or unmodified, in source code or in binary form.
132199SN/A *
142199SN/A * Copyright (c) 2008 The Regents of The University of Michigan
152199SN/A * All rights reserved.
162199SN/A *
172199SN/A * Redistribution and use in source and binary forms, with or without
182199SN/A * modification, are permitted provided that the following conditions are
192199SN/A * met: redistributions of source code must retain the above copyright
202199SN/A * notice, this list of conditions and the following disclaimer;
212199SN/A * redistributions in binary form must reproduce the above copyright
222199SN/A * notice, this list of conditions and the following disclaimer in the
232199SN/A * documentation and/or other materials provided with the distribution;
242199SN/A * neither the name of the copyright holders nor the names of its
252199SN/A * contributors may be used to endorse or promote products derived from
262199SN/A * this software without specific prior written permission.
272665Ssaidi@eecs.umich.edu *
282665Ssaidi@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
292665Ssaidi@eecs.umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
302665Ssaidi@eecs.umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
312199SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
322199SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
332561SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
342226SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
352561SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
362199SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
372199SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
382680Sktlim@umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
392199SN/A *
402199SN/A * Authors: Ali Saidi
412199SN/A */
422199SN/A
432199SN/A/* @file
442199SN/A * Device model for Intel's I/O AT DMA copy engine.
452209SN/A */
462199SN/A
472199SN/A#include <algorithm>
482458SN/A
492199SN/A#include "base/cp_annotate.hh"
502199SN/A#include "base/trace.hh"
512199SN/A#include "debug/DMACopyEngine.hh"
522199SN/A#include "dev/copy_engine.hh"
532199SN/A#include "mem/packet.hh"
544111Sgblack@eecs.umich.edu#include "mem/packet_access.hh"
554188Sgblack@eecs.umich.edu#include "params/CopyEngine.hh"
564188Sgblack@eecs.umich.edu#include "sim/stats.hh"
574188Sgblack@eecs.umich.edu#include "sim/system.hh"
584188Sgblack@eecs.umich.edu
594188Sgblack@eecs.umich.eduusing namespace CopyEngineReg;
604188Sgblack@eecs.umich.edu
614188Sgblack@eecs.umich.eduCopyEngine::CopyEngine(const Params *p)
624111Sgblack@eecs.umich.edu    : PciDev(p)
634111Sgblack@eecs.umich.edu{
644188Sgblack@eecs.umich.edu    // All Reg regs are initialized to 0 by default
654188Sgblack@eecs.umich.edu    regs.chanCount = p->ChanCnt;
664111Sgblack@eecs.umich.edu    regs.xferCap = findMsbSet(p->XferCap);
674111Sgblack@eecs.umich.edu    regs.attnStatus = 0;
684111Sgblack@eecs.umich.edu
694111Sgblack@eecs.umich.edu    if (regs.chanCount > 64)
704188Sgblack@eecs.umich.edu        fatal("CopyEngine interface doesn't support more than 64 DMA engines\n");
714188Sgblack@eecs.umich.edu
724188Sgblack@eecs.umich.edu    for (int x = 0; x < regs.chanCount; x++) {
734111Sgblack@eecs.umich.edu        CopyEngineChannel *ch = new CopyEngineChannel(this, x);
744111Sgblack@eecs.umich.edu        chan.push_back(ch);
754111Sgblack@eecs.umich.edu    }
764111Sgblack@eecs.umich.edu}
774111Sgblack@eecs.umich.edu
784111Sgblack@eecs.umich.edu
794111Sgblack@eecs.umich.eduCopyEngine::CopyEngineChannel::CopyEngineChannel(CopyEngine *_ce, int cid)
804111Sgblack@eecs.umich.edu    : cePort(_ce, _ce->sys, _ce->params()->min_backoff_delay,
814111Sgblack@eecs.umich.edu             _ce->params()->max_backoff_delay),
824111Sgblack@eecs.umich.edu      ce(_ce), channelId(cid), busy(false), underReset(false),
834111Sgblack@eecs.umich.edu    refreshNext(false), latBeforeBegin(ce->params()->latBeforeBegin),
844111Sgblack@eecs.umich.edu    latAfterCompletion(ce->params()->latAfterCompletion),
854111Sgblack@eecs.umich.edu    completionDataReg(0), nextState(Idle), drainEvent(NULL),
864111Sgblack@eecs.umich.edu    fetchCompleteEvent(this), addrCompleteEvent(this),
874111Sgblack@eecs.umich.edu    readCompleteEvent(this), writeCompleteEvent(this),
884111Sgblack@eecs.umich.edu    statusCompleteEvent(this)
894111Sgblack@eecs.umich.edu
904111Sgblack@eecs.umich.edu{
914111Sgblack@eecs.umich.edu        cr.status.dma_transfer_status(3);
924111Sgblack@eecs.umich.edu        cr.descChainAddr = 0;
934111Sgblack@eecs.umich.edu        cr.completionAddr = 0;
944111Sgblack@eecs.umich.edu
954111Sgblack@eecs.umich.edu        curDmaDesc = new DmaDesc;
964111Sgblack@eecs.umich.edu        memset(curDmaDesc, 0, sizeof(DmaDesc));
974111Sgblack@eecs.umich.edu        copyBuffer = new uint8_t[ce->params()->XferCap];
984111Sgblack@eecs.umich.edu}
994111Sgblack@eecs.umich.edu
1004111Sgblack@eecs.umich.eduCopyEngine::~CopyEngine()
1014111Sgblack@eecs.umich.edu{
1024111Sgblack@eecs.umich.edu    for (int x = 0; x < chan.size(); x++) {
1034111Sgblack@eecs.umich.edu        delete chan[x];
1044111Sgblack@eecs.umich.edu    }
1054111Sgblack@eecs.umich.edu}
1064111Sgblack@eecs.umich.edu
1074111Sgblack@eecs.umich.eduCopyEngine::CopyEngineChannel::~CopyEngineChannel()
1084111Sgblack@eecs.umich.edu{
1094111Sgblack@eecs.umich.edu    delete curDmaDesc;
1104111Sgblack@eecs.umich.edu    delete [] copyBuffer;
1114111Sgblack@eecs.umich.edu}
1124111Sgblack@eecs.umich.edu
1134111Sgblack@eecs.umich.eduPort *
1144111Sgblack@eecs.umich.eduCopyEngine::getPort(const std::string &if_name, int idx)
1154111Sgblack@eecs.umich.edu{
1164111Sgblack@eecs.umich.edu    if (if_name == "dma") {
1174111Sgblack@eecs.umich.edu        if (idx < chan.size())
1184111Sgblack@eecs.umich.edu            return chan[idx]->getPort();
1194111Sgblack@eecs.umich.edu    }
1204111Sgblack@eecs.umich.edu    return PciDev::getPort(if_name, idx);
1214111Sgblack@eecs.umich.edu}
1224111Sgblack@eecs.umich.edu
1234111Sgblack@eecs.umich.edu
1244111Sgblack@eecs.umich.eduPort *
1254188Sgblack@eecs.umich.eduCopyEngine::CopyEngineChannel::getPort()
1264111Sgblack@eecs.umich.edu{
1274111Sgblack@eecs.umich.edu    return &cePort;
1284111Sgblack@eecs.umich.edu}
1294111Sgblack@eecs.umich.edu
1304111Sgblack@eecs.umich.eduvoid
1314111Sgblack@eecs.umich.eduCopyEngine::CopyEngineChannel::recvCommand()
1324111Sgblack@eecs.umich.edu{
133    if (cr.command.start_dma()) {
134        assert(!busy);
135        cr.status.dma_transfer_status(0);
136        nextState = DescriptorFetch;
137        fetchAddress = cr.descChainAddr;
138        if (ce->getState() == SimObject::Running)
139            fetchDescriptor(cr.descChainAddr);
140    } else if (cr.command.append_dma()) {
141        if (!busy) {
142            nextState = AddressFetch;
143            if (ce->getState() == SimObject::Running)
144                fetchNextAddr(lastDescriptorAddr);
145        } else
146            refreshNext = true;
147    } else if (cr.command.reset_dma()) {
148        if (busy)
149            underReset = true;
150        else {
151            cr.status.dma_transfer_status(3);
152            nextState = Idle;
153        }
154    } else if (cr.command.resume_dma() || cr.command.abort_dma() ||
155            cr.command.suspend_dma())
156        panic("Resume, Abort, and Suspend are not supported\n");
157    cr.command(0);
158}
159
160Tick
161CopyEngine::read(PacketPtr pkt)
162{
163    int bar;
164    Addr daddr;
165
166    if (!getBAR(pkt->getAddr(), bar, daddr))
167        panic("Invalid PCI memory access to unmapped memory.\n");
168
169    // Only Memory register BAR is allowed
170    assert(bar == 0);
171
172    int size = pkt->getSize();
173    if (size != sizeof(uint64_t) && size != sizeof(uint32_t) &&
174        size != sizeof(uint16_t) && size != sizeof(uint8_t)) {
175        panic("Unknown size for MMIO access: %d\n", pkt->getSize());
176    }
177
178    DPRINTF(DMACopyEngine, "Read device register %#X size: %d\n", daddr, size);
179
180    pkt->allocate();
181
182    ///
183    /// Handle read of register here
184    ///
185
186    if (daddr < 0x80) {
187        switch (daddr) {
188          case GEN_CHANCOUNT:
189            assert(size == sizeof(regs.chanCount));
190            pkt->set<uint8_t>(regs.chanCount);
191            break;
192          case GEN_XFERCAP:
193            assert(size == sizeof(regs.xferCap));
194            pkt->set<uint8_t>(regs.xferCap);
195            break;
196          case GEN_INTRCTRL:
197            assert(size == sizeof(uint8_t));
198            pkt->set<uint8_t>(regs.intrctrl());
199            regs.intrctrl.master_int_enable(0);
200            break;
201          case GEN_ATTNSTATUS:
202            assert(size == sizeof(regs.attnStatus));
203            pkt->set<uint32_t>(regs.attnStatus);
204            regs.attnStatus = 0;
205            break;
206          default:
207            panic("Read request to unknown register number: %#x\n", daddr);
208        }
209        pkt->makeAtomicResponse();
210        return pioDelay;
211    }
212
213
214    // Find which channel we're accessing
215    int chanid = 0;
216    daddr -= 0x80;
217    while (daddr >= 0x80) {
218        chanid++;
219        daddr -= 0x80;
220    }
221
222    if (chanid >= regs.chanCount)
223        panic("Access to channel %d (device only configured for %d channels)",
224                chanid, regs.chanCount);
225
226    ///
227    /// Channel registers are handled here
228    ///
229    chan[chanid]->channelRead(pkt, daddr, size);
230
231    pkt->makeAtomicResponse();
232    return pioDelay;
233}
234
235void
236CopyEngine::CopyEngineChannel::channelRead(Packet *pkt, Addr daddr, int size)
237{
238    switch (daddr) {
239      case CHAN_CONTROL:
240        assert(size == sizeof(uint16_t));
241        pkt->set<uint16_t>(cr.ctrl());
242        cr.ctrl.in_use(1);
243        break;
244      case CHAN_STATUS:
245        assert(size == sizeof(uint64_t));
246        pkt->set<uint64_t>(cr.status() | ~busy);
247        break;
248      case CHAN_CHAINADDR:
249        assert(size == sizeof(uint64_t) || size == sizeof(uint32_t));
250        if (size == sizeof(uint64_t))
251            pkt->set<uint64_t>(cr.descChainAddr);
252        else
253            pkt->set<uint32_t>(bits(cr.descChainAddr,0,31));
254        break;
255      case CHAN_CHAINADDR_HIGH:
256        assert(size == sizeof(uint32_t));
257        pkt->set<uint32_t>(bits(cr.descChainAddr,32,63));
258        break;
259      case CHAN_COMMAND:
260        assert(size == sizeof(uint8_t));
261        pkt->set<uint32_t>(cr.command());
262        break;
263      case CHAN_CMPLNADDR:
264        assert(size == sizeof(uint64_t) || size == sizeof(uint32_t));
265        if (size == sizeof(uint64_t))
266            pkt->set<uint64_t>(cr.completionAddr);
267        else
268            pkt->set<uint32_t>(bits(cr.completionAddr,0,31));
269        break;
270      case CHAN_CMPLNADDR_HIGH:
271        assert(size == sizeof(uint32_t));
272        pkt->set<uint32_t>(bits(cr.completionAddr,32,63));
273        break;
274      case CHAN_ERROR:
275        assert(size == sizeof(uint32_t));
276        pkt->set<uint32_t>(cr.error());
277        break;
278      default:
279        panic("Read request to unknown channel register number: (%d)%#x\n",
280                channelId, daddr);
281    }
282}
283
284
285Tick
286CopyEngine::write(PacketPtr pkt)
287{
288    int bar;
289    Addr daddr;
290
291
292    if (!getBAR(pkt->getAddr(), bar, daddr))
293        panic("Invalid PCI memory access to unmapped memory.\n");
294
295    // Only Memory register BAR is allowed
296    assert(bar == 0);
297
298    int size = pkt->getSize();
299
300    ///
301    /// Handle write of register here
302    ///
303
304    if (size == sizeof(uint64_t)) {
305        uint64_t val M5_VAR_USED = pkt->get<uint64_t>();
306        DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n", daddr, val);
307    } else if (size == sizeof(uint32_t)) {
308        uint32_t val M5_VAR_USED = pkt->get<uint32_t>();
309        DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n", daddr, val);
310    } else if (size == sizeof(uint16_t)) {
311        uint16_t val M5_VAR_USED = pkt->get<uint16_t>();
312        DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n", daddr, val);
313    } else if (size == sizeof(uint8_t)) {
314        uint8_t val M5_VAR_USED = pkt->get<uint8_t>();
315        DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n", daddr, val);
316    } else {
317        panic("Unknown size for MMIO access: %d\n", size);
318    }
319
320    if (daddr < 0x80) {
321        switch (daddr) {
322          case GEN_CHANCOUNT:
323          case GEN_XFERCAP:
324          case GEN_ATTNSTATUS:
325            DPRINTF(DMACopyEngine, "Warning, ignorning write to register %x\n",
326                    daddr);
327            break;
328          case GEN_INTRCTRL:
329            regs.intrctrl.master_int_enable(bits(pkt->get<uint8_t>(),0,1));
330            break;
331          default:
332            panic("Read request to unknown register number: %#x\n", daddr);
333        }
334        pkt->makeAtomicResponse();
335        return pioDelay;
336    }
337
338    // Find which channel we're accessing
339    int chanid = 0;
340    daddr -= 0x80;
341    while (daddr >= 0x80) {
342        chanid++;
343        daddr -= 0x80;
344    }
345
346    if (chanid >= regs.chanCount)
347        panic("Access to channel %d (device only configured for %d channels)",
348                chanid, regs.chanCount);
349
350    ///
351    /// Channel registers are handled here
352    ///
353    chan[chanid]->channelWrite(pkt, daddr, size);
354
355    pkt->makeAtomicResponse();
356    return pioDelay;
357}
358
359void
360CopyEngine::CopyEngineChannel::channelWrite(Packet *pkt, Addr daddr, int size)
361{
362    switch (daddr) {
363      case CHAN_CONTROL:
364        assert(size == sizeof(uint16_t));
365        int old_int_disable;
366        old_int_disable = cr.ctrl.interrupt_disable();
367        cr.ctrl(pkt->get<uint16_t>());
368        if (cr.ctrl.interrupt_disable())
369            cr.ctrl.interrupt_disable(0);
370        else
371            cr.ctrl.interrupt_disable(old_int_disable);
372        break;
373      case CHAN_STATUS:
374        assert(size == sizeof(uint64_t));
375        DPRINTF(DMACopyEngine, "Warning, ignorning write to register %x\n",
376                    daddr);
377        break;
378      case CHAN_CHAINADDR:
379        assert(size == sizeof(uint64_t) || size == sizeof(uint32_t));
380        if (size == sizeof(uint64_t))
381            cr.descChainAddr = pkt->get<uint64_t>();
382        else
383            cr.descChainAddr =  (uint64_t)pkt->get<uint32_t>() |
384                (cr.descChainAddr & ~mask(32));
385        DPRINTF(DMACopyEngine, "Chain Address %x\n", cr.descChainAddr);
386        break;
387      case CHAN_CHAINADDR_HIGH:
388        assert(size == sizeof(uint32_t));
389        cr.descChainAddr =  ((uint64_t)pkt->get<uint32_t>() <<32) |
390            (cr.descChainAddr & mask(32));
391        DPRINTF(DMACopyEngine, "Chain Address %x\n", cr.descChainAddr);
392        break;
393      case CHAN_COMMAND:
394        assert(size == sizeof(uint8_t));
395        cr.command(pkt->get<uint8_t>());
396        recvCommand();
397        break;
398      case CHAN_CMPLNADDR:
399        assert(size == sizeof(uint64_t) || size == sizeof(uint32_t));
400        if (size == sizeof(uint64_t))
401            cr.completionAddr = pkt->get<uint64_t>();
402        else
403            cr.completionAddr =  pkt->get<uint32_t>() |
404                (cr.completionAddr & ~mask(32));
405        break;
406      case CHAN_CMPLNADDR_HIGH:
407        assert(size == sizeof(uint32_t));
408        cr.completionAddr =  ((uint64_t)pkt->get<uint32_t>() <<32) |
409            (cr.completionAddr & mask(32));
410        break;
411      case CHAN_ERROR:
412        assert(size == sizeof(uint32_t));
413        cr.error(~pkt->get<uint32_t>() & cr.error());
414        break;
415      default:
416        panic("Read request to unknown channel register number: (%d)%#x\n",
417                channelId, daddr);
418    }
419}
420
421void
422CopyEngine::regStats()
423{
424    using namespace Stats;
425    bytesCopied
426        .init(regs.chanCount)
427        .name(name() + ".bytes_copied")
428        .desc("Number of bytes copied by each engine")
429        .flags(total)
430        ;
431    copiesProcessed
432        .init(regs.chanCount)
433        .name(name() + ".copies_processed")
434        .desc("Number of copies processed by each engine")
435        .flags(total)
436        ;
437}
438
439void
440CopyEngine::CopyEngineChannel::fetchDescriptor(Addr address)
441{
442    anDq();
443    anBegin("FetchDescriptor");
444    DPRINTF(DMACopyEngine, "Reading descriptor from at memory location %#x(%#x)\n",
445           address, ce->platform->pciToDma(address));
446    assert(address);
447    busy = true;
448
449    DPRINTF(DMACopyEngine, "dmaAction: %#x, %d bytes, to addr %#x\n",
450            ce->platform->pciToDma(address), sizeof(DmaDesc), curDmaDesc);
451
452    cePort.dmaAction(MemCmd::ReadReq, ce->platform->pciToDma(address),
453                     sizeof(DmaDesc), &fetchCompleteEvent,
454                     (uint8_t*)curDmaDesc, latBeforeBegin);
455    lastDescriptorAddr = address;
456}
457
458void
459CopyEngine::CopyEngineChannel::fetchDescComplete()
460{
461    DPRINTF(DMACopyEngine, "Read of descriptor complete\n");
462
463    if ((curDmaDesc->command & DESC_CTRL_NULL)) {
464        DPRINTF(DMACopyEngine, "Got NULL descriptor, skipping\n");
465        assert(!(curDmaDesc->command & DESC_CTRL_CP_STS));
466        if (curDmaDesc->command & DESC_CTRL_CP_STS) {
467            panic("Shouldn't be able to get here\n");
468            nextState = CompletionWrite;
469            if (inDrain()) return;
470            writeCompletionStatus();
471        } else {
472            anBegin("Idle");
473            anWait();
474            busy = false;
475            nextState = Idle;
476            inDrain();
477        }
478        return;
479    }
480
481    if (curDmaDesc->command & ~DESC_CTRL_CP_STS)
482        panic("Descriptor has flag other that completion status set\n");
483
484    nextState = DMARead;
485    if (inDrain()) return;
486    readCopyBytes();
487}
488
489void
490CopyEngine::CopyEngineChannel::readCopyBytes()
491{
492    anBegin("ReadCopyBytes");
493    DPRINTF(DMACopyEngine, "Reading %d bytes from buffer to memory location %#x(%#x)\n",
494           curDmaDesc->len, curDmaDesc->dest,
495           ce->platform->pciToDma(curDmaDesc->src));
496    cePort.dmaAction(MemCmd::ReadReq, ce->platform->pciToDma(curDmaDesc->src),
497                     curDmaDesc->len, &readCompleteEvent, copyBuffer, 0);
498}
499
500void
501CopyEngine::CopyEngineChannel::readCopyBytesComplete()
502{
503    DPRINTF(DMACopyEngine, "Read of bytes to copy complete\n");
504
505    nextState = DMAWrite;
506    if (inDrain()) return;
507    writeCopyBytes();
508}
509
510void
511CopyEngine::CopyEngineChannel::writeCopyBytes()
512{
513    anBegin("WriteCopyBytes");
514    DPRINTF(DMACopyEngine, "Writing %d bytes from buffer to memory location %#x(%#x)\n",
515           curDmaDesc->len, curDmaDesc->dest,
516           ce->platform->pciToDma(curDmaDesc->dest));
517
518    cePort.dmaAction(MemCmd::WriteReq, ce->platform->pciToDma(curDmaDesc->dest),
519                     curDmaDesc->len, &writeCompleteEvent, copyBuffer, 0);
520
521    ce->bytesCopied[channelId] += curDmaDesc->len;
522    ce->copiesProcessed[channelId]++;
523}
524
525void
526CopyEngine::CopyEngineChannel::writeCopyBytesComplete()
527{
528    DPRINTF(DMACopyEngine, "Write of bytes to copy complete user1: %#x\n",
529            curDmaDesc->user1);
530
531    cr.status.compl_desc_addr(lastDescriptorAddr >> 6);
532    completionDataReg = cr.status() | 1;
533
534    anQ("DMAUsedDescQ", channelId, 1);
535    anQ("AppRecvQ", curDmaDesc->user1, curDmaDesc->len);
536    if (curDmaDesc->command & DESC_CTRL_CP_STS) {
537        nextState = CompletionWrite;
538        if (inDrain()) return;
539        writeCompletionStatus();
540        return;
541    }
542
543    continueProcessing();
544}
545
546void
547CopyEngine::CopyEngineChannel::continueProcessing()
548{
549    busy = false;
550
551    if (underReset) {
552        anBegin("Reset");
553        anWait();
554        underReset = false;
555        refreshNext = false;
556        busy = false;
557        nextState = Idle;
558        return;
559    }
560
561    if (curDmaDesc->next) {
562        nextState = DescriptorFetch;
563        fetchAddress = curDmaDesc->next;
564        if (inDrain()) return;
565        fetchDescriptor(curDmaDesc->next);
566    } else if (refreshNext) {
567        nextState = AddressFetch;
568        refreshNext = false;
569        if (inDrain()) return;
570        fetchNextAddr(lastDescriptorAddr);
571    } else {
572        inDrain();
573        nextState = Idle;
574        anWait();
575        anBegin("Idle");
576    }
577}
578
579void
580CopyEngine::CopyEngineChannel::writeCompletionStatus()
581{
582    anBegin("WriteCompletionStatus");
583    DPRINTF(DMACopyEngine, "Writing completion status %#x to address %#x(%#x)\n",
584            completionDataReg, cr.completionAddr,
585            ce->platform->pciToDma(cr.completionAddr));
586
587    cePort.dmaAction(MemCmd::WriteReq,
588                     ce->platform->pciToDma(cr.completionAddr),
589                     sizeof(completionDataReg), &statusCompleteEvent,
590                     (uint8_t*)&completionDataReg, latAfterCompletion);
591}
592
593void
594CopyEngine::CopyEngineChannel::writeStatusComplete()
595{
596    DPRINTF(DMACopyEngine, "Writing completion status complete\n");
597    continueProcessing();
598}
599
600void
601CopyEngine::CopyEngineChannel::fetchNextAddr(Addr address)
602{
603    anBegin("FetchNextAddr");
604    DPRINTF(DMACopyEngine, "Fetching next address...\n");
605    busy = true;
606    cePort.dmaAction(MemCmd::ReadReq,
607                     ce->platform->pciToDma(address + offsetof(DmaDesc, next)),
608                     sizeof(Addr), &addrCompleteEvent,
609                     (uint8_t*)curDmaDesc + offsetof(DmaDesc, next), 0);
610}
611
612void
613CopyEngine::CopyEngineChannel::fetchAddrComplete()
614{
615    DPRINTF(DMACopyEngine, "Fetching next address complete: %#x\n",
616            curDmaDesc->next);
617    if (!curDmaDesc->next) {
618        DPRINTF(DMACopyEngine, "Got NULL descriptor, nothing more to do\n");
619        busy = false;
620        nextState = Idle;
621        anWait();
622        anBegin("Idle");
623        inDrain();
624        return;
625    }
626    nextState = DescriptorFetch;
627    fetchAddress = curDmaDesc->next;
628    if (inDrain()) return;
629    fetchDescriptor(curDmaDesc->next);
630}
631
632bool
633CopyEngine::CopyEngineChannel::inDrain()
634{
635    if (ce->getState() == SimObject::Draining) {
636        DPRINTF(DMACopyEngine, "processing drain\n");
637        assert(drainEvent);
638        drainEvent->process();
639        drainEvent = NULL;
640    }
641
642    return ce->getState() != SimObject::Running;
643}
644
645unsigned int
646CopyEngine::CopyEngineChannel::drain(Event *de)
647{
648    if (nextState == Idle || ce->getState() != SimObject::Running)
649        return 0;
650    unsigned int count = 1;
651    count += cePort.drain(de);
652
653    DPRINTF(DMACopyEngine, "unable to drain, returning %d\n", count);
654    drainEvent = de;
655    return count;
656}
657
658unsigned int
659CopyEngine::drain(Event *de)
660{
661    unsigned int count;
662    count = pioPort.drain(de) + dmaPort.drain(de) + configPort.drain(de);
663    for (int x = 0;x < chan.size(); x++)
664        count += chan[x]->drain(de);
665
666    if (count)
667        changeState(Draining);
668    else
669        changeState(Drained);
670
671    DPRINTF(DMACopyEngine, "call to CopyEngine::drain() returning %d\n", count);
672    return count;
673}
674
675void
676CopyEngine::serialize(std::ostream &os)
677{
678    PciDev::serialize(os);
679    regs.serialize(os);
680    for (int x =0; x < chan.size(); x++) {
681        nameOut(os, csprintf("%s.channel%d", name(), x));
682        chan[x]->serialize(os);
683    }
684}
685
686void
687CopyEngine::unserialize(Checkpoint *cp, const std::string &section)
688{
689    PciDev::unserialize(cp, section);
690    regs.unserialize(cp, section);
691    for (int x = 0; x < chan.size(); x++)
692        chan[x]->unserialize(cp, csprintf("%s.channel%d", section, x));
693}
694
695void
696CopyEngine::CopyEngineChannel::serialize(std::ostream &os)
697{
698    SERIALIZE_SCALAR(channelId);
699    SERIALIZE_SCALAR(busy);
700    SERIALIZE_SCALAR(underReset);
701    SERIALIZE_SCALAR(refreshNext);
702    SERIALIZE_SCALAR(lastDescriptorAddr);
703    SERIALIZE_SCALAR(completionDataReg);
704    SERIALIZE_SCALAR(fetchAddress);
705    int nextState = this->nextState;
706    SERIALIZE_SCALAR(nextState);
707    arrayParamOut(os, "curDmaDesc", (uint8_t*)curDmaDesc, sizeof(DmaDesc));
708    SERIALIZE_ARRAY(copyBuffer, ce->params()->XferCap);
709    cr.serialize(os);
710
711}
712void
713CopyEngine::CopyEngineChannel::unserialize(Checkpoint *cp, const std::string &section)
714{
715    UNSERIALIZE_SCALAR(channelId);
716    UNSERIALIZE_SCALAR(busy);
717    UNSERIALIZE_SCALAR(underReset);
718    UNSERIALIZE_SCALAR(refreshNext);
719    UNSERIALIZE_SCALAR(lastDescriptorAddr);
720    UNSERIALIZE_SCALAR(completionDataReg);
721    UNSERIALIZE_SCALAR(fetchAddress);
722    int nextState;
723    UNSERIALIZE_SCALAR(nextState);
724    this->nextState = (ChannelState)nextState;
725    arrayParamIn(cp, section, "curDmaDesc", (uint8_t*)curDmaDesc, sizeof(DmaDesc));
726    UNSERIALIZE_ARRAY(copyBuffer, ce->params()->XferCap);
727    cr.unserialize(cp, section);
728
729}
730
731void
732CopyEngine::CopyEngineChannel::restartStateMachine()
733{
734    switch(nextState) {
735      case AddressFetch:
736        fetchNextAddr(lastDescriptorAddr);
737        break;
738      case DescriptorFetch:
739        fetchDescriptor(fetchAddress);
740        break;
741      case DMARead:
742        readCopyBytes();
743        break;
744      case DMAWrite:
745        writeCopyBytes();
746        break;
747      case CompletionWrite:
748        writeCompletionStatus();
749        break;
750      case Idle:
751        break;
752      default:
753        panic("Unknown state for CopyEngineChannel\n");
754    }
755}
756
757void
758CopyEngine::resume()
759{
760    SimObject::resume();
761    for (int x = 0;x < chan.size(); x++)
762        chan[x]->resume();
763}
764
765
766void
767CopyEngine::CopyEngineChannel::resume()
768{
769    DPRINTF(DMACopyEngine, "Restarting state machine at state %d\n", nextState);
770    restartStateMachine();
771}
772
773CopyEngine *
774CopyEngineParams::create()
775{
776    return new CopyEngine(this);
777}
778