| /* |
| * Copyright (c) 2004-2005 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: Nathan Binkert |
| * Lisa Hsu |
| */ |
| |
| /** @file |
| * Device module for modelling the National Semiconductor |
| * DP83820 ethernet controller. Does not support priority queueing |
| */ |
| |
| #include "dev/net/ns_gige.hh" |
| |
| #include <deque> |
| #include <memory> |
| #include <string> |
| |
| #include "base/debug.hh" |
| #include "base/inet.hh" |
| #include "base/types.hh" |
| #include "config/the_isa.hh" |
| #include "debug/EthernetAll.hh" |
| #include "dev/net/etherlink.hh" |
| #include "mem/packet.hh" |
| #include "mem/packet_access.hh" |
| #include "params/NSGigE.hh" |
| #include "sim/system.hh" |
| |
| // clang complains about std::set being overloaded with Packet::set if |
| // we open up the entire namespace std |
| using std::make_shared; |
| using std::min; |
| using std::ostream; |
| using std::string; |
| |
| const char *NsRxStateStrings[] = |
| { |
| "rxIdle", |
| "rxDescRefr", |
| "rxDescRead", |
| "rxFifoBlock", |
| "rxFragWrite", |
| "rxDescWrite", |
| "rxAdvance" |
| }; |
| |
| const char *NsTxStateStrings[] = |
| { |
| "txIdle", |
| "txDescRefr", |
| "txDescRead", |
| "txFifoBlock", |
| "txFragRead", |
| "txDescWrite", |
| "txAdvance" |
| }; |
| |
| const char *NsDmaState[] = |
| { |
| "dmaIdle", |
| "dmaReading", |
| "dmaWriting", |
| "dmaReadWaiting", |
| "dmaWriteWaiting" |
| }; |
| |
| using namespace Net; |
| using namespace TheISA; |
| |
| /////////////////////////////////////////////////////////////////////// |
| // |
| // NSGigE PCI Device |
| // |
| NSGigE::NSGigE(Params *p) |
| : EtherDevBase(p), ioEnable(false), |
| txFifo(p->tx_fifo_size), rxFifo(p->rx_fifo_size), |
| txPacket(0), rxPacket(0), txPacketBufPtr(NULL), rxPacketBufPtr(NULL), |
| txXferLen(0), rxXferLen(0), rxDmaFree(false), txDmaFree(false), |
| txState(txIdle), txEnable(false), CTDD(false), txHalt(false), |
| txFragPtr(0), txDescCnt(0), txDmaState(dmaIdle), rxState(rxIdle), |
| rxEnable(false), CRDD(false), rxPktBytes(0), rxHalt(false), |
| rxFragPtr(0), rxDescCnt(0), rxDmaState(dmaIdle), extstsEnable(false), |
| eepromState(eepromStart), eepromClk(false), eepromBitsToRx(0), |
| eepromOpcode(0), eepromAddress(0), eepromData(0), |
| dmaReadDelay(p->dma_read_delay), dmaWriteDelay(p->dma_write_delay), |
| dmaReadFactor(p->dma_read_factor), dmaWriteFactor(p->dma_write_factor), |
| rxDmaData(NULL), rxDmaAddr(0), rxDmaLen(0), |
| txDmaData(NULL), txDmaAddr(0), txDmaLen(0), |
| rxDmaReadEvent([this]{ rxDmaReadDone(); }, name()), |
| rxDmaWriteEvent([this]{ rxDmaWriteDone(); }, name()), |
| txDmaReadEvent([this]{ txDmaReadDone(); }, name()), |
| txDmaWriteEvent([this]{ txDmaWriteDone(); }, name()), |
| dmaDescFree(p->dma_desc_free), dmaDataFree(p->dma_data_free), |
| txDelay(p->tx_delay), rxDelay(p->rx_delay), |
| rxKickTick(0), |
| rxKickEvent([this]{ rxKick(); }, name()), |
| txKickTick(0), |
| txKickEvent([this]{ txKick(); }, name()), |
| txEvent([this]{ txEventTransmit(); }, name()), |
| rxFilterEnable(p->rx_filter), |
| acceptBroadcast(false), acceptMulticast(false), acceptUnicast(false), |
| acceptPerfect(false), acceptArp(false), multicastHashEnable(false), |
| intrDelay(p->intr_delay), intrTick(0), cpuPendingIntr(false), |
| intrEvent(0), interface(0) |
| { |
| |
| |
| interface = new NSGigEInt(name() + ".int0", this); |
| |
| regsReset(); |
| memcpy(&rom.perfectMatch, p->hardware_address.bytes(), ETH_ADDR_LEN); |
| |
| memset(&rxDesc32, 0, sizeof(rxDesc32)); |
| memset(&txDesc32, 0, sizeof(txDesc32)); |
| memset(&rxDesc64, 0, sizeof(rxDesc64)); |
| memset(&txDesc64, 0, sizeof(txDesc64)); |
| } |
| |
| NSGigE::~NSGigE() |
| { |
| delete interface; |
| } |
| |
| /** |
| * This is to write to the PCI general configuration registers |
| */ |
| Tick |
| NSGigE::writeConfig(PacketPtr pkt) |
| { |
| int offset = pkt->getAddr() & PCI_CONFIG_SIZE; |
| if (offset < PCI_DEVICE_SPECIFIC) |
| PciDevice::writeConfig(pkt); |
| else |
| panic("Device specific PCI config space not implemented!\n"); |
| |
| switch (offset) { |
| // seems to work fine without all these PCI settings, but i |
| // put in the IO to double check, an assertion will fail if we |
| // need to properly implement it |
| case PCI_COMMAND: |
| if (config.data[offset] & PCI_CMD_IOSE) |
| ioEnable = true; |
| else |
| ioEnable = false; |
| break; |
| } |
| |
| return configDelay; |
| } |
| |
| EtherInt* |
| NSGigE::getEthPort(const std::string &if_name, int idx) |
| { |
| if (if_name == "interface") { |
| if (interface->getPeer()) |
| panic("interface already connected to\n"); |
| return interface; |
| } |
| return NULL; |
| } |
| |
| /** |
| * This reads the device registers, which are detailed in the NS83820 |
| * spec sheet |
| */ |
| Tick |
| NSGigE::read(PacketPtr pkt) |
| { |
| assert(ioEnable); |
| |
| //The mask is to give you only the offset into the device register file |
| Addr daddr = pkt->getAddr() & 0xfff; |
| DPRINTF(EthernetPIO, "read da=%#x pa=%#x size=%d\n", |
| daddr, pkt->getAddr(), pkt->getSize()); |
| |
| |
| // there are some reserved registers, you can see ns_gige_reg.h and |
| // the spec sheet for details |
| if (daddr > LAST && daddr <= RESERVED) { |
| panic("Accessing reserved register"); |
| } else if (daddr > RESERVED && daddr <= 0x3FC) { |
| return readConfig(pkt); |
| } else if (daddr >= MIB_START && daddr <= MIB_END) { |
| // don't implement all the MIB's. hopefully the kernel |
| // doesn't actually DEPEND upon their values |
| // MIB are just hardware stats keepers |
| pkt->set<uint32_t>(0); |
| pkt->makeAtomicResponse(); |
| return pioDelay; |
| } else if (daddr > 0x3FC) |
| panic("Something is messed up!\n"); |
| |
| assert(pkt->getSize() == sizeof(uint32_t)); |
| uint32_t ® = *pkt->getPtr<uint32_t>(); |
| uint16_t rfaddr; |
| |
| switch (daddr) { |
| case CR: |
| reg = regs.command; |
| //these are supposed to be cleared on a read |
| reg &= ~(CR_RXD | CR_TXD | CR_TXR | CR_RXR); |
| break; |
| |
| case CFGR: |
| reg = regs.config; |
| break; |
| |
| case MEAR: |
| reg = regs.mear; |
| break; |
| |
| case PTSCR: |
| reg = regs.ptscr; |
| break; |
| |
| case ISR: |
| reg = regs.isr; |
| devIntrClear(ISR_ALL); |
| 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 TX_CFG: |
| 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 RX_CFG: |
| reg = regs.rxcfg; |
| break; |
| |
| case PQCR: |
| reg = regs.pqcr; |
| break; |
| |
| case WCSR: |
| reg = regs.wcsr; |
| break; |
| |
| case PCR: |
| reg = regs.pcr; |
| break; |
| |
| // see the spec sheet for how RFCR and RFDR work |
| // basically, you write to RFCR to tell the machine |
| // what you want to do next, then you act upon RFDR, |
| // and the device will be prepared b/c of what you |
| // wrote to RFCR |
| case RFCR: |
| reg = regs.rfcr; |
| break; |
| |
| case RFDR: |
| rfaddr = (uint16_t)(regs.rfcr & RFCR_RFADDR); |
| switch (rfaddr) { |
| // Read from perfect match ROM octets |
| case 0x000: |
| reg = rom.perfectMatch[1]; |
| reg = reg << 8; |
| reg += rom.perfectMatch[0]; |
| break; |
| case 0x002: |
| reg = rom.perfectMatch[3] << 8; |
| reg += rom.perfectMatch[2]; |
| break; |
| case 0x004: |
| reg = rom.perfectMatch[5] << 8; |
| reg += rom.perfectMatch[4]; |
| break; |
| default: |
| // Read filter hash table |
| if (rfaddr >= FHASH_ADDR && |
| rfaddr < FHASH_ADDR + FHASH_SIZE) { |
| |
| // Only word-aligned reads supported |
| if (rfaddr % 2) |
| panic("unaligned read from filter hash table!"); |
| |
| reg = rom.filterHash[rfaddr - FHASH_ADDR + 1] << 8; |
| reg += rom.filterHash[rfaddr - FHASH_ADDR]; |
| break; |
| } |
| |
| panic("reading RFDR for something other than pattern" |
| " matching or hashing! %#x\n", rfaddr); |
| } |
| 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; |
| |
| case M5REG: |
| reg = 0; |
| if (params()->rx_thread) |
| reg |= M5REG_RX_THREAD; |
| if (params()->tx_thread) |
| reg |= M5REG_TX_THREAD; |
| if (params()->rss) |
| reg |= M5REG_RSS; |
| break; |
| |
| default: |
| panic("reading unimplemented register: addr=%#x", daddr); |
| } |
| |
| DPRINTF(EthernetPIO, "read from %#x: data=%d data=%#x\n", |
| daddr, reg, reg); |
| |
| pkt->makeAtomicResponse(); |
| return pioDelay; |
| } |
| |
| Tick |
| NSGigE::write(PacketPtr pkt) |
| { |
| assert(ioEnable); |
| |
| Addr daddr = pkt->getAddr() & 0xfff; |
| DPRINTF(EthernetPIO, "write da=%#x pa=%#x size=%d\n", |
| daddr, pkt->getAddr(), pkt->getSize()); |
| |
| if (daddr > LAST && daddr <= RESERVED) { |
| panic("Accessing reserved register"); |
| } else if (daddr > RESERVED && daddr <= 0x3FC) { |
| return writeConfig(pkt); |
| } else if (daddr > 0x3FC) |
| panic("Something is messed up!\n"); |
| |
| if (pkt->getSize() == sizeof(uint32_t)) { |
| uint32_t reg = pkt->get<uint32_t>(); |
| uint16_t rfaddr; |
| |
| DPRINTF(EthernetPIO, "write data=%d data=%#x\n", reg, reg); |
| |
| switch (daddr) { |
| case CR: |
| regs.command = reg; |
| if (reg & CR_TXD) { |
| txEnable = false; |
| } else if (reg & CR_TXE) { |
| txEnable = true; |
| |
| // the kernel is enabling the transmit machine |
| if (txState == txIdle) |
| txKick(); |
| } |
| |
| if (reg & CR_RXD) { |
| rxEnable = false; |
| } else if (reg & CR_RXE) { |
| rxEnable = true; |
| |
| if (rxState == rxIdle) |
| rxKick(); |
| } |
| |
| 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 CFGR: |
| if (reg & CFGR_LNKSTS || |
| reg & CFGR_SPDSTS || |
| reg & CFGR_DUPSTS || |
| reg & CFGR_RESERVED || |
| reg & CFGR_T64ADDR || |
| reg & CFGR_PCI64_DET) { |
| // First clear all writable bits |
| regs.config &= CFGR_LNKSTS | CFGR_SPDSTS | CFGR_DUPSTS | |
| CFGR_RESERVED | CFGR_T64ADDR | |
| CFGR_PCI64_DET; |
| // Now set the appropriate writable bits |
| regs.config |= reg & ~(CFGR_LNKSTS | CFGR_SPDSTS | CFGR_DUPSTS | |
| CFGR_RESERVED | CFGR_T64ADDR | |
| CFGR_PCI64_DET); |
| } |
| |
| // all these #if 0's are because i don't THINK the kernel needs to |
| // have these implemented. if there is a problem relating to one of |
| // these, you may need to add functionality in. |
| |
| // grouped together and #if 0'ed to avoid empty if body and make clang happy |
| #if 0 |
| if (reg & CFGR_TBI_EN) ; |
| if (reg & CFGR_MODE_1000) ; |
| |
| if (reg & CFGR_PINT_DUPSTS || |
| reg & CFGR_PINT_LNKSTS || |
| reg & CFGR_PINT_SPDSTS) |
| ; |
| |
| if (reg & CFGR_TMRTEST) ; |
| if (reg & CFGR_MRM_DIS) ; |
| if (reg & CFGR_MWI_DIS) ; |
| |
| if (reg & CFGR_DATA64_EN) ; |
| if (reg & CFGR_M64ADDR) ; |
| if (reg & CFGR_PHY_RST) ; |
| if (reg & CFGR_PHY_DIS) ; |
| |
| if (reg & CFGR_REQALG) ; |
| if (reg & CFGR_SB) ; |
| if (reg & CFGR_POW) ; |
| if (reg & CFGR_EXD) ; |
| if (reg & CFGR_PESEL) ; |
| if (reg & CFGR_BROM_DIS) ; |
| if (reg & CFGR_EXT_125) ; |
| if (reg & CFGR_BEM) ; |
| |
| if (reg & CFGR_T64ADDR) ; |
| // panic("CFGR_T64ADDR is read only register!\n"); |
| #endif |
| if (reg & CFGR_AUTO_1000) |
| panic("CFGR_AUTO_1000 not implemented!\n"); |
| |
| if (reg & CFGR_PCI64_DET) |
| panic("CFGR_PCI64_DET is read only register!\n"); |
| |
| if (reg & CFGR_EXTSTS_EN) |
| extstsEnable = true; |
| else |
| extstsEnable = false; |
| break; |
| |
| case MEAR: |
| // Clear writable bits |
| regs.mear &= MEAR_EEDO; |
| // Set appropriate writable bits |
| regs.mear |= reg & ~MEAR_EEDO; |
| |
| // FreeBSD uses the EEPROM to read PMATCH (for the MAC address) |
| // even though it could get it through RFDR |
| if (reg & MEAR_EESEL) { |
| // Rising edge of clock |
| if (reg & MEAR_EECLK && !eepromClk) |
| eepromKick(); |
| } |
| else { |
| eepromState = eepromStart; |
| regs.mear &= ~MEAR_EEDI; |
| } |
| |
| eepromClk = reg & MEAR_EECLK; |
| |
| // since phy is completely faked, MEAR_MD* don't matter |
| |
| // grouped together and #if 0'ed to avoid empty if body and make clang happy |
| #if 0 |
| if (reg & MEAR_MDIO) ; |
| if (reg & MEAR_MDDIR) ; |
| if (reg & MEAR_MDC) ; |
| #endif |
| break; |
| |
| case PTSCR: |
| regs.ptscr = reg & ~(PTSCR_RBIST_RDONLY); |
| // these control BISTs for various parts of chip - we |
| // don't care or do just fake that the BIST is done |
| if (reg & PTSCR_RBIST_EN) |
| regs.ptscr |= PTSCR_RBIST_DONE; |
| if (reg & PTSCR_EEBIST_EN) |
| regs.ptscr &= ~PTSCR_EEBIST_EN; |
| if (reg & PTSCR_EELOAD_EN) |
| regs.ptscr &= ~PTSCR_EELOAD_EN; |
| 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 TX_CFG: |
| regs.txcfg = reg; |
| #if 0 |
| if (reg & TX_CFG_CSI) ; |
| if (reg & TX_CFG_HBI) ; |
| if (reg & TX_CFG_MLB) ; |
| if (reg & TX_CFG_ATP) ; |
| if (reg & TX_CFG_ECRETRY) { |
| /* |
| * this could easily be implemented, but considering |
| * the network is just a fake pipe, wouldn't make |
| * sense to do this |
| */ |
| } |
| |
| if (reg & TX_CFG_BRST_DIS) ; |
| #endif |
| |
| #if 0 |
| /* we handle our own DMA, ignore the kernel's exhortations */ |
| if (reg & TX_CFG_MXDMA) ; |
| #endif |
| |
| // also, we currently don't care about fill/drain |
| // thresholds though this may change in the future with |
| // more realistic networks or a driver which changes it |
| // according to feedback |
| |
| break; |
| |
| case GPIOR: |
| // Only write writable bits |
| regs.gpior &= GPIOR_UNUSED | GPIOR_GP5_IN | GPIOR_GP4_IN |
| | GPIOR_GP3_IN | GPIOR_GP2_IN | GPIOR_GP1_IN; |
| regs.gpior |= reg & ~(GPIOR_UNUSED | GPIOR_GP5_IN | GPIOR_GP4_IN |
| | GPIOR_GP3_IN | GPIOR_GP2_IN | GPIOR_GP1_IN); |
| /* these just control general purpose i/o pins, don't matter */ |
| break; |
| |
| case RXDP: |
| regs.rxdp = reg; |
| CRDD = false; |
| break; |
| |
| case RXDP_HI: |
| regs.rxdp_hi = reg; |
| break; |
| |
| case RX_CFG: |
| regs.rxcfg = reg; |
| #if 0 |
| if (reg & RX_CFG_AEP) ; |
| if (reg & RX_CFG_ARP) ; |
| if (reg & RX_CFG_STRIPCRC) ; |
| if (reg & RX_CFG_RX_RD) ; |
| if (reg & RX_CFG_ALP) ; |
| if (reg & RX_CFG_AIRL) ; |
| |
| /* we handle our own DMA, ignore what kernel says about it */ |
| if (reg & RX_CFG_MXDMA) ; |
| |
| //also, we currently don't care about fill/drain thresholds |
| //though this may change in the future with more realistic |
| //networks or a driver which changes it according to feedback |
| if (reg & (RX_CFG_DRTH | RX_CFG_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; |
| multicastHashEnable = (reg & RFCR_MHEN) ? true : false; |
| |
| #if 0 |
| if (reg & RFCR_APAT) |
| panic("RFCR_APAT not implemented!\n"); |
| #endif |
| if (reg & RFCR_UHEN) |
| panic("Unicast hash filtering not used by drivers!\n"); |
| |
| if (reg & RFCR_ULM) |
| panic("RFCR_ULM not implemented!\n"); |
| |
| break; |
| |
| case RFDR: |
| rfaddr = (uint16_t)(regs.rfcr & RFCR_RFADDR); |
| switch (rfaddr) { |
| case 0x000: |
| rom.perfectMatch[0] = (uint8_t)reg; |
| rom.perfectMatch[1] = (uint8_t)(reg >> 8); |
| break; |
| case 0x002: |
| rom.perfectMatch[2] = (uint8_t)reg; |
| rom.perfectMatch[3] = (uint8_t)(reg >> 8); |
| break; |
| case 0x004: |
| rom.perfectMatch[4] = (uint8_t)reg; |
| rom.perfectMatch[5] = (uint8_t)(reg >> 8); |
| break; |
| default: |
| |
| if (rfaddr >= FHASH_ADDR && |
| rfaddr < FHASH_ADDR + FHASH_SIZE) { |
| |
| // Only word-aligned writes supported |
| if (rfaddr % 2) |
| panic("unaligned write to filter hash table!"); |
| |
| rom.filterHash[rfaddr - FHASH_ADDR] = (uint8_t)reg; |
| rom.filterHash[rfaddr - FHASH_ADDR + 1] |
| = (uint8_t)(reg >> 8); |
| break; |
| } |
| panic("writing RFDR for something other than pattern matching " |
| "or hashing! %#x\n", rfaddr); |
| } |
| |
| case BRAR: |
| regs.brar = reg; |
| break; |
| |
| 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"); |
| |
| 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: |
| // Only write the writable bits |
| regs.tanar &= TANAR_RF1 | TANAR_RF2 | TANAR_UNUSED; |
| regs.tanar |= reg & ~(TANAR_RF1 | TANAR_RF2 | TANAR_UNUSED); |
| |
| // Pause capability unimplemented |
| #if 0 |
| if (reg & TANAR_PS2) ; |
| if (reg & TANAR_PS1) ; |
| #endif |
| |
| 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("invalid register access daddr=%#x", daddr); |
| } |
| } else { |
| panic("Invalid Request Size"); |
| } |
| pkt->makeAtomicResponse(); |
| return pioDelay; |
| } |
| |
| void |
| NSGigE::devIntrPost(uint32_t interrupts) |
| { |
| if (interrupts & ISR_RESERVE) |
| panic("Cannot set a reserved interrupt"); |
| |
| if (interrupts & ISR_NOIMPL) |
| warn("interrupt not implemented %#x\n", interrupts); |
| |
| interrupts &= ISR_IMPL; |
| regs.isr |= interrupts; |
| |
| if (interrupts & regs.imr) { |
| if (interrupts & ISR_SWI) { |
| totalSwi++; |
| } |
| if (interrupts & ISR_RXIDLE) { |
| totalRxIdle++; |
| } |
| if (interrupts & ISR_RXOK) { |
| totalRxOk++; |
| } |
| if (interrupts & ISR_RXDESC) { |
| totalRxDesc++; |
| } |
| if (interrupts & ISR_TXOK) { |
| totalTxOk++; |
| } |
| if (interrupts & ISR_TXIDLE) { |
| totalTxIdle++; |
| } |
| if (interrupts & ISR_TXDESC) { |
| totalTxDesc++; |
| } |
| if (interrupts & ISR_RXORN) { |
| totalRxOrn++; |
| } |
| } |
| |
| DPRINTF(EthernetIntr, |
| "interrupt written to ISR: intr=%#x isr=%#x imr=%#x\n", |
| interrupts, regs.isr, regs.imr); |
| |
| if ((regs.isr & regs.imr)) { |
| Tick when = curTick(); |
| if ((regs.isr & regs.imr & ISR_NODELAY) == 0) |
| when += intrDelay; |
| postedInterrupts++; |
| cpuIntrPost(when); |
| } |
| } |
| |
| /* writing this interrupt counting stats inside this means that this function |
| is now limited to being used to clear all interrupts upon the kernel |
| reading isr and servicing. just telling you in case you were thinking |
| of expanding use. |
| */ |
| void |
| NSGigE::devIntrClear(uint32_t interrupts) |
| { |
| if (interrupts & ISR_RESERVE) |
| panic("Cannot clear a reserved interrupt"); |
| |
| if (regs.isr & regs.imr & ISR_SWI) { |
| postedSwi++; |
| } |
| if (regs.isr & regs.imr & ISR_RXIDLE) { |
| postedRxIdle++; |
| } |
| if (regs.isr & regs.imr & ISR_RXOK) { |
| postedRxOk++; |
| } |
| if (regs.isr & regs.imr & ISR_RXDESC) { |
| postedRxDesc++; |
| } |
| if (regs.isr & regs.imr & ISR_TXOK) { |
| postedTxOk++; |
| } |
| if (regs.isr & regs.imr & ISR_TXIDLE) { |
| postedTxIdle++; |
| } |
| if (regs.isr & regs.imr & ISR_TXDESC) { |
| postedTxDesc++; |
| } |
| if (regs.isr & regs.imr & ISR_RXORN) { |
| postedRxOrn++; |
| } |
| |
| interrupts &= ~ISR_NOIMPL; |
| regs.isr &= ~interrupts; |
| |
| DPRINTF(EthernetIntr, |
| "interrupt cleared from ISR: intr=%x isr=%x imr=%x\n", |
| interrupts, regs.isr, regs.imr); |
| |
| if (!(regs.isr & regs.imr)) |
| cpuIntrClear(); |
| } |
| |
| void |
| NSGigE::devIntrChangeMask() |
| { |
| DPRINTF(EthernetIntr, "interrupt mask changed: isr=%x imr=%x masked=%x\n", |
| regs.isr, regs.imr, regs.isr & regs.imr); |
| |
| if (regs.isr & regs.imr) |
| cpuIntrPost(curTick()); |
| else |
| cpuIntrClear(); |
| } |
| |
| void |
| NSGigE::cpuIntrPost(Tick when) |
| { |
| // If the interrupt you want to post is later than an interrupt |
| // already scheduled, just let it post in the coming one and don't |
| // schedule another. |
| // HOWEVER, must be sure that the scheduled intrTick is in the |
| // future (this was formerly the source of a bug) |
| /** |
| * @todo this warning should be removed and the intrTick code should |
| * be fixed. |
| */ |
| assert(when >= curTick()); |
| assert(intrTick >= curTick() || intrTick == 0); |
| if (when > intrTick && intrTick != 0) { |
| DPRINTF(EthernetIntr, "don't need to schedule event...intrTick=%d\n", |
| intrTick); |
| return; |
| } |
| |
| intrTick = when; |
| if (intrTick < curTick()) { |
| intrTick = curTick(); |
| } |
| |
| DPRINTF(EthernetIntr, "going to schedule an interrupt for intrTick=%d\n", |
| intrTick); |
| |
| if (intrEvent) |
| intrEvent->squash(); |
| |
| intrEvent = new EventFunctionWrapper([this]{ cpuInterrupt(); }, |
| name(), true); |
| schedule(intrEvent, intrTick); |
| } |
| |
| void |
| NSGigE::cpuInterrupt() |
| { |
| assert(intrTick == curTick()); |
| |
| // Whether or not there's a pending interrupt, we don't care about |
| // it anymore |
| intrEvent = 0; |
| intrTick = 0; |
| |
| // Don't send an interrupt if there's already one |
| if (cpuPendingIntr) { |
| DPRINTF(EthernetIntr, |
| "would send an interrupt now, but there's already pending\n"); |
| } else { |
| // Send interrupt |
| cpuPendingIntr = true; |
| |
| DPRINTF(EthernetIntr, "posting interrupt\n"); |
| intrPost(); |
| } |
| } |
| |
| void |
| NSGigE::cpuIntrClear() |
| { |
| if (!cpuPendingIntr) |
| return; |
| |
| if (intrEvent) { |
| intrEvent->squash(); |
| intrEvent = 0; |
| } |
| |
| intrTick = 0; |
| |
| cpuPendingIntr = false; |
| |
| DPRINTF(EthernetIntr, "clearing interrupt\n"); |
| intrClear(); |
| } |
| |
| bool |
| NSGigE::cpuIntrPending() const |
| { return cpuPendingIntr; } |
| |
| void |
| NSGigE::txReset() |
| { |
| |
| DPRINTF(Ethernet, "transmit reset\n"); |
| |
| CTDD = false; |
| txEnable = false;; |
| txFragPtr = 0; |
| assert(txDescCnt == 0); |
| txFifo.clear(); |
| txState = txIdle; |
| assert(txDmaState == dmaIdle); |
| } |
| |
| void |
| NSGigE::rxReset() |
| { |
| DPRINTF(Ethernet, "receive reset\n"); |
| |
| CRDD = false; |
| assert(rxPktBytes == 0); |
| rxEnable = false; |
| rxFragPtr = 0; |
| assert(rxDescCnt == 0); |
| assert(rxDmaState == dmaIdle); |
| rxFifo.clear(); |
| rxState = rxIdle; |
| } |
| |
| void |
| NSGigE::regsReset() |
| { |
| memset(®s, 0, sizeof(regs)); |
| regs.config = (CFGR_LNKSTS | CFGR_TBI_EN | CFGR_MODE_1000); |
| regs.mear = 0x12; |
| regs.txcfg = 0x120; // set drain threshold to 1024 bytes and |
| // fill threshold to 32 bytes |
| regs.rxcfg = 0x4; // set drain threshold to 16 bytes |
| regs.srr = 0x0103; // set the silicon revision to rev B or 0x103 |
| regs.mibc = MIBC_FRZ; |
| regs.vdr = 0x81; // set the vlan tag type to 802.1q |
| regs.tesr = 0xc000; // TBI capable of both full and half duplex |
| regs.brar = 0xffffffff; |
| |
| extstsEnable = false; |
| acceptBroadcast = false; |
| acceptMulticast = false; |
| acceptUnicast = false; |
| acceptPerfect = false; |
| acceptArp = false; |
| } |
| |
| bool |
| NSGigE::doRxDmaRead() |
| { |
| assert(rxDmaState == dmaIdle || rxDmaState == dmaReadWaiting); |
| rxDmaState = dmaReading; |
| |
| if (dmaPending() || drainState() != DrainState::Running) |
| rxDmaState = dmaReadWaiting; |
| else |
| dmaRead(rxDmaAddr, rxDmaLen, &rxDmaReadEvent, (uint8_t*)rxDmaData); |
| |
| return true; |
| } |
| |
| void |
| NSGigE::rxDmaReadDone() |
| { |
| assert(rxDmaState == dmaReading); |
| rxDmaState = dmaIdle; |
| |
| DPRINTF(EthernetDMA, "rx dma read paddr=%#x len=%d\n", |
| rxDmaAddr, rxDmaLen); |
| DDUMP(EthernetDMA, rxDmaData, rxDmaLen); |
| |
| // If the transmit state machine has a pending DMA, let it go first |
| if (txDmaState == dmaReadWaiting || txDmaState == dmaWriteWaiting) |
| txKick(); |
| |
| rxKick(); |
| } |
| |
| bool |
| NSGigE::doRxDmaWrite() |
| { |
| assert(rxDmaState == dmaIdle || rxDmaState == dmaWriteWaiting); |
| rxDmaState = dmaWriting; |
| |
| if (dmaPending() || drainState() != DrainState::Running) |
| rxDmaState = dmaWriteWaiting; |
| else |
| dmaWrite(rxDmaAddr, rxDmaLen, &rxDmaWriteEvent, (uint8_t*)rxDmaData); |
| return true; |
| } |
| |
| void |
| NSGigE::rxDmaWriteDone() |
| { |
| assert(rxDmaState == dmaWriting); |
| rxDmaState = dmaIdle; |
| |
| DPRINTF(EthernetDMA, "rx dma write paddr=%#x len=%d\n", |
| rxDmaAddr, rxDmaLen); |
| DDUMP(EthernetDMA, rxDmaData, rxDmaLen); |
| |
| // If the transmit state machine has a pending DMA, let it go first |
| if (txDmaState == dmaReadWaiting || txDmaState == dmaWriteWaiting) |
| txKick(); |
| |
| rxKick(); |
| } |
| |
| void |
| NSGigE::rxKick() |
| { |
| bool is64bit = (bool)(regs.config & CFGR_M64ADDR); |
| |
| DPRINTF(EthernetSM, |
| "receive kick rxState=%s (rxBuf.size=%d) %d-bit\n", |
| NsRxStateStrings[rxState], rxFifo.size(), is64bit ? 64 : 32); |
| |
| Addr link, bufptr; |
| uint32_t &cmdsts = is64bit ? rxDesc64.cmdsts : rxDesc32.cmdsts; |
| uint32_t &extsts = is64bit ? rxDesc64.extsts : rxDesc32.extsts; |
| |
| next: |
| if (rxKickTick > curTick()) { |
| DPRINTF(EthernetSM, "receive kick exiting, can't run till %d\n", |
| rxKickTick); |
| |
| goto exit; |
| } |
| |
| // Go to the next state machine clock tick. |
| rxKickTick = clockEdge(Cycles(1)); |
| |
| switch(rxDmaState) { |
| case dmaReadWaiting: |
| if (doRxDmaRead()) |
| goto exit; |
| break; |
| case dmaWriteWaiting: |
| if (doRxDmaWrite()) |
| goto exit; |
| break; |
| default: |
| break; |
| } |
| |
| link = is64bit ? (Addr)rxDesc64.link : (Addr)rxDesc32.link; |
| bufptr = is64bit ? (Addr)rxDesc64.bufptr : (Addr)rxDesc32.bufptr; |
| |
| // see state machine from spec for details |
| // the way this works is, if you finish work on one state and can |
| // go directly to another, you do that through jumping to the |
| // label "next". however, if you have intermediate work, like DMA |
| // so that you can't go to the next state yet, you go to exit and |
| // exit the loop. however, when the DMA is done it will trigger |
| // an event and come back to this loop. |
| switch (rxState) { |
| case rxIdle: |
| if (!rxEnable) { |
| DPRINTF(EthernetSM, "Receive Disabled! Nothing to do.\n"); |
| goto exit; |
| } |
| |
| if (CRDD) { |
| rxState = rxDescRefr; |
| |
| rxDmaAddr = regs.rxdp & 0x3fffffff; |
| rxDmaData = |
| is64bit ? (void *)&rxDesc64.link : (void *)&rxDesc32.link; |
| rxDmaLen = is64bit ? sizeof(rxDesc64.link) : sizeof(rxDesc32.link); |
| rxDmaFree = dmaDescFree; |
| |
| descDmaReads++; |
| descDmaRdBytes += rxDmaLen; |
| |
| if (doRxDmaRead()) |
| goto exit; |
| } else { |
| rxState = rxDescRead; |
| |
| rxDmaAddr = regs.rxdp & 0x3fffffff; |
| rxDmaData = is64bit ? (void *)&rxDesc64 : (void *)&rxDesc32; |
| rxDmaLen = is64bit ? sizeof(rxDesc64) : sizeof(rxDesc32); |
| rxDmaFree = dmaDescFree; |
| |
| descDmaReads++; |
| descDmaRdBytes += rxDmaLen; |
| |
| if (doRxDmaRead()) |
| goto exit; |
| } |
| break; |
| |
| case rxDescRefr: |
| if (rxDmaState != dmaIdle) |
| goto exit; |
| |
| rxState = rxAdvance; |
| break; |
| |
| case rxDescRead: |
| if (rxDmaState != dmaIdle) |
| goto exit; |
| |
| DPRINTF(EthernetDesc, "rxDesc: addr=%08x read descriptor\n", |
| regs.rxdp & 0x3fffffff); |
| DPRINTF(EthernetDesc, |
| "rxDesc: link=%#x bufptr=%#x cmdsts=%08x extsts=%08x\n", |
| link, bufptr, cmdsts, extsts); |
| |
| if (cmdsts & CMDSTS_OWN) { |
| devIntrPost(ISR_RXIDLE); |
| rxState = rxIdle; |
| goto exit; |
| } else { |
| rxState = rxFifoBlock; |
| rxFragPtr = bufptr; |
| rxDescCnt = cmdsts & CMDSTS_LEN_MASK; |
| } |
| break; |
| |
| case rxFifoBlock: |
| if (!rxPacket) { |
| /** |
| * @todo in reality, we should be able to start processing |
| * the packet as it arrives, and not have to wait for the |
| * full packet ot be in the receive fifo. |
| */ |
| if (rxFifo.empty()) |
| goto exit; |
| |
| DPRINTF(EthernetSM, "****processing receive of new packet****\n"); |
| |
| // If we don't have a packet, grab a new one from the fifo. |
| rxPacket = rxFifo.front(); |
| rxPktBytes = rxPacket->length; |
| rxPacketBufPtr = rxPacket->data; |
| |
| #if TRACING_ON |
| if (DTRACE(Ethernet)) { |
| IpPtr ip(rxPacket); |
| if (ip) { |
| DPRINTF(Ethernet, "ID is %d\n", ip->id()); |
| TcpPtr tcp(ip); |
| if (tcp) { |
| DPRINTF(Ethernet, |
| "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n", |
| tcp->sport(), tcp->dport(), tcp->seq(), |
| tcp->ack()); |
| } |
| } |
| } |
| #endif |
| |
| // sanity check - i think the driver behaves like this |
| assert(rxDescCnt >= rxPktBytes); |
| rxFifo.pop(); |
| } |
| |
| |
| // dont' need the && rxDescCnt > 0 if driver sanity check |
| // above holds |
| if (rxPktBytes > 0) { |
| rxState = rxFragWrite; |
| // don't need min<>(rxPktBytes,rxDescCnt) if above sanity |
| // check holds |
| rxXferLen = rxPktBytes; |
| |
| rxDmaAddr = rxFragPtr & 0x3fffffff; |
| rxDmaData = rxPacketBufPtr; |
| rxDmaLen = rxXferLen; |
| rxDmaFree = dmaDataFree; |
| |
| if (doRxDmaWrite()) |
| goto exit; |
| |
| } else { |
| rxState = rxDescWrite; |
| |
| //if (rxPktBytes == 0) { /* packet is done */ |
| assert(rxPktBytes == 0); |
| DPRINTF(EthernetSM, "done with receiving packet\n"); |
| |
| cmdsts |= CMDSTS_OWN; |
| cmdsts &= ~CMDSTS_MORE; |
| cmdsts |= CMDSTS_OK; |
| cmdsts &= 0xffff0000; |
| cmdsts += rxPacket->length; //i.e. set CMDSTS_SIZE |
| |
| #if 0 |
| /* |
| * all the driver uses these are for its own stats keeping |
| * which we don't care about, aren't necessary for |
| * functionality and doing this would just slow us down. |
| * if they end up using this in a later version for |
| * functional purposes, just undef |
| */ |
| if (rxFilterEnable) { |
| cmdsts &= ~CMDSTS_DEST_MASK; |
| const EthAddr &dst = rxFifoFront()->dst(); |
| if (dst->unicast()) |
| cmdsts |= CMDSTS_DEST_SELF; |
| if (dst->multicast()) |
| cmdsts |= CMDSTS_DEST_MULTI; |
| if (dst->broadcast()) |
| cmdsts |= CMDSTS_DEST_MASK; |
| } |
| #endif |
| |
| IpPtr ip(rxPacket); |
| if (extstsEnable && ip) { |
| extsts |= EXTSTS_IPPKT; |
| rxIpChecksums++; |
| if (cksum(ip) != 0) { |
| DPRINTF(EthernetCksum, "Rx IP Checksum Error\n"); |
| extsts |= EXTSTS_IPERR; |
| } |
| TcpPtr tcp(ip); |
| UdpPtr udp(ip); |
| if (tcp) { |
| extsts |= EXTSTS_TCPPKT; |
| rxTcpChecksums++; |
| if (cksum(tcp) != 0) { |
| DPRINTF(EthernetCksum, "Rx TCP Checksum Error\n"); |
| extsts |= EXTSTS_TCPERR; |
| |
| } |
| } else if (udp) { |
| extsts |= EXTSTS_UDPPKT; |
| rxUdpChecksums++; |
| if (cksum(udp) != 0) { |
| DPRINTF(EthernetCksum, "Rx UDP Checksum Error\n"); |
| extsts |= EXTSTS_UDPERR; |
| } |
| } |
| } |
| rxPacket = 0; |
| |
| /* |
| * the driver seems to always receive into desc buffers |
| * of size 1514, so you never have a pkt that is split |
| * into multiple descriptors on the receive side, so |
| * i don't implement that case, hence the assert above. |
| */ |
| |
| DPRINTF(EthernetDesc, |
| "rxDesc: addr=%08x writeback cmdsts extsts\n", |
| regs.rxdp & 0x3fffffff); |
| DPRINTF(EthernetDesc, |
| "rxDesc: link=%#x bufptr=%#x cmdsts=%08x extsts=%08x\n", |
| link, bufptr, cmdsts, extsts); |
| |
| rxDmaAddr = regs.rxdp & 0x3fffffff; |
| rxDmaData = &cmdsts; |
| if (is64bit) { |
| rxDmaAddr += offsetof(ns_desc64, cmdsts); |
| rxDmaLen = sizeof(rxDesc64.cmdsts) + sizeof(rxDesc64.extsts); |
| } else { |
| rxDmaAddr += offsetof(ns_desc32, cmdsts); |
| rxDmaLen = sizeof(rxDesc32.cmdsts) + sizeof(rxDesc32.extsts); |
| } |
| rxDmaFree = dmaDescFree; |
| |
| descDmaWrites++; |
| descDmaWrBytes += rxDmaLen; |
| |
| if (doRxDmaWrite()) |
| goto exit; |
| } |
| break; |
| |
| case rxFragWrite: |
| if (rxDmaState != dmaIdle) |
| goto exit; |
| |
| rxPacketBufPtr += rxXferLen; |
| rxFragPtr += rxXferLen; |
| rxPktBytes -= rxXferLen; |
| |
| rxState = rxFifoBlock; |
| break; |
| |
| case rxDescWrite: |
| if (rxDmaState != dmaIdle) |
| goto exit; |
| |
| assert(cmdsts & CMDSTS_OWN); |
| |
| assert(rxPacket == 0); |
| devIntrPost(ISR_RXOK); |
| |
| if (cmdsts & CMDSTS_INTR) |
| devIntrPost(ISR_RXDESC); |
| |
| if (!rxEnable) { |
| DPRINTF(EthernetSM, "Halting the RX state machine\n"); |
| rxState = rxIdle; |
| goto exit; |
| } else |
| rxState = rxAdvance; |
| break; |
| |
| case rxAdvance: |
| if (link == 0) { |
| devIntrPost(ISR_RXIDLE); |
| rxState = rxIdle; |
| CRDD = true; |
| goto exit; |
| } else { |
| if (rxDmaState != dmaIdle) |
| goto exit; |
| rxState = rxDescRead; |
| regs.rxdp = link; |
| CRDD = false; |
| |
| rxDmaAddr = regs.rxdp & 0x3fffffff; |
| rxDmaData = is64bit ? (void *)&rxDesc64 : (void *)&rxDesc32; |
| rxDmaLen = is64bit ? sizeof(rxDesc64) : sizeof(rxDesc32); |
| rxDmaFree = dmaDescFree; |
| |
| if (doRxDmaRead()) |
| goto exit; |
| } |
| break; |
| |
| default: |
| panic("Invalid rxState!"); |
| } |
| |
| DPRINTF(EthernetSM, "entering next rxState=%s\n", |
| NsRxStateStrings[rxState]); |
| goto next; |
| |
| exit: |
| /** |
| * @todo do we want to schedule a future kick? |
| */ |
| DPRINTF(EthernetSM, "rx state machine exited rxState=%s\n", |
| NsRxStateStrings[rxState]); |
| |
| if (!rxKickEvent.scheduled()) |
| schedule(rxKickEvent, rxKickTick); |
| } |
| |
| void |
| NSGigE::transmit() |
| { |
| if (txFifo.empty()) { |
| DPRINTF(Ethernet, "nothing to transmit\n"); |
| return; |
| } |
| |
| DPRINTF(Ethernet, "Attempt Pkt Transmit: txFifo length=%d\n", |
| txFifo.size()); |
| if (interface->sendPacket(txFifo.front())) { |
| #if TRACING_ON |
| if (DTRACE(Ethernet)) { |
| IpPtr ip(txFifo.front()); |
| if (ip) { |
| DPRINTF(Ethernet, "ID is %d\n", ip->id()); |
| TcpPtr tcp(ip); |
| if (tcp) { |
| DPRINTF(Ethernet, |
| "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n", |
| tcp->sport(), tcp->dport(), tcp->seq(), |
| tcp->ack()); |
| } |
| } |
| } |
| #endif |
| |
| DDUMP(EthernetData, txFifo.front()->data, txFifo.front()->length); |
| txBytes += txFifo.front()->length; |
| txPackets++; |
| |
| DPRINTF(Ethernet, "Successful Xmit! now txFifoAvail is %d\n", |
| txFifo.avail()); |
| txFifo.pop(); |
| |
| /* |
| * normally do a writeback of the descriptor here, and ONLY |
| * after that is done, send this interrupt. but since our |
| * stuff never actually fails, just do this interrupt here, |
| * otherwise the code has to stray from this nice format. |
| * besides, it's functionally the same. |
| */ |
| devIntrPost(ISR_TXOK); |
| } |
| |
| if (!txFifo.empty() && !txEvent.scheduled()) { |
| DPRINTF(Ethernet, "reschedule transmit\n"); |
| schedule(txEvent, curTick() + retryTime); |
| } |
| } |
| |
| bool |
| NSGigE::doTxDmaRead() |
| { |
| assert(txDmaState == dmaIdle || txDmaState == dmaReadWaiting); |
| txDmaState = dmaReading; |
| |
| if (dmaPending() || drainState() != DrainState::Running) |
| txDmaState = dmaReadWaiting; |
| else |
| dmaRead(txDmaAddr, txDmaLen, &txDmaReadEvent, (uint8_t*)txDmaData); |
| |
| return true; |
| } |
| |
| void |
| NSGigE::txDmaReadDone() |
| { |
| assert(txDmaState == dmaReading); |
| txDmaState = dmaIdle; |
| |
| DPRINTF(EthernetDMA, "tx dma read paddr=%#x len=%d\n", |
| txDmaAddr, txDmaLen); |
| DDUMP(EthernetDMA, txDmaData, txDmaLen); |
| |
| // If the receive state machine has a pending DMA, let it go first |
| if (rxDmaState == dmaReadWaiting || rxDmaState == dmaWriteWaiting) |
| rxKick(); |
| |
| txKick(); |
| } |
| |
| bool |
| NSGigE::doTxDmaWrite() |
| { |
| assert(txDmaState == dmaIdle || txDmaState == dmaWriteWaiting); |
| txDmaState = dmaWriting; |
| |
| if (dmaPending() || drainState() != DrainState::Running) |
| txDmaState = dmaWriteWaiting; |
| else |
| dmaWrite(txDmaAddr, txDmaLen, &txDmaWriteEvent, (uint8_t*)txDmaData); |
| return true; |
| } |
| |
| void |
| NSGigE::txDmaWriteDone() |
| { |
| assert(txDmaState == dmaWriting); |
| txDmaState = dmaIdle; |
| |
| DPRINTF(EthernetDMA, "tx dma write paddr=%#x len=%d\n", |
| txDmaAddr, txDmaLen); |
| DDUMP(EthernetDMA, txDmaData, txDmaLen); |
| |
| // If the receive state machine has a pending DMA, let it go first |
| if (rxDmaState == dmaReadWaiting || rxDmaState == dmaWriteWaiting) |
| rxKick(); |
| |
| txKick(); |
| } |
| |
| void |
| NSGigE::txKick() |
| { |
| bool is64bit = (bool)(regs.config & CFGR_M64ADDR); |
| |
| DPRINTF(EthernetSM, "transmit kick txState=%s %d-bit\n", |
| NsTxStateStrings[txState], is64bit ? 64 : 32); |
| |
| Addr link, bufptr; |
| uint32_t &cmdsts = is64bit ? txDesc64.cmdsts : txDesc32.cmdsts; |
| uint32_t &extsts = is64bit ? txDesc64.extsts : txDesc32.extsts; |
| |
| next: |
| if (txKickTick > curTick()) { |
| DPRINTF(EthernetSM, "transmit kick exiting, can't run till %d\n", |
| txKickTick); |
| goto exit; |
| } |
| |
| // Go to the next state machine clock tick. |
| txKickTick = clockEdge(Cycles(1)); |
| |
| switch(txDmaState) { |
| case dmaReadWaiting: |
| if (doTxDmaRead()) |
| goto exit; |
| break; |
| case dmaWriteWaiting: |
| if (doTxDmaWrite()) |
| goto exit; |
| break; |
| default: |
| break; |
| } |
| |
| link = is64bit ? (Addr)txDesc64.link : (Addr)txDesc32.link; |
| bufptr = is64bit ? (Addr)txDesc64.bufptr : (Addr)txDesc32.bufptr; |
| switch (txState) { |
| case txIdle: |
| if (!txEnable) { |
| DPRINTF(EthernetSM, "Transmit disabled. Nothing to do.\n"); |
| goto exit; |
| } |
| |
| if (CTDD) { |
| txState = txDescRefr; |
| |
| txDmaAddr = regs.txdp & 0x3fffffff; |
| txDmaData = |
| is64bit ? (void *)&txDesc64.link : (void *)&txDesc32.link; |
| txDmaLen = is64bit ? sizeof(txDesc64.link) : sizeof(txDesc32.link); |
| txDmaFree = dmaDescFree; |
| |
| descDmaReads++; |
| descDmaRdBytes += txDmaLen; |
| |
| if (doTxDmaRead()) |
| goto exit; |
| |
| } else { |
| txState = txDescRead; |
| |
| txDmaAddr = regs.txdp & 0x3fffffff; |
| txDmaData = is64bit ? (void *)&txDesc64 : (void *)&txDesc32; |
| txDmaLen = is64bit ? sizeof(txDesc64) : sizeof(txDesc32); |
| txDmaFree = dmaDescFree; |
| |
| descDmaReads++; |
| descDmaRdBytes += txDmaLen; |
| |
| if (doTxDmaRead()) |
| goto exit; |
| } |
| break; |
| |
| case txDescRefr: |
| if (txDmaState != dmaIdle) |
| goto exit; |
| |
| txState = txAdvance; |
| break; |
| |
| case txDescRead: |
| if (txDmaState != dmaIdle) |
| goto exit; |
| |
| DPRINTF(EthernetDesc, "txDesc: addr=%08x read descriptor\n", |
| regs.txdp & 0x3fffffff); |
| DPRINTF(EthernetDesc, |
| "txDesc: link=%#x bufptr=%#x cmdsts=%#08x extsts=%#08x\n", |
| link, bufptr, cmdsts, extsts); |
| |
| if (cmdsts & CMDSTS_OWN) { |
| txState = txFifoBlock; |
| txFragPtr = bufptr; |
| txDescCnt = cmdsts & CMDSTS_LEN_MASK; |
| } else { |
| devIntrPost(ISR_TXIDLE); |
| txState = txIdle; |
| goto exit; |
| } |
| break; |
| |
| case txFifoBlock: |
| if (!txPacket) { |
| DPRINTF(EthernetSM, "****starting the tx of a new packet****\n"); |
| txPacket = make_shared<EthPacketData>(16384); |
| txPacketBufPtr = txPacket->data; |
| } |
| |
| if (txDescCnt == 0) { |
| DPRINTF(EthernetSM, "the txDescCnt == 0, done with descriptor\n"); |
| if (cmdsts & CMDSTS_MORE) { |
| DPRINTF(EthernetSM, "there are more descriptors to come\n"); |
| txState = txDescWrite; |
| |
| cmdsts &= ~CMDSTS_OWN; |
| |
| txDmaAddr = regs.txdp & 0x3fffffff; |
| txDmaData = &cmdsts; |
| if (is64bit) { |
| txDmaAddr += offsetof(ns_desc64, cmdsts); |
| txDmaLen = sizeof(txDesc64.cmdsts); |
| } else { |
| txDmaAddr += offsetof(ns_desc32, cmdsts); |
| txDmaLen = sizeof(txDesc32.cmdsts); |
| } |
| txDmaFree = dmaDescFree; |
| |
| if (doTxDmaWrite()) |
| goto exit; |
| |
| } else { /* this packet is totally done */ |
| DPRINTF(EthernetSM, "This packet is done, let's wrap it up\n"); |
| /* deal with the the packet that just finished */ |
| if ((regs.vtcr & VTCR_PPCHK) && extstsEnable) { |
| IpPtr ip(txPacket); |
| if (extsts & EXTSTS_UDPPKT) { |
| UdpPtr udp(ip); |
| if (udp) { |
| udp->sum(0); |
| udp->sum(cksum(udp)); |
| txUdpChecksums++; |
| } else { |
| Debug::breakpoint(); |
| warn_once("UDPPKT set, but not UDP!\n"); |
| } |
| } else if (extsts & EXTSTS_TCPPKT) { |
| TcpPtr tcp(ip); |
| if (tcp) { |
| tcp->sum(0); |
| tcp->sum(cksum(tcp)); |
| txTcpChecksums++; |
| } else { |
| warn_once("TCPPKT set, but not UDP!\n"); |
| } |
| } |
| if (extsts & EXTSTS_IPPKT) { |
| if (ip) { |
| ip->sum(0); |
| ip->sum(cksum(ip)); |
| txIpChecksums++; |
| } else { |
| warn_once("IPPKT set, but not UDP!\n"); |
| } |
| } |
| } |
| |
| txPacket->simLength = txPacketBufPtr - txPacket->data; |
| txPacket->length = txPacketBufPtr - txPacket->data; |
| // this is just because the receive can't handle a |
| // packet bigger want to make sure |
| if (txPacket->length > 1514) |
| panic("transmit packet too large, %s > 1514\n", |
| txPacket->length); |
| |
| #ifndef NDEBUG |
| bool success = |
| #endif |
| txFifo.push(txPacket); |
| assert(success); |
| |
| /* |
| * this following section is not tqo spec, but |
| * functionally shouldn't be any different. normally, |
| * the chip will wait til the transmit has occurred |
| * before writing back the descriptor because it has |
| * to wait to see that it was successfully transmitted |
| * to decide whether to set CMDSTS_OK or not. |
| * however, in the simulator since it is always |
| * successfully transmitted, and writing it exactly to |
| * spec would complicate the code, we just do it here |
| */ |
| |
| cmdsts &= ~CMDSTS_OWN; |
| cmdsts |= CMDSTS_OK; |
| |
| DPRINTF(EthernetDesc, |
| "txDesc writeback: cmdsts=%08x extsts=%08x\n", |
| cmdsts, extsts); |
| |
| txDmaFree = dmaDescFree; |
| txDmaAddr = regs.txdp & 0x3fffffff; |
| txDmaData = &cmdsts; |
| if (is64bit) { |
| txDmaAddr += offsetof(ns_desc64, cmdsts); |
| txDmaLen = |
| sizeof(txDesc64.cmdsts) + sizeof(txDesc64.extsts); |
| } else { |
| txDmaAddr += offsetof(ns_desc32, cmdsts); |
| txDmaLen = |
| sizeof(txDesc32.cmdsts) + sizeof(txDesc32.extsts); |
| } |
| |
| descDmaWrites++; |
| descDmaWrBytes += txDmaLen; |
| |
| transmit(); |
| txPacket = 0; |
| |
| if (!txEnable) { |
| DPRINTF(EthernetSM, "halting TX state machine\n"); |
| txState = txIdle; |
| goto exit; |
| } else |
| txState = txAdvance; |
| |
| if (doTxDmaWrite()) |
| goto exit; |
| } |
| } else { |
| DPRINTF(EthernetSM, "this descriptor isn't done yet\n"); |
| if (!txFifo.full()) { |
| txState = txFragRead; |
| |
| /* |
| * The number of bytes transferred is either whatever |
| * is left in the descriptor (txDescCnt), or if there |
| * is not enough room in the fifo, just whatever room |
| * is left in the fifo |
| */ |
| txXferLen = min<uint32_t>(txDescCnt, txFifo.avail()); |
| |
| txDmaAddr = txFragPtr & 0x3fffffff; |
| txDmaData = txPacketBufPtr; |
| txDmaLen = txXferLen; |
| txDmaFree = dmaDataFree; |
| |
| if (doTxDmaRead()) |
| goto exit; |
| } else { |
| txState = txFifoBlock; |
| transmit(); |
| |
| goto exit; |
| } |
| |
| } |
| break; |
| |
| case txFragRead: |
| if (txDmaState != dmaIdle) |
| goto exit; |
| |
| txPacketBufPtr += txXferLen; |
| txFragPtr += txXferLen; |
| txDescCnt -= txXferLen; |
| txFifo.reserve(txXferLen); |
| |
| txState = txFifoBlock; |
| break; |
| |
| case txDescWrite: |
| if (txDmaState != dmaIdle) |
| goto exit; |
| |
| if (cmdsts & CMDSTS_INTR) |
| devIntrPost(ISR_TXDESC); |
| |
| if (!txEnable) { |
| DPRINTF(EthernetSM, "halting TX state machine\n"); |
| txState = txIdle; |
| goto exit; |
| } else |
| txState = txAdvance; |
| break; |
| |
| case txAdvance: |
| if (link == 0) { |
| devIntrPost(ISR_TXIDLE); |
| txState = txIdle; |
| goto exit; |
| } else { |
| if (txDmaState != dmaIdle) |
| goto exit; |
| txState = txDescRead; |
| regs.txdp = link; |
| CTDD = false; |
| |
| txDmaAddr = link & 0x3fffffff; |
| txDmaData = is64bit ? (void *)&txDesc64 : (void *)&txDesc32; |
| txDmaLen = is64bit ? sizeof(txDesc64) : sizeof(txDesc32); |
| txDmaFree = dmaDescFree; |
| |
| if (doTxDmaRead()) |
| goto exit; |
| } |
| break; |
| |
| default: |
| panic("invalid state"); |
| } |
| |
| DPRINTF(EthernetSM, "entering next txState=%s\n", |
| NsTxStateStrings[txState]); |
| goto next; |
| |
| exit: |
| /** |
| * @todo do we want to schedule a future kick? |
| */ |
| DPRINTF(EthernetSM, "tx state machine exited txState=%s\n", |
| NsTxStateStrings[txState]); |
| |
| if (!txKickEvent.scheduled()) |
| schedule(txKickEvent, txKickTick); |
| } |
| |
| /** |
| * Advance the EEPROM state machine |
| * Called on rising edge of EEPROM clock bit in MEAR |
| */ |
| void |
| NSGigE::eepromKick() |
| { |
| switch (eepromState) { |
| |
| case eepromStart: |
| |
| // Wait for start bit |
| if (regs.mear & MEAR_EEDI) { |
| // Set up to get 2 opcode bits |
| eepromState = eepromGetOpcode; |
| eepromBitsToRx = 2; |
| eepromOpcode = 0; |
| } |
| break; |
| |
| case eepromGetOpcode: |
| eepromOpcode <<= 1; |
| eepromOpcode += (regs.mear & MEAR_EEDI) ? 1 : 0; |
| --eepromBitsToRx; |
| |
| // Done getting opcode |
| if (eepromBitsToRx == 0) { |
| if (eepromOpcode != EEPROM_READ) |
| panic("only EEPROM reads are implemented!"); |
| |
| // Set up to get address |
| eepromState = eepromGetAddress; |
| eepromBitsToRx = 6; |
| eepromAddress = 0; |
| } |
| break; |
| |
| case eepromGetAddress: |
| eepromAddress <<= 1; |
| eepromAddress += (regs.mear & MEAR_EEDI) ? 1 : 0; |
| --eepromBitsToRx; |
| |
| // Done getting address |
| if (eepromBitsToRx == 0) { |
| |
| if (eepromAddress >= EEPROM_SIZE) |
| panic("EEPROM read access out of range!"); |
| |
| switch (eepromAddress) { |
| |
| case EEPROM_PMATCH2_ADDR: |
| eepromData = rom.perfectMatch[5]; |
| eepromData <<= 8; |
| eepromData += rom.perfectMatch[4]; |
| break; |
| |
| case EEPROM_PMATCH1_ADDR: |
| eepromData = rom.perfectMatch[3]; |
| eepromData <<= 8; |
| eepromData += rom.perfectMatch[2]; |
| break; |
| |
| case EEPROM_PMATCH0_ADDR: |
| eepromData = rom.perfectMatch[1]; |
| eepromData <<= 8; |
| eepromData += rom.perfectMatch[0]; |
| break; |
| |
| default: |
| panic("FreeBSD driver only uses EEPROM to read PMATCH!"); |
| } |
| // Set up to read data |
| eepromState = eepromRead; |
| eepromBitsToRx = 16; |
| |
| // Clear data in bit |
| regs.mear &= ~MEAR_EEDI; |
| } |
| break; |
| |
| case eepromRead: |
| // Clear Data Out bit |
| regs.mear &= ~MEAR_EEDO; |
| // Set bit to value of current EEPROM bit |
| regs.mear |= (eepromData & 0x8000) ? MEAR_EEDO : 0x0; |
| |
| eepromData <<= 1; |
| --eepromBitsToRx; |
| |
| // All done |
| if (eepromBitsToRx == 0) { |
| eepromState = eepromStart; |
| } |
| break; |
| |
| default: |
| panic("invalid EEPROM state"); |
| } |
| |
| } |
| |
| void |
| NSGigE::transferDone() |
| { |
| if (txFifo.empty()) { |
| DPRINTF(Ethernet, "transfer complete: txFifo empty...nothing to do\n"); |
| return; |
| } |
| |
| DPRINTF(Ethernet, "transfer complete: data in txFifo...schedule xmit\n"); |
| |
| reschedule(txEvent, clockEdge(Cycles(1)), true); |
| } |
| |
| bool |
| NSGigE::rxFilter(const EthPacketPtr &packet) |
| { |
| EthPtr eth = packet; |
| bool drop = true; |
| string type; |
| |
| const EthAddr &dst = eth->dst(); |
| if (dst.unicast()) { |
| // If we're accepting all unicast addresses |
| if (acceptUnicast) |
| drop = false; |
| |
| // If we make a perfect match |
| if (acceptPerfect && dst == rom.perfectMatch) |
| drop = false; |
| |
| if (acceptArp && eth->type() == ETH_TYPE_ARP) |
| drop = false; |
| |
| } else if (dst.broadcast()) { |
| // if we're accepting broadcasts |
| if (acceptBroadcast) |
| drop = false; |
| |
| } else if (dst.multicast()) { |
| // if we're accepting all multicasts |
| if (acceptMulticast) |
| drop = false; |
| |
| // Multicast hashing faked - all packets accepted |
| if (multicastHashEnable) |
| drop = false; |
| } |
| |
| if (drop) { |
| DPRINTF(Ethernet, "rxFilter drop\n"); |
| DDUMP(EthernetData, packet->data, packet->length); |
| } |
| |
| return drop; |
| } |
| |
| bool |
| NSGigE::recvPacket(EthPacketPtr packet) |
| { |
| rxBytes += packet->length; |
| rxPackets++; |
| |
| DPRINTF(Ethernet, "Receiving packet from wire, rxFifoAvail=%d\n", |
| rxFifo.avail()); |
| |
| if (!rxEnable) { |
| DPRINTF(Ethernet, "receive disabled...packet dropped\n"); |
| return true; |
| } |
| |
| if (!rxFilterEnable) { |
| DPRINTF(Ethernet, |
| "receive packet filtering disabled . . . packet dropped\n"); |
| return true; |
| } |
| |
| if (rxFilter(packet)) { |
| DPRINTF(Ethernet, "packet filtered...dropped\n"); |
| return true; |
| } |
| |
| if (rxFifo.avail() < packet->length) { |
| #if TRACING_ON |
| IpPtr ip(packet); |
| TcpPtr tcp(ip); |
| if (ip) { |
| DPRINTF(Ethernet, |
| "packet won't fit in receive buffer...pkt ID %d dropped\n", |
| ip->id()); |
| if (tcp) { |
| DPRINTF(Ethernet, "Seq=%d\n", tcp->seq()); |
| } |
| } |
| #endif |
| droppedPackets++; |
| devIntrPost(ISR_RXORN); |
| return false; |
| } |
| |
| rxFifo.push(packet); |
| |
| rxKick(); |
| return true; |
| } |
| |
| |
| void |
| NSGigE::drainResume() |
| { |
| Drainable::drainResume(); |
| |
| // During drain we could have left the state machines in a waiting state and |
| // they wouldn't get out until some other event occured to kick them. |
| // This way they'll get out immediately |
| txKick(); |
| rxKick(); |
| } |
| |
| |
| //===================================================================== |
| // |
| // |
| void |
| NSGigE::serialize(CheckpointOut &cp) const |
| { |
| // Serialize the PciDevice base class |
| PciDevice::serialize(cp); |
| |
| /* |
| * Finalize any DMA events now. |
| */ |
| // @todo will mem system save pending dma? |
| |
| /* |
| * Serialize the device registers |
| */ |
| SERIALIZE_SCALAR(regs.command); |
| SERIALIZE_SCALAR(regs.config); |
| SERIALIZE_SCALAR(regs.mear); |
| SERIALIZE_SCALAR(regs.ptscr); |
| SERIALIZE_SCALAR(regs.isr); |
| SERIALIZE_SCALAR(regs.imr); |
| SERIALIZE_SCALAR(regs.ier); |
| SERIALIZE_SCALAR(regs.ihr); |
| SERIALIZE_SCALAR(regs.txdp); |
| SERIALIZE_SCALAR(regs.txdp_hi); |
| SERIALIZE_SCALAR(regs.txcfg); |
| SERIALIZE_SCALAR(regs.gpior); |
| SERIALIZE_SCALAR(regs.rxdp); |
| SERIALIZE_SCALAR(regs.rxdp_hi); |
| SERIALIZE_SCALAR(regs.rxcfg); |
| SERIALIZE_SCALAR(regs.pqcr); |
| SERIALIZE_SCALAR(regs.wcsr); |
| SERIALIZE_SCALAR(regs.pcr); |
| SERIALIZE_SCALAR(regs.rfcr); |
| SERIALIZE_SCALAR(regs.rfdr); |
| SERIALIZE_SCALAR(regs.brar); |
| SERIALIZE_SCALAR(regs.brdr); |
| SERIALIZE_SCALAR(regs.srr); |
| SERIALIZE_SCALAR(regs.mibc); |
| SERIALIZE_SCALAR(regs.vrcr); |
| SERIALIZE_SCALAR(regs.vtcr); |
| SERIALIZE_SCALAR(regs.vdr); |
| SERIALIZE_SCALAR(regs.ccsr); |
| SERIALIZE_SCALAR(regs.tbicr); |
| SERIALIZE_SCALAR(regs.tbisr); |
| SERIALIZE_SCALAR(regs.tanar); |
| SERIALIZE_SCALAR(regs.tanlpar); |
| SERIALIZE_SCALAR(regs.taner); |
| SERIALIZE_SCALAR(regs.tesr); |
| |
| SERIALIZE_ARRAY(rom.perfectMatch, ETH_ADDR_LEN); |
| SERIALIZE_ARRAY(rom.filterHash, FHASH_SIZE); |
| |
| SERIALIZE_SCALAR(ioEnable); |
| |
| /* |
| * Serialize the data Fifos |
| */ |
| rxFifo.serialize("rxFifo", cp); |
| txFifo.serialize("txFifo", cp); |
| |
| /* |
| * Serialize the various helper variables |
| */ |
| bool txPacketExists = txPacket != nullptr; |
| SERIALIZE_SCALAR(txPacketExists); |
| if (txPacketExists) { |
| txPacket->simLength = txPacketBufPtr - txPacket->data; |
| txPacket->length = txPacketBufPtr - txPacket->data; |
| txPacket->serialize("txPacket", cp); |
| uint32_t txPktBufPtr = (uint32_t) (txPacketBufPtr - txPacket->data); |
| SERIALIZE_SCALAR(txPktBufPtr); |
| } |
| |
| bool rxPacketExists = rxPacket != nullptr; |
| SERIALIZE_SCALAR(rxPacketExists); |
| if (rxPacketExists) { |
| rxPacket->serialize("rxPacket", cp); |
| uint32_t rxPktBufPtr = (uint32_t) (rxPacketBufPtr - rxPacket->data); |
| SERIALIZE_SCALAR(rxPktBufPtr); |
| } |
| |
| SERIALIZE_SCALAR(txXferLen); |
| SERIALIZE_SCALAR(rxXferLen); |
| |
| /* |
| * Serialize Cached Descriptors |
| */ |
| SERIALIZE_SCALAR(rxDesc64.link); |
| SERIALIZE_SCALAR(rxDesc64.bufptr); |
| SERIALIZE_SCALAR(rxDesc64.cmdsts); |
| SERIALIZE_SCALAR(rxDesc64.extsts); |
| SERIALIZE_SCALAR(txDesc64.link); |
| SERIALIZE_SCALAR(txDesc64.bufptr); |
| SERIALIZE_SCALAR(txDesc64.cmdsts); |
| SERIALIZE_SCALAR(txDesc64.extsts); |
| SERIALIZE_SCALAR(rxDesc32.link); |
| SERIALIZE_SCALAR(rxDesc32.bufptr); |
| SERIALIZE_SCALAR(rxDesc32.cmdsts); |
| SERIALIZE_SCALAR(rxDesc32.extsts); |
| SERIALIZE_SCALAR(txDesc32.link); |
| SERIALIZE_SCALAR(txDesc32.bufptr); |
| SERIALIZE_SCALAR(txDesc32.cmdsts); |
| SERIALIZE_SCALAR(txDesc32.extsts); |
| SERIALIZE_SCALAR(extstsEnable); |
| |
| /* |
| * Serialize tx state machine |
| */ |
| int txState = this->txState; |
| SERIALIZE_SCALAR(txState); |
| SERIALIZE_SCALAR(txEnable); |
| SERIALIZE_SCALAR(CTDD); |
| SERIALIZE_SCALAR(txFragPtr); |
| SERIALIZE_SCALAR(txDescCnt); |
| int txDmaState = this->txDmaState; |
| SERIALIZE_SCALAR(txDmaState); |
| SERIALIZE_SCALAR(txKickTick); |
| |
| /* |
| * Serialize rx state machine |
| */ |
| int rxState = this->rxState; |
| SERIALIZE_SCALAR(rxState); |
| SERIALIZE_SCALAR(rxEnable); |
| SERIALIZE_SCALAR(CRDD); |
| SERIALIZE_SCALAR(rxPktBytes); |
| SERIALIZE_SCALAR(rxFragPtr); |
| SERIALIZE_SCALAR(rxDescCnt); |
| int rxDmaState = this->rxDmaState; |
| SERIALIZE_SCALAR(rxDmaState); |
| SERIALIZE_SCALAR(rxKickTick); |
| |
| /* |
| * Serialize EEPROM state machine |
| */ |
| int eepromState = this->eepromState; |
| SERIALIZE_SCALAR(eepromState); |
| SERIALIZE_SCALAR(eepromClk); |
| SERIALIZE_SCALAR(eepromBitsToRx); |
| SERIALIZE_SCALAR(eepromOpcode); |
| SERIALIZE_SCALAR(eepromAddress); |
| SERIALIZE_SCALAR(eepromData); |
| |
| /* |
| * If there's a pending transmit, store the time so we can |
| * reschedule it later |
| */ |
| Tick transmitTick = txEvent.scheduled() ? txEvent.when() - curTick() : 0; |
| SERIALIZE_SCALAR(transmitTick); |
| |
| /* |
| * receive address filter settings |
| */ |
| SERIALIZE_SCALAR(rxFilterEnable); |
| SERIALIZE_SCALAR(acceptBroadcast); |
| SERIALIZE_SCALAR(acceptMulticast); |
| SERIALIZE_SCALAR(acceptUnicast); |
| SERIALIZE_SCALAR(acceptPerfect); |
| SERIALIZE_SCALAR(acceptArp); |
| SERIALIZE_SCALAR(multicastHashEnable); |
| |
| /* |
| * Keep track of pending interrupt status. |
| */ |
| SERIALIZE_SCALAR(intrTick); |
| SERIALIZE_SCALAR(cpuPendingIntr); |
| Tick intrEventTick = 0; |
| if (intrEvent) |
| intrEventTick = intrEvent->when(); |
| SERIALIZE_SCALAR(intrEventTick); |
| |
| } |
| |
| void |
| NSGigE::unserialize(CheckpointIn &cp) |
| { |
| // Unserialize the PciDevice base class |
| PciDevice::unserialize(cp); |
| |
| UNSERIALIZE_SCALAR(regs.command); |
| UNSERIALIZE_SCALAR(regs.config); |
| UNSERIALIZE_SCALAR(regs.mear); |
| UNSERIALIZE_SCALAR(regs.ptscr); |
| UNSERIALIZE_SCALAR(regs.isr); |
| UNSERIALIZE_SCALAR(regs.imr); |
| UNSERIALIZE_SCALAR(regs.ier); |
| UNSERIALIZE_SCALAR(regs.ihr); |
| UNSERIALIZE_SCALAR(regs.txdp); |
| UNSERIALIZE_SCALAR(regs.txdp_hi); |
| UNSERIALIZE_SCALAR(regs.txcfg); |
| UNSERIALIZE_SCALAR(regs.gpior); |
| UNSERIALIZE_SCALAR(regs.rxdp); |
| UNSERIALIZE_SCALAR(regs.rxdp_hi); |
| UNSERIALIZE_SCALAR(regs.rxcfg); |
| UNSERIALIZE_SCALAR(regs.pqcr); |
| UNSERIALIZE_SCALAR(regs.wcsr); |
| UNSERIALIZE_SCALAR(regs.pcr); |
| UNSERIALIZE_SCALAR(regs.rfcr); |
| UNSERIALIZE_SCALAR(regs.rfdr); |
| UNSERIALIZE_SCALAR(regs.brar); |
| UNSERIALIZE_SCALAR(regs.brdr); |
| UNSERIALIZE_SCALAR(regs.srr); |
| UNSERIALIZE_SCALAR(regs.mibc); |
| UNSERIALIZE_SCALAR(regs.vrcr); |
| UNSERIALIZE_SCALAR(regs.vtcr); |
| UNSERIALIZE_SCALAR(regs.vdr); |
| UNSERIALIZE_SCALAR(regs.ccsr); |
| UNSERIALIZE_SCALAR(regs.tbicr); |
| UNSERIALIZE_SCALAR(regs.tbisr); |
| UNSERIALIZE_SCALAR(regs.tanar); |
| UNSERIALIZE_SCALAR(regs.tanlpar); |
| UNSERIALIZE_SCALAR(regs.taner); |
| UNSERIALIZE_SCALAR(regs.tesr); |
| |
| UNSERIALIZE_ARRAY(rom.perfectMatch, ETH_ADDR_LEN); |
| UNSERIALIZE_ARRAY(rom.filterHash, FHASH_SIZE); |
| |
| UNSERIALIZE_SCALAR(ioEnable); |
| |
| /* |
| * unserialize the data fifos |
| */ |
| rxFifo.unserialize("rxFifo", cp); |
| txFifo.unserialize("txFifo", cp); |
| |
| /* |
| * unserialize the various helper variables |
| */ |
| bool txPacketExists; |
| UNSERIALIZE_SCALAR(txPacketExists); |
| if (txPacketExists) { |
| txPacket = make_shared<EthPacketData>(16384); |
| txPacket->unserialize("txPacket", cp); |
| uint32_t txPktBufPtr; |
| UNSERIALIZE_SCALAR(txPktBufPtr); |
| txPacketBufPtr = (uint8_t *) txPacket->data + txPktBufPtr; |
| } else |
| txPacket = 0; |
| |
| bool rxPacketExists; |
| UNSERIALIZE_SCALAR(rxPacketExists); |
| rxPacket = 0; |
| if (rxPacketExists) { |
| rxPacket = make_shared<EthPacketData>(); |
| rxPacket->unserialize("rxPacket", cp); |
| uint32_t rxPktBufPtr; |
| UNSERIALIZE_SCALAR(rxPktBufPtr); |
| rxPacketBufPtr = (uint8_t *) rxPacket->data + rxPktBufPtr; |
| } else |
| rxPacket = 0; |
| |
| UNSERIALIZE_SCALAR(txXferLen); |
| UNSERIALIZE_SCALAR(rxXferLen); |
| |
| /* |
| * Unserialize Cached Descriptors |
| */ |
| UNSERIALIZE_SCALAR(rxDesc64.link); |
| UNSERIALIZE_SCALAR(rxDesc64.bufptr); |
| UNSERIALIZE_SCALAR(rxDesc64.cmdsts); |
| UNSERIALIZE_SCALAR(rxDesc64.extsts); |
| UNSERIALIZE_SCALAR(txDesc64.link); |
| UNSERIALIZE_SCALAR(txDesc64.bufptr); |
| UNSERIALIZE_SCALAR(txDesc64.cmdsts); |
| UNSERIALIZE_SCALAR(txDesc64.extsts); |
| UNSERIALIZE_SCALAR(rxDesc32.link); |
| UNSERIALIZE_SCALAR(rxDesc32.bufptr); |
| UNSERIALIZE_SCALAR(rxDesc32.cmdsts); |
| UNSERIALIZE_SCALAR(rxDesc32.extsts); |
| UNSERIALIZE_SCALAR(txDesc32.link); |
| UNSERIALIZE_SCALAR(txDesc32.bufptr); |
| UNSERIALIZE_SCALAR(txDesc32.cmdsts); |
| UNSERIALIZE_SCALAR(txDesc32.extsts); |
| UNSERIALIZE_SCALAR(extstsEnable); |
| |
| /* |
| * unserialize tx state machine |
| */ |
| int txState; |
| UNSERIALIZE_SCALAR(txState); |
| this->txState = (TxState) txState; |
| UNSERIALIZE_SCALAR(txEnable); |
| UNSERIALIZE_SCALAR(CTDD); |
| UNSERIALIZE_SCALAR(txFragPtr); |
| UNSERIALIZE_SCALAR(txDescCnt); |
| int txDmaState; |
| UNSERIALIZE_SCALAR(txDmaState); |
| this->txDmaState = (DmaState) txDmaState; |
| UNSERIALIZE_SCALAR(txKickTick); |
| if (txKickTick) |
| schedule(txKickEvent, txKickTick); |
| |
| /* |
| * unserialize rx state machine |
| */ |
| int rxState; |
| UNSERIALIZE_SCALAR(rxState); |
| this->rxState = (RxState) rxState; |
| UNSERIALIZE_SCALAR(rxEnable); |
| UNSERIALIZE_SCALAR(CRDD); |
| UNSERIALIZE_SCALAR(rxPktBytes); |
| UNSERIALIZE_SCALAR(rxFragPtr); |
| UNSERIALIZE_SCALAR(rxDescCnt); |
| int rxDmaState; |
| UNSERIALIZE_SCALAR(rxDmaState); |
| this->rxDmaState = (DmaState) rxDmaState; |
| UNSERIALIZE_SCALAR(rxKickTick); |
| if (rxKickTick) |
| schedule(rxKickEvent, rxKickTick); |
| |
| /* |
| * Unserialize EEPROM state machine |
| */ |
| int eepromState; |
| UNSERIALIZE_SCALAR(eepromState); |
| this->eepromState = (EEPROMState) eepromState; |
| UNSERIALIZE_SCALAR(eepromClk); |
| UNSERIALIZE_SCALAR(eepromBitsToRx); |
| UNSERIALIZE_SCALAR(eepromOpcode); |
| UNSERIALIZE_SCALAR(eepromAddress); |
| UNSERIALIZE_SCALAR(eepromData); |
| |
| /* |
| * If there's a pending transmit, reschedule it now |
| */ |
| Tick transmitTick; |
| UNSERIALIZE_SCALAR(transmitTick); |
| if (transmitTick) |
| schedule(txEvent, curTick() + transmitTick); |
| |
| /* |
| * unserialize receive address filter settings |
| */ |
| UNSERIALIZE_SCALAR(rxFilterEnable); |
| UNSERIALIZE_SCALAR(acceptBroadcast); |
| UNSERIALIZE_SCALAR(acceptMulticast); |
| UNSERIALIZE_SCALAR(acceptUnicast); |
| UNSERIALIZE_SCALAR(acceptPerfect); |
| UNSERIALIZE_SCALAR(acceptArp); |
| UNSERIALIZE_SCALAR(multicastHashEnable); |
| |
| /* |
| * Keep track of pending interrupt status. |
| */ |
| UNSERIALIZE_SCALAR(intrTick); |
| UNSERIALIZE_SCALAR(cpuPendingIntr); |
| Tick intrEventTick; |
| UNSERIALIZE_SCALAR(intrEventTick); |
| if (intrEventTick) { |
| intrEvent = new EventFunctionWrapper([this]{ cpuInterrupt(); }, |
| name(), true); |
| schedule(intrEvent, intrEventTick); |
| } |
| } |
| |
| NSGigE * |
| NSGigEParams::create() |
| { |
| return new NSGigE(this); |
| } |