| /* |
| * 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/bus/bus.hh" |
| #include "mem/bus/dma_interface.hh" |
| #include "mem/bus/pio_interface.hh" |
| #include "mem/bus/pio_interface_impl.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" |
| #include "dev/pciconfigall.hh" |
| #include "dev/tsunami_cchip.hh" |
| |
| 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 std; |
| |
| /////////////////////////////////////////////////////////////////////// |
| // |
| // EtherDev PCI Device |
| // |
| EtherDev::EtherDev(const std::string &name, IntrControl *i, Tick intr_delay, |
| PhysicalMemory *pmem, Tick tx_delay, Tick rx_delay, |
| MemoryController *mmu, HierParams *hier, Bus *header_bus, |
| Bus *payload_bus, Tick pio_latency, bool dma_desc_free, |
| bool dma_data_free, Tick dma_read_delay, Tick dma_write_delay, |
| Tick dma_read_factor, Tick dma_write_factor, PciConfigAll *cf, |
| PciConfigData *cd, Tsunami *t, uint32_t bus, uint32_t dev, |
| uint32_t func, bool rx_filter, const int eaddr[6], Addr addr) |
| : PciDev(name, mmu, cf, cd, bus, dev, func), tsunami(t), |
| addr(addr), txPacketBufPtr(NULL), rxPacketBufPtr(NULL), |
| txXferLen(0), rxXferLen(0), txPktXmitted(0), txState(txIdle), CTDD(false), |
| txFifoCnt(0), txFifoAvail(MAX_TX_FIFO_SIZE), txHalt(false), |
| txFragPtr(0), txDescCnt(0), txDmaState(dmaIdle), rxState(rxIdle), |
| CRDD(false), rxPktBytes(0), rxFifoCnt(0), rxHalt(false), |
| rxFragPtr(0), rxDescCnt(0), rxDmaState(dmaIdle), extstsEnable(false), |
| rxDmaReadEvent(this), rxDmaWriteEvent(this), |
| txDmaReadEvent(this), txDmaWriteEvent(this), |
| dmaDescFree(dma_desc_free), dmaDataFree(dma_data_free), |
| txDelay(tx_delay), rxDelay(rx_delay), rxKickTick(0), txKickTick(0), |
| txEvent(this), rxFilterEnable(rx_filter), acceptBroadcast(false), |
| acceptMulticast(false), acceptUnicast(false), |
| acceptPerfect(false), acceptArp(false), |
| physmem(pmem), intctrl(i), intrTick(0), |
| cpuPendingIntr(false), intrEvent(0), interface(0), pioLatency(pio_latency) |
| { |
| mmu->add_child(this, Range<Addr>(addr, addr + size)); |
| tsunami->ethernet = this; |
| |
| if (header_bus) { |
| pioInterface = newPioInterface(name, hier, header_bus, this, |
| &EtherDev::cacheAccess); |
| pioInterface->addAddrRange(addr, addr + size - 1); |
| if (payload_bus) |
| dmaInterface = new DMAInterface<Bus>(name + ".dma", |
| header_bus, payload_bus, 1); |
| else |
| dmaInterface = new DMAInterface<Bus>(name + ".dma", |
| header_bus, header_bus, 1); |
| } else if (payload_bus) { |
| pioInterface = newPioInterface(name, hier, payload_bus, this, |
| &EtherDev::cacheAccess); |
| pioInterface->addAddrRange(addr, addr + size - 1); |
| dmaInterface = new DMAInterface<Bus>(name + ".dma", |
| payload_bus, payload_bus, 1); |
| |
| } |
| |
| |
| intrDelay = US2Ticks(intr_delay); |
| dmaReadDelay = dma_read_delay; |
| dmaWriteDelay = dma_write_delay; |
| dmaReadFactor = dma_read_factor; |
| dmaWriteFactor = dma_write_factor; |
| |
| memset(®s, 0, sizeof(regs)); |
| regsReset(); |
| rom.perfectMatch[0] = eaddr[0]; |
| rom.perfectMatch[1] = eaddr[1]; |
| rom.perfectMatch[2] = eaddr[2]; |
| rom.perfectMatch[3] = eaddr[3]; |
| rom.perfectMatch[4] = eaddr[4]; |
| rom.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 * Stats::constant(8) / simSeconds; |
| rxBandwidth = rxBytes * Stats::constant(8) / simSeconds; |
| txPacketRate = txPackets / simSeconds; |
| rxPacketRate = rxPackets / simSeconds; |
| } |
| |
| /** |
| * This is to read the PCI general configuration registers |
| */ |
| 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"); |
| } |
| } |
| |
| /** |
| * This is to write to the PCI general configuration registers |
| */ |
| 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"); |
| } |
| |
| /** |
| * This reads the device registers, which are detailed in the NS83820 |
| * spec sheet |
| */ |
| Fault |
| EtherDev::read(MemReqPtr &req, uint8_t *data) |
| { |
| //The mask is to give you only the offset into the device register file |
| Addr daddr = req->paddr & 0xfff; |
| DPRINTF(EthernetPIO, "read da=%#x pa=%#x va=%#x size=%d\n", |
| daddr, req->paddr, req->vaddr, req->size); |
| |
| |
| //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) { |
| ReadConfig(daddr & 0xff, req->size, data); |
| return No_Fault; |
| } 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 |
| uint32_t ® = *(uint32_t *) data; |
| reg = 0; |
| return No_Fault; |
| } else if (daddr > 0x3FC) |
| panic("Something is messed up!\n"); |
| |
| switch (req->size) { |
| case sizeof(uint32_t): |
| { |
| uint32_t ® = *(uint32_t *)data; |
| |
| 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 CFG: |
| 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 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; |
| |
| //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: |
| DPRINTF(Ethernet, "reading from RFDR\n"); |
| switch (regs.rfcr & RFCR_RFADDR) { |
| 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: |
| 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(EthernetPIO, "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) |
| { |
| Addr daddr = req->paddr & 0xfff; |
| DPRINTF(EthernetPIO, "write da=%#x pa=%#x va=%#x size=%d\n", |
| daddr, req->paddr, req->vaddr, req->size); |
| |
| if (daddr > LAST && daddr <= RESERVED) { |
| panic("Accessing reserved register"); |
| } else if (daddr > RESERVED && daddr <= 0x3FC) { |
| WriteConfig(daddr & 0xff, req->size, *(uint32_t *)data); |
| return No_Fault; |
| } else if (daddr > 0x3FC) |
| panic("Something is messed up!\n"); |
| |
| if (req->size == sizeof(uint32_t)) { |
| uint32_t reg = *(uint32_t *)data; |
| DPRINTF(EthernetPIO, "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) { |
| //the kernel is enabling the transmit machine |
| 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: |
| 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"); |
| |
| regs.config |= reg & ~(CFG_LNKSTS | CFG_SPDSTS | CFG_DUPSTS | CFG_RESERVED | |
| CFG_T64ADDR | CFG_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 |
| #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) ; |
| |
| 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 (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 & ~(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 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 |
| |
| |
| /* we handle our own DMA, ignore the kernel's exhortations */ |
| if (reg & TXCFG_MXDMA) ; |
| |
| break; |
| |
| case GPIOR: |
| regs.gpior = reg; |
| /* these just control general purpose i/o pins, don't matter */ |
| break; |
| |
| case RXDP: |
| regs.rxdp = reg; |
| break; |
| |
| case RXDP_HI: |
| regs.rxdp_hi = reg; |
| 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 |
| |
| /* we handle our own DMA, ignore what kernel says about it */ |
| if (reg & RXCFG_MXDMA) ; |
| |
| #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; |
| DPRINTF(Ethernet, "Writing to RFCR, RFADDR is %#x\n", reg & RFCR_RFADDR); |
| |
| 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); |
| |
| bool delay = false; |
| |
| 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; |
| delay = true; |
| } |
| |
| 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) { |
| delay = true; |
| regs.isr |= ISR_RXOK; |
| } |
| |
| if ((regs.isr & regs.imr)) { |
| Tick when = curTick; |
| if (delay) |
| when += intrDelay; |
| cpuIntrPost(when); |
| } |
| } |
| |
| 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)) |
| cpuIntrClear(); |
| } |
| |
| void |
| EtherDev::devIntrChangeMask() |
| { |
| DPRINTF(Ethernet, "interrupt mask changed\n"); |
| |
| if (regs.isr & regs.imr) |
| cpuIntrPost(curTick); |
| else |
| cpuIntrClear(); |
| } |
| |
| void |
| EtherDev::cpuIntrPost(Tick when) |
| { |
| if (when > intrTick && intrTick != 0) |
| return; |
| |
| intrTick = when; |
| |
| if (intrEvent) { |
| intrEvent->squash(); |
| intrEvent = 0; |
| } |
| |
| if (when < curTick) { |
| cpuInterrupt(); |
| } else { |
| intrEvent = new IntrEvent(this, true); |
| intrEvent->schedule(intrTick); |
| } |
| } |
| |
| void |
| EtherDev::cpuInterrupt() |
| { |
| // Don't send an interrupt if there's already one |
| if (cpuPendingIntr) |
| return; |
| |
| // Don't send an interrupt if it's supposed to be delayed |
| if (intrTick > curTick) |
| return; |
| |
| // Whether or not there's a pending interrupt, we don't care about |
| // it anymore |
| intrEvent = 0; |
| intrTick = 0; |
| |
| // Send interrupt |
| cpuPendingIntr = true; |
| /** @todo rework the intctrl to be tsunami ok */ |
| //intctrl->post(TheISA::INTLEVEL_IRQ1, TheISA::INTINDEX_ETHERNET); |
| tsunami->cchip->postDRIR(configData->config.hdr.pci0.interruptLine); |
| } |
| |
| void |
| EtherDev::cpuIntrClear() |
| { |
| if (cpuPendingIntr) { |
| cpuPendingIntr = false; |
| /** @todo rework the intctrl to be tsunami ok */ |
| //intctrl->clear(TheISA::INTLEVEL_IRQ1, TheISA::INTINDEX_ETHERNET); |
| tsunami->cchip->clearDRIR(configData->config.hdr.pci0.interruptLine); |
| } |
| } |
| |
| bool |
| EtherDev::cpuIntrPending() const |
| { return cpuPendingIntr; } |
| |
| void |
| EtherDev::txReset() |
| { |
| |
| DPRINTF(Ethernet, "transmit reset\n"); |
| |
| CTDD = false; |
| txFifoCnt = 0; |
| txFifoAvail = MAX_TX_FIFO_SIZE; |
| txHalt = false; |
| txFragPtr = 0; |
| assert(txDescCnt == 0); |
| txFifo.clear(); |
| regs.command &= ~CR_TXE; |
| txState = txIdle; |
| assert(txDmaState == dmaIdle); |
| } |
| |
| void |
| EtherDev::rxReset() |
| { |
| DPRINTF(Ethernet, "receive reset\n"); |
| |
| CRDD = false; |
| assert(rxPktBytes == 0); |
| rxFifoCnt = 0; |
| rxHalt = false; |
| rxFragPtr = 0; |
| assert(rxDescCnt == 0); |
| assert(rxDmaState == dmaIdle); |
| rxFifo.clear(); |
| regs.command &= ~CR_RXE; |
| rxState = rxIdle; |
| } |
| |
| void |
| EtherDev::rxDmaReadCopy() |
| { |
| assert(rxDmaState == dmaReading); |
| |
| memcpy(rxDmaData, physmem->dma_addr(rxDmaAddr, rxDmaLen), rxDmaLen); |
| rxDmaState = dmaIdle; |
| |
| DPRINTF(EthernetDMA, "rx dma read paddr=%#x len=%d\n", |
| rxDmaAddr, rxDmaLen); |
| DDUMP(EthernetDMA, rxDmaData, rxDmaLen); |
| } |
| |
| bool |
| EtherDev::doRxDmaRead() |
| { |
| assert(rxDmaState == dmaIdle || rxDmaState == dmaReadWaiting); |
| rxDmaState = dmaReading; |
| |
| if (dmaInterface && !rxDmaFree) { |
| if (dmaInterface->busy()) |
| rxDmaState = dmaReadWaiting; |
| else |
| dmaInterface->doDMA(Read, rxDmaAddr, rxDmaLen, curTick, |
| &rxDmaReadEvent); |
| return true; |
| } |
| |
| if (dmaReadDelay == 0 && dmaReadFactor == 0) { |
| rxDmaReadCopy(); |
| return false; |
| } |
| |
| Tick factor = ((rxDmaLen + ULL(63)) >> ULL(6)) * dmaReadFactor; |
| Tick start = curTick + dmaReadDelay + factor; |
| rxDmaReadEvent.schedule(start); |
| return true; |
| } |
| |
| void |
| EtherDev::rxDmaReadDone() |
| { |
| assert(rxDmaState == dmaReading); |
| rxDmaReadCopy(); |
| |
| // If the transmit state machine has a pending DMA, let it go first |
| if (txDmaState == dmaReadWaiting || txDmaState == dmaWriteWaiting) |
| txKick(); |
| |
| rxKick(); |
| } |
| |
| void |
| EtherDev::rxDmaWriteCopy() |
| { |
| assert(rxDmaState == dmaWriting); |
| |
| memcpy(physmem->dma_addr(rxDmaAddr, rxDmaLen), rxDmaData, rxDmaLen); |
| rxDmaState = dmaIdle; |
| |
| DPRINTF(EthernetDMA, "rx dma write paddr=%#x len=%d\n", |
| rxDmaAddr, rxDmaLen); |
| DDUMP(EthernetDMA, rxDmaData, rxDmaLen); |
| } |
| |
| bool |
| EtherDev::doRxDmaWrite() |
| { |
| assert(rxDmaState == dmaIdle || rxDmaState == dmaWriteWaiting); |
| rxDmaState = dmaWriting; |
| |
| if (dmaInterface && !rxDmaFree) { |
| if (dmaInterface->busy()) |
| rxDmaState = dmaWriteWaiting; |
| else |
| dmaInterface->doDMA(WriteInvalidate, rxDmaAddr, rxDmaLen, curTick, |
| &rxDmaWriteEvent); |
| return true; |
| } |
| |
| if (dmaWriteDelay == 0 && dmaWriteFactor == 0) { |
| rxDmaWriteCopy(); |
| return false; |
| } |
| |
| Tick factor = ((rxDmaLen + ULL(63)) >> ULL(6)) * dmaWriteFactor; |
| Tick start = curTick + dmaWriteDelay + factor; |
| rxDmaWriteEvent.schedule(start); |
| return true; |
| } |
| |
| void |
| EtherDev::rxDmaWriteDone() |
| { |
| assert(rxDmaState == dmaWriting); |
| rxDmaWriteCopy(); |
| |
| // If the transmit state machine has a pending DMA, let it go first |
| if (txDmaState == dmaReadWaiting || txDmaState == dmaWriteWaiting) |
| txKick(); |
| |
| rxKick(); |
| } |
| |
| void |
| EtherDev::rxKick() |
| { |
| DPRINTF(Ethernet, "receive kick state=%s (rxBuf.size=%d)\n", |
| NsRxStateStrings[rxState], rxFifo.size()); |
| |
| if (rxKickTick > curTick) { |
| DPRINTF(Ethernet, "receive kick exiting, can't run till %d\n", |
| rxKickTick); |
| return; |
| } |
| |
| next: |
| switch(rxDmaState) { |
| case dmaReadWaiting: |
| if (doRxDmaRead()) |
| goto exit; |
| break; |
| case dmaWriteWaiting: |
| if (doRxDmaWrite()) |
| goto exit; |
| break; |
| default: |
| break; |
| } |
| |
| // 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 (!regs.command & CR_RXE) { |
| DPRINTF(Ethernet, "Receive Disabled! Nothing to do.\n"); |
| goto exit; |
| } |
| |
| if (CRDD) { |
| rxState = rxDescRefr; |
| |
| rxDmaAddr = regs.rxdp & 0x3fffffff; |
| rxDmaData = &rxDescCache + offsetof(ns_desc, link); |
| rxDmaLen = sizeof(rxDescCache.link); |
| rxDmaFree = dmaDescFree; |
| |
| if (doRxDmaRead()) |
| goto exit; |
| } else { |
| rxState = rxDescRead; |
| |
| rxDmaAddr = regs.rxdp & 0x3fffffff; |
| rxDmaData = &rxDescCache; |
| rxDmaLen = sizeof(ns_desc); |
| rxDmaFree = dmaDescFree; |
| |
| if (doRxDmaRead()) |
| goto exit; |
| } |
| break; |
| |
| case rxDescRefr: |
| if (rxDmaState != dmaIdle) |
| goto exit; |
| |
| rxState = rxAdvance; |
| break; |
| |
| case rxDescRead: |
| if (rxDmaState != dmaIdle) |
| goto exit; |
| |
| if (rxDescCache.cmdsts & CMDSTS_OWN) { |
| rxState = rxIdle; |
| } else { |
| rxState = rxFifoBlock; |
| rxFragPtr = rxDescCache.bufptr; |
| rxDescCnt = rxDescCache.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; |
| |
| // If we don't have a packet, grab a new one from the fifo. |
| rxPacket = rxFifo.front(); |
| rxPktBytes = rxPacket->length; |
| rxPacketBufPtr = rxPacket->data; |
| |
| // sanity check - i think the driver behaves like this |
| assert(rxDescCnt >= rxPktBytes); |
| |
| // Must clear the value before popping to decrement the |
| // reference count |
| rxFifo.front() = NULL; |
| rxFifo.pop_front(); |
| } |
| |
| |
| // 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); |
| |
| rxFifoCnt -= rxPacket->length; |
| rxPacket = 0; |
| |
| rxDescCache.cmdsts |= CMDSTS_OWN; |
| rxDescCache.cmdsts &= ~CMDSTS_MORE; |
| rxDescCache.cmdsts |= CMDSTS_OK; |
| rxDescCache.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) { |
| 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; |
| } |
| #endif |
| |
| eth_header *eth = (eth_header *) rxPacket->data; |
| // eth->type 0x800 indicated that it's an ip packet. |
| if (eth->type == 0x800 && extstsEnable) { |
| rxDescCache.extsts |= EXTSTS_IPPKT; |
| if (!ipChecksum(rxPacket, false)) |
| rxDescCache.extsts |= EXTSTS_IPERR; |
| ip_header *ip = rxFifo.front()->getIpHdr(); |
| |
| if (ip->protocol == 6) { |
| rxDescCache.extsts |= EXTSTS_TCPPKT; |
| if (!tcpChecksum(rxPacket, false)) |
| rxDescCache.extsts |= EXTSTS_TCPERR; |
| } else if (ip->protocol == 17) { |
| rxDescCache.extsts |= EXTSTS_UDPPKT; |
| if (!udpChecksum(rxPacket, false)) |
| rxDescCache.extsts |= EXTSTS_UDPERR; |
| } |
| } |
| |
| /* 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. |
| */ |
| |
| rxDmaAddr = (regs.rxdp + offsetof(ns_desc, cmdsts)) & 0x3fffffff; |
| rxDmaData = &(rxDescCache.cmdsts); |
| rxDmaLen = sizeof(rxDescCache.cmdsts) + sizeof(rxDescCache.extsts); |
| rxDmaFree = dmaDescFree; |
| |
| 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(rxDescCache.cmdsts & CMDSTS_OWN); |
| |
| assert(rxPacket == 0); |
| devIntrPost(ISR_RXOK); |
| |
| if (rxDescCache.cmdsts & CMDSTS_INTR) |
| devIntrPost(ISR_RXDESC); |
| |
| if (rxHalt) { |
| rxState = rxIdle; |
| rxHalt = false; |
| } else |
| rxState = rxAdvance; |
| break; |
| |
| case rxAdvance: |
| if (rxDescCache.link == 0) { |
| rxState = rxIdle; |
| return; |
| } else { |
| rxState = rxDescRead; |
| regs.rxdp = rxDescCache.link; |
| CRDD = false; |
| |
| rxDmaAddr = regs.rxdp & 0x3fffffff; |
| rxDmaData = &rxDescCache; |
| rxDmaLen = sizeof(ns_desc); |
| rxDmaFree = dmaDescFree; |
| |
| if (doRxDmaRead()) |
| goto exit; |
| } |
| break; |
| |
| default: |
| panic("Invalid rxState!"); |
| } |
| |
| |
| DPRINTF(Ethernet, "entering next rx state = %s\n", |
| NsRxStateStrings[rxState]); |
| |
| if (rxState == rxIdle) { |
| regs.command &= ~CR_RXE; |
| devIntrPost(ISR_RXIDLE); |
| return; |
| } |
| |
| goto next; |
| |
| exit: |
| /** |
| * @todo do we want to schedule a future kick? |
| */ |
| DPRINTF(Ethernet, "rx state machine exited state=%s\n", |
| NsRxStateStrings[rxState]); |
| } |
| |
| void |
| EtherDev::transmit() |
| { |
| if (txFifo.empty()) { |
| DPRINTF(Ethernet, "nothing to transmit\n"); |
| return; |
| } |
| |
| if (interface->sendPacket(txFifo.front())) { |
| DPRINTF(Ethernet, "transmit packet\n"); |
| DDUMP(Ethernet, txFifo.front()->data, txFifo.front()->length); |
| txBytes += txFifo.front()->length; |
| txPackets++; |
| |
| txFifoCnt -= (txFifo.front()->length - txPktXmitted); |
| txPktXmitted = 0; |
| txFifo.front() = NULL; |
| txFifo.pop_front(); |
| |
| /* 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"); |
| txEvent.schedule(curTick + 1000); |
| } |
| } |
| |
| void |
| EtherDev::txDmaReadCopy() |
| { |
| assert(txDmaState == dmaReading); |
| |
| memcpy(txDmaData, physmem->dma_addr(txDmaAddr, txDmaLen), txDmaLen); |
| txDmaState = dmaIdle; |
| |
| DPRINTF(EthernetDMA, "tx dma read paddr=%#x len=%d\n", |
| txDmaAddr, txDmaLen); |
| DDUMP(EthernetDMA, txDmaData, txDmaLen); |
| } |
| |
| bool |
| EtherDev::doTxDmaRead() |
| { |
| assert(txDmaState == dmaIdle || txDmaState == dmaReadWaiting); |
| txDmaState = dmaReading; |
| |
| if (dmaInterface && !txDmaFree) { |
| if (dmaInterface->busy()) |
| txDmaState = dmaReadWaiting; |
| else |
| dmaInterface->doDMA(Read, txDmaAddr, txDmaLen, curTick, |
| &txDmaReadEvent); |
| return true; |
| } |
| |
| if (dmaReadDelay == 0 && dmaReadFactor == 0.0) { |
| txDmaReadCopy(); |
| return false; |
| } |
| |
| Tick factor = ((txDmaLen + ULL(63)) >> ULL(6)) * dmaReadFactor; |
| Tick start = curTick + dmaReadDelay + factor; |
| txDmaReadEvent.schedule(start); |
| return true; |
| } |
| |
| void |
| EtherDev::txDmaReadDone() |
| { |
| assert(txDmaState == dmaReading); |
| txDmaReadCopy(); |
| |
| // If the receive state machine has a pending DMA, let it go first |
| if (rxDmaState == dmaReadWaiting || rxDmaState == dmaWriteWaiting) |
| rxKick(); |
| |
| txKick(); |
| } |
| |
| void |
| EtherDev::txDmaWriteCopy() |
| { |
| assert(txDmaState == dmaWriting); |
| |
| memcpy(physmem->dma_addr(txDmaAddr, txDmaLen), txDmaData, txDmaLen); |
| txDmaState = dmaIdle; |
| |
| DPRINTF(EthernetDMA, "tx dma write paddr=%#x len=%d\n", |
| txDmaAddr, txDmaLen); |
| DDUMP(EthernetDMA, txDmaData, txDmaLen); |
| } |
| |
| bool |
| EtherDev::doTxDmaWrite() |
| { |
| assert(txDmaState == dmaIdle || txDmaState == dmaWriteWaiting); |
| txDmaState = dmaWriting; |
| |
| if (dmaInterface && !txDmaFree) { |
| if (dmaInterface->busy()) |
| txDmaState = dmaWriteWaiting; |
| else |
| dmaInterface->doDMA(WriteInvalidate, txDmaAddr, txDmaLen, curTick, |
| &txDmaWriteEvent); |
| return true; |
| } |
| |
| if (dmaWriteDelay == 0 && dmaWriteFactor == 0.0) { |
| txDmaWriteCopy(); |
| return false; |
| } |
| |
| Tick factor = ((txDmaLen + ULL(63)) >> ULL(6)) * dmaWriteFactor; |
| Tick start = curTick + dmaWriteDelay + factor; |
| txDmaWriteEvent.schedule(start); |
| return true; |
| } |
| |
| void |
| EtherDev::txDmaWriteDone() |
| { |
| assert(txDmaState == dmaWriting); |
| txDmaWriteCopy(); |
| |
| // If the receive state machine has a pending DMA, let it go first |
| if (rxDmaState == dmaReadWaiting || rxDmaState == dmaWriteWaiting) |
| rxKick(); |
| |
| txKick(); |
| } |
| |
| void |
| EtherDev::txKick() |
| { |
| DPRINTF(Ethernet, "transmit kick state=%s\n", NsTxStateStrings[txState]); |
| |
| if (rxKickTick > curTick) { |
| DPRINTF(Ethernet, "receive kick exiting, can't run till %d\n", |
| rxKickTick); |
| |
| return; |
| } |
| |
| next: |
| switch(txDmaState) { |
| case dmaReadWaiting: |
| if (doTxDmaRead()) |
| goto exit; |
| break; |
| case dmaWriteWaiting: |
| if (doTxDmaWrite()) |
| goto exit; |
| break; |
| default: |
| break; |
| } |
| |
| switch (txState) { |
| case txIdle: |
| if (!regs.command & CR_TXE) { |
| DPRINTF(Ethernet, "Transmit disabled. Nothing to do.\n"); |
| goto exit; |
| } |
| |
| if (CTDD) { |
| txState = txDescRefr; |
| |
| txDmaAddr = txDescCache.link & 0x3fffffff; |
| txDmaData = &txDescCache; |
| txDmaLen = sizeof(txDescCache.link); |
| txDmaFree = dmaDescFree; |
| |
| if (doTxDmaRead()) |
| goto exit; |
| |
| } else { |
| txState = txDescRead; |
| |
| txDmaAddr = regs.txdp & 0x3fffffff; |
| txDmaData = &txDescCache + offsetof(ns_desc, link); |
| txDmaLen = sizeof(ns_desc); |
| txDmaFree = dmaDescFree; |
| |
| if (doTxDmaRead()) |
| goto exit; |
| } |
| break; |
| |
| case txDescRefr: |
| if (txDmaState != dmaIdle) |
| goto exit; |
| |
| txState = txAdvance; |
| break; |
| |
| case txDescRead: |
| if (txDmaState != dmaIdle) |
| goto exit; |
| |
| if (txDescCache.cmdsts & CMDSTS_OWN) { |
| txState = txFifoBlock; |
| txFragPtr = txDescCache.bufptr; |
| txDescCnt = txDescCache.cmdsts & CMDSTS_LEN_MASK; |
| } else { |
| txState = txIdle; |
| } |
| break; |
| |
| case txFifoBlock: |
| if (!txPacket) { |
| DPRINTF(Ethernet, "starting the tx of a new packet\n"); |
| txPacket = new EtherPacket; |
| txPacket->data = new uint8_t[16384]; |
| txPacketBufPtr = txPacket->data; |
| } |
| |
| if (txDescCnt == 0) { |
| DPRINTF(Ethernet, "the txDescCnt == 0, done with descriptor\n"); |
| if (txDescCache.cmdsts & CMDSTS_MORE) { |
| DPRINTF(Ethernet, "there are more descriptors to come\n"); |
| txState = txDescWrite; |
| |
| txDescCache.cmdsts &= ~CMDSTS_OWN; |
| |
| txDmaAddr = (regs.txdp + offsetof(ns_desc, cmdsts)) & 0x3fffffff; |
| txDmaData = &(txDescCache.cmdsts); |
| txDmaLen = sizeof(txDescCache.cmdsts); |
| txDmaFree = dmaDescFree; |
| |
| if (doTxDmaWrite()) |
| goto exit; |
| |
| } else { /* this packet is totally done */ |
| DPRINTF(Ethernet, "This packet is done, let's wrap it up\n"); |
| /* deal with the the packet that just finished */ |
| if ((regs.vtcr & VTCR_PPCHK) && extstsEnable) { |
| if (txDescCache.extsts & EXTSTS_UDPPKT) { |
| udpChecksum(txPacket, true); |
| } else if (txDescCache.extsts & EXTSTS_TCPPKT) { |
| tcpChecksum(txPacket, true); |
| } else if (txDescCache.extsts & EXTSTS_IPPKT) { |
| ipChecksum(txPacket, true); |
| } |
| } |
| |
| txPacket->length = txPacketBufPtr - txPacket->data; |
| /* this is just because the receive can't handle a packet bigger |
| want to make sure */ |
| assert(txPacket->length <= 1514); |
| txFifo.push_back(txPacket); |
| |
| |
| /* this following section is not to 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 |
| */ |
| txDescCache.cmdsts &= ~CMDSTS_OWN; |
| txDescCache.cmdsts |= CMDSTS_OK; |
| |
| txDmaAddr = regs.txdp & 0x3fffffff; |
| txDmaData = &txDescCache + offsetof(ns_desc, cmdsts); |
| txDmaLen = sizeof(txDescCache.cmdsts) + sizeof(txDescCache.extsts); |
| txDmaFree = dmaDescFree; |
| |
| |
| if (doTxDmaWrite()) |
| goto exit; |
| |
| txPacket = 0; |
| transmit(); |
| |
| if (txHalt) { |
| txState = txIdle; |
| txHalt = false; |
| } else |
| txState = txAdvance; |
| } |
| } else { |
| DPRINTF(Ethernet, "this descriptor isn't done yet\n"); |
| /* the fill thresh is in units of 32 bytes, shift right by 8 to get the |
| value, shift left by 5 to get the real number of bytes */ |
| if (txFifoAvail < ((regs.txcfg & TXCFG_FLTH_MASK) >> 3)) { |
| DPRINTF(Ethernet, "txFifoAvail=%d, regs.txcfg & TXCFG_FLTH_MASK = %#x\n", |
| txFifoAvail, regs.txcfg & TXCFG_FLTH_MASK); |
| goto exit; |
| } |
| |
| 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, txFifoAvail); |
| |
| txDmaAddr = txFragPtr & 0x3fffffff; |
| txDmaData = txPacketBufPtr; |
| txDmaLen = txXferLen; |
| txDmaFree = dmaDataFree; |
| |
| if (doTxDmaRead()) |
| goto exit; |
| } |
| break; |
| |
| case txFragRead: |
| if (txDmaState != dmaIdle) |
| goto exit; |
| |
| txPacketBufPtr += txXferLen; |
| txFragPtr += txXferLen; |
| txFifoCnt += txXferLen; |
| txDescCnt -= txXferLen; |
| |
| txState = txFifoBlock; |
| break; |
| |
| case txDescWrite: |
| if (txDmaState != dmaIdle) |
| goto exit; |
| |
| if (txFifoCnt >= ((regs.txcfg & TXCFG_DRTH_MASK) << 5)) { |
| if (txFifo.empty()) { |
| uint32_t xmitted = (uint32_t) (txPacketBufPtr - txPacket->data - txPktXmitted); |
| txFifoCnt -= xmitted; |
| txPktXmitted += xmitted; |
| } else { |
| transmit(); |
| } |
| } |
| |
| if (txDescCache.cmdsts & CMDSTS_INTR) { |
| devIntrPost(ISR_TXDESC); |
| } |
| |
| txState = txAdvance; |
| break; |
| |
| case txAdvance: |
| if (txDescCache.link == 0) { |
| txState = txIdle; |
| } else { |
| txState = txDescRead; |
| regs.txdp = txDescCache.link; |
| CTDD = false; |
| |
| txDmaAddr = txDescCache.link & 0x3fffffff; |
| txDmaData = &txDescCache; |
| txDmaLen = sizeof(ns_desc); |
| txDmaFree = dmaDescFree; |
| |
| if (doTxDmaRead()) |
| goto exit; |
| } |
| break; |
| |
| default: |
| panic("invalid state"); |
| } |
| |
| DPRINTF(Ethernet, "entering next tx state=%s\n", |
| NsTxStateStrings[txState]); |
| |
| if (txState == txIdle) { |
| regs.command &= ~CR_TXE; |
| devIntrPost(ISR_TXIDLE); |
| return; |
| } |
| |
| goto next; |
| |
| exit: |
| /** |
| * @todo do we want to schedule a future kick? |
| */ |
| DPRINTF(Ethernet, "tx state machine exited state=%s\n", |
| NsTxStateStrings[txState]); |
| } |
| |
| void |
| EtherDev::transferDone() |
| { |
| if (txFifo.empty()) |
| return; |
| |
| DPRINTF(Ethernet, "schedule transmit\n"); |
| |
| if (txEvent.scheduled()) |
| txEvent.reschedule(curTick + 1); |
| else |
| txEvent.schedule(curTick + 1); |
| } |
| |
| 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(rom.perfectMatch, packet->data, sizeof(rom.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); |
| rxFifoCnt += packet->length; |
| interface->recvDone(); |
| |
| rxKick(); |
| return true; |
| } |
| |
| /** |
| * does a udp checksum. if gen is true, then it generates it and puts it in the right place |
| * else, it just checks what it calculates against the value in the header in packet |
| */ |
| 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 |
| EtherDev::serialize(ostream &os) |
| { |
| /* |
| * Finalize any DMA events now. |
| */ |
| if (rxDmaReadEvent.scheduled()) |
| rxDmaReadCopy(); |
| if (rxDmaWriteEvent.scheduled()) |
| rxDmaWriteCopy(); |
| if (txDmaReadEvent.scheduled()) |
| txDmaReadCopy(); |
| if (txDmaWriteEvent.scheduled()) |
| txDmaWriteCopy(); |
| |
| /* |
| * 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.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, EADDR_LEN); |
| |
| /* |
| * Serialize the various helper variables |
| */ |
| uint32_t txPktBufPtr = (uint32_t) txPacketBufPtr; |
| SERIALIZE_SCALAR(txPktBufPtr); |
| uint32_t rxPktBufPtr = (uint32_t) rxPktBufPtr; |
| SERIALIZE_SCALAR(rxPktBufPtr); |
| SERIALIZE_SCALAR(txXferLen); |
| SERIALIZE_SCALAR(rxXferLen); |
| SERIALIZE_SCALAR(txPktXmitted); |
| |
| bool txPacketExists = txPacket; |
| SERIALIZE_SCALAR(txPacketExists); |
| bool rxPacketExists = rxPacket; |
| SERIALIZE_SCALAR(rxPacketExists); |
| |
| /* |
| * Serialize DescCaches |
| */ |
| SERIALIZE_SCALAR(txDescCache.link); |
| SERIALIZE_SCALAR(txDescCache.bufptr); |
| SERIALIZE_SCALAR(txDescCache.cmdsts); |
| SERIALIZE_SCALAR(txDescCache.extsts); |
| SERIALIZE_SCALAR(rxDescCache.link); |
| SERIALIZE_SCALAR(rxDescCache.bufptr); |
| SERIALIZE_SCALAR(rxDescCache.cmdsts); |
| SERIALIZE_SCALAR(rxDescCache.extsts); |
| |
| /* |
| * Serialize tx state machine |
| */ |
| int txNumPkts = txFifo.size(); |
| SERIALIZE_SCALAR(txNumPkts); |
| int txState = this->txState; |
| SERIALIZE_SCALAR(txState); |
| SERIALIZE_SCALAR(CTDD); |
| SERIALIZE_SCALAR(txFifoCnt); |
| SERIALIZE_SCALAR(txFifoAvail); |
| SERIALIZE_SCALAR(txHalt); |
| SERIALIZE_SCALAR(txFragPtr); |
| SERIALIZE_SCALAR(txDescCnt); |
| int txDmaState = this->txDmaState; |
| SERIALIZE_SCALAR(txDmaState); |
| |
| /* |
| * Serialize rx state machine |
| */ |
| int rxNumPkts = rxFifo.size(); |
| SERIALIZE_SCALAR(rxNumPkts); |
| int rxState = this->rxState; |
| SERIALIZE_SCALAR(rxState); |
| SERIALIZE_SCALAR(CRDD); |
| SERIALIZE_SCALAR(rxPktBytes); |
| SERIALIZE_SCALAR(rxFifoCnt); |
| SERIALIZE_SCALAR(rxHalt); |
| SERIALIZE_SCALAR(rxDescCnt); |
| int rxDmaState = this->rxDmaState; |
| SERIALIZE_SCALAR(rxDmaState); |
| |
| SERIALIZE_SCALAR(extstsEnable); |
| |
| /* |
| * 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); |
| |
| /* |
| * Keep track of pending interrupt status. |
| */ |
| SERIALIZE_SCALAR(intrTick); |
| SERIALIZE_SCALAR(cpuPendingIntr); |
| Tick intrEventTick = 0; |
| if (intrEvent) |
| intrEventTick = intrEvent->when(); |
| SERIALIZE_SCALAR(intrEventTick); |
| |
| int i = 0; |
| for (pktiter_t p = rxFifo.begin(); p != rxFifo.end(); ++p) { |
| nameOut(os, csprintf("%s.rxFifo%d", name(), i++)); |
| (*p)->serialize(os); |
| } |
| if (rxPacketExists) { |
| nameOut(os, csprintf("%s.rxPacket", name())); |
| rxPacket->serialize(os); |
| } |
| i = 0; |
| for (pktiter_t p = txFifo.begin(); p != txFifo.end(); ++p) { |
| nameOut(os, csprintf("%s.txFifo%d", name(), i++)); |
| (*p)->serialize(os); |
| } |
| if (txPacketExists) { |
| nameOut(os, csprintf("%s.txPacket", name())); |
| txPacket->serialize(os); |
| } |
| } |
| |
| void |
| EtherDev::unserialize(Checkpoint *cp, const std::string §ion) |
| { |
| 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.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, EADDR_LEN); |
| |
| /* |
| * unserialize the various helper variables |
| */ |
| uint32_t txPktBufPtr; |
| UNSERIALIZE_SCALAR(txPktBufPtr); |
| txPacketBufPtr = (uint8_t *) txPktBufPtr; |
| uint32_t rxPktBufPtr; |
| UNSERIALIZE_SCALAR(rxPktBufPtr); |
| rxPacketBufPtr = (uint8_t *) rxPktBufPtr; |
| UNSERIALIZE_SCALAR(txXferLen); |
| UNSERIALIZE_SCALAR(rxXferLen); |
| UNSERIALIZE_SCALAR(txPktXmitted); |
| |
| bool txPacketExists; |
| UNSERIALIZE_SCALAR(txPacketExists); |
| bool rxPacketExists; |
| UNSERIALIZE_SCALAR(rxPacketExists); |
| |
| /* |
| * Unserialize DescCaches |
| */ |
| UNSERIALIZE_SCALAR(txDescCache.link); |
| UNSERIALIZE_SCALAR(txDescCache.bufptr); |
| UNSERIALIZE_SCALAR(txDescCache.cmdsts); |
| UNSERIALIZE_SCALAR(txDescCache.extsts); |
| UNSERIALIZE_SCALAR(rxDescCache.link); |
| UNSERIALIZE_SCALAR(rxDescCache.bufptr); |
| UNSERIALIZE_SCALAR(rxDescCache.cmdsts); |
| UNSERIALIZE_SCALAR(rxDescCache.extsts); |
| |
| /* |
| * unserialize tx state machine |
| */ |
| int txNumPkts; |
| UNSERIALIZE_SCALAR(txNumPkts); |
| int txState; |
| UNSERIALIZE_SCALAR(txState); |
| this->txState = (TxState) txState; |
| UNSERIALIZE_SCALAR(CTDD); |
| UNSERIALIZE_SCALAR(txFifoCnt); |
| UNSERIALIZE_SCALAR(txFifoAvail); |
| UNSERIALIZE_SCALAR(txHalt); |
| UNSERIALIZE_SCALAR(txFragPtr); |
| UNSERIALIZE_SCALAR(txDescCnt); |
| int txDmaState; |
| UNSERIALIZE_SCALAR(txDmaState); |
| this->txDmaState = (DmaState) txDmaState; |
| |
| /* |
| * unserialize rx state machine |
| */ |
| int rxNumPkts; |
| UNSERIALIZE_SCALAR(rxNumPkts); |
| int rxState; |
| UNSERIALIZE_SCALAR(rxState); |
| this->rxState = (RxState) rxState; |
| UNSERIALIZE_SCALAR(CRDD); |
| UNSERIALIZE_SCALAR(rxPktBytes); |
| UNSERIALIZE_SCALAR(rxFifoCnt); |
| UNSERIALIZE_SCALAR(rxHalt); |
| UNSERIALIZE_SCALAR(rxDescCnt); |
| int rxDmaState; |
| UNSERIALIZE_SCALAR(rxDmaState); |
| this->rxDmaState = (DmaState) rxDmaState; |
| |
| UNSERIALIZE_SCALAR(extstsEnable); |
| |
| /* |
| * If there's a pending transmit, store the time so we can |
| * reschedule it later |
| */ |
| Tick transmitTick; |
| UNSERIALIZE_SCALAR(transmitTick); |
| if (transmitTick) |
| txEvent.schedule(curTick + transmitTick); |
| |
| /* |
| * Keep track of pending interrupt status. |
| */ |
| UNSERIALIZE_SCALAR(intrTick); |
| UNSERIALIZE_SCALAR(cpuPendingIntr); |
| Tick intrEventTick; |
| UNSERIALIZE_SCALAR(intrEventTick); |
| if (intrEventTick) { |
| intrEvent = new IntrEvent(this, true); |
| intrEvent->schedule(intrEventTick); |
| } |
| |
| for (int i = 0; i < rxNumPkts; ++i) { |
| PacketPtr p = new EtherPacket; |
| p->unserialize(cp, csprintf("%s.rxFifo%d", section, i)); |
| rxFifo.push_back(p); |
| } |
| rxPacket = NULL; |
| if (rxPacketExists) { |
| rxPacket = new EtherPacket; |
| rxPacket->unserialize(cp, csprintf("%s.rxPacket", section)); |
| } |
| for (int i = 0; i < txNumPkts; ++i) { |
| PacketPtr p = new EtherPacket; |
| p->unserialize(cp, csprintf("%s.rxFifo%d", section, i)); |
| txFifo.push_back(p); |
| } |
| if (txPacketExists) { |
| txPacket = new EtherPacket; |
| txPacket->unserialize(cp, csprintf("%s.txPacket", section)); |
| } |
| } |
| |
| |
| Tick |
| EtherDev::cacheAccess(MemReqPtr &req) |
| { |
| DPRINTF(EthernetPIO, "timing access to paddr=%#x (daddr=%#x)\n", |
| req->paddr, req->paddr - addr); |
| return curTick + pioLatency; |
| } |
| //===================================================================== |
| |
| |
| 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<IntrControl *> intr_ctrl; |
| Param<Tick> intr_delay; |
| SimObjectParam<MemoryController *> mmu; |
| SimObjectParam<PhysicalMemory *> physmem; |
| Param<Addr> addr; |
| Param<bool> rx_filter; |
| Param<string> hardware_address; |
| SimObjectParam<Bus*> header_bus; |
| SimObjectParam<Bus*> payload_bus; |
| SimObjectParam<HierParams *> hier; |
| Param<Tick> pio_latency; |
| Param<bool> dma_desc_free; |
| Param<bool> dma_data_free; |
| Param<Tick> dma_read_delay; |
| Param<Tick> dma_write_delay; |
| Param<Tick> dma_read_factor; |
| Param<Tick> dma_write_factor; |
| 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(intr_ctrl, "Interrupt Controller"), |
| INIT_PARAM_DFLT(intr_delay, "Interrupt Delay in microseconds", 0), |
| INIT_PARAM(mmu, "Memory Controller"), |
| INIT_PARAM(physmem, "Physical Memory"), |
| INIT_PARAM(addr, "Device Address"), |
| 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_DFLT(header_bus, "The IO Bus to attach to for headers", NULL), |
| INIT_PARAM_DFLT(payload_bus, "The IO Bus to attach to for payload", NULL), |
| INIT_PARAM_DFLT(hier, "Hierarchy global variables", &defaultHierParams), |
| INIT_PARAM_DFLT(pio_latency, "Programmed IO latency", 1000), |
| INIT_PARAM_DFLT(dma_desc_free, "DMA of Descriptors is free", false), |
| INIT_PARAM_DFLT(dma_data_free, "DMA of Data is free", false), |
| INIT_PARAM_DFLT(dma_read_delay, "fixed delay for dma reads", 0), |
| INIT_PARAM_DFLT(dma_write_delay, "fixed delay for dma writes", 0), |
| INIT_PARAM_DFLT(dma_read_factor, "multiplier for dma reads", 0), |
| INIT_PARAM_DFLT(dma_write_factor, "multiplier for dma writes", 0), |
| 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(), intr_ctrl, intr_delay, |
| physmem, tx_delay, rx_delay, mmu, hier, header_bus, |
| payload_bus, pio_latency, dma_desc_free, dma_data_free, |
| dma_read_delay, dma_write_delay, dma_read_factor, |
| dma_write_factor, configspace, configdata, |
| tsunami, pci_bus, pci_dev, pci_func, rx_filter, eaddr, |
| addr); |
| } |
| |
| REGISTER_SIM_OBJECT("EtherDev", EtherDev) |