| /* |
| * Copyright (c) 2008 The Regents of The University of Michigan |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * Authors: Ali Saidi |
| */ |
| |
| /* @file |
| * Device model for Intel's I/O AT DMA copy engine. |
| */ |
| |
| #include <algorithm> |
| |
| #include "base/cp_annotate.hh" |
| #include "base/trace.hh" |
| #include "debug/DMACopyEngine.hh" |
| #include "dev/copy_engine.hh" |
| #include "mem/packet.hh" |
| #include "mem/packet_access.hh" |
| #include "params/CopyEngine.hh" |
| #include "sim/stats.hh" |
| #include "sim/system.hh" |
| |
| using namespace CopyEngineReg; |
| using namespace std; |
| |
| CopyEngine::CopyEngine(const Params *p) |
| : PciDev(p) |
| { |
| // All Reg regs are initialized to 0 by default |
| regs.chanCount = p->ChanCnt; |
| regs.xferCap = findMsbSet(p->XferCap); |
| regs.attnStatus = 0; |
| |
| if (regs.chanCount > 64) |
| fatal("CopyEngine interface doesn't support more than 64 DMA engines\n"); |
| |
| for (int x = 0; x < regs.chanCount; x++) { |
| CopyEngineChannel *ch = new CopyEngineChannel(this, x); |
| chan.push_back(ch); |
| } |
| } |
| |
| |
| CopyEngine::CopyEngineChannel::CopyEngineChannel(CopyEngine *_ce, int cid) |
| : ce(_ce), channelId(cid), busy(false), underReset(false), |
| refreshNext(false), latBeforeBegin(ce->params()->latBeforeBegin), |
| latAfterCompletion(ce->params()->latAfterCompletion), |
| completionDataReg(0), nextState(Idle), drainEvent(NULL), |
| fetchCompleteEvent(this), addrCompleteEvent(this), |
| readCompleteEvent(this), writeCompleteEvent(this), |
| statusCompleteEvent(this) |
| |
| { |
| cr.status.dma_transfer_status(3); |
| cr.descChainAddr = 0; |
| cr.completionAddr = 0; |
| |
| curDmaDesc = new DmaDesc; |
| memset(curDmaDesc, 0, sizeof(DmaDesc)); |
| copyBuffer = new uint8_t[ce->params()->XferCap]; |
| } |
| |
| CopyEngine::~CopyEngine() |
| { |
| for (int x = 0; x < chan.size(); x++) { |
| delete chan[x]; |
| } |
| } |
| |
| CopyEngine::CopyEngineChannel::~CopyEngineChannel() |
| { |
| delete curDmaDesc; |
| delete [] copyBuffer; |
| delete cePort; |
| } |
| |
| void |
| CopyEngine::init() |
| { |
| PciDev::init(); |
| for (int x = 0; x < chan.size(); x++) |
| chan[x]->init(); |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::init() |
| { |
| Port *peer; |
| |
| cePort = new DmaPort(ce, ce->sys, ce->params()->min_backoff_delay, |
| ce->params()->max_backoff_delay); |
| peer = ce->dmaPort->getPeer()->getOwner()->getPort(""); |
| peer->setPeer(cePort); |
| cePort->setPeer(peer); |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::recvCommand() |
| { |
| if (cr.command.start_dma()) { |
| assert(!busy); |
| cr.status.dma_transfer_status(0); |
| nextState = DescriptorFetch; |
| fetchAddress = cr.descChainAddr; |
| if (ce->getState() == SimObject::Running) |
| fetchDescriptor(cr.descChainAddr); |
| } else if (cr.command.append_dma()) { |
| if (!busy) { |
| nextState = AddressFetch; |
| if (ce->getState() == SimObject::Running) |
| fetchNextAddr(lastDescriptorAddr); |
| } else |
| refreshNext = true; |
| } else if (cr.command.reset_dma()) { |
| if (busy) |
| underReset = true; |
| else { |
| cr.status.dma_transfer_status(3); |
| nextState = Idle; |
| } |
| } else if (cr.command.resume_dma() || cr.command.abort_dma() || |
| cr.command.suspend_dma()) |
| panic("Resume, Abort, and Suspend are not supported\n"); |
| cr.command(0); |
| } |
| |
| Tick |
| CopyEngine::read(PacketPtr pkt) |
| { |
| int bar; |
| Addr daddr; |
| |
| if (!getBAR(pkt->getAddr(), bar, daddr)) |
| panic("Invalid PCI memory access to unmapped memory.\n"); |
| |
| // Only Memory register BAR is allowed |
| assert(bar == 0); |
| |
| int size = pkt->getSize(); |
| if (size != sizeof(uint64_t) && size != sizeof(uint32_t) && |
| size != sizeof(uint16_t) && size != sizeof(uint8_t)) { |
| panic("Unknown size for MMIO access: %d\n", pkt->getSize()); |
| } |
| |
| DPRINTF(DMACopyEngine, "Read device register %#X size: %d\n", daddr, size); |
| |
| pkt->allocate(); |
| |
| /// |
| /// Handle read of register here |
| /// |
| |
| if (daddr < 0x80) { |
| switch (daddr) { |
| case GEN_CHANCOUNT: |
| assert(size == sizeof(regs.chanCount)); |
| pkt->set<uint8_t>(regs.chanCount); |
| break; |
| case GEN_XFERCAP: |
| assert(size == sizeof(regs.xferCap)); |
| pkt->set<uint8_t>(regs.xferCap); |
| break; |
| case GEN_INTRCTRL: |
| assert(size == sizeof(uint8_t)); |
| pkt->set<uint8_t>(regs.intrctrl()); |
| regs.intrctrl.master_int_enable(0); |
| break; |
| case GEN_ATTNSTATUS: |
| assert(size == sizeof(regs.attnStatus)); |
| pkt->set<uint32_t>(regs.attnStatus); |
| regs.attnStatus = 0; |
| break; |
| default: |
| panic("Read request to unknown register number: %#x\n", daddr); |
| } |
| pkt->makeAtomicResponse(); |
| return pioDelay; |
| } |
| |
| |
| // Find which channel we're accessing |
| int chanid = 0; |
| daddr -= 0x80; |
| while (daddr >= 0x80) { |
| chanid++; |
| daddr -= 0x80; |
| } |
| |
| if (chanid >= regs.chanCount) |
| panic("Access to channel %d (device only configured for %d channels)", |
| chanid, regs.chanCount); |
| |
| /// |
| /// Channel registers are handled here |
| /// |
| chan[chanid]->channelRead(pkt, daddr, size); |
| |
| pkt->makeAtomicResponse(); |
| return pioDelay; |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::channelRead(Packet *pkt, Addr daddr, int size) |
| { |
| switch (daddr) { |
| case CHAN_CONTROL: |
| assert(size == sizeof(uint16_t)); |
| pkt->set<uint16_t>(cr.ctrl()); |
| cr.ctrl.in_use(1); |
| break; |
| case CHAN_STATUS: |
| assert(size == sizeof(uint64_t)); |
| pkt->set<uint64_t>(cr.status() | ~busy); |
| break; |
| case CHAN_CHAINADDR: |
| assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); |
| if (size == sizeof(uint64_t)) |
| pkt->set<uint64_t>(cr.descChainAddr); |
| else |
| pkt->set<uint32_t>(bits(cr.descChainAddr,0,31)); |
| break; |
| case CHAN_CHAINADDR_HIGH: |
| assert(size == sizeof(uint32_t)); |
| pkt->set<uint32_t>(bits(cr.descChainAddr,32,63)); |
| break; |
| case CHAN_COMMAND: |
| assert(size == sizeof(uint8_t)); |
| pkt->set<uint32_t>(cr.command()); |
| break; |
| case CHAN_CMPLNADDR: |
| assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); |
| if (size == sizeof(uint64_t)) |
| pkt->set<uint64_t>(cr.completionAddr); |
| else |
| pkt->set<uint32_t>(bits(cr.completionAddr,0,31)); |
| break; |
| case CHAN_CMPLNADDR_HIGH: |
| assert(size == sizeof(uint32_t)); |
| pkt->set<uint32_t>(bits(cr.completionAddr,32,63)); |
| break; |
| case CHAN_ERROR: |
| assert(size == sizeof(uint32_t)); |
| pkt->set<uint32_t>(cr.error()); |
| break; |
| default: |
| panic("Read request to unknown channel register number: (%d)%#x\n", |
| channelId, daddr); |
| } |
| } |
| |
| |
| Tick |
| CopyEngine::write(PacketPtr pkt) |
| { |
| int bar; |
| Addr daddr; |
| |
| |
| if (!getBAR(pkt->getAddr(), bar, daddr)) |
| panic("Invalid PCI memory access to unmapped memory.\n"); |
| |
| // Only Memory register BAR is allowed |
| assert(bar == 0); |
| |
| int size = pkt->getSize(); |
| |
| /// |
| /// Handle write of register here |
| /// |
| |
| if (size == sizeof(uint64_t)) { |
| uint64_t val M5_VAR_USED = pkt->get<uint64_t>(); |
| DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n", daddr, val); |
| } else if (size == sizeof(uint32_t)) { |
| uint32_t val M5_VAR_USED = pkt->get<uint32_t>(); |
| DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n", daddr, val); |
| } else if (size == sizeof(uint16_t)) { |
| uint16_t val M5_VAR_USED = pkt->get<uint16_t>(); |
| DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n", daddr, val); |
| } else if (size == sizeof(uint8_t)) { |
| uint8_t val M5_VAR_USED = pkt->get<uint8_t>(); |
| DPRINTF(DMACopyEngine, "Wrote device register %#X value %#X\n", daddr, val); |
| } else { |
| panic("Unknown size for MMIO access: %d\n", size); |
| } |
| |
| if (daddr < 0x80) { |
| switch (daddr) { |
| case GEN_CHANCOUNT: |
| case GEN_XFERCAP: |
| case GEN_ATTNSTATUS: |
| DPRINTF(DMACopyEngine, "Warning, ignorning write to register %x\n", |
| daddr); |
| break; |
| case GEN_INTRCTRL: |
| regs.intrctrl.master_int_enable(bits(pkt->get<uint8_t>(),0,1)); |
| break; |
| default: |
| panic("Read request to unknown register number: %#x\n", daddr); |
| } |
| pkt->makeAtomicResponse(); |
| return pioDelay; |
| } |
| |
| // Find which channel we're accessing |
| int chanid = 0; |
| daddr -= 0x80; |
| while (daddr >= 0x80) { |
| chanid++; |
| daddr -= 0x80; |
| } |
| |
| if (chanid >= regs.chanCount) |
| panic("Access to channel %d (device only configured for %d channels)", |
| chanid, regs.chanCount); |
| |
| /// |
| /// Channel registers are handled here |
| /// |
| chan[chanid]->channelWrite(pkt, daddr, size); |
| |
| pkt->makeAtomicResponse(); |
| return pioDelay; |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::channelWrite(Packet *pkt, Addr daddr, int size) |
| { |
| switch (daddr) { |
| case CHAN_CONTROL: |
| assert(size == sizeof(uint16_t)); |
| int old_int_disable; |
| old_int_disable = cr.ctrl.interrupt_disable(); |
| cr.ctrl(pkt->get<uint16_t>()); |
| if (cr.ctrl.interrupt_disable()) |
| cr.ctrl.interrupt_disable(0); |
| else |
| cr.ctrl.interrupt_disable(old_int_disable); |
| break; |
| case CHAN_STATUS: |
| assert(size == sizeof(uint64_t)); |
| DPRINTF(DMACopyEngine, "Warning, ignorning write to register %x\n", |
| daddr); |
| break; |
| case CHAN_CHAINADDR: |
| assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); |
| if (size == sizeof(uint64_t)) |
| cr.descChainAddr = pkt->get<uint64_t>(); |
| else |
| cr.descChainAddr = (uint64_t)pkt->get<uint32_t>() | |
| (cr.descChainAddr & ~mask(32)); |
| DPRINTF(DMACopyEngine, "Chain Address %x\n", cr.descChainAddr); |
| break; |
| case CHAN_CHAINADDR_HIGH: |
| assert(size == sizeof(uint32_t)); |
| cr.descChainAddr = ((uint64_t)pkt->get<uint32_t>() <<32) | |
| (cr.descChainAddr & mask(32)); |
| DPRINTF(DMACopyEngine, "Chain Address %x\n", cr.descChainAddr); |
| break; |
| case CHAN_COMMAND: |
| assert(size == sizeof(uint8_t)); |
| cr.command(pkt->get<uint8_t>()); |
| recvCommand(); |
| break; |
| case CHAN_CMPLNADDR: |
| assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); |
| if (size == sizeof(uint64_t)) |
| cr.completionAddr = pkt->get<uint64_t>(); |
| else |
| cr.completionAddr = pkt->get<uint32_t>() | |
| (cr.completionAddr & ~mask(32)); |
| break; |
| case CHAN_CMPLNADDR_HIGH: |
| assert(size == sizeof(uint32_t)); |
| cr.completionAddr = ((uint64_t)pkt->get<uint32_t>() <<32) | |
| (cr.completionAddr & mask(32)); |
| break; |
| case CHAN_ERROR: |
| assert(size == sizeof(uint32_t)); |
| cr.error(~pkt->get<uint32_t>() & cr.error()); |
| break; |
| default: |
| panic("Read request to unknown channel register number: (%d)%#x\n", |
| channelId, daddr); |
| } |
| } |
| |
| void |
| CopyEngine::regStats() |
| { |
| using namespace Stats; |
| bytesCopied |
| .init(regs.chanCount) |
| .name(name() + ".bytes_copied") |
| .desc("Number of bytes copied by each engine") |
| .flags(total) |
| ; |
| copiesProcessed |
| .init(regs.chanCount) |
| .name(name() + ".copies_processed") |
| .desc("Number of copies processed by each engine") |
| .flags(total) |
| ; |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::fetchDescriptor(Addr address) |
| { |
| anDq(); |
| anBegin("FetchDescriptor"); |
| DPRINTF(DMACopyEngine, "Reading descriptor from at memory location %#x(%#x)\n", |
| address, ce->platform->pciToDma(address)); |
| assert(address); |
| busy = true; |
| |
| DPRINTF(DMACopyEngine, "dmaAction: %#x, %d bytes, to addr %#x\n", |
| ce->platform->pciToDma(address), sizeof(DmaDesc), curDmaDesc); |
| |
| cePort->dmaAction(MemCmd::ReadReq, ce->platform->pciToDma(address), |
| sizeof(DmaDesc), &fetchCompleteEvent, (uint8_t*)curDmaDesc, |
| latBeforeBegin); |
| lastDescriptorAddr = address; |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::fetchDescComplete() |
| { |
| DPRINTF(DMACopyEngine, "Read of descriptor complete\n"); |
| |
| if ((curDmaDesc->command & DESC_CTRL_NULL)) { |
| DPRINTF(DMACopyEngine, "Got NULL descriptor, skipping\n"); |
| assert(!(curDmaDesc->command & DESC_CTRL_CP_STS)); |
| if (curDmaDesc->command & DESC_CTRL_CP_STS) { |
| panic("Shouldn't be able to get here\n"); |
| nextState = CompletionWrite; |
| if (inDrain()) return; |
| writeCompletionStatus(); |
| } else { |
| anBegin("Idle"); |
| anWait(); |
| busy = false; |
| nextState = Idle; |
| inDrain(); |
| } |
| return; |
| } |
| |
| if (curDmaDesc->command & ~DESC_CTRL_CP_STS) |
| panic("Descriptor has flag other that completion status set\n"); |
| |
| nextState = DMARead; |
| if (inDrain()) return; |
| readCopyBytes(); |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::readCopyBytes() |
| { |
| anBegin("ReadCopyBytes"); |
| DPRINTF(DMACopyEngine, "Reading %d bytes from buffer to memory location %#x(%#x)\n", |
| curDmaDesc->len, curDmaDesc->dest, |
| ce->platform->pciToDma(curDmaDesc->src)); |
| cePort->dmaAction(MemCmd::ReadReq, ce->platform->pciToDma(curDmaDesc->src), |
| curDmaDesc->len, &readCompleteEvent, copyBuffer, 0); |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::readCopyBytesComplete() |
| { |
| DPRINTF(DMACopyEngine, "Read of bytes to copy complete\n"); |
| |
| nextState = DMAWrite; |
| if (inDrain()) return; |
| writeCopyBytes(); |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::writeCopyBytes() |
| { |
| anBegin("WriteCopyBytes"); |
| DPRINTF(DMACopyEngine, "Writing %d bytes from buffer to memory location %#x(%#x)\n", |
| curDmaDesc->len, curDmaDesc->dest, |
| ce->platform->pciToDma(curDmaDesc->dest)); |
| |
| cePort->dmaAction(MemCmd::WriteReq, ce->platform->pciToDma(curDmaDesc->dest), |
| curDmaDesc->len, &writeCompleteEvent, copyBuffer, 0); |
| |
| ce->bytesCopied[channelId] += curDmaDesc->len; |
| ce->copiesProcessed[channelId]++; |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::writeCopyBytesComplete() |
| { |
| DPRINTF(DMACopyEngine, "Write of bytes to copy complete user1: %#x\n", |
| curDmaDesc->user1); |
| |
| cr.status.compl_desc_addr(lastDescriptorAddr >> 6); |
| completionDataReg = cr.status() | 1; |
| |
| anQ("DMAUsedDescQ", channelId, 1); |
| anQ("AppRecvQ", curDmaDesc->user1, curDmaDesc->len); |
| if (curDmaDesc->command & DESC_CTRL_CP_STS) { |
| nextState = CompletionWrite; |
| if (inDrain()) return; |
| writeCompletionStatus(); |
| return; |
| } |
| |
| continueProcessing(); |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::continueProcessing() |
| { |
| busy = false; |
| |
| if (underReset) { |
| anBegin("Reset"); |
| anWait(); |
| underReset = false; |
| refreshNext = false; |
| busy = false; |
| nextState = Idle; |
| return; |
| } |
| |
| if (curDmaDesc->next) { |
| nextState = DescriptorFetch; |
| fetchAddress = curDmaDesc->next; |
| if (inDrain()) return; |
| fetchDescriptor(curDmaDesc->next); |
| } else if (refreshNext) { |
| nextState = AddressFetch; |
| refreshNext = false; |
| if (inDrain()) return; |
| fetchNextAddr(lastDescriptorAddr); |
| } else { |
| inDrain(); |
| nextState = Idle; |
| anWait(); |
| anBegin("Idle"); |
| } |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::writeCompletionStatus() |
| { |
| anBegin("WriteCompletionStatus"); |
| DPRINTF(DMACopyEngine, "Writing completion status %#x to address %#x(%#x)\n", |
| completionDataReg, cr.completionAddr, |
| ce->platform->pciToDma(cr.completionAddr)); |
| |
| cePort->dmaAction(MemCmd::WriteReq, ce->platform->pciToDma(cr.completionAddr), |
| sizeof(completionDataReg), &statusCompleteEvent, |
| (uint8_t*)&completionDataReg, latAfterCompletion); |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::writeStatusComplete() |
| { |
| DPRINTF(DMACopyEngine, "Writing completion status complete\n"); |
| continueProcessing(); |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::fetchNextAddr(Addr address) |
| { |
| anBegin("FetchNextAddr"); |
| DPRINTF(DMACopyEngine, "Fetching next address...\n"); |
| busy = true; |
| cePort->dmaAction(MemCmd::ReadReq, ce->platform->pciToDma(address + |
| offsetof(DmaDesc, next)), sizeof(Addr), &addrCompleteEvent, |
| (uint8_t*)curDmaDesc + offsetof(DmaDesc, next), 0); |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::fetchAddrComplete() |
| { |
| DPRINTF(DMACopyEngine, "Fetching next address complete: %#x\n", |
| curDmaDesc->next); |
| if (!curDmaDesc->next) { |
| DPRINTF(DMACopyEngine, "Got NULL descriptor, nothing more to do\n"); |
| busy = false; |
| nextState = Idle; |
| anWait(); |
| anBegin("Idle"); |
| inDrain(); |
| return; |
| } |
| nextState = DescriptorFetch; |
| fetchAddress = curDmaDesc->next; |
| if (inDrain()) return; |
| fetchDescriptor(curDmaDesc->next); |
| } |
| |
| bool |
| CopyEngine::CopyEngineChannel::inDrain() |
| { |
| if (ce->getState() == SimObject::Draining) { |
| DPRINTF(DMACopyEngine, "processing drain\n"); |
| assert(drainEvent); |
| drainEvent->process(); |
| drainEvent = NULL; |
| } |
| |
| return ce->getState() != SimObject::Running; |
| } |
| |
| unsigned int |
| CopyEngine::CopyEngineChannel::drain(Event *de) |
| { |
| if (nextState == Idle || ce->getState() != SimObject::Running) |
| return 0; |
| unsigned int count = 1; |
| count += cePort->drain(de); |
| |
| DPRINTF(DMACopyEngine, "unable to drain, returning %d\n", count); |
| drainEvent = de; |
| return count; |
| } |
| |
| unsigned int |
| CopyEngine::drain(Event *de) |
| { |
| unsigned int count; |
| count = pioPort->drain(de) + dmaPort->drain(de) + configPort->drain(de); |
| for (int x = 0;x < chan.size(); x++) |
| count += chan[x]->drain(de); |
| |
| if (count) |
| changeState(Draining); |
| else |
| changeState(Drained); |
| |
| DPRINTF(DMACopyEngine, "call to CopyEngine::drain() returning %d\n", count); |
| return count; |
| } |
| |
| void |
| CopyEngine::serialize(std::ostream &os) |
| { |
| PciDev::serialize(os); |
| regs.serialize(os); |
| for (int x =0; x < chan.size(); x++) { |
| nameOut(os, csprintf("%s.channel%d", name(), x)); |
| chan[x]->serialize(os); |
| } |
| } |
| |
| void |
| CopyEngine::unserialize(Checkpoint *cp, const std::string §ion) |
| { |
| PciDev::unserialize(cp, section); |
| regs.unserialize(cp, section); |
| for (int x = 0; x < chan.size(); x++) |
| chan[x]->unserialize(cp, csprintf("%s.channel%d", section, x)); |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::serialize(std::ostream &os) |
| { |
| SERIALIZE_SCALAR(channelId); |
| SERIALIZE_SCALAR(busy); |
| SERIALIZE_SCALAR(underReset); |
| SERIALIZE_SCALAR(refreshNext); |
| SERIALIZE_SCALAR(lastDescriptorAddr); |
| SERIALIZE_SCALAR(completionDataReg); |
| SERIALIZE_SCALAR(fetchAddress); |
| int nextState = this->nextState; |
| SERIALIZE_SCALAR(nextState); |
| arrayParamOut(os, "curDmaDesc", (uint8_t*)curDmaDesc, sizeof(DmaDesc)); |
| SERIALIZE_ARRAY(copyBuffer, ce->params()->XferCap); |
| cr.serialize(os); |
| |
| } |
| void |
| CopyEngine::CopyEngineChannel::unserialize(Checkpoint *cp, const std::string §ion) |
| { |
| UNSERIALIZE_SCALAR(channelId); |
| UNSERIALIZE_SCALAR(busy); |
| UNSERIALIZE_SCALAR(underReset); |
| UNSERIALIZE_SCALAR(refreshNext); |
| UNSERIALIZE_SCALAR(lastDescriptorAddr); |
| UNSERIALIZE_SCALAR(completionDataReg); |
| UNSERIALIZE_SCALAR(fetchAddress); |
| int nextState; |
| UNSERIALIZE_SCALAR(nextState); |
| this->nextState = (ChannelState)nextState; |
| arrayParamIn(cp, section, "curDmaDesc", (uint8_t*)curDmaDesc, sizeof(DmaDesc)); |
| UNSERIALIZE_ARRAY(copyBuffer, ce->params()->XferCap); |
| cr.unserialize(cp, section); |
| |
| } |
| |
| void |
| CopyEngine::CopyEngineChannel::restartStateMachine() |
| { |
| switch(nextState) { |
| case AddressFetch: |
| fetchNextAddr(lastDescriptorAddr); |
| break; |
| case DescriptorFetch: |
| fetchDescriptor(fetchAddress); |
| break; |
| case DMARead: |
| readCopyBytes(); |
| break; |
| case DMAWrite: |
| writeCopyBytes(); |
| break; |
| case CompletionWrite: |
| writeCompletionStatus(); |
| break; |
| case Idle: |
| break; |
| default: |
| panic("Unknown state for CopyEngineChannel\n"); |
| } |
| } |
| |
| void |
| CopyEngine::resume() |
| { |
| SimObject::resume(); |
| for (int x = 0;x < chan.size(); x++) |
| chan[x]->resume(); |
| } |
| |
| |
| void |
| CopyEngine::CopyEngineChannel::resume() |
| { |
| DPRINTF(DMACopyEngine, "Restarting state machine at state %d\n", nextState); |
| restartStateMachine(); |
| } |
| |
| CopyEngine * |
| CopyEngineParams::create() |
| { |
| return new CopyEngine(this); |
| } |