+++ /dev/null
-# 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
-
-from m5.SimObject import SimObject
-from m5.params import *
-from m5.proxy import *
-from PciDevice import PciDevice
-
-class CopyEngine(PciDevice):
- type = 'CopyEngine'
- cxx_header = "dev/copy_engine.hh"
- dma = VectorMasterPort("Copy engine DMA port")
- VendorID = 0x8086
- DeviceID = 0x1a38
- Revision = 0xA2 # CM2 stepping (newest listed)
- SubsystemID = 0
- SubsystemVendorID = 0
- Status = 0x0000
- SubClassCode = 0x08
- ClassCode = 0x80
- ProgIF = 0x00
- MaximumLatency = 0x00
- MinimumGrant = 0xff
- InterruptLine = 0x20
- InterruptPin = 0x01
- BAR0Size = '1kB'
-
- ChanCnt = Param.UInt8(4, "Number of DMA channels that exist on device")
- XferCap = Param.MemorySize('4kB', "Number of bits of transfer size that are supported")
-
- latBeforeBegin = Param.Latency('20ns', "Latency after a DMA command is seen before it's proccessed")
- latAfterCompletion = Param.Latency('20ns', "Latency after a DMA command is complete before it's reported as such")
-
-
Return()
SimObject('BadDevice.py')
-SimObject('CopyEngine.py')
SimObject('DiskImage.py')
SimObject('Ethernet.py')
SimObject('I2C.py')
SimObject('Uart.py')
Source('baddev.cc')
-Source('copy_engine.cc')
Source('disk_image.cc')
Source('dma_device.cc')
Source('etherbus.cc')
DebugFlag('DiskImageRead')
DebugFlag('DiskImageWrite')
DebugFlag('DMA')
-DebugFlag('DMACopyEngine')
DebugFlag('Ethernet')
DebugFlag('MultiEthernet')
DebugFlag('MultiEthernetPkt')
+++ /dev/null
-/*
- * Copyright (c) 2012 ARM Limited
- * All rights reserved
- *
- * The license below extends only to copyright in the software and shall
- * not be construed as granting a license to any other intellectual
- * property including but not limited to intellectual property relating
- * to a hardware implementation of the functionality of the software
- * licensed hereunder. You may use the software subject to the license
- * terms below provided that you ensure that this notice is replicated
- * unmodified and in its entirety in all distributions of the software,
- * modified or unmodified, in source code or in binary form.
- *
- * 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 "debug/Drain.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;
-
-CopyEngine::CopyEngine(const Params *p)
- : PciDevice(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)
- : cePort(_ce, _ce->sys),
- ce(_ce), channelId(cid), busy(false), underReset(false),
- refreshNext(false), latBeforeBegin(ce->params()->latBeforeBegin),
- latAfterCompletion(ce->params()->latAfterCompletion),
- completionDataReg(0), nextState(Idle),
- 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;
-}
-
-BaseMasterPort &
-CopyEngine::getMasterPort(const std::string &if_name, PortID idx)
-{
- if (if_name != "dma") {
- // pass it along to our super class
- return PciDevice::getMasterPort(if_name, idx);
- } else {
- if (idx >= static_cast<int>(chan.size())) {
- panic("CopyEngine::getMasterPort: unknown index %d\n", idx);
- }
-
- return chan[idx]->getMasterPort();
- }
-}
-
-
-BaseMasterPort &
-CopyEngine::CopyEngineChannel::getMasterPort()
-{
- return cePort;
-}
-
-void
-CopyEngine::CopyEngineChannel::recvCommand()
-{
- if (cr.command.start_dma()) {
- assert(!busy);
- cr.status.dma_transfer_status(0);
- nextState = DescriptorFetch;
- fetchAddress = cr.descChainAddr;
- if (ce->drainState() == DrainState::Running)
- fetchDescriptor(cr.descChainAddr);
- } else if (cr.command.append_dma()) {
- if (!busy) {
- nextState = AddressFetch;
- if (ce->drainState() == DrainState::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);
-
- ///
- /// 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->pciToDma(address));
- assert(address);
- busy = true;
-
- DPRINTF(DMACopyEngine, "dmaAction: %#x, %d bytes, to addr %#x\n",
- ce->pciToDma(address), sizeof(DmaDesc), curDmaDesc);
-
- cePort.dmaAction(MemCmd::ReadReq, ce->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->pciToDma(curDmaDesc->src));
- cePort.dmaAction(MemCmd::ReadReq, ce->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->pciToDma(curDmaDesc->dest));
-
- cePort.dmaAction(MemCmd::WriteReq, ce->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->pciToDma(cr.completionAddr));
-
- cePort.dmaAction(MemCmd::WriteReq,
- ce->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->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 (drainState() == DrainState::Draining) {
- DPRINTF(Drain, "CopyEngine done draining, processing drain event\n");
- signalDrainDone();
- }
-
- return ce->drainState() != DrainState::Running;
-}
-
-DrainState
-CopyEngine::CopyEngineChannel::drain()
-{
- if (nextState == Idle || ce->drainState() != DrainState::Running) {
- return DrainState::Drained;
- } else {
- DPRINTF(Drain, "CopyEngineChannel not drained\n");
- return DrainState::Draining;
- }
-}
-
-void
-CopyEngine::serialize(CheckpointOut &cp) const
-{
- PciDevice::serialize(cp);
- regs.serialize(cp);
- for (int x =0; x < chan.size(); x++)
- chan[x]->serializeSection(cp, csprintf("channel%d", x));
-}
-
-void
-CopyEngine::unserialize(CheckpointIn &cp)
-{
- PciDevice::unserialize(cp);
- regs.unserialize(cp);
- for (int x = 0; x < chan.size(); x++)
- chan[x]->unserializeSection(cp, csprintf("channel%d", x));
-}
-
-void
-CopyEngine::CopyEngineChannel::serialize(CheckpointOut &cp) const
-{
- 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(cp, "curDmaDesc", (uint8_t*)curDmaDesc, sizeof(DmaDesc));
- SERIALIZE_ARRAY(copyBuffer, ce->params()->XferCap);
- cr.serialize(cp);
-
-}
-void
-CopyEngine::CopyEngineChannel::unserialize(CheckpointIn &cp)
-{
- 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, "curDmaDesc", (uint8_t*)curDmaDesc, sizeof(DmaDesc));
- UNSERIALIZE_ARRAY(copyBuffer, ce->params()->XferCap);
- cr.unserialize(cp);
-
-}
-
-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::CopyEngineChannel::drainResume()
-{
- DPRINTF(DMACopyEngine, "Restarting state machine at state %d\n", nextState);
- restartStateMachine();
-}
-
-CopyEngine *
-CopyEngineParams::create()
-{
- return new CopyEngine(this);
-}
+++ /dev/null
-/*
- * Copyright (c) 2012 ARM Limited
- * All rights reserved
- *
- * The license below extends only to copyright in the software and shall
- * not be construed as granting a license to any other intellectual
- * property including but not limited to intellectual property relating
- * to a hardware implementation of the functionality of the software
- * licensed hereunder. You may use the software subject to the license
- * terms below provided that you ensure that this notice is replicated
- * unmodified and in its entirety in all distributions of the software,
- * modified or unmodified, in source code or in binary form.
- *
- * 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 Acceleration Technology (I/OAT).
- * A DMA asyncronous copy engine
- */
-
-#ifndef __DEV_COPY_ENGINE_HH__
-#define __DEV_COPY_ENGINE_HH__
-
-#include <vector>
-
-#include "base/cp_annotate.hh"
-#include "base/statistics.hh"
-#include "dev/copy_engine_defs.hh"
-#include "dev/pci/device.hh"
-#include "params/CopyEngine.hh"
-#include "sim/drain.hh"
-#include "sim/eventq.hh"
-
-class CopyEngine : public PciDevice
-{
- class CopyEngineChannel : public Drainable, public Serializable
- {
- private:
- DmaPort cePort;
- CopyEngine *ce;
- CopyEngineReg::ChanRegs cr;
- int channelId;
- CopyEngineReg::DmaDesc *curDmaDesc;
- uint8_t *copyBuffer;
-
- bool busy;
- bool underReset;
- bool refreshNext;
- Addr lastDescriptorAddr;
- Addr fetchAddress;
-
- Tick latBeforeBegin;
- Tick latAfterCompletion;
-
- uint64_t completionDataReg;
-
- enum ChannelState {
- Idle,
- AddressFetch,
- DescriptorFetch,
- DMARead,
- DMAWrite,
- CompletionWrite
- };
-
- ChannelState nextState;
-
- public:
- CopyEngineChannel(CopyEngine *_ce, int cid);
- virtual ~CopyEngineChannel();
- BaseMasterPort &getMasterPort();
-
- std::string name() { assert(ce); return ce->name() + csprintf("-chan%d", channelId); }
- virtual Tick read(PacketPtr pkt)
- { panic("CopyEngineChannel has no I/O access\n");}
- virtual Tick write(PacketPtr pkt)
- { panic("CopyEngineChannel has no I/O access\n"); }
-
- void channelRead(PacketPtr pkt, Addr daddr, int size);
- void channelWrite(PacketPtr pkt, Addr daddr, int size);
-
- DrainState drain() override;
- void drainResume() override;
-
- void serialize(CheckpointOut &cp) const override;
- void unserialize(CheckpointIn &cp) override;
-
- private:
- void fetchDescriptor(Addr address);
- void fetchDescComplete();
- EventWrapper<CopyEngineChannel, &CopyEngineChannel::fetchDescComplete>
- fetchCompleteEvent;
-
- void fetchNextAddr(Addr address);
- void fetchAddrComplete();
- EventWrapper<CopyEngineChannel, &CopyEngineChannel::fetchAddrComplete>
- addrCompleteEvent;
-
- void readCopyBytes();
- void readCopyBytesComplete();
- EventWrapper<CopyEngineChannel, &CopyEngineChannel::readCopyBytesComplete>
- readCompleteEvent;
-
- void writeCopyBytes();
- void writeCopyBytesComplete();
- EventWrapper <CopyEngineChannel, &CopyEngineChannel::writeCopyBytesComplete>
- writeCompleteEvent;
-
- void writeCompletionStatus();
- void writeStatusComplete();
- EventWrapper <CopyEngineChannel, &CopyEngineChannel::writeStatusComplete>
- statusCompleteEvent;
-
-
- void continueProcessing();
- void recvCommand();
- bool inDrain();
- void restartStateMachine();
- inline void anBegin(const char *s)
- {
- CPA::cpa()->hwBegin(CPA::FL_NONE, ce->sys,
- channelId, "CopyEngine", s);
- }
-
- inline void anWait()
- {
- CPA::cpa()->hwWe(CPA::FL_NONE, ce->sys,
- channelId, "CopyEngine", "DMAUnusedDescQ", channelId);
- }
-
- inline void anDq()
- {
- CPA::cpa()->hwDq(CPA::FL_NONE, ce->sys,
- channelId, "CopyEngine", "DMAUnusedDescQ", channelId);
- }
-
- inline void anPq()
- {
- CPA::cpa()->hwDq(CPA::FL_NONE, ce->sys,
- channelId, "CopyEngine", "DMAUnusedDescQ", channelId);
- }
-
- inline void anQ(const char * s, uint64_t id, int size = 1)
- {
- CPA::cpa()->hwQ(CPA::FL_NONE, ce->sys, channelId,
- "CopyEngine", s, id, NULL, size);
- }
-
- };
-
- private:
-
- Stats::Vector bytesCopied;
- Stats::Vector copiesProcessed;
-
- // device registers
- CopyEngineReg::Regs regs;
-
- // Array of channels each one with regs/dma port/etc
- std::vector<CopyEngineChannel*> chan;
-
- public:
- typedef CopyEngineParams Params;
- const Params *
- params() const
- {
- return dynamic_cast<const Params *>(_params);
- }
- CopyEngine(const Params *params);
- ~CopyEngine();
-
- void regStats() override;
-
- BaseMasterPort &getMasterPort(const std::string &if_name,
- PortID idx = InvalidPortID) override;
-
- Tick read(PacketPtr pkt) override;
- Tick write(PacketPtr pkt) override;
-
- void serialize(CheckpointOut &cp) const override;
- void unserialize(CheckpointIn &cp) override;
-};
-
-#endif //__DEV_COPY_ENGINE_HH__
-
+++ /dev/null
-/*
- * 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
- * Register and structure descriptions for Intel's I/O AT DMA Engine
- */
-#include "base/bitfield.hh"
-#include "sim/serialize.hh"
-
-namespace CopyEngineReg {
-
-
-// General Channel independant registers, 128 bytes starting at 0x00
-const uint32_t GEN_CHANCOUNT = 0x00;
-const uint32_t GEN_XFERCAP = 0x01;
-const uint32_t GEN_INTRCTRL = 0x03;
-const uint32_t GEN_ATTNSTATUS = 0x04;
-
-
-// Channel specific registers, each block is 128 bytes, starting at 0x80
-const uint32_t CHAN_CONTROL = 0x00;
-const uint32_t CHAN_STATUS = 0x04;
-const uint32_t CHAN_CHAINADDR = 0x0C;
-const uint32_t CHAN_CHAINADDR_LOW = 0x0C;
-const uint32_t CHAN_CHAINADDR_HIGH = 0x10;
-const uint32_t CHAN_COMMAND = 0x14;
-const uint32_t CHAN_CMPLNADDR = 0x18;
-const uint32_t CHAN_CMPLNADDR_LOW = 0x18;
-const uint32_t CHAN_CMPLNADDR_HIGH = 0x1C;
-const uint32_t CHAN_ERROR = 0x28;
-
-
-const uint32_t DESC_CTRL_INT_GEN = 0x00000001;
-const uint32_t DESC_CTRL_SRC_SN = 0x00000002;
-const uint32_t DESC_CTRL_DST_SN = 0x00000004;
-const uint32_t DESC_CTRL_CP_STS = 0x00000008;
-const uint32_t DESC_CTRL_FRAME = 0x00000010;
-const uint32_t DESC_CTRL_NULL = 0x00000020;
-
-struct DmaDesc {
- uint32_t len;
- uint32_t command;
- Addr src;
- Addr dest;
- Addr next;
- uint64_t reserved1;
- uint64_t reserved2;
- uint64_t user1;
- uint64_t user2;
-};
-
-#define ADD_FIELD8(NAME, OFFSET, BITS) \
- inline uint8_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \
- inline void NAME(uint8_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); }
-
-#define ADD_FIELD16(NAME, OFFSET, BITS) \
- inline uint16_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \
- inline void NAME(uint16_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); }
-
-#define ADD_FIELD32(NAME, OFFSET, BITS) \
- inline uint32_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \
- inline void NAME(uint32_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); }
-
-#define ADD_FIELD64(NAME, OFFSET, BITS) \
- inline uint64_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \
- inline void NAME(uint64_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); }
-
-template<class T>
-struct Reg {
- T _data;
- T operator()() { return _data; }
- const Reg<T> &operator=(T d) { _data = d; return *this;}
- bool operator==(T d) { return d == _data; }
- void operator()(T d) { _data = d; }
- Reg() { _data = 0; }
- void serialize(CheckpointOut &cp) const
- {
- SERIALIZE_SCALAR(_data);
- }
- void unserialize(CheckpointIn &cp)
- {
- UNSERIALIZE_SCALAR(_data);
- }
-};
-
-
-struct Regs : public Serializable {
- uint8_t chanCount;
- uint8_t xferCap;
-
- struct INTRCTRL : public Reg<uint8_t> { // 0x03
- using Reg<uint8_t>::operator =;
- ADD_FIELD8(master_int_enable,0,1);
- ADD_FIELD8(interrupt_status,1,1);
- ADD_FIELD8(interrupt,2,1);
- };
- INTRCTRL intrctrl;
-
- uint32_t attnStatus; // Read clears
-
- void serialize(CheckpointOut &cp) const override
- {
- SERIALIZE_SCALAR(chanCount);
- SERIALIZE_SCALAR(xferCap);
- paramOut(cp, "intrctrl", intrctrl._data);
- SERIALIZE_SCALAR(attnStatus);
- }
-
- void unserialize(CheckpointIn &cp) override
- {
- UNSERIALIZE_SCALAR(chanCount);
- UNSERIALIZE_SCALAR(xferCap);
- paramIn(cp, "intrctrl", intrctrl._data);
- UNSERIALIZE_SCALAR(attnStatus);
- }
-
-};
-
-struct ChanRegs : public Serializable {
- struct CHANCTRL : public Reg<uint16_t> { // channelX + 0x00
- using Reg<uint16_t>::operator =;
- ADD_FIELD16(interrupt_disable,0,1);
- ADD_FIELD16(error_completion_enable, 2,1);
- ADD_FIELD16(any_error_abort_enable,3,1);
- ADD_FIELD16(error_int_enable,4,1);
- ADD_FIELD16(desc_addr_snoop_control,5,1);
- ADD_FIELD16(in_use, 8,1);
- };
- CHANCTRL ctrl;
-
- struct CHANSTS : public Reg<uint64_t> { // channelX + 0x04
- ADD_FIELD64(dma_transfer_status, 0, 3);
- ADD_FIELD64(unaffiliated_error, 3, 1);
- ADD_FIELD64(soft_error, 4, 1);
- ADD_FIELD64(compl_desc_addr, 6, 58);
- };
- CHANSTS status;
-
- uint64_t descChainAddr;
-
- struct CHANCMD : public Reg<uint8_t> { // channelX + 0x14
- ADD_FIELD8(start_dma,0,1);
- ADD_FIELD8(append_dma,1,1);
- ADD_FIELD8(suspend_dma,2,1);
- ADD_FIELD8(abort_dma,3,1);
- ADD_FIELD8(resume_dma,4,1);
- ADD_FIELD8(reset_dma,5,1);
- };
- CHANCMD command;
-
- uint64_t completionAddr;
-
- struct CHANERR : public Reg<uint32_t> { // channel X + 0x28
- ADD_FIELD32(source_addr_error,0,1);
- ADD_FIELD32(dest_addr_error,1,1);
- ADD_FIELD32(ndesc_addr_error,2,1);
- ADD_FIELD32(desc_error,3,1);
- ADD_FIELD32(chain_addr_error,4,1);
- ADD_FIELD32(chain_cmd_error,5,1);
- ADD_FIELD32(chipset_parity_error,6,1);
- ADD_FIELD32(dma_parity_error,7,1);
- ADD_FIELD32(read_data_error,8,1);
- ADD_FIELD32(write_data_error,9,1);
- ADD_FIELD32(desc_control_error,10,1);
- ADD_FIELD32(desc_len_error,11,1);
- ADD_FIELD32(completion_addr_error,12,1);
- ADD_FIELD32(interrupt_config_error,13,1);
- ADD_FIELD32(soft_error,14,1);
- ADD_FIELD32(unaffiliated_error,15,1);
- };
- CHANERR error;
-
- void serialize(CheckpointOut &cp) const override
- {
- paramOut(cp, "ctrl", ctrl._data);
- paramOut(cp, "status", status._data);
- SERIALIZE_SCALAR(descChainAddr);
- paramOut(cp, "command", command._data);
- SERIALIZE_SCALAR(completionAddr);
- paramOut(cp, "error", error._data);
- }
-
- void unserialize(CheckpointIn &cp) override
- {
- paramIn(cp, "ctrl", ctrl._data);
- paramIn(cp, "status", status._data);
- UNSERIALIZE_SCALAR(descChainAddr);
- paramIn(cp, "command", command._data);
- UNSERIALIZE_SCALAR(completionAddr);
- paramIn(cp, "error", error._data);
- }
-
-
-};
-
-} // namespace CopyEngineReg
-
-
--- /dev/null
+# 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
+
+from m5.SimObject import SimObject
+from m5.params import *
+from m5.proxy import *
+from PciDevice import PciDevice
+
+class CopyEngine(PciDevice):
+ type = 'CopyEngine'
+ cxx_header = "dev/pci/copy_engine.hh"
+ dma = VectorMasterPort("Copy engine DMA port")
+ VendorID = 0x8086
+ DeviceID = 0x1a38
+ Revision = 0xA2 # CM2 stepping (newest listed)
+ SubsystemID = 0
+ SubsystemVendorID = 0
+ Status = 0x0000
+ SubClassCode = 0x08
+ ClassCode = 0x80
+ ProgIF = 0x00
+ MaximumLatency = 0x00
+ MinimumGrant = 0xff
+ InterruptLine = 0x20
+ InterruptPin = 0x01
+ BAR0Size = '1kB'
+
+ ChanCnt = Param.UInt8(4, "Number of DMA channels that exist on device")
+ XferCap = Param.MemorySize('4kB', "Number of bits of transfer size that are supported")
+
+ latBeforeBegin = Param.Latency('20ns', "Latency after a DMA command is seen before it's proccessed")
+ latAfterCompletion = Param.Latency('20ns', "Latency after a DMA command is complete before it's reported as such")
+
+
Source('host.cc')
DebugFlag('PciHost')
+SimObject('CopyEngine.py')
+Source('copy_engine.cc')
+DebugFlag('DMACopyEngine')
--- /dev/null
+/*
+ * Copyright (c) 2012 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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 "dev/pci/copy_engine.hh"
+
+#include <algorithm>
+
+#include "base/cp_annotate.hh"
+#include "base/trace.hh"
+#include "debug/DMACopyEngine.hh"
+#include "debug/Drain.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;
+
+CopyEngine::CopyEngine(const Params *p)
+ : PciDevice(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)
+ : cePort(_ce, _ce->sys),
+ ce(_ce), channelId(cid), busy(false), underReset(false),
+ refreshNext(false), latBeforeBegin(ce->params()->latBeforeBegin),
+ latAfterCompletion(ce->params()->latAfterCompletion),
+ completionDataReg(0), nextState(Idle),
+ 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;
+}
+
+BaseMasterPort &
+CopyEngine::getMasterPort(const std::string &if_name, PortID idx)
+{
+ if (if_name != "dma") {
+ // pass it along to our super class
+ return PciDevice::getMasterPort(if_name, idx);
+ } else {
+ if (idx >= static_cast<int>(chan.size())) {
+ panic("CopyEngine::getMasterPort: unknown index %d\n", idx);
+ }
+
+ return chan[idx]->getMasterPort();
+ }
+}
+
+
+BaseMasterPort &
+CopyEngine::CopyEngineChannel::getMasterPort()
+{
+ return cePort;
+}
+
+void
+CopyEngine::CopyEngineChannel::recvCommand()
+{
+ if (cr.command.start_dma()) {
+ assert(!busy);
+ cr.status.dma_transfer_status(0);
+ nextState = DescriptorFetch;
+ fetchAddress = cr.descChainAddr;
+ if (ce->drainState() == DrainState::Running)
+ fetchDescriptor(cr.descChainAddr);
+ } else if (cr.command.append_dma()) {
+ if (!busy) {
+ nextState = AddressFetch;
+ if (ce->drainState() == DrainState::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);
+
+ ///
+ /// 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->pciToDma(address));
+ assert(address);
+ busy = true;
+
+ DPRINTF(DMACopyEngine, "dmaAction: %#x, %d bytes, to addr %#x\n",
+ ce->pciToDma(address), sizeof(DmaDesc), curDmaDesc);
+
+ cePort.dmaAction(MemCmd::ReadReq, ce->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->pciToDma(curDmaDesc->src));
+ cePort.dmaAction(MemCmd::ReadReq, ce->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->pciToDma(curDmaDesc->dest));
+
+ cePort.dmaAction(MemCmd::WriteReq, ce->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->pciToDma(cr.completionAddr));
+
+ cePort.dmaAction(MemCmd::WriteReq,
+ ce->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->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 (drainState() == DrainState::Draining) {
+ DPRINTF(Drain, "CopyEngine done draining, processing drain event\n");
+ signalDrainDone();
+ }
+
+ return ce->drainState() != DrainState::Running;
+}
+
+DrainState
+CopyEngine::CopyEngineChannel::drain()
+{
+ if (nextState == Idle || ce->drainState() != DrainState::Running) {
+ return DrainState::Drained;
+ } else {
+ DPRINTF(Drain, "CopyEngineChannel not drained\n");
+ return DrainState::Draining;
+ }
+}
+
+void
+CopyEngine::serialize(CheckpointOut &cp) const
+{
+ PciDevice::serialize(cp);
+ regs.serialize(cp);
+ for (int x =0; x < chan.size(); x++)
+ chan[x]->serializeSection(cp, csprintf("channel%d", x));
+}
+
+void
+CopyEngine::unserialize(CheckpointIn &cp)
+{
+ PciDevice::unserialize(cp);
+ regs.unserialize(cp);
+ for (int x = 0; x < chan.size(); x++)
+ chan[x]->unserializeSection(cp, csprintf("channel%d", x));
+}
+
+void
+CopyEngine::CopyEngineChannel::serialize(CheckpointOut &cp) const
+{
+ 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(cp, "curDmaDesc", (uint8_t*)curDmaDesc, sizeof(DmaDesc));
+ SERIALIZE_ARRAY(copyBuffer, ce->params()->XferCap);
+ cr.serialize(cp);
+
+}
+void
+CopyEngine::CopyEngineChannel::unserialize(CheckpointIn &cp)
+{
+ 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, "curDmaDesc", (uint8_t*)curDmaDesc, sizeof(DmaDesc));
+ UNSERIALIZE_ARRAY(copyBuffer, ce->params()->XferCap);
+ cr.unserialize(cp);
+
+}
+
+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::CopyEngineChannel::drainResume()
+{
+ DPRINTF(DMACopyEngine, "Restarting state machine at state %d\n", nextState);
+ restartStateMachine();
+}
+
+CopyEngine *
+CopyEngineParams::create()
+{
+ return new CopyEngine(this);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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 Acceleration Technology (I/OAT).
+ * A DMA asyncronous copy engine
+ */
+
+#ifndef __DEV_PCI_COPY_ENGINE_HH__
+#define __DEV_PCI_COPY_ENGINE_HH__
+
+#include <vector>
+
+#include "base/cp_annotate.hh"
+#include "base/statistics.hh"
+#include "dev/pci/copy_engine_defs.hh"
+#include "dev/pci/device.hh"
+#include "params/CopyEngine.hh"
+#include "sim/drain.hh"
+#include "sim/eventq.hh"
+
+class CopyEngine : public PciDevice
+{
+ class CopyEngineChannel : public Drainable, public Serializable
+ {
+ private:
+ DmaPort cePort;
+ CopyEngine *ce;
+ CopyEngineReg::ChanRegs cr;
+ int channelId;
+ CopyEngineReg::DmaDesc *curDmaDesc;
+ uint8_t *copyBuffer;
+
+ bool busy;
+ bool underReset;
+ bool refreshNext;
+ Addr lastDescriptorAddr;
+ Addr fetchAddress;
+
+ Tick latBeforeBegin;
+ Tick latAfterCompletion;
+
+ uint64_t completionDataReg;
+
+ enum ChannelState {
+ Idle,
+ AddressFetch,
+ DescriptorFetch,
+ DMARead,
+ DMAWrite,
+ CompletionWrite
+ };
+
+ ChannelState nextState;
+
+ public:
+ CopyEngineChannel(CopyEngine *_ce, int cid);
+ virtual ~CopyEngineChannel();
+ BaseMasterPort &getMasterPort();
+
+ std::string name() { assert(ce); return ce->name() + csprintf("-chan%d", channelId); }
+ virtual Tick read(PacketPtr pkt)
+ { panic("CopyEngineChannel has no I/O access\n");}
+ virtual Tick write(PacketPtr pkt)
+ { panic("CopyEngineChannel has no I/O access\n"); }
+
+ void channelRead(PacketPtr pkt, Addr daddr, int size);
+ void channelWrite(PacketPtr pkt, Addr daddr, int size);
+
+ DrainState drain() override;
+ void drainResume() override;
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+
+ private:
+ void fetchDescriptor(Addr address);
+ void fetchDescComplete();
+ EventWrapper<CopyEngineChannel, &CopyEngineChannel::fetchDescComplete>
+ fetchCompleteEvent;
+
+ void fetchNextAddr(Addr address);
+ void fetchAddrComplete();
+ EventWrapper<CopyEngineChannel, &CopyEngineChannel::fetchAddrComplete>
+ addrCompleteEvent;
+
+ void readCopyBytes();
+ void readCopyBytesComplete();
+ EventWrapper<CopyEngineChannel, &CopyEngineChannel::readCopyBytesComplete>
+ readCompleteEvent;
+
+ void writeCopyBytes();
+ void writeCopyBytesComplete();
+ EventWrapper <CopyEngineChannel, &CopyEngineChannel::writeCopyBytesComplete>
+ writeCompleteEvent;
+
+ void writeCompletionStatus();
+ void writeStatusComplete();
+ EventWrapper <CopyEngineChannel, &CopyEngineChannel::writeStatusComplete>
+ statusCompleteEvent;
+
+
+ void continueProcessing();
+ void recvCommand();
+ bool inDrain();
+ void restartStateMachine();
+ inline void anBegin(const char *s)
+ {
+ CPA::cpa()->hwBegin(CPA::FL_NONE, ce->sys,
+ channelId, "CopyEngine", s);
+ }
+
+ inline void anWait()
+ {
+ CPA::cpa()->hwWe(CPA::FL_NONE, ce->sys,
+ channelId, "CopyEngine", "DMAUnusedDescQ", channelId);
+ }
+
+ inline void anDq()
+ {
+ CPA::cpa()->hwDq(CPA::FL_NONE, ce->sys,
+ channelId, "CopyEngine", "DMAUnusedDescQ", channelId);
+ }
+
+ inline void anPq()
+ {
+ CPA::cpa()->hwDq(CPA::FL_NONE, ce->sys,
+ channelId, "CopyEngine", "DMAUnusedDescQ", channelId);
+ }
+
+ inline void anQ(const char * s, uint64_t id, int size = 1)
+ {
+ CPA::cpa()->hwQ(CPA::FL_NONE, ce->sys, channelId,
+ "CopyEngine", s, id, NULL, size);
+ }
+
+ };
+
+ private:
+
+ Stats::Vector bytesCopied;
+ Stats::Vector copiesProcessed;
+
+ // device registers
+ CopyEngineReg::Regs regs;
+
+ // Array of channels each one with regs/dma port/etc
+ std::vector<CopyEngineChannel*> chan;
+
+ public:
+ typedef CopyEngineParams Params;
+ const Params *
+ params() const
+ {
+ return dynamic_cast<const Params *>(_params);
+ }
+ CopyEngine(const Params *params);
+ ~CopyEngine();
+
+ void regStats() override;
+
+ BaseMasterPort &getMasterPort(const std::string &if_name,
+ PortID idx = InvalidPortID) override;
+
+ Tick read(PacketPtr pkt) override;
+ Tick write(PacketPtr pkt) override;
+
+ void serialize(CheckpointOut &cp) const override;
+ void unserialize(CheckpointIn &cp) override;
+};
+
+#endif //__DEV_PCI_COPY_ENGINE_HH__
+
--- /dev/null
+/*
+ * 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
+ * Register and structure descriptions for Intel's I/O AT DMA Engine
+ */
+#include "base/bitfield.hh"
+#include "sim/serialize.hh"
+
+namespace CopyEngineReg {
+
+
+// General Channel independant registers, 128 bytes starting at 0x00
+const uint32_t GEN_CHANCOUNT = 0x00;
+const uint32_t GEN_XFERCAP = 0x01;
+const uint32_t GEN_INTRCTRL = 0x03;
+const uint32_t GEN_ATTNSTATUS = 0x04;
+
+
+// Channel specific registers, each block is 128 bytes, starting at 0x80
+const uint32_t CHAN_CONTROL = 0x00;
+const uint32_t CHAN_STATUS = 0x04;
+const uint32_t CHAN_CHAINADDR = 0x0C;
+const uint32_t CHAN_CHAINADDR_LOW = 0x0C;
+const uint32_t CHAN_CHAINADDR_HIGH = 0x10;
+const uint32_t CHAN_COMMAND = 0x14;
+const uint32_t CHAN_CMPLNADDR = 0x18;
+const uint32_t CHAN_CMPLNADDR_LOW = 0x18;
+const uint32_t CHAN_CMPLNADDR_HIGH = 0x1C;
+const uint32_t CHAN_ERROR = 0x28;
+
+
+const uint32_t DESC_CTRL_INT_GEN = 0x00000001;
+const uint32_t DESC_CTRL_SRC_SN = 0x00000002;
+const uint32_t DESC_CTRL_DST_SN = 0x00000004;
+const uint32_t DESC_CTRL_CP_STS = 0x00000008;
+const uint32_t DESC_CTRL_FRAME = 0x00000010;
+const uint32_t DESC_CTRL_NULL = 0x00000020;
+
+struct DmaDesc {
+ uint32_t len;
+ uint32_t command;
+ Addr src;
+ Addr dest;
+ Addr next;
+ uint64_t reserved1;
+ uint64_t reserved2;
+ uint64_t user1;
+ uint64_t user2;
+};
+
+#define ADD_FIELD8(NAME, OFFSET, BITS) \
+ inline uint8_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \
+ inline void NAME(uint8_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); }
+
+#define ADD_FIELD16(NAME, OFFSET, BITS) \
+ inline uint16_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \
+ inline void NAME(uint16_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); }
+
+#define ADD_FIELD32(NAME, OFFSET, BITS) \
+ inline uint32_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \
+ inline void NAME(uint32_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); }
+
+#define ADD_FIELD64(NAME, OFFSET, BITS) \
+ inline uint64_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \
+ inline void NAME(uint64_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); }
+
+template<class T>
+struct Reg {
+ T _data;
+ T operator()() { return _data; }
+ const Reg<T> &operator=(T d) { _data = d; return *this;}
+ bool operator==(T d) { return d == _data; }
+ void operator()(T d) { _data = d; }
+ Reg() { _data = 0; }
+ void serialize(CheckpointOut &cp) const
+ {
+ SERIALIZE_SCALAR(_data);
+ }
+ void unserialize(CheckpointIn &cp)
+ {
+ UNSERIALIZE_SCALAR(_data);
+ }
+};
+
+
+struct Regs : public Serializable {
+ uint8_t chanCount;
+ uint8_t xferCap;
+
+ struct INTRCTRL : public Reg<uint8_t> { // 0x03
+ using Reg<uint8_t>::operator =;
+ ADD_FIELD8(master_int_enable,0,1);
+ ADD_FIELD8(interrupt_status,1,1);
+ ADD_FIELD8(interrupt,2,1);
+ };
+ INTRCTRL intrctrl;
+
+ uint32_t attnStatus; // Read clears
+
+ void serialize(CheckpointOut &cp) const override
+ {
+ SERIALIZE_SCALAR(chanCount);
+ SERIALIZE_SCALAR(xferCap);
+ paramOut(cp, "intrctrl", intrctrl._data);
+ SERIALIZE_SCALAR(attnStatus);
+ }
+
+ void unserialize(CheckpointIn &cp) override
+ {
+ UNSERIALIZE_SCALAR(chanCount);
+ UNSERIALIZE_SCALAR(xferCap);
+ paramIn(cp, "intrctrl", intrctrl._data);
+ UNSERIALIZE_SCALAR(attnStatus);
+ }
+
+};
+
+struct ChanRegs : public Serializable {
+ struct CHANCTRL : public Reg<uint16_t> { // channelX + 0x00
+ using Reg<uint16_t>::operator =;
+ ADD_FIELD16(interrupt_disable,0,1);
+ ADD_FIELD16(error_completion_enable, 2,1);
+ ADD_FIELD16(any_error_abort_enable,3,1);
+ ADD_FIELD16(error_int_enable,4,1);
+ ADD_FIELD16(desc_addr_snoop_control,5,1);
+ ADD_FIELD16(in_use, 8,1);
+ };
+ CHANCTRL ctrl;
+
+ struct CHANSTS : public Reg<uint64_t> { // channelX + 0x04
+ ADD_FIELD64(dma_transfer_status, 0, 3);
+ ADD_FIELD64(unaffiliated_error, 3, 1);
+ ADD_FIELD64(soft_error, 4, 1);
+ ADD_FIELD64(compl_desc_addr, 6, 58);
+ };
+ CHANSTS status;
+
+ uint64_t descChainAddr;
+
+ struct CHANCMD : public Reg<uint8_t> { // channelX + 0x14
+ ADD_FIELD8(start_dma,0,1);
+ ADD_FIELD8(append_dma,1,1);
+ ADD_FIELD8(suspend_dma,2,1);
+ ADD_FIELD8(abort_dma,3,1);
+ ADD_FIELD8(resume_dma,4,1);
+ ADD_FIELD8(reset_dma,5,1);
+ };
+ CHANCMD command;
+
+ uint64_t completionAddr;
+
+ struct CHANERR : public Reg<uint32_t> { // channel X + 0x28
+ ADD_FIELD32(source_addr_error,0,1);
+ ADD_FIELD32(dest_addr_error,1,1);
+ ADD_FIELD32(ndesc_addr_error,2,1);
+ ADD_FIELD32(desc_error,3,1);
+ ADD_FIELD32(chain_addr_error,4,1);
+ ADD_FIELD32(chain_cmd_error,5,1);
+ ADD_FIELD32(chipset_parity_error,6,1);
+ ADD_FIELD32(dma_parity_error,7,1);
+ ADD_FIELD32(read_data_error,8,1);
+ ADD_FIELD32(write_data_error,9,1);
+ ADD_FIELD32(desc_control_error,10,1);
+ ADD_FIELD32(desc_len_error,11,1);
+ ADD_FIELD32(completion_addr_error,12,1);
+ ADD_FIELD32(interrupt_config_error,13,1);
+ ADD_FIELD32(soft_error,14,1);
+ ADD_FIELD32(unaffiliated_error,15,1);
+ };
+ CHANERR error;
+
+ void serialize(CheckpointOut &cp) const override
+ {
+ paramOut(cp, "ctrl", ctrl._data);
+ paramOut(cp, "status", status._data);
+ SERIALIZE_SCALAR(descChainAddr);
+ paramOut(cp, "command", command._data);
+ SERIALIZE_SCALAR(completionAddr);
+ paramOut(cp, "error", error._data);
+ }
+
+ void unserialize(CheckpointIn &cp) override
+ {
+ paramIn(cp, "ctrl", ctrl._data);
+ paramIn(cp, "status", status._data);
+ UNSERIALIZE_SCALAR(descChainAddr);
+ paramIn(cp, "command", command._data);
+ UNSERIALIZE_SCALAR(completionAddr);
+ paramIn(cp, "error", error._data);
+ }
+
+
+};
+
+} // namespace CopyEngineReg
+
+