From: Andreas Sandberg Date: Thu, 10 Dec 2015 10:35:16 +0000 (+0000) Subject: dev: Move the CopyEngine class to src/dev/pci X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=38e2860c36997858131dc47e1152bb583f68608e;p=gem5.git dev: Move the CopyEngine class to src/dev/pci --HG-- rename : src/dev/CopyEngine.py => src/dev/pci/CopyEngine.py rename : src/dev/copy_engine.cc => src/dev/pci/copy_engine.cc rename : src/dev/copy_engine.hh => src/dev/pci/copy_engine.hh rename : src/dev/copy_engine_defs.hh => src/dev/pci/copy_engine_defs.hh --- diff --git a/src/dev/CopyEngine.py b/src/dev/CopyEngine.py deleted file mode 100644 index 2db9746d1..000000000 --- a/src/dev/CopyEngine.py +++ /dev/null @@ -1,59 +0,0 @@ -# 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") - - diff --git a/src/dev/SConscript b/src/dev/SConscript index 923417078..53bb28456 100644 --- a/src/dev/SConscript +++ b/src/dev/SConscript @@ -40,7 +40,6 @@ if env['TARGET_ISA'] == 'null': Return() SimObject('BadDevice.py') -SimObject('CopyEngine.py') SimObject('DiskImage.py') SimObject('Ethernet.py') SimObject('I2C.py') @@ -51,7 +50,6 @@ SimObject('Terminal.py') SimObject('Uart.py') Source('baddev.cc') -Source('copy_engine.cc') Source('disk_image.cc') Source('dma_device.cc') Source('etherbus.cc') @@ -85,7 +83,6 @@ Source('uart8250.cc') DebugFlag('DiskImageRead') DebugFlag('DiskImageWrite') DebugFlag('DMA') -DebugFlag('DMACopyEngine') DebugFlag('Ethernet') DebugFlag('MultiEthernet') DebugFlag('MultiEthernetPkt') diff --git a/src/dev/copy_engine.cc b/src/dev/copy_engine.cc deleted file mode 100644 index dfeaf1218..000000000 --- a/src/dev/copy_engine.cc +++ /dev/null @@ -1,748 +0,0 @@ -/* - * 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 - -#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(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(regs.chanCount); - break; - case GEN_XFERCAP: - assert(size == sizeof(regs.xferCap)); - pkt->set(regs.xferCap); - break; - case GEN_INTRCTRL: - assert(size == sizeof(uint8_t)); - pkt->set(regs.intrctrl()); - regs.intrctrl.master_int_enable(0); - break; - case GEN_ATTNSTATUS: - assert(size == sizeof(regs.attnStatus)); - pkt->set(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(cr.ctrl()); - cr.ctrl.in_use(1); - break; - case CHAN_STATUS: - assert(size == sizeof(uint64_t)); - pkt->set(cr.status() | ~busy); - break; - case CHAN_CHAINADDR: - assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); - if (size == sizeof(uint64_t)) - pkt->set(cr.descChainAddr); - else - pkt->set(bits(cr.descChainAddr,0,31)); - break; - case CHAN_CHAINADDR_HIGH: - assert(size == sizeof(uint32_t)); - pkt->set(bits(cr.descChainAddr,32,63)); - break; - case CHAN_COMMAND: - assert(size == sizeof(uint8_t)); - pkt->set(cr.command()); - break; - case CHAN_CMPLNADDR: - assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); - if (size == sizeof(uint64_t)) - pkt->set(cr.completionAddr); - else - pkt->set(bits(cr.completionAddr,0,31)); - break; - case CHAN_CMPLNADDR_HIGH: - assert(size == sizeof(uint32_t)); - pkt->set(bits(cr.completionAddr,32,63)); - break; - case CHAN_ERROR: - assert(size == sizeof(uint32_t)); - pkt->set(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(); - 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(); - 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(); - 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(); - 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(),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()); - 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(); - else - cr.descChainAddr = (uint64_t)pkt->get() | - (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() <<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()); - recvCommand(); - break; - case CHAN_CMPLNADDR: - assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); - if (size == sizeof(uint64_t)) - cr.completionAddr = pkt->get(); - else - cr.completionAddr = pkt->get() | - (cr.completionAddr & ~mask(32)); - break; - case CHAN_CMPLNADDR_HIGH: - assert(size == sizeof(uint32_t)); - cr.completionAddr = ((uint64_t)pkt->get() <<32) | - (cr.completionAddr & mask(32)); - break; - case CHAN_ERROR: - assert(size == sizeof(uint32_t)); - cr.error(~pkt->get() & 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); -} diff --git a/src/dev/copy_engine.hh b/src/dev/copy_engine.hh deleted file mode 100644 index 754ea5e15..000000000 --- a/src/dev/copy_engine.hh +++ /dev/null @@ -1,212 +0,0 @@ -/* - * 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 - -#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 - fetchCompleteEvent; - - void fetchNextAddr(Addr address); - void fetchAddrComplete(); - EventWrapper - addrCompleteEvent; - - void readCopyBytes(); - void readCopyBytesComplete(); - EventWrapper - readCompleteEvent; - - void writeCopyBytes(); - void writeCopyBytesComplete(); - EventWrapper - writeCompleteEvent; - - void writeCompletionStatus(); - void writeStatusComplete(); - EventWrapper - 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 chan; - - public: - typedef CopyEngineParams Params; - const Params * - params() const - { - return dynamic_cast(_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__ - diff --git a/src/dev/copy_engine_defs.hh b/src/dev/copy_engine_defs.hh deleted file mode 100644 index 9a88802ed..000000000 --- a/src/dev/copy_engine_defs.hh +++ /dev/null @@ -1,225 +0,0 @@ -/* - * 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 -struct Reg { - T _data; - T operator()() { return _data; } - const Reg &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 { // 0x03 - using Reg::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 { // channelX + 0x00 - using Reg::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 { // 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 { // 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 { // 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 - - diff --git a/src/dev/pci/CopyEngine.py b/src/dev/pci/CopyEngine.py new file mode 100644 index 000000000..f1b9df1b9 --- /dev/null +++ b/src/dev/pci/CopyEngine.py @@ -0,0 +1,59 @@ +# 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") + + diff --git a/src/dev/pci/SConscript b/src/dev/pci/SConscript index 14214424b..2c48403f8 100644 --- a/src/dev/pci/SConscript +++ b/src/dev/pci/SConscript @@ -55,3 +55,6 @@ SimObject('PciHost.py') Source('host.cc') DebugFlag('PciHost') +SimObject('CopyEngine.py') +Source('copy_engine.cc') +DebugFlag('DMACopyEngine') diff --git a/src/dev/pci/copy_engine.cc b/src/dev/pci/copy_engine.cc new file mode 100644 index 000000000..fba96a110 --- /dev/null +++ b/src/dev/pci/copy_engine.cc @@ -0,0 +1,749 @@ +/* + * 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 + +#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(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(regs.chanCount); + break; + case GEN_XFERCAP: + assert(size == sizeof(regs.xferCap)); + pkt->set(regs.xferCap); + break; + case GEN_INTRCTRL: + assert(size == sizeof(uint8_t)); + pkt->set(regs.intrctrl()); + regs.intrctrl.master_int_enable(0); + break; + case GEN_ATTNSTATUS: + assert(size == sizeof(regs.attnStatus)); + pkt->set(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(cr.ctrl()); + cr.ctrl.in_use(1); + break; + case CHAN_STATUS: + assert(size == sizeof(uint64_t)); + pkt->set(cr.status() | ~busy); + break; + case CHAN_CHAINADDR: + assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); + if (size == sizeof(uint64_t)) + pkt->set(cr.descChainAddr); + else + pkt->set(bits(cr.descChainAddr,0,31)); + break; + case CHAN_CHAINADDR_HIGH: + assert(size == sizeof(uint32_t)); + pkt->set(bits(cr.descChainAddr,32,63)); + break; + case CHAN_COMMAND: + assert(size == sizeof(uint8_t)); + pkt->set(cr.command()); + break; + case CHAN_CMPLNADDR: + assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); + if (size == sizeof(uint64_t)) + pkt->set(cr.completionAddr); + else + pkt->set(bits(cr.completionAddr,0,31)); + break; + case CHAN_CMPLNADDR_HIGH: + assert(size == sizeof(uint32_t)); + pkt->set(bits(cr.completionAddr,32,63)); + break; + case CHAN_ERROR: + assert(size == sizeof(uint32_t)); + pkt->set(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(); + 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(); + 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(); + 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(); + 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(),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()); + 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(); + else + cr.descChainAddr = (uint64_t)pkt->get() | + (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() <<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()); + recvCommand(); + break; + case CHAN_CMPLNADDR: + assert(size == sizeof(uint64_t) || size == sizeof(uint32_t)); + if (size == sizeof(uint64_t)) + cr.completionAddr = pkt->get(); + else + cr.completionAddr = pkt->get() | + (cr.completionAddr & ~mask(32)); + break; + case CHAN_CMPLNADDR_HIGH: + assert(size == sizeof(uint32_t)); + cr.completionAddr = ((uint64_t)pkt->get() <<32) | + (cr.completionAddr & mask(32)); + break; + case CHAN_ERROR: + assert(size == sizeof(uint32_t)); + cr.error(~pkt->get() & 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); +} diff --git a/src/dev/pci/copy_engine.hh b/src/dev/pci/copy_engine.hh new file mode 100644 index 000000000..f548c478b --- /dev/null +++ b/src/dev/pci/copy_engine.hh @@ -0,0 +1,212 @@ +/* + * 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 + +#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 + fetchCompleteEvent; + + void fetchNextAddr(Addr address); + void fetchAddrComplete(); + EventWrapper + addrCompleteEvent; + + void readCopyBytes(); + void readCopyBytesComplete(); + EventWrapper + readCompleteEvent; + + void writeCopyBytes(); + void writeCopyBytesComplete(); + EventWrapper + writeCompleteEvent; + + void writeCompletionStatus(); + void writeStatusComplete(); + EventWrapper + 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 chan; + + public: + typedef CopyEngineParams Params; + const Params * + params() const + { + return dynamic_cast(_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__ + diff --git a/src/dev/pci/copy_engine_defs.hh b/src/dev/pci/copy_engine_defs.hh new file mode 100644 index 000000000..9a88802ed --- /dev/null +++ b/src/dev/pci/copy_engine_defs.hh @@ -0,0 +1,225 @@ +/* + * 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 +struct Reg { + T _data; + T operator()() { return _data; } + const Reg &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 { // 0x03 + using Reg::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 { // channelX + 0x00 + using Reg::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 { // 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 { // 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 { // 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 + +