diff options
author | Lisa Hsu <hsul@eecs.umich.edu> | 2004-03-12 11:04:58 -0500 |
---|---|---|
committer | Lisa Hsu <hsul@eecs.umich.edu> | 2004-03-12 11:04:58 -0500 |
commit | 48e6ed48e0672b7b5f5fa65d8fec450483a31676 (patch) | |
tree | e00dec409f9d3387db5bae0e48895a9baf0ff9d6 /dev/ns_gige.cc | |
parent | c82113d02296c40e1a2eac84ba55ae7ffbcf4419 (diff) | |
download | gem5-48e6ed48e0672b7b5f5fa65d8fec450483a31676.tar.xz |
first pass at ns_ethernet device. more will come later as i merge in nate's new ether infrastructure.
dev/etherpkt.hh:
add some stuff for support of the NS ethernet device.
--HG--
extra : convert_revision : 51f6508463b6394055e3428a42b7de490a9ae6c1
Diffstat (limited to 'dev/ns_gige.cc')
-rw-r--r-- | dev/ns_gige.cc | 1933 |
1 files changed, 1933 insertions, 0 deletions
diff --git a/dev/ns_gige.cc b/dev/ns_gige.cc new file mode 100644 index 000000000..e3c31597e --- /dev/null +++ b/dev/ns_gige.cc @@ -0,0 +1,1933 @@ +/* + * Copyright (c) 2003 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. + */ + +/* @file + * Device module for modelling the National Semiconductor + * DP83820 ethernet controller. Does not support priority queueing + */ +#include <cstdio> +#include <deque> +#include <string> + +#include "base/inet.hh" +#include "cpu/exec_context.hh" +#include "cpu/intr_control.hh" +#include "dev/dma.hh" +#include "dev/ns_gige.hh" +#include "dev/etherlink.hh" +#include "mem/functional_mem/memory_control.hh" +#include "mem/functional_mem/physical_memory.hh" +#include "sim/builder.hh" +#include "sim/host.hh" +#include "sim/sim_stats.hh" +#include "targetarch/vtophys.hh" + +using namespace std; + +/////////////////////////////////////////////////////////////////////// +// +// EtherDev PCI Device +// +EtherDev::EtherDev(const string &_name, DmaEngine *de, bool use_interface, + IntrControl *i, MemoryController *mmu, PhysicalMemory *pmem, + PCIConfigAll *cf, PciConfigData *cd, Tsunami *t, uint32_t bus, + uint32_t dev, uint32_t func, bool rx_filter, + const int eaddr[6], Tick tx_delay, Tick rx_delay, Addr addr, + Addr mask) + : PciDev(_name, mmu, cf, cd, bus, dev, func), tsunami(t), + addr(addr), mask(mask), txPacketLen(0), + txPacketBufPtr(NULL), rxPacketBufPtr(NULL), rxDescBufPtr(NULL), + fragLen(0), rxCopied(0), txState(txIdle), CTDD(false), txFifoCnt(0), + txFifoAvail(MAX_TX_FIFO_SIZE), txHalt(false), txPacketFlag(false), + txFragPtr(0), txDescCnt(0), rxState(rxIdle), CRDD(false), + rxPktBytes(0), rxFifoCnt(0), rxHalt(false), rxPacketFlag(false), + rxFragPtr(0), rxDescCnt(0), extstsEnable(false), maxTxBurst(0), + maxRxBurst(0), physmem(pmem), + rxDescDoneCB(this), rxDoneCB(this), txDescDoneCB(this), txDoneCB(this), + dma(de), readRequest(use_interface), writeRequest(use_interface), + readDescRequest(use_interface), writeDescRequest(use_interface), + interface(NULL), intctrl(i), txDelay(tx_delay), rxDelay(rx_delay), + txEvent(this), cpuPendingIntr(false), rxFilterEnable(rx_filter), + acceptBroadcast(false), acceptMulticast(false), acceptUnicast(false), + acceptPerfect(false), acceptArp(false) +{ + tsunami->ethernet = this; + + memset(®s, 0, sizeof(regs)); + regsReset(); + regs.perfectMatch[0] = eaddr[0]; + regs.perfectMatch[1] = eaddr[1]; + regs.perfectMatch[2] = eaddr[2]; + regs.perfectMatch[3] = eaddr[3]; + regs.perfectMatch[4] = eaddr[4]; + regs.perfectMatch[5] = eaddr[5]; + +} + +EtherDev::~EtherDev() +{} + +void +EtherDev::regStats() +{ + txBytes + .name(name() + ".txBytes") + .desc("Bytes Transmitted") + .prereq(txBytes) + ; + + rxBytes + .name(name() + ".rxBytes") + .desc("Bytes Received") + .prereq(rxBytes) + ; + + txPackets + .name(name() + ".txPackets") + .desc("Number of Packets Transmitted") + .prereq(txBytes) + ; + + rxPackets + .name(name() + ".rxPackets") + .desc("Number of Packets Received") + .prereq(rxBytes) + ; + + txBandwidth + .name(name() + ".txBandwidth") + .desc("Transmit Bandwidth (bits/s)") + .precision(0) + .prereq(txBytes) + ; + + rxBandwidth + .name(name() + ".rxBandwidth") + .desc("Receive Bandwidth (bits/s)") + .precision(0) + .prereq(rxBytes) + ; + + txPacketRate + .name(name() + ".txPPS") + .desc("Packet Tranmission Rate (packets/s)") + .precision(0) + .prereq(txBytes) + ; + + rxPacketRate + .name(name() + ".rxPPS") + .desc("Packet Reception Rate (packets/s)") + .precision(0) + .prereq(rxBytes) + ; + + txBandwidth = txBytes * Statistics::constant(8) / simSeconds; + rxBandwidth = rxBytes * Statistics::constant(8) / simSeconds; + txPacketRate = txPackets / simSeconds; + rxPacketRate = rxPackets / simSeconds; +} + +void +EtherDev::ReadConfig(int offset, int size, uint8_t *data) +{ + if (offset < PCI_DEVICE_SPECIFIC) + PciDev::ReadConfig(offset, size, data); + else { + panic("need to do this\n"); + } +} + +void +EtherDev::WriteConfig(int offset, int size, uint32_t data) +{ + if (offset < PCI_DEVICE_SPECIFIC) + PciDev::WriteConfig(offset, size, data); + else + panic("Need to do that\n"); +} + +Fault +EtherDev::read(MemReqPtr req, uint8_t *data) +{ + DPRINTF(Ethernet, "read va=%#x size=%d\n", req->vaddr, req->size); + + Addr daddr = req->paddr - addr; + + if (daddr > LAST) + panic("Accessing reserved register"); + + switch (req->size) { + case sizeof(uint32_t): + { + uint32_t ® = *(uint32_t *)data; + + switch (daddr) { + case CR: + reg = regs.command; + reg &= ~(CR_RXD | CR_TXD | CR_TXR | CR_RXR); + break; + + case CFG: + reg = regs.config; + break; + + case MEAR: + reg = regs.mear; + break; + + case PTSCR: + reg = regs.ptscr; + break; + + case ISR: + reg = regs.isr; + regs.isr = 0; + break; + + case IMR: + reg = regs.imr; + break; + + case IER: + reg = regs.ier; + break; + + case IHR: + reg = regs.ihr; + break; + + case TXDP: + reg = regs.txdp; + break; + + case TXDP_HI: + reg = regs.txdp_hi; + break; + + case TXCFG: + reg = regs.txcfg; + break; + + case GPIOR: + reg = regs.gpior; + break; + + case RXDP: + reg = regs.rxdp; + break; + + case RXDP_HI: + reg = regs.rxdp_hi; + break; + + case RXCFG: + reg = regs.rxcfg; + break; + + case PQCR: + reg = regs.pqcr; + break; + + case WCSR: + reg = regs.wcsr; + break; + + case PCR: + reg = regs.pcr; + break; + + case RFCR: + reg = regs.rfcr; + break; + + case RFDR: + + switch (regs.rfcr & RFCR_RFADDR) { + case 0x000: + reg = regs.perfectMatch[1] << 8; + reg += regs.perfectMatch[0]; + break; + case 0x002: + reg = regs.perfectMatch[3] << 8; + reg += regs.perfectMatch[2]; + break; + case 0x004: + reg = regs.perfectMatch[5] << 8; + reg += regs.perfectMatch[4]; + break; + default: + panic("reading from RFDR for something for other than PMATCH!\n"); + //didn't implement other RFDR functionality b/c driver didn't use + } + break; + + case SRR: + reg = regs.srr; + break; + + case MIBC: + reg = regs.mibc; + reg &= ~(MIBC_MIBS | MIBC_ACLR); + break; + + case VRCR: + reg = regs.vrcr; + break; + + case VTCR: + reg = regs.vtcr; + break; + + case VDR: + reg = regs.vdr; + break; + + case CCSR: + reg = regs.ccsr; + break; + + case TBICR: + reg = regs.tbicr; + break; + + case TBISR: + reg = regs.tbisr; + break; + + case TANAR: + reg = regs.tanar; + break; + + case TANLPAR: + reg = regs.tanlpar; + break; + + case TANER: + reg = regs.taner; + break; + + case TESR: + reg = regs.tesr; + break; + + default: + panic("reading unimplemented register: addr = %#x", daddr); + } + + DPRINTF(Ethernet, "read from %#x: data=%d data=%#x\n", daddr, reg, reg); + } + break; + + default: + panic("accessing register with invalid size: addr=%#x, size=%d", + daddr, req->size); + } + + return No_Fault; +} + +Fault +EtherDev::write(MemReqPtr req, const uint8_t *data) +{ + DPRINTF(Ethernet, "write va=%#x size=%d\n", req->vaddr, req->size); + + Addr daddr = req->paddr - addr; + + if (daddr > LAST && daddr <= RESERVED) + panic("Accessing reserved register"); + + if (daddr > RESERVED) + panic("higher memory accesses not implemented!\n"); + + if (req->size == sizeof(uint32_t)) { + uint32_t reg = *(uint32_t *)data; + DPRINTF(Ethernet, "write data=%d data=%#x\n", reg, reg); + + switch (daddr) { + case CR: + regs.command = reg; + if ((reg & (CR_TXE | CR_TXD)) == (CR_TXE | CR_TXD)) { + txHalt = true; + } else if (reg & CR_TXE) { + if (txState == txIdle) + txKick(); + } else if (reg & CR_TXD) { + txHalt = true; + } + + if ((reg & (CR_RXE | CR_RXD)) == (CR_RXE | CR_RXD)) { + rxHalt = true; + } else if (reg & CR_RXE) { + if (rxState == rxIdle) { + rxKick(); + } + } else if (reg & CR_RXD) { + rxHalt = true; + } + + if (reg & CR_TXR) + txReset(); + + if (reg & CR_RXR) + rxReset(); + + if (reg & CR_SWI) + devIntrPost(ISR_SWI); + + if (reg & CR_RST) { + txReset(); + rxReset(); + regsReset(); + } + break; + + case CFG: + regs.config = reg; + if (reg & CFG_LNKSTS || reg & CFG_SPDSTS || reg & CFG_DUPSTS + || reg & CFG_RESERVED || reg & CFG_T64ADDR + || reg & CFG_PCI64_DET) + panic("writing to read-only or reserved CFG bits!\n"); + +#if 0 + if (reg & CFG_TBI_EN) ; + if (reg & CFG_MODE_1000) ; +#endif + + if (reg & CFG_AUTO_1000) + panic("CFG_AUTO_1000 not implemented!\n"); + +#if 0 + if (reg & CFG_PINT_DUPSTS || reg & CFG_PINT_LNKSTS || reg & CFG_PINT_SPDSTS) ; + if (reg & CFG_TMRTEST) ; + if (reg & CFG_MRM_DIS) ; + if (reg & CFG_MWI_DIS) ; +#endif + + if (reg & CFG_T64ADDR) + panic("CFG_T64ADDR is read only register!\n"); + + if (reg & CFG_PCI64_DET) + panic("CFG_PCI64_DET is read only register!\n"); + +#if 0 + if (reg & CFG_DATA64_EN) ; + if (reg & CFG_M64ADDR) ; + if (reg & CFG_PHY_RST) ; + if (reg & CFG_PHY_DIS) ; +#endif + + if (reg & CFG_EXTSTS_EN) + extstsEnable = true; + else + extstsEnable = false; + +#if 0 + if (reg & CFG_REQALG) ; + if (reg & CFG_SB) ; + if (reg & CFG_POW) ; + if (reg & CFG_EXD) ; + if (reg & CFG_PESEL) ; + if (reg & CFG_BROM_DIS) ; + if (reg & CFG_EXT_125) ; + if (reg & CFG_BEM) ; +#endif + break; + + case MEAR: + regs.mear = reg; + /* since phy is completely faked, MEAR_MD* don't matter + and since the driver never uses MEAR_EE*, they don't matter */ +#if 0 + if (reg & MEAR_EEDI) ; + if (reg & MEAR_EEDO) ; //this one is read only + if (reg & MEAR_EECLK) ; + if (reg & MEAR_EESEL) ; + if (reg & MEAR_MDIO) ; + if (reg & MEAR_MDDIR) ; + if (reg & MEAR_MDC) ; +#endif + break; + + case PTSCR: + regs.ptscr = reg; + /* these control BISTs for various parts of chip - we don't care or do */ + break; + + case ISR: /* writing to the ISR has no effect */ + panic("ISR is a read only register!\n"); + + case IMR: + regs.imr = reg; + devIntrChangeMask(); + break; + + case IER: + regs.ier = reg; + break; + + case IHR: + regs.ihr = reg; + /* not going to implement real interrupt holdoff */ + break; + + case TXDP: + regs.txdp = (reg & 0xFFFFFFFC); + assert(txState == txIdle); + CTDD = false; + break; + + case TXDP_HI: + regs.txdp_hi = reg; + break; + + case TXCFG: + regs.txcfg = reg; +#if 0 + if (reg & TXCFG_CSI) ; + if (reg & TXCFG_HBI) ; + if (reg & TXCFG_MLB) ; + if (reg & TXCFG_ATP) ; + if (reg & TXCFG_ECRETRY) ; /* this could easily be implemented, but + considering the network is just a fake + pipe, wouldn't make sense to do this */ + + if (reg & TXCFG_BRST_DIS) ; +#endif + +#if 0 /* current 2.6 driver doesn't use these. if we upgrade, may need these */ + if (reg & TXCFG_MXDMA1024) + maxTxBurst = 1024; + + if (reg & TXCFG_MXDMA8) + maxTxBurst = 8; + + if (reg & TXCFG_MXDMA16) + maxTxBurst = 16; + + if (reg & TXCFG_MXDMA32) + maxTxBurst = 32; + + if (reg & TXCFG_MXDMA64) + maxTxBurst = 64; + + if (reg & TXCFG_MXDMA128) + maxTxBurst = 128; + + if (reg & TXCFG_MXDMA256) + maxTxBurst = 256; +#endif + + if (reg & TXCFG_MXDMA512) + maxTxBurst = 512; + + break; + + case GPIOR: + regs.gpior = reg; + /* these just control general purpose i/o pins, don't matter */ + break; + + case RXCFG: + regs.rxcfg = reg; +#if 0 + if (reg & RXCFG_AEP) ; + if (reg & RXCFG_ARP) ; + if (reg & RXCFG_STRIPCRC) ; + if (reg & RXCFG_RX_RD) ; + if (reg & RXCFG_ALP) ; + if (reg & RXCFG_AIRL) ; +#endif + + if (reg & RXCFG_MXDMA512) + maxRxBurst = 512; + +#if 0 + if (reg & (RXCFG_DRTH | RXCFG_DRTH0)) ; +#endif + break; + + case PQCR: + /* there is no priority queueing used in the linux 2.6 driver */ + regs.pqcr = reg; + break; + + case WCSR: + /* not going to implement wake on LAN */ + regs.wcsr = reg; + break; + + case PCR: + /* not going to implement pause control */ + regs.pcr = reg; + break; + + case RFCR: + regs.rfcr = reg; + rxFilterEnable = (reg & RFCR_RFEN) ? true : false; + + acceptBroadcast = (reg & RFCR_AAB) ? true : false; + + acceptMulticast = (reg & RFCR_AAM) ? true : false; + + acceptUnicast = (reg & RFCR_AAU) ? true : false; + + acceptPerfect = (reg & RFCR_APM) ? true : false; + + acceptArp = (reg & RFCR_AARP) ? true : false; + + if (reg & RFCR_APAT) + panic("RFCR_APAT not implemented!\n"); + + if (reg & RFCR_MHEN || reg & RFCR_UHEN) + panic("hash filtering not implemented!\n"); + + if (reg & RFCR_ULM) + panic("RFCR_ULM not implemented!\n"); + + break; + + case RFDR: + panic("the driver never writes to RFDR, something is wrong!\n"); + + case BRAR: + panic("the driver never uses BRAR, something is wrong!\n"); + + case BRDR: + panic("the driver never uses BRDR, something is wrong!\n"); + + case SRR: + panic("SRR is read only register!\n"); + + case MIBC: + panic("the driver never uses MIBC, something is wrong!\n"); + + case VRCR: + regs.vrcr = reg; + break; + + case VTCR: + regs.vtcr = reg; + break; + + case VDR: + panic("the driver never uses VDR, something is wrong!\n"); + break; + + case CCSR: + /* not going to implement clockrun stuff */ + regs.ccsr = reg; + break; + + case TBICR: + regs.tbicr = reg; + if (reg & TBICR_MR_LOOPBACK) + panic("TBICR_MR_LOOPBACK never used, something wrong!\n"); + + if (reg & TBICR_MR_AN_ENABLE) { + regs.tanlpar = regs.tanar; + regs.tbisr |= (TBISR_MR_AN_COMPLETE | TBISR_MR_LINK_STATUS); + } + +#if 0 + if (reg & TBICR_MR_RESTART_AN) ; +#endif + + break; + + case TBISR: + panic("TBISR is read only register!\n"); + + case TANAR: + regs.tanar = reg; + if (reg & TANAR_PS2) + panic("this isn't used in driver, something wrong!\n"); + + if (reg & TANAR_PS1) + panic("this isn't used in driver, something wrong!\n"); + break; + + case TANLPAR: + panic("this should only be written to by the fake phy!\n"); + + case TANER: + panic("TANER is read only register!\n"); + + case TESR: + regs.tesr = reg; + break; + + default: + panic("thought i covered all the register, what is this? addr=%#x", + daddr); + } + } else + panic("Invalid Request Size"); + + return No_Fault; +} + +void +EtherDev::devIntrPost(uint32_t interrupts) +{ +DPRINTF(Ethernet, "interrupt posted intr=%x isr=%x imr=%x\n", + interrupts, regs.isr, regs.imr); + +if (interrupts & ISR_RESERVE) + panic("Cannot set a reserved interrupt"); + +if (interrupts & ISR_TXRCMP) + regs.isr |= ISR_TXRCMP; + +if (interrupts & ISR_RXRCMP) + regs.isr |= ISR_RXRCMP; + +//ISR_DPERR not implemented +//ISR_SSERR not implemented +//ISR_RMABT not implemented +//ISR_RXSOVR not implemented +//ISR_HIBINT not implemented +//ISR_PHY not implemented +//ISR_PME not implemented + +if (interrupts & ISR_SWI) + regs.isr |= ISR_SWI; + +//ISR_MIB not implemented +//ISR_TXURN not implemented + + if (interrupts & ISR_TXIDLE) + regs.isr |= ISR_TXIDLE; + + if (interrupts & ISR_TXERR) + regs.isr |= ISR_TXERR; + + if (interrupts & ISR_TXDESC) + regs.isr |= ISR_TXDESC; + + if (interrupts & ISR_TXOK) + regs.isr |= ISR_TXOK; + + if (interrupts & ISR_RXORN) + regs.isr |= ISR_RXORN; + + if (interrupts & ISR_RXIDLE) + regs.isr |= ISR_RXIDLE; + +//ISR_RXEARLY not implemented + + if (interrupts & ISR_RXERR) + regs.isr |= ISR_RXERR; + + if (interrupts & ISR_RXOK) + regs.isr |= ISR_RXOK; + + if ((regs.isr & regs.imr)) + cpuIntrPost(); +} + +void +EtherDev::devIntrClear(uint32_t interrupts) +{ + DPRINTF(Ethernet, "interrupt cleared intr=%x isr=%x imr=%x\n", + interrupts, regs.isr, regs.imr); + + if (interrupts & ISR_RESERVE) + panic("Cannot clear a reserved interrupt"); + + if (interrupts & ISR_TXRCMP) + regs.isr &= ~ISR_TXRCMP; + + if (interrupts & ISR_RXRCMP) + regs.isr &= ~ISR_RXRCMP; + +//ISR_DPERR not implemented +//ISR_SSERR not implemented +//ISR_RMABT not implemented +//ISR_RXSOVR not implemented +//ISR_HIBINT not implemented +//ISR_PHY not implemented +//ISR_PME not implemented + + if (interrupts & ISR_SWI) + regs.isr &= ~ISR_SWI; + +//ISR_MIB not implemented +//ISR_TXURN not implemented + + if (interrupts & ISR_TXIDLE) + regs.isr &= ~ISR_TXIDLE; + + if (interrupts & ISR_TXERR) + regs.isr &= ~ISR_TXERR; + + if (interrupts & ISR_TXDESC) + regs.isr &= ~ISR_TXDESC; + + if (interrupts & ISR_TXOK) + regs.isr &= ~ISR_TXOK; + + if (interrupts & ISR_RXORN) + regs.isr &= ~ISR_RXORN; + + if (interrupts & ISR_RXIDLE) + regs.isr &= ~ISR_RXIDLE; + +//ISR_RXEARLY not implemented + + if (interrupts & ISR_RXERR) + regs.isr &= ~ISR_RXERR; + + if (interrupts & ISR_RXOK) + regs.isr &= ~ISR_RXOK; + + if ((regs.isr & regs.imr)) + cpuIntrPost(); + + if (!(regs.isr & regs.imr)) + cpuIntrClear(); +} + +void +EtherDev::devIntrChangeMask() +{ + DPRINTF(Ethernet, "iterrupt mask changed\n"); + + if (regs.isr & regs.imr) + cpuIntrPost(); + else + cpuIntrClear(); +} + +void +EtherDev::cpuIntrPost() +{ + if (!cpuPendingIntr) { + if (regs.ier) { + cpuPendingIntr = true; + intctrl->post(TheISA::INTLEVEL_IRQ1, TheISA::INTINDEX_ETHERNET); + } + } +} + +void +EtherDev::cpuIntrClear() +{ + if (cpuPendingIntr) { + cpuPendingIntr = false; + intctrl->clear(TheISA::INTLEVEL_IRQ1, TheISA::INTINDEX_ETHERNET); + } +} + +bool +EtherDev::cpuIntrPending() const +{ return cpuPendingIntr; } + +void +EtherDev::txReset() +{ + + DPRINTF(Ethernet, "transmit reset\n"); + + txPacketFlag = false; + CTDD = false; + txFifoCnt = 0; + txFifoAvail = 0; + txHalt = false; + txFifo.clear(); + descAddrFifo.clear(); + regs.command &= ~CR_TXE; + txState = txIdle; +} + +void +EtherDev::rxReset() +{ + DPRINTF(Ethernet, "receive reset\n"); + + rxPacketFlag = false; + CRDD = false; + fragLen = 0; + rxFifoCnt = 0; + rxHalt = false; + rxFifo.clear(); + regs.command &= ~CR_RXE; + rxState = rxIdle; +} + +/** + * This sets up a DMA transfer to read one data segment from the rxFifo into + * the buffer indicated by rxDescCache.bufptr. Assumes the value of rxFragPtr + * is already correctly set. + */ +void +EtherDev::writeOneFrag() +{ + /* i think there is no need for an "in use" warning here like in old */ + fragLen = rxFifo.front()->length; //length of whole packet + fragLen = (fragLen < rxDescCnt) ? fragLen : rxDescCnt; + + writePhys.addr = rxFragPtr; + writePhys.length = fragLen; + + // Set up DMA request area + writeRequest.init(&rxDoneCB, 0, false, &writePhys, 1, fragLen, + rxDescBufPtr, fragLen, curTick); + + dma->doTransfer(&readRequest); +} + +void +EtherDev::rxKick() +{ + DPRINTF(Ethernet, "receive state machine activated!\n"); + + if (CRDD) { + rxState = rxDescRefr; + readOneDesc(rx, LINK_LEN); + } else { + rxState = rxDescRead; + readOneDesc(rx); + } +} + +EtherDev::RxDescDone::RxDescDone(EtherDev *e) + : ethernet(e) +{ +} + +std::string +EtherDev::RxDescDone::name() const +{ + return ethernet->name() + ".rxDescDoneCB"; +} + +void +EtherDev::RxDescDone::process() +{ + DPRINTF(Ethernet, "receive descriptor done callback\n"); + ethernet->rxDescDone(); +} + +void +EtherDev::rxDescDone() +{ + if (rxState == rxDescRefr) { + if (rxDescCache.link == 0) { + rxState = rxIdle; + regs.command &= ~CR_RXE; + devIntrPost(ISR_RXIDLE); + return; + } else { + rxState = rxDescRead; + regs.rxdp = rxDescCache.link; + CRDD = false; + readOneDesc(rx); + } + } else if (rxState == rxDescRead) { + if (rxDescCache.cmdsts & CMDSTS_OWN) { + rxState = rxIdle; + regs.command &= ~CR_RXE; + devIntrPost(ISR_RXIDLE); + } else { + rxState = rxFifoBlock; + rxFragPtr = rxDescCache.bufptr; + rxDescCnt = rxDescCache.cmdsts & CMDSTS_LEN_MASK; + + if (!rxFifo.empty()) { + rxState = rxFragWrite; + if (!rxPacketFlag) { // reading a new packet + rxPacketBufPtr = rxFifo.front()->data; + rxPacketBufPtr -= rxDescCnt; + rxDescBufPtr = rxPacketBufPtr; + rxCopied = 0; + } else { + rxDescBufPtr = rxPacketBufPtr - rxDescCnt; + } + writeOneFrag(); + } + } + } else if (rxState == rxDescWrite) { + devIntrPost(ISR_RXOK); + + if (rxDescCache.cmdsts & CMDSTS_INTR) + devIntrPost(ISR_RXDESC); + + if (rxDescCache.link == 0 || ((rxPktBytes != 0) && rxHalt)) { + rxState = rxIdle; + regs.command &= ~CR_RXE; + devIntrPost(ISR_RXIDLE); + rxHalt = false; + } else { + rxState = rxDescRead; + regs.rxdp = rxDescCache.link; + CRDD = false; + readOneDesc(rx); + } + } +} + +EtherDev::RxDone::RxDone(EtherDev *e) + : ethernet(e) +{ +} + +std::string +EtherDev::RxDone::name() const +{ + return ethernet->name() + ".rxDoneCB"; +} + +void +EtherDev::RxDone::process() +{ + DPRINTF(Ethernet, "receive done callback\n"); + ethernet->rxDone(); +} + +void +EtherDev::rxDone() +{ + DPRINTF(Ethernet, "packet received to host memory\n"); + + if (!rxDescCache.cmdsts & CMDSTS_OWN) + panic("This descriptor is already owned by the driver!\n"); + + rxState = rxFifoBlock; + rxCopied += fragLen; + rxFifoCnt -= fragLen; + + if (rxDescCnt) { /* there is still data left in the descriptor */ + rxState = rxFragWrite; + rxDescBufPtr += fragLen; + writeOneFrag(); + } else { + rxState = rxDescWrite; + if (rxPktBytes == 0) { /* packet is done */ + rxDescCache.cmdsts |= CMDSTS_OWN; + rxDescCache.cmdsts &= ~CMDSTS_MORE; + rxDescCache.cmdsts |= CMDSTS_OK; + rxDescCache.cmdsts += rxCopied; //i.e. set CMDSTS_SIZE + + rxPacketFlag = false; + if (rxFilterEnable) { + rxDescCache.cmdsts &= ~CMDSTS_DEST_MASK; + if (rxFifo.front()->IsUnicast()) + rxDescCache.cmdsts |= CMDSTS_DEST_SELF; + if (rxFifo.front()->IsMulticast()) + rxDescCache.cmdsts |= CMDSTS_DEST_MULTI; + if (rxFifo.front()->IsBroadcast()) + rxDescCache.cmdsts |= CMDSTS_DEST_MASK; + } + + PacketPtr &pkt = rxFifo.front(); + eth_header *eth = (eth_header *) pkt->data; + if (eth->type == 0x800 && extstsEnable) { + rxDescCache.extsts |= EXTSTS_IPPKT; + if (!ipChecksum(pkt, false)) + rxDescCache.extsts |= EXTSTS_IPERR; + ip_header *ip = rxFifo.front()->getIpHdr(); + + if (ip->protocol == 6) { + rxDescCache.extsts |= EXTSTS_TCPPKT; + if (!tcpChecksum(pkt, false)) + rxDescCache.extsts |= EXTSTS_TCPERR; + } else if (ip->protocol == 17) { + rxDescCache.extsts |= EXTSTS_UDPPKT; + if (!udpChecksum(pkt, false)) + rxDescCache.extsts |= EXTSTS_UDPERR; + } + } + + rxFifo.front() = NULL; + rxFifo.pop_front(); + } else { /* just the descriptor is done */ + rxDescCache.cmdsts |= CMDSTS_OWN; + rxDescCache.cmdsts |= CMDSTS_MORE; + } + writeDescPhys.addr = regs.rxdp + LINK_LEN + BUFPTR_LEN; + writeDescPhys.length = CMDSTS_LEN; + + writeDescRequest.init(&rxDescDoneCB, 0, true, &writeDescPhys, 1, + CMDSTS_LEN, (uint8_t *) &rxDescCache.cmdsts, + CMDSTS_LEN, curTick); + } +} + +/** + * This sets up a DMA transfer to read one descriptor into the network device. + */ +void +EtherDev::readOneDesc(dir_t dir, uint32_t len) { + readDescPhys.addr = (dir == tx) ? regs.txdp : regs.rxdp; + readDescPhys.length = len; + + ns_desc *cache = (dir == tx) ? &txDescCache : &rxDescCache; + + /* THIS ASSUMES THAT DESC_LEN < regs.txcfg's maxdma value, + which is 512 bytes in the driver, so i'll just hard code it here */ + readDescRequest.init(&txDescDoneCB, 0, false, &readDescPhys, 1, + len, (uint8_t *) cache , len, curTick); + + dma->doTransfer(&readDescRequest); +} + +/** + * This sets up a DMA transfer to read one data segment of the descriptor in + * txDescCache. Assumes the value of txFragPtr is already correctly set + */ +void +EtherDev::readOneFrag() +{ + /* i think there is no need for an "in use" warning here like in old */ + fragLen = (txDescCnt < txFifoAvail) ? txDescCnt : txFifoAvail; + readPhys.addr = txFragPtr; + readPhys.length = fragLen; + + // Set up DMA request area + readRequest.init(&txDoneCB, 0, false, &readPhys, 1, fragLen, + txPacketBufPtr, fragLen, curTick); + + dma->doTransfer(&readRequest); +} + +void +EtherDev::transmit() +{ + if (txFifo.empty()) { + DPRINTF(Ethernet, "nothing to transmit\n"); + return; + } + + if (interface->sendPacket(txFifo.front())) { + DPRINTF(Ethernet, "transmit packet\n"); + txBytes += txFifo.front()->length; + txPackets++; + + txFifoCnt -= txFifo.front()->length; + + txFifo.front() = NULL; + txFifo.pop_front(); + + txDescCache.cmdsts &= ~CMDSTS_OK; + } else { + txDescCache.cmdsts &= ~CMDSTS_ERR; + } + + txDescCache.cmdsts &= ~CMDSTS_OWN; + + writeDescPhys.addr = descAddrFifo.front() + LINK_LEN + BUFPTR_LEN; + writeDescPhys.length = CMDSTS_LEN; + + descAddrFifo.front() = 0; + descAddrFifo.pop_front(); + + writeDescRequest.init(&txDescDoneCB, 0, true, &writeDescPhys, 1, + writeDescPhys.length, + (uint8_t *) &(txDescCache.cmdsts), + writeDescPhys.length, curTick); + + dma->doTransfer(&writeDescRequest); + + transmit(); +} + +void +EtherDev::txKick() +{ + DPRINTF(Ethernet, "transmit state machine activated\n"); +#if 0 + if (DTRACE(Ethernet)) + txDump(); +#endif + + if (CTDD) { + txState = txDescRefr; + readOneDesc(tx, LINK_LEN); + } else { + txState = txDescRead; + readOneDesc(tx); + } +} + +EtherDev::TxDescDone::TxDescDone(EtherDev *e) + : ethernet(e) +{ +} + +std::string +EtherDev::TxDescDone::name() const +{ + return ethernet->name() + ".txDescDoneCB"; +} + +void +EtherDev::TxDescDone::process() +{ + DPRINTF(Ethernet, "transmit descriptor done callback\n"); + ethernet->txDescDone(); + +} + +void +EtherDev::txDescDone() +{ + if (txState == txFifoBlock) { + if (txDescCache.cmdsts & CMDSTS_OK) { + devIntrPost(ISR_TXOK); + } else if (txDescCache.cmdsts & CMDSTS_ERR) { + devIntrPost(ISR_TXERR); + } + } else if (txState == txDescRefr || txState == txDescWrite) { + + if (txState == txDescWrite) { + if (txDescCache.cmdsts & CMDSTS_INTR) { + devIntrPost(ISR_TXDESC); + } + } + + if (txDescCache.link == 0) { + txState = txIdle; + regs.command &= ~CR_TXE; + devIntrPost(ISR_TXIDLE); + return; + } else { + txState = txDescRead; + regs.txdp = txDescCache.link; + CTDD = false; + readOneDesc(tx); + } + } else if (txState == txDescRead) { + if (txDescCache.cmdsts & CMDSTS_OWN) { + txState = txFifoBlock; + txFragPtr = txDescCache.bufptr; + txDescCnt = txDescCache.cmdsts & CMDSTS_LEN_MASK; + + if (txFifoAvail >= ((regs.txcfg & TXCFG_FLTH_MASK) >> 8)) { + txState = txFragRead; + if (!txPacketFlag) { + txPacketFlag = true; + /* find the total length of this packet */ + txPacketLen = txDescCnt; + bool more = txDescCache.cmdsts & CMDSTS_MORE; + uint8_t *addr = (uint8_t *) regs.txdp; + while (more) { + addr = physmem->dma_addr(((ns_desc *) addr)->link, sizeof(ns_desc)); + /* !!!!!!mask needed? */ + txPacketLen += ((ns_desc *)addr)->cmdsts & CMDSTS_LEN_MASK; + more = ((ns_desc *) addr)->cmdsts & CMDSTS_MORE; + } + PacketPtr &packet = txDoneCB.packet; + packet = new EtherPacket; + packet->length = txPacketLen; + packet->data = new uint8_t[txPacketLen]; + txPacketBufPtr = packet->data; + } + readOneFrag(); + } + } else { + txState = txIdle; + regs.command &= ~CR_TXE; + devIntrPost(ISR_TXIDLE); + } + } +} + +EtherDev::TxDone::TxDone(EtherDev *e) + : ethernet(e) +{ +} + +std::string +EtherDev::TxDone::name() const +{ + return ethernet->name() + ".txDoneCB"; +} + + +void +EtherDev::TxDone::process() +{ + DPRINTF(Ethernet, "transmit done callback\n"); + ethernet->txDone(packet); +} + +void +EtherDev::txDone(PacketPtr packet) +{ + DPRINTF(Ethernet, "transmit done\n"); + + if (!txDescCache.cmdsts & CMDSTS_OWN) + panic("This descriptor is already owned by the driver!\n"); + + txState = txFifoBlock; + + txPacketBufPtr += fragLen; /* hope this ptr manipulation is right! */ + txDescCnt -= fragLen; + txFifoCnt += fragLen; + + if (txFifoCnt >= (regs.txcfg & TXCFG_DRTH_MASK)) { + if (txFifo.empty()) { + txFifoCnt -= (uint32_t) (txPacketBufPtr - packet->data); + } else { + transmit(); + } + } + + if (txDescCnt) { /* if there is still more data to go in this desc */ + if (txFifoAvail >= regs.txcfg & TXCFG_FLTH_MASK) { + txState = txFragRead; + readOneFrag(); + } + } else { /* this descriptor is done */ + /* but there is more descriptors for this packet */ + if (txDescCache.cmdsts & CMDSTS_MORE) { + txState = txDescWrite; + txDescCache.cmdsts &= ~CMDSTS_OWN; + writeDescPhys.addr = regs.txdp + LINK_LEN + BUFPTR_LEN; + writeDescPhys.length = CMDSTS_LEN; + + writeDescRequest.init(&txDescDoneCB, 0, true, &writeDescPhys, 1, + writeDescPhys.length, + (uint8_t*) &txDescCache.cmdsts, + writeDescPhys.length, curTick); + } else { /* this packet is totally done */ + /* deal with the the packet that just finished */ + if (regs.vtcr & VTCR_PPCHK && extstsEnable) { + if (txDescCache.extsts & EXTSTS_UDPPKT) { + udpChecksum(packet, true); + } else if (txDescCache.extsts & EXTSTS_TCPPKT) { + tcpChecksum(packet, true); + } else if (txDescCache.extsts & EXTSTS_IPPKT) { + ipChecksum(packet, true); + } + } + + txFifo.push_back(packet); + transmit(); + txPacketFlag = false; + descAddrFifo.push_back(regs.txdp); + + /* if there is not another descriptor ready for reading, go idle */ + if (txDescCache.link == 0 || txHalt) { + txState = txIdle; + devIntrPost(ISR_TXIDLE); + txHalt = false; + } else { /* else go read next descriptor */ + txState = txDescRead; + regs.txdp = txDescCache.link; + CTDD = false; + readOneDesc(tx); + } + } + } +} + +void +EtherDev::transferDone() +{ + if (txFifo.empty()) + return; + + DPRINTF(Ethernet, "schedule transmit\n"); + + if (txEvent.scheduled()) + txEvent.reschedule(curTick + 1); + else + txEvent.schedule(curTick + 1); +} + +void +EtherDev::txDump() const +{ +#if 0 + int i = tx_ptr; + for (int loop = 0; loop < tx_ring_len; loop++) { + es_desc *desc = &tx_ring[i]; + + if (desc->addr) + cprintf("desc[%d]: addr=%#x, len=%d, flags=%#x\n", + i, desc->addr, desc->length, desc->flags); + + if (++i >= tx_ring_len) + i = 0; + } +#endif +} + +void +EtherDev::rxDump() const +{ +#if 0 + int i = rx_ptr; + for (int loop = 0; loop < rx_ring_len; loop++) { + es_desc *desc = &rx_ring[i]; + + if (desc->addr) + cprintf("desc[%d]: addr=%#x, len=%d, flags=%#x\n", + i, desc->addr, desc->length, desc->flags); + + if (++i >= rx_ring_len) + i = 0; + } +#endif +} + +bool +EtherDev::rxFilter(PacketPtr packet) +{ + bool drop = true; + string type; + + if (packet->IsUnicast()) { + type = "unicast"; + + // If we're accepting all unicast addresses + if (acceptUnicast) + drop = false; + + // If we make a perfect match + if ((acceptPerfect) + && (memcmp(regs.perfectMatch, packet->data, sizeof(regs.perfectMatch)) == 0)) + drop = false; + + eth_header *eth = (eth_header *) packet->data; + if ((acceptArp) && (eth->type == 0x806)) + drop = false; + + } else if (packet->IsBroadcast()) { + type = "broadcast"; + + // if we're accepting broadcasts + if (acceptBroadcast) + drop = false; + + } else if (packet->IsMulticast()) { + type = "multicast"; + + // if we're accepting all multicasts + if (acceptMulticast) + drop = false; + + } else { + type = "unknown"; + + // oh well, punt on this one + } + + if (drop) { + DPRINTF(Ethernet, "rxFilter drop\n"); + DDUMP(EthernetData, packet->data, packet->length); + } + + return drop; +} + +bool +EtherDev::recvPacket(PacketPtr packet) +{ + rxBytes += packet->length; + rxPackets++; + + if (rxState == rxIdle) { + DPRINTF(Ethernet, "receive disabled...packet dropped\n"); + interface->recvDone(); + return true; + } + + if (rxFilterEnable && rxFilter(packet)) { + DPRINTF(Ethernet, "packet filtered...dropped\n"); + interface->recvDone(); + return true; + } + + if (rxFifoCnt + packet->length >= MAX_RX_FIFO_SIZE) { + DPRINTF(Ethernet, + "packet will not fit in receive buffer...packet dropped\n"); + devIntrPost(ISR_RXORN); + return false; + } + + rxFifo.push_back(packet); + rxPktBytes = packet->length; + rxFifoCnt += packet->length; + interface->recvDone(); + + return true; +} + +bool +EtherDev::udpChecksum(PacketPtr packet, bool gen) +{ + udp_header *hdr = (udp_header *) packet->getTransportHdr(); + + ip_header *ip = packet->getIpHdr(); + + pseudo_header *pseudo = new pseudo_header; + + pseudo->src_ip_addr = ip->src_ip_addr; + pseudo->dest_ip_addr = ip->dest_ip_addr; + pseudo->protocol = ip->protocol; + pseudo->len = hdr->len; + + uint16_t cksum = checksumCalc((uint16_t *) pseudo, (uint16_t *) hdr, + (uint32_t) hdr->len); + + delete pseudo; + if (gen) + hdr->chksum = cksum; + else + if (cksum != 0) + return false; + + return true; +} + +bool +EtherDev::tcpChecksum(PacketPtr packet, bool gen) +{ + tcp_header *hdr = (tcp_header *) packet->getTransportHdr(); + + ip_header *ip = packet->getIpHdr(); + + pseudo_header *pseudo = new pseudo_header; + + pseudo->src_ip_addr = ip->src_ip_addr; + pseudo->dest_ip_addr = ip->dest_ip_addr; + pseudo->protocol = ip->protocol; + pseudo->len = ip->dgram_len - (ip->vers_len & 0xf); + + uint16_t cksum = checksumCalc((uint16_t *) pseudo, (uint16_t *) hdr, + (uint32_t) pseudo->len); + + delete pseudo; + if (gen) + hdr->chksum = cksum; + else + if (cksum != 0) + return false; + + return true; +} + +bool +EtherDev::ipChecksum(PacketPtr packet, bool gen) +{ + ip_header *hdr = packet->getIpHdr(); + + uint16_t cksum = checksumCalc(NULL, (uint16_t *) hdr, (hdr->vers_len & 0xf)); + + if (gen) + hdr->hdr_chksum = cksum; + else + if (cksum != 0) + return false; + + return true; +} + +uint16_t +EtherDev::checksumCalc(uint16_t *pseudo, uint16_t *buf, uint32_t len) +{ + uint32_t sum = 0; + + uint16_t last_pad = 0; + if (len & 1) { + last_pad = buf[len/2] & 0xff; + len--; + sum += last_pad; + } + + if (pseudo) { + sum = pseudo[0] + pseudo[1] + pseudo[2] + + pseudo[3] + pseudo[4] + pseudo[5]; + } + + for (int i=0; i < (len/2); ++i) { + sum += buf[i]; + } + + while (sum >> 16) + sum = (sum >> 16) + (sum & 0xffff); + + return ~sum; +} + +//===================================================================== +// +// +void +dp_regs::serialize(ostream &os) +{ + SERIALIZE_SCALAR(command); + SERIALIZE_SCALAR(config); + SERIALIZE_SCALAR(isr); + SERIALIZE_SCALAR(imr); +} + +void +dp_regs::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(command); + UNSERIALIZE_SCALAR(config); + UNSERIALIZE_SCALAR(isr); + UNSERIALIZE_SCALAR(imr); +#if 0 + UNSERIALIZE_SCALAR(tx_ring); + UNSERIALIZE_SCALAR(rx_ring); + UNSERIALIZE_SCALAR(tx_ring_len); + UNSERIALIZE_SCALAR(rx_ring_len); + UNSERIALIZE_SCALAR(rom_addr); + UNSERIALIZE_SCALAR(rom_data); + UNSERIALIZE_SCALAR(rxfilt_ctl); + UNSERIALIZE_SCALAR(rxfilt_data); + + UNSERIALIZE_ARRAY(perfect,EADDR_LEN); + UNSERIALIZE_ARRAY(hash_table,ES_HASH_SIZE); + + UNSERIALIZE_SCALAR(tx_ring_ptr); + UNSERIALIZE_SCALAR(rx_ring_ptr); +#endif +} + +//--------------------------------------- + +void +EtherPacket::serialize(ostream &os) +{ + SERIALIZE_SCALAR(length); + SERIALIZE_ARRAY(data, length); +} + +void +EtherPacket::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(length); + data = new uint8_t[length]; + UNSERIALIZE_ARRAY(data, length); +} + +//--------------------------------------- + +void +EtherDev::serialize(ostream &os) +{ + +#if 0 + regs.serialize(os); + + // tx_ring & rx_ring are contained in the physmem... + SERIALIZE_SCALAR(cpuPendingIntr); + SERIALIZE_SCALAR(tx_ptr); + SERIALIZE_SCALAR(rx_ptr); + + SERIALIZE_SCALAR(rxDoneCB.ptr); + SERIALIZE_SCALAR(rxDoneCB.ignore); + + SERIALIZE_SCALAR(txDoneCB.ptr); + SERIALIZE_SCALAR(txDoneCB.ignore); + + for (int i=0; i<ES_MAX_DMA_SEGS; ++i) { + paramOut(os, csprintf("readPhys%d.addr",i), readPhys[i].addr); + paramOut(os, csprintf("readPhys%d.length",i), readPhys[i].length); + paramOut(os, csprintf("writePhys%d.addr",i), writePhys[i].addr); + paramOut(os, csprintf("writePhys%d.length",i), writePhys[i].length); + } + + SERIALIZE_SCALAR(txEnable); + SERIALIZE_SCALAR(rxEnable); + SERIALIZE_SCALAR(txDelay); + SERIALIZE_SCALAR(rxDelay); + + SERIALIZE_SCALAR(txbuf_len); + + //Calculate the number here, actually dump them at end + int numTxPkts=0; + for (pktiter_t p=txbuf.begin(); p!=txbuf.end(); ++p) { + numTxPkts++; + } + SERIALIZE_SCALAR(numTxPkts); + + SERIALIZE_SCALAR(rxbuf_len); + int numRxPkts=0; + for (pktiter_t p=rxbuf.begin(); p!=rxbuf.end(); ++p) { + numRxPkts++; + } + SERIALIZE_SCALAR(numRxPkts); + + // output whether the tx and rx packets exist + bool txPacketExists = false; + if (txDoneCB.packet) + txPacketExists = true; + SERIALIZE_SCALAR(txPacketExists); + + bool rxPacketExists = false; + if (rxPacket) + rxPacketExists = true; + SERIALIZE_SCALAR(rxPacketExists); + + // output the names (unique by pointer) of the read and write requests + paramOut(os, csprintf("readReqName"), readRequest.name()); + paramOut(os, csprintf("writeReqName"), writeRequest.name()); + + // Serialize txPacket, because its data is needed for readRequest + if (txPacketExists) { + nameOut(os, csprintf("%s.txPacket", name())); + txDoneCB.packet->serialize(os); + } + + // Serialize rxPacket, because its data is needed for writeRequest + if (rxPacketExists) { + nameOut(os, csprintf("%s.rxPacket", name())); + rxPacket->serialize(os); + } + + // create a section for the readRequest + nameOut(os, readRequest.name()); + paramOut(os, csprintf("parent"), name()); + paramOut(os, csprintf("id"), 0); + readRequest.serialize(os); + + // create a section for the writeRequest + nameOut(os, writeRequest.name()); + paramOut(os, csprintf("parent"), name()); + paramOut(os, csprintf("id"), 1); + writeRequest.serialize(os); + + //Redo the buffers, this time outputing them to the file + numTxPkts = 0; + for (pktiter_t p=txbuf.begin(); p!=txbuf.end(); ++p) { + nameOut(os, csprintf("%s.txbuf%d", name(),numTxPkts++)); + (*p)->serialize(os); + } + + numRxPkts = 0; + for (pktiter_t p=rxbuf.begin(); p!=rxbuf.end(); ++p) { + nameOut(os, csprintf("%s.rxbuf%d", name(),numRxPkts++)); + (*p)->serialize(os); + } +#endif +} + +void +EtherDev::unserialize(Checkpoint *cp, const std::string §ion) +{ +#if 0 + regs.unserialize(cp, section); + + UNSERIALIZE_SCALAR(cpuPendingIntr); + + // initialize the tx_ring + txReset(); + + // initialize the rx_ring + rxReset(); + + UNSERIALIZE_SCALAR(tx_ptr); + UNSERIALIZE_SCALAR(rx_ptr); + + PacketPtr p; + UNSERIALIZE_SCALAR(txbuf_len); + int numTxPkts; + UNSERIALIZE_SCALAR(numTxPkts); + for (int i=0; i<numTxPkts; ++i) { + p = new EtherPacket; + p->unserialize(cp, csprintf("%s.txbuf%d", section, i)); + txbuf.push_back(p); + } + + UNSERIALIZE_SCALAR(rxbuf_len); + int numRxPkts; + UNSERIALIZE_SCALAR(numRxPkts); + for (int i=0; i<numRxPkts; ++i) { + p = new EtherPacket; + p->unserialize(cp, csprintf("%s.rxbuf%d", section, i)); + rxbuf.push_back(p); + } + + UNSERIALIZE_SCALAR(rxDoneCB.ptr); + UNSERIALIZE_SCALAR(rxDoneCB.ignore); + + UNSERIALIZE_SCALAR(txDoneCB.ptr); + UNSERIALIZE_SCALAR(txDoneCB.ignore); + + for (int i=0; i<ES_MAX_DMA_SEGS; ++i) { + paramIn(cp, section, csprintf("readPhys%d.addr",i), + readPhys[i].addr); + paramIn(cp, section, csprintf("readPhys%d.length",i), + readPhys[i].length); + paramIn(cp, section, csprintf("writePhys%d.addr",i), + writePhys[i].addr); + paramIn(cp, section, csprintf("writePhys%d.length",i), + writePhys[i].length); + } + + UNSERIALIZE_SCALAR(txEnable); + UNSERIALIZE_SCALAR(rxEnable); + UNSERIALIZE_SCALAR(txDelay); + UNSERIALIZE_SCALAR(rxDelay); + + // Unserialize the current txPacket + bool txPacketExists; + UNSERIALIZE_SCALAR(txPacketExists); + + txDoneCB.packet = NULL; + if (txPacketExists) { + txDoneCB.packet = new EtherPacket; + txDoneCB.packet->unserialize(cp, csprintf("%s.txPacket", section)); + } + + // Unserialize the current rxPacket + bool rxPacketExists; + UNSERIALIZE_SCALAR(rxPacketExists); + + rxPacket = NULL; + if (rxPacketExists) { + rxPacket = new EtherPacket; + rxPacket->unserialize(cp, csprintf("%s.rxPacket", section)); + } + + std::string readReqName, writeReqName; + UNSERIALIZE_SCALAR(readReqName); + UNSERIALIZE_SCALAR(writeReqName); + + // Unserialize and fixup the readRequest + readRequest.unserialize(cp, readReqName); + readRequest.phys = readPhys; + readRequest.bufferCB = 0; + readRequest.dmaDoneCB = &txDoneCB; + readRequest.data = NULL; + + if (txDoneCB.packet) + readRequest.data = txDoneCB.packet->data; + + // Unserialize and fixup the writeRequest + writeRequest.unserialize(cp, writeReqName); + writeRequest.phys = writePhys; + writeRequest.bufferCB = 0; + writeRequest.dmaDoneCB = &rxDoneCB; + writeRequest.data = NULL; + + if (rxPacket) + writeRequest.data = rxPacket->data; +#endif +} + + +//===================================================================== + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(EtherDevInt) + + SimObjectParam<EtherInt *> peer; + SimObjectParam<EtherDev *> device; + +END_DECLARE_SIM_OBJECT_PARAMS(EtherDevInt) + +BEGIN_INIT_SIM_OBJECT_PARAMS(EtherDevInt) + + INIT_PARAM_DFLT(peer, "peer interface", NULL), + INIT_PARAM(device, "Ethernet device of this interface") + +END_INIT_SIM_OBJECT_PARAMS(EtherDevInt) + +CREATE_SIM_OBJECT(EtherDevInt) +{ + EtherDevInt *dev_int = new EtherDevInt(getInstanceName(), device); + + EtherInt *p = (EtherInt *)peer; + if (p) { + dev_int->setPeer(p); + p->setPeer(dev_int); + } + + return dev_int; +} + +REGISTER_SIM_OBJECT("EtherDevInt", EtherDevInt) + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(EtherDev) + + Param<Tick> tx_delay; + Param<Tick> rx_delay; + SimObjectParam<DmaEngine *> engine; + Param<bool> use_interface; + SimObjectParam<IntrControl *> intr_ctrl; + SimObjectParam<MemoryController *> mmu; + SimObjectParam<PhysicalMemory *> physmem; + Param<Addr> addr; + Param<Addr> mask; + Param<bool> rx_filter; + Param<string> hardware_address; + SimObjectParam<PCIConfigAll *> configspace; + SimObjectParam<PciConfigData *> configdata; + SimObjectParam<Tsunami *> tsunami; + Param<uint32_t> pci_bus; + Param<uint32_t> pci_dev; + Param<uint32_t> pci_func; + +END_DECLARE_SIM_OBJECT_PARAMS(EtherDev) + +BEGIN_INIT_SIM_OBJECT_PARAMS(EtherDev) + + INIT_PARAM_DFLT(tx_delay, "Transmit Delay", 1000), + INIT_PARAM_DFLT(rx_delay, "Receive Delay", 1000), + INIT_PARAM(engine, "DMA Engine"), + INIT_PARAM_DFLT(use_interface, "Use DMA Interface", true), + INIT_PARAM(intr_ctrl, "Interrupt Controller"), + INIT_PARAM(mmu, "Memory Controller"), + INIT_PARAM(physmem, "Physical Memory"), + INIT_PARAM(addr, "Device Address"), + INIT_PARAM(mask, "Address Mask"), + INIT_PARAM_DFLT(rx_filter, "Enable Receive Filter", true), + INIT_PARAM_DFLT(hardware_address, "Ethernet Hardware Address", + "00:99:00:00:00:01"), + INIT_PARAM(configspace, "PCI Configspace"), + INIT_PARAM(configdata, "PCI Config data"), + INIT_PARAM(tsunami, "Tsunami"), + INIT_PARAM(pci_bus, "PCI bus"), + INIT_PARAM(pci_dev, "PCI device number"), + INIT_PARAM(pci_func, "PCI function code") + +END_INIT_SIM_OBJECT_PARAMS(EtherDev) + + +CREATE_SIM_OBJECT(EtherDev) +{ + int eaddr[6]; + sscanf(((string)hardware_address).c_str(), "%x:%x:%x:%x:%x:%x", + &eaddr[0], &eaddr[1], &eaddr[2], &eaddr[3], &eaddr[4], &eaddr[5]); + + return new EtherDev(getInstanceName(), engine, use_interface, + intr_ctrl, mmu, physmem, configspace, configdata, + tsunami, pci_bus, pci_dev, pci_func, rx_filter, eaddr, + tx_delay, rx_delay, addr, mask); +} + +REGISTER_SIM_OBJECT("EtherDev", EtherDev) |