2 * Copyright (c) 2008 The Regents of The University of Michigan
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.
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.
32 * Device model for Intel's I/O AT DMA copy engine.
37 #include "base/trace.hh"
38 #include "dev/copy_engine.hh"
39 #include "mem/packet.hh"
40 #include "mem/packet_access.hh"
41 #include "params/CopyEngine.hh"
42 #include "sim/stats.hh"
43 #include "sim/system.hh"
45 using namespace CopyEngineReg
;
48 CopyEngine::CopyEngine(const Params
*p
)
51 // All Reg regs are initialized to 0 by default
52 regs
.chanCount
= p
->ChanCnt
;
53 regs
.xferCap
= findMsbSet(p
->XferCap
);
56 if (regs
.chanCount
> 64)
57 fatal("CopyEngine interface doesn't support more than 64 DMA engines\n");
59 for (int x
= 0; x
< regs
.chanCount
; x
++) {
60 CopyEngineChannel
*ch
= new CopyEngineChannel(this, x
);
66 CopyEngine::CopyEngineChannel::CopyEngineChannel(CopyEngine
*_ce
, int cid
)
67 : ce(_ce
), channelId(cid
), busy(false), underReset(false),
68 refreshNext(false), latBeforeBegin(ce
->params()->latBeforeBegin
),
69 latAfterCompletion(ce
->params()->latAfterCompletion
),
70 completionDataReg(0), nextState(Idle
), drainEvent(NULL
),
71 fetchCompleteEvent(this), addrCompleteEvent(this),
72 readCompleteEvent(this), writeCompleteEvent(this),
73 statusCompleteEvent(this)
76 cr
.status
.dma_transfer_status(3);
78 cr
.completionAddr
= 0;
80 curDmaDesc
= new DmaDesc
;
81 memset(curDmaDesc
, 0, sizeof(DmaDesc
));
82 copyBuffer
= new uint8_t[ce
->params()->XferCap
];
85 CopyEngine::~CopyEngine()
87 for (int x
= 0; x
< chan
.size(); x
++) {
92 CopyEngine::CopyEngineChannel::~CopyEngineChannel()
103 for (int x
= 0; x
< chan
.size(); x
++)
108 CopyEngine::CopyEngineChannel::init()
112 cePort
= new DmaPort(ce
, ce
->sys
);
113 peer
= ce
->dmaPort
->getPeer()->getOwner()->getPort("");
114 peer
->setPeer(cePort
);
115 cePort
->setPeer(peer
);
119 CopyEngine::CopyEngineChannel::recvCommand()
121 if (cr
.command
.start_dma()) {
123 cr
.status
.dma_transfer_status(0);
124 nextState
= DescriptorFetch
;
125 fetchAddress
= cr
.descChainAddr
;
126 if (ce
->getState() == SimObject::Running
)
127 fetchDescriptor(cr
.descChainAddr
);
128 } else if (cr
.command
.append_dma()) {
130 nextState
= AddressFetch
;
131 if (ce
->getState() == SimObject::Running
)
132 fetchNextAddr(lastDescriptorAddr
);
135 } else if (cr
.command
.reset_dma()) {
139 cr
.status
.dma_transfer_status(3);
142 } else if (cr
.command
.resume_dma() || cr
.command
.abort_dma() ||
143 cr
.command
.suspend_dma())
144 panic("Resume, Abort, and Suspend are not supported\n");
149 CopyEngine::read(PacketPtr pkt
)
154 if (!getBAR(pkt
->getAddr(), bar
, daddr
))
155 panic("Invalid PCI memory access to unmapped memory.\n");
157 // Only Memory register BAR is allowed
160 int size
= pkt
->getSize();
161 if (size
!= sizeof(uint64_t) && size
!= sizeof(uint32_t) &&
162 size
!= sizeof(uint16_t) && size
!= sizeof(uint8_t)) {
163 panic("Unknown size for MMIO access: %d\n", pkt
->getSize());
166 DPRINTF(DMACopyEngine
, "Read device register %#X size: %d\n", daddr
, size
);
171 /// Handle read of register here
177 assert(size
== sizeof(regs
.chanCount
));
178 pkt
->set
<uint8_t>(regs
.chanCount
);
181 assert(size
== sizeof(regs
.xferCap
));
182 pkt
->set
<uint8_t>(regs
.xferCap
);
185 assert(size
== sizeof(uint8_t));
186 pkt
->set
<uint8_t>(regs
.intrctrl());
187 regs
.intrctrl
.master_int_enable(0);
190 assert(size
== sizeof(regs
.attnStatus
));
191 pkt
->set
<uint32_t>(regs
.attnStatus
);
195 panic("Read request to unknown register number: %#x\n", daddr
);
197 pkt
->makeAtomicResponse();
202 // Find which channel we're accessing
205 while (daddr
>= 0x80) {
210 if (chanid
>= regs
.chanCount
)
211 panic("Access to channel %d (device only configured for %d channels)",
212 chanid
, regs
.chanCount
);
215 /// Channel registers are handled here
217 chan
[chanid
]->channelRead(pkt
, daddr
, size
);
219 pkt
->makeAtomicResponse();
224 CopyEngine::CopyEngineChannel::channelRead(Packet
*pkt
, Addr daddr
, int size
)
228 assert(size
== sizeof(uint16_t));
229 pkt
->set
<uint16_t>(cr
.ctrl());
233 assert(size
== sizeof(uint64_t));
234 pkt
->set
<uint64_t>(cr
.status() | ~busy
);
237 assert(size
== sizeof(uint64_t) || size
== sizeof(uint32_t));
238 if (size
== sizeof(uint64_t))
239 pkt
->set
<uint64_t>(cr
.descChainAddr
);
241 pkt
->set
<uint32_t>(bits(cr
.descChainAddr
,0,31));
243 case CHAN_CHAINADDR_HIGH
:
244 assert(size
== sizeof(uint32_t));
245 pkt
->set
<uint32_t>(bits(cr
.descChainAddr
,32,63));
248 assert(size
== sizeof(uint8_t));
249 pkt
->set
<uint32_t>(cr
.command());
252 assert(size
== sizeof(uint64_t) || size
== sizeof(uint32_t));
253 if (size
== sizeof(uint64_t))
254 pkt
->set
<uint64_t>(cr
.completionAddr
);
256 pkt
->set
<uint32_t>(bits(cr
.completionAddr
,0,31));
258 case CHAN_CMPLNADDR_HIGH
:
259 assert(size
== sizeof(uint32_t));
260 pkt
->set
<uint32_t>(bits(cr
.completionAddr
,32,63));
263 assert(size
== sizeof(uint32_t));
264 pkt
->set
<uint32_t>(cr
.error());
267 panic("Read request to unknown channel register number: (%d)%#x\n",
274 CopyEngine::write(PacketPtr pkt
)
280 if (!getBAR(pkt
->getAddr(), bar
, daddr
))
281 panic("Invalid PCI memory access to unmapped memory.\n");
283 // Only Memory register BAR is allowed
286 int size
= pkt
->getSize();
289 /// Handle write of register here
292 if (size
== sizeof(uint64_t)) {
293 uint64_t val M5_VAR_USED
= pkt
->get
<uint64_t>();
294 DPRINTF(DMACopyEngine
, "Wrote device register %#X value %#X\n", daddr
, val
);
295 } else if (size
== sizeof(uint32_t)) {
296 uint32_t val M5_VAR_USED
= pkt
->get
<uint32_t>();
297 DPRINTF(DMACopyEngine
, "Wrote device register %#X value %#X\n", daddr
, val
);
298 } else if (size
== sizeof(uint16_t)) {
299 uint16_t val M5_VAR_USED
= pkt
->get
<uint16_t>();
300 DPRINTF(DMACopyEngine
, "Wrote device register %#X value %#X\n", daddr
, val
);
301 } else if (size
== sizeof(uint8_t)) {
302 uint8_t val M5_VAR_USED
= pkt
->get
<uint8_t>();
303 DPRINTF(DMACopyEngine
, "Wrote device register %#X value %#X\n", daddr
, val
);
305 panic("Unknown size for MMIO access: %d\n", size
);
313 DPRINTF(DMACopyEngine
, "Warning, ignorning write to register %x\n",
317 regs
.intrctrl
.master_int_enable(bits(pkt
->get
<uint8_t>(),0,1));
320 panic("Read request to unknown register number: %#x\n", daddr
);
322 pkt
->makeAtomicResponse();
326 // Find which channel we're accessing
329 while (daddr
>= 0x80) {
334 if (chanid
>= regs
.chanCount
)
335 panic("Access to channel %d (device only configured for %d channels)",
336 chanid
, regs
.chanCount
);
339 /// Channel registers are handled here
341 chan
[chanid
]->channelWrite(pkt
, daddr
, size
);
343 pkt
->makeAtomicResponse();
348 CopyEngine::CopyEngineChannel::channelWrite(Packet
*pkt
, Addr daddr
, int size
)
352 assert(size
== sizeof(uint16_t));
354 old_int_disable
= cr
.ctrl
.interrupt_disable();
355 cr
.ctrl(pkt
->get
<uint16_t>());
356 if (cr
.ctrl
.interrupt_disable())
357 cr
.ctrl
.interrupt_disable(0);
359 cr
.ctrl
.interrupt_disable(old_int_disable
);
362 assert(size
== sizeof(uint64_t));
363 DPRINTF(DMACopyEngine
, "Warning, ignorning write to register %x\n",
367 assert(size
== sizeof(uint64_t) || size
== sizeof(uint32_t));
368 if (size
== sizeof(uint64_t))
369 cr
.descChainAddr
= pkt
->get
<uint64_t>();
371 cr
.descChainAddr
= (uint64_t)pkt
->get
<uint32_t>() |
372 (cr
.descChainAddr
& ~mask(32));
373 DPRINTF(DMACopyEngine
, "Chain Address %x\n", cr
.descChainAddr
);
375 case CHAN_CHAINADDR_HIGH
:
376 assert(size
== sizeof(uint32_t));
377 cr
.descChainAddr
= ((uint64_t)pkt
->get
<uint32_t>() <<32) |
378 (cr
.descChainAddr
& mask(32));
379 DPRINTF(DMACopyEngine
, "Chain Address %x\n", cr
.descChainAddr
);
382 assert(size
== sizeof(uint8_t));
383 cr
.command(pkt
->get
<uint8_t>());
387 assert(size
== sizeof(uint64_t) || size
== sizeof(uint32_t));
388 if (size
== sizeof(uint64_t))
389 cr
.completionAddr
= pkt
->get
<uint64_t>();
391 cr
.completionAddr
= pkt
->get
<uint32_t>() |
392 (cr
.completionAddr
& ~mask(32));
394 case CHAN_CMPLNADDR_HIGH
:
395 assert(size
== sizeof(uint32_t));
396 cr
.completionAddr
= ((uint64_t)pkt
->get
<uint32_t>() <<32) |
397 (cr
.completionAddr
& mask(32));
400 assert(size
== sizeof(uint32_t));
401 cr
.error(~pkt
->get
<uint32_t>() & cr
.error());
404 panic("Read request to unknown channel register number: (%d)%#x\n",
410 CopyEngine::regStats()
412 using namespace Stats
;
414 .init(regs
.chanCount
)
415 .name(name() + ".bytes_copied")
416 .desc("Number of bytes copied by each engine")
420 .init(regs
.chanCount
)
421 .name(name() + ".copies_processed")
422 .desc("Number of copies processed by each engine")
428 CopyEngine::CopyEngineChannel::fetchDescriptor(Addr address
)
430 DPRINTF(DMACopyEngine
, "Reading descriptor from at memory location %#x(%#x)\n",
431 address
, ce
->platform
->pciToDma(address
));
435 DPRINTF(DMACopyEngine
, "dmaAction: %#x, %d bytes, to addr %#x\n",
436 ce
->platform
->pciToDma(address
), sizeof(DmaDesc
), curDmaDesc
);
438 cePort
->dmaAction(MemCmd::ReadReq
, ce
->platform
->pciToDma(address
),
439 sizeof(DmaDesc
), &fetchCompleteEvent
, (uint8_t*)curDmaDesc
,
441 lastDescriptorAddr
= address
;
445 CopyEngine::CopyEngineChannel::fetchDescComplete()
447 DPRINTF(DMACopyEngine
, "Read of descriptor complete\n");
449 if ((curDmaDesc
->command
& DESC_CTRL_NULL
)) {
450 DPRINTF(DMACopyEngine
, "Got NULL descriptor, skipping\n");
451 assert(!(curDmaDesc
->command
& DESC_CTRL_CP_STS
));
452 if (curDmaDesc
->command
& DESC_CTRL_CP_STS
) {
453 panic("Shouldn't be able to get here\n");
454 nextState
= CompletionWrite
;
455 if (inDrain()) return;
456 writeCompletionStatus();
465 if (curDmaDesc
->command
& ~DESC_CTRL_CP_STS
)
466 panic("Descriptor has flag other that completion status set\n");
469 if (inDrain()) return;
474 CopyEngine::CopyEngineChannel::readCopyBytes()
476 DPRINTF(DMACopyEngine
, "Reading %d bytes from buffer to memory location %#x(%#x)\n",
477 curDmaDesc
->len
, curDmaDesc
->dest
,
478 ce
->platform
->pciToDma(curDmaDesc
->src
));
479 cePort
->dmaAction(MemCmd::ReadReq
, ce
->platform
->pciToDma(curDmaDesc
->src
),
480 curDmaDesc
->len
, &readCompleteEvent
, copyBuffer
, 0);
484 CopyEngine::CopyEngineChannel::readCopyBytesComplete()
486 DPRINTF(DMACopyEngine
, "Read of bytes to copy complete\n");
488 nextState
= DMAWrite
;
489 if (inDrain()) return;
494 CopyEngine::CopyEngineChannel::writeCopyBytes()
496 DPRINTF(DMACopyEngine
, "Writing %d bytes from buffer to memory location %#x(%#x)\n",
497 curDmaDesc
->len
, curDmaDesc
->dest
,
498 ce
->platform
->pciToDma(curDmaDesc
->dest
));
500 cePort
->dmaAction(MemCmd::WriteReq
, ce
->platform
->pciToDma(curDmaDesc
->dest
),
501 curDmaDesc
->len
, &writeCompleteEvent
, copyBuffer
, 0);
503 ce
->bytesCopied
[channelId
] += curDmaDesc
->len
;
504 ce
->copiesProcessed
[channelId
]++;
508 CopyEngine::CopyEngineChannel::writeCopyBytesComplete()
510 DPRINTF(DMACopyEngine
, "Write of bytes to copy complete user1: %#x\n",
513 cr
.status
.compl_desc_addr(lastDescriptorAddr
>> 6);
514 completionDataReg
= cr
.status() | 1;
516 if (curDmaDesc
->command
& DESC_CTRL_CP_STS
) {
517 nextState
= CompletionWrite
;
518 if (inDrain()) return;
519 writeCompletionStatus();
523 continueProcessing();
527 CopyEngine::CopyEngineChannel::continueProcessing()
539 if (curDmaDesc
->next
) {
540 nextState
= DescriptorFetch
;
541 fetchAddress
= curDmaDesc
->next
;
542 if (inDrain()) return;
543 fetchDescriptor(curDmaDesc
->next
);
544 } else if (refreshNext
) {
545 nextState
= AddressFetch
;
547 if (inDrain()) return;
548 fetchNextAddr(lastDescriptorAddr
);
556 CopyEngine::CopyEngineChannel::writeCompletionStatus()
558 DPRINTF(DMACopyEngine
, "Writing completion status %#x to address %#x(%#x)\n",
559 completionDataReg
, cr
.completionAddr
,
560 ce
->platform
->pciToDma(cr
.completionAddr
));
562 cePort
->dmaAction(MemCmd::WriteReq
, ce
->platform
->pciToDma(cr
.completionAddr
),
563 sizeof(completionDataReg
), &statusCompleteEvent
,
564 (uint8_t*)&completionDataReg
, latAfterCompletion
);
568 CopyEngine::CopyEngineChannel::writeStatusComplete()
570 DPRINTF(DMACopyEngine
, "Writing completion status complete\n");
571 continueProcessing();
575 CopyEngine::CopyEngineChannel::fetchNextAddr(Addr address
)
577 DPRINTF(DMACopyEngine
, "Fetching next address...\n");
579 cePort
->dmaAction(MemCmd::ReadReq
, ce
->platform
->pciToDma(address
+
580 offsetof(DmaDesc
, next
)), sizeof(Addr
), &addrCompleteEvent
,
581 (uint8_t*)curDmaDesc
+ offsetof(DmaDesc
, next
), 0);
585 CopyEngine::CopyEngineChannel::fetchAddrComplete()
587 DPRINTF(DMACopyEngine
, "Fetching next address complete: %#x\n",
589 if (!curDmaDesc
->next
) {
590 DPRINTF(DMACopyEngine
, "Got NULL descriptor, nothing more to do\n");
596 nextState
= DescriptorFetch
;
597 fetchAddress
= curDmaDesc
->next
;
598 if (inDrain()) return;
599 fetchDescriptor(curDmaDesc
->next
);
603 CopyEngine::CopyEngineChannel::inDrain()
605 if (ce
->getState() == SimObject::Draining
) {
606 DPRINTF(DMACopyEngine
, "processing drain\n");
608 drainEvent
->process();
612 return ce
->getState() != SimObject::Running
;
616 CopyEngine::CopyEngineChannel::drain(Event
*de
)
618 if (nextState
== Idle
|| ce
->getState() != SimObject::Running
)
620 unsigned int count
= 1;
621 count
+= cePort
->drain(de
);
623 DPRINTF(DMACopyEngine
, "unable to drain, returning %d\n", count
);
629 CopyEngine::drain(Event
*de
)
632 count
= pioPort
->drain(de
) + dmaPort
->drain(de
) + configPort
->drain(de
);
633 for (int x
= 0;x
< chan
.size(); x
++)
634 count
+= chan
[x
]->drain(de
);
637 changeState(Draining
);
639 changeState(Drained
);
641 DPRINTF(DMACopyEngine
, "call to CopyEngine::drain() returning %d\n", count
);
646 CopyEngine::serialize(std::ostream
&os
)
648 PciDev::serialize(os
);
650 for (int x
=0; x
< chan
.size(); x
++) {
651 nameOut(os
, csprintf("%s.channel%d", name(), x
));
652 chan
[x
]->serialize(os
);
657 CopyEngine::unserialize(Checkpoint
*cp
, const std::string
§ion
)
659 PciDev::unserialize(cp
, section
);
660 regs
.unserialize(cp
, section
);
661 for (int x
= 0; x
< chan
.size(); x
++)
662 chan
[x
]->unserialize(cp
, csprintf("%s.channel%d", section
, x
));
666 CopyEngine::CopyEngineChannel::serialize(std::ostream
&os
)
668 SERIALIZE_SCALAR(channelId
);
669 SERIALIZE_SCALAR(busy
);
670 SERIALIZE_SCALAR(underReset
);
671 SERIALIZE_SCALAR(refreshNext
);
672 SERIALIZE_SCALAR(lastDescriptorAddr
);
673 SERIALIZE_SCALAR(completionDataReg
);
674 SERIALIZE_SCALAR(fetchAddress
);
675 int nextState
= this->nextState
;
676 SERIALIZE_SCALAR(nextState
);
677 arrayParamOut(os
, "curDmaDesc", (uint8_t*)curDmaDesc
, sizeof(DmaDesc
));
678 SERIALIZE_ARRAY(copyBuffer
, ce
->params()->XferCap
);
683 CopyEngine::CopyEngineChannel::unserialize(Checkpoint
*cp
, const std::string
§ion
)
685 UNSERIALIZE_SCALAR(channelId
);
686 UNSERIALIZE_SCALAR(busy
);
687 UNSERIALIZE_SCALAR(underReset
);
688 UNSERIALIZE_SCALAR(refreshNext
);
689 UNSERIALIZE_SCALAR(lastDescriptorAddr
);
690 UNSERIALIZE_SCALAR(completionDataReg
);
691 UNSERIALIZE_SCALAR(fetchAddress
);
693 UNSERIALIZE_SCALAR(nextState
);
694 this->nextState
= (ChannelState
)nextState
;
695 arrayParamIn(cp
, section
, "curDmaDesc", (uint8_t*)curDmaDesc
, sizeof(DmaDesc
));
696 UNSERIALIZE_ARRAY(copyBuffer
, ce
->params()->XferCap
);
697 cr
.unserialize(cp
, section
);
702 CopyEngine::CopyEngineChannel::restartStateMachine()
706 fetchNextAddr(lastDescriptorAddr
);
708 case DescriptorFetch
:
709 fetchDescriptor(fetchAddress
);
717 case CompletionWrite
:
718 writeCompletionStatus();
723 panic("Unknown state for CopyEngineChannel\n");
731 for (int x
= 0;x
< chan
.size(); x
++)
737 CopyEngine::CopyEngineChannel::resume()
739 DPRINTF(DMACopyEngine
, "Restarting state machine at state %d\n", nextState
);
740 restartStateMachine();
744 CopyEngineParams::create()
746 return new CopyEngine(this);