/* * Copyright (c) 2004 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. */ #include #include #include #include "base/inet.hh" #include "cpu/exec_context.hh" #include "cpu/intr_control.hh" #include "dev/dma.hh" #include "dev/etherlink.hh" #include "dev/sinic.hh" #include "dev/pciconfigall.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/debug.hh" #include "sim/eventq.hh" #include "sim/host.hh" #include "sim/stats.hh" #include "targetarch/vtophys.hh" using namespace Net; namespace Sinic { const char *RxStateStrings[] = { "rxIdle", "rxFifoBlock", "rxBeginCopy", "rxCopy", "rxCopyDone" }; const char *TxStateStrings[] = { "txIdle", "txFifoBlock", "txBeginCopy", "txCopy", "txCopyDone" }; /////////////////////////////////////////////////////////////////////// // // Sinic PCI Device // Base::Base(Params *p) : PciDev(p), rxEnable(false), txEnable(false), intrDelay(US2Ticks(p->intr_delay)), intrTick(0), cpuIntrEnable(false), cpuPendingIntr(false), intrEvent(0), interface(NULL) { } Device::Device(Params *p) : Base(p), plat(p->plat), physmem(p->physmem), rxFifo(p->rx_fifo_size), txFifo(p->tx_fifo_size), rxKickTick(0), txKickTick(0), txEvent(this), rxDmaEvent(this), txDmaEvent(this), dmaReadDelay(p->dma_read_delay), dmaReadFactor(p->dma_read_factor), dmaWriteDelay(p->dma_write_delay), dmaWriteFactor(p->dma_write_factor) { reset(); if (p->header_bus) { pioInterface = newPioInterface(p->name, p->hier, p->header_bus, this, &Device::cacheAccess); pioLatency = p->pio_latency * p->header_bus->clockRatio; if (p->payload_bus) dmaInterface = new DMAInterface(p->name + ".dma", p->header_bus, p->payload_bus, 1); else dmaInterface = new DMAInterface(p->name + ".dma", p->header_bus, p->header_bus, 1); } else if (p->payload_bus) { pioInterface = newPioInterface(p->name, p->hier, p->payload_bus, this, &Device::cacheAccess); pioLatency = p->pio_latency * p->payload_bus->clockRatio; dmaInterface = new DMAInterface(p->name + ".dma", p->payload_bus, p->payload_bus, 1); } } Device::~Device() {} void Device::regStats() { rxBytes .name(name() + ".rxBytes") .desc("Bytes Received") .prereq(rxBytes) ; rxBandwidth .name(name() + ".rxBandwidth") .desc("Receive Bandwidth (bits/s)") .precision(0) .prereq(rxBytes) ; rxPackets .name(name() + ".rxPackets") .desc("Number of Packets Received") .prereq(rxBytes) ; rxPacketRate .name(name() + ".rxPPS") .desc("Packet Reception Rate (packets/s)") .precision(0) .prereq(rxBytes) ; rxIpPackets .name(name() + ".rxIpPackets") .desc("Number of IP Packets Received") .prereq(rxBytes) ; rxTcpPackets .name(name() + ".rxTcpPackets") .desc("Number of Packets Received") .prereq(rxBytes) ; rxUdpPackets .name(name() + ".rxUdpPackets") .desc("Number of UDP Packets Received") .prereq(rxBytes) ; rxIpChecksums .name(name() + ".rxIpChecksums") .desc("Number of rx IP Checksums done by device") .precision(0) .prereq(rxBytes) ; rxTcpChecksums .name(name() + ".rxTcpChecksums") .desc("Number of rx TCP Checksums done by device") .precision(0) .prereq(rxBytes) ; rxUdpChecksums .name(name() + ".rxUdpChecksums") .desc("Number of rx UDP Checksums done by device") .precision(0) .prereq(rxBytes) ; txBytes .name(name() + ".txBytes") .desc("Bytes Transmitted") .prereq(txBytes) ; txBandwidth .name(name() + ".txBandwidth") .desc("Transmit Bandwidth (bits/s)") .precision(0) .prereq(txBytes) ; txPackets .name(name() + ".txPackets") .desc("Number of Packets Transmitted") .prereq(txBytes) ; txPacketRate .name(name() + ".txPPS") .desc("Packet Tranmission Rate (packets/s)") .precision(0) .prereq(txBytes) ; txIpPackets .name(name() + ".txIpPackets") .desc("Number of IP Packets Transmitted") .prereq(txBytes) ; txTcpPackets .name(name() + ".txTcpPackets") .desc("Number of TCP Packets Transmitted") .prereq(txBytes) ; txUdpPackets .name(name() + ".txUdpPackets") .desc("Number of Packets Transmitted") .prereq(txBytes) ; txIpChecksums .name(name() + ".txIpChecksums") .desc("Number of tx IP Checksums done by device") .precision(0) .prereq(txBytes) ; txTcpChecksums .name(name() + ".txTcpChecksums") .desc("Number of tx TCP Checksums done by device") .precision(0) .prereq(txBytes) ; txUdpChecksums .name(name() + ".txUdpChecksums") .desc("Number of tx UDP Checksums done by device") .precision(0) .prereq(txBytes) ; txBandwidth = txBytes * Stats::constant(8) / simSeconds; rxBandwidth = rxBytes * Stats::constant(8) / simSeconds; txPacketRate = txPackets / simSeconds; rxPacketRate = rxPackets / simSeconds; } /** * This is to write to the PCI general configuration registers */ void Device::WriteConfig(int offset, int size, uint32_t data) { switch (offset) { case PCI0_BASE_ADDR0: // Need to catch writes to BARs to update the PIO interface PciDev::WriteConfig(offset, size, data); if (BARAddrs[0] != 0) { if (pioInterface) pioInterface->addAddrRange(RangeSize(BARAddrs[0], BARSize[0])); BARAddrs[0] &= EV5::PAddrUncachedMask; } break; default: PciDev::WriteConfig(offset, size, data); } } /** * This reads the device registers, which are detailed in the NS83820 * spec sheet */ Fault Device::read(MemReqPtr &req, uint8_t *data) { assert(config.hdr.command & PCI_CMD_MSE); //The mask is to give you only the offset into the device register file Addr daddr = req->paddr & 0xfff; if (Regs::regSize(daddr) == 0) panic("invalid address: da=%#x pa=%#x va=%#x size=%d", daddr, req->paddr, req->vaddr, req->size); if (req->size != Regs::regSize(daddr)) panic("invalid size for reg %s: da=%#x pa=%#x va=%#x size=%d", Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size); DPRINTF(EthernetPIO, "read reg=%s da=%#x pa=%#x va=%#x size=%d\n", Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size); uint32_t ®32 = *(uint32_t *)data; uint64_t ®64 = *(uint64_t *)data; switch (daddr) { case Regs::Config: reg32 = regs.Config; break; case Regs::RxMaxCopy: reg32 = regs.RxMaxCopy; break; case Regs::TxMaxCopy: reg32 = regs.TxMaxCopy; break; case Regs::RxThreshold: reg32 = regs.RxThreshold; break; case Regs::TxThreshold: reg32 = regs.TxThreshold; break; case Regs::IntrStatus: reg32 = regs.IntrStatus; devIntrClear(); break; case Regs::IntrMask: reg32 = regs.IntrMask; break; case Regs::RxData: reg64 = regs.RxData; break; case Regs::RxDone: case Regs::RxWait: reg64 = Regs::set_RxDone_FifoLen(regs.RxDone, min(rxFifo.packets(), 255)); break; case Regs::TxData: reg64 = regs.TxData; break; case Regs::TxDone: case Regs::TxWait: reg64 = Regs::set_TxDone_FifoLen(regs.TxDone, min(txFifo.packets(), 255)); break; case Regs::HwAddr: reg64 = params()->eaddr; break; default: panic("reading write only register %s: da=%#x pa=%#x va=%#x size=%d", Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size); } DPRINTF(EthernetPIO, "read reg=%s done val=%#x\n", Regs::regName(daddr), Regs::regSize(daddr) == 4 ? reg32 : reg64); return No_Fault; } Fault Device::write(MemReqPtr &req, const uint8_t *data) { assert(config.hdr.command & PCI_CMD_MSE); Addr daddr = req->paddr & 0xfff; if (Regs::regSize(daddr) == 0) panic("invalid address: da=%#x pa=%#x va=%#x size=%d", daddr, req->paddr, req->vaddr, req->size); if (req->size != Regs::regSize(daddr)) panic("invalid size: reg=%s da=%#x pa=%#x va=%#x size=%d", Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size); uint32_t reg32 = *(uint32_t *)data; uint64_t reg64 = *(uint64_t *)data; DPRINTF(EthernetPIO, "write reg=%s val=%#x da=%#x pa=%#x va=%#x size=%d\n", Regs::regName(daddr), Regs::regSize(daddr) == 4 ? reg32 : reg64, daddr, req->paddr, req->vaddr, req->size); switch (daddr) { case Regs::Config: changeConfig(reg32); break; case Regs::RxThreshold: regs.RxThreshold = reg32; break; case Regs::TxThreshold: regs.TxThreshold = reg32; break; case Regs::IntrMask: devIntrChangeMask(reg32); break; case Regs::RxData: if (rxState != rxIdle) panic("receive machine busy with another request!"); regs.RxDone = 0; regs.RxData = reg64; if (rxEnable) { rxState = rxFifoBlock; rxKick(); } break; case Regs::TxData: if (txState != txIdle) panic("transmit machine busy with another request!"); regs.TxDone = 0; regs.TxData = reg64; if (txEnable) { txState = txFifoBlock; txKick(); } break; default: panic("writing read only register %s: da=%#x pa=%#x va=%#x size=%d", Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size); } return No_Fault; } void Device::devIntrPost(uint32_t interrupts) { if ((interrupts & Regs::Intr_Res)) panic("Cannot set a reserved interrupt"); regs.IntrStatus |= interrupts; DPRINTF(EthernetIntr, "interrupt written to intStatus: intr=%#x status=%#x mask=%#x\n", interrupts, regs.IntrStatus, regs.IntrMask); if ((regs.IntrStatus & regs.IntrMask)) { Tick when = curTick; if ((regs.IntrStatus & regs.IntrMask & Regs::Intr_NoDelay) == 0) when += intrDelay; cpuIntrPost(when); } } void Device::devIntrClear(uint32_t interrupts) { if ((interrupts & Regs::Intr_Res)) panic("Cannot clear a reserved interrupt"); regs.IntrStatus &= ~interrupts; DPRINTF(EthernetIntr, "interrupt cleared from intStatus: intr=%x status=%x mask=%x\n", interrupts, regs.IntrStatus, regs.IntrMask); if (!(regs.IntrStatus & regs.IntrMask)) cpuIntrClear(); } void Device::devIntrChangeMask(uint32_t newmask) { if (regs.IntrMask == newmask) return; regs.IntrMask = newmask; DPRINTF(EthernetIntr, "interrupt mask changed: intStatus=%x intMask=%x masked=%x\n", regs.IntrStatus, regs.IntrMask, regs.IntrStatus & regs.IntrMask); if (regs.IntrStatus & regs.IntrMask) cpuIntrPost(curTick); else cpuIntrClear(); } void Base::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 (!cpuIntrEnable) { DPRINTF(EthernetIntr, "interrupts not enabled.\n", intrTick); return; } if (when > intrTick && intrTick != 0) { DPRINTF(EthernetIntr, "don't need to schedule event...intrTick=%d\n", intrTick); return; } intrTick = when; if (intrTick < curTick) { debug_break(); intrTick = curTick; } DPRINTF(EthernetIntr, "going to schedule an interrupt for intrTick=%d\n", intrTick); if (intrEvent) intrEvent->squash(); intrEvent = new IntrEvent(this, true); intrEvent->schedule(intrTick); } void Base::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 Base::cpuIntrClear() { if (!cpuPendingIntr) return; if (intrEvent) { intrEvent->squash(); intrEvent = 0; } intrTick = 0; cpuPendingIntr = false; DPRINTF(EthernetIntr, "clearing cchip interrupt\n"); intrClear(); } bool Base::cpuIntrPending() const { return cpuPendingIntr; } void Device::changeConfig(uint32_t newconf) { uint32_t changed = regs.Config ^ newconf; if (!changed) return; regs.Config = newconf; if ((changed & Regs::Config_Reset)) { assert(regs.Config & Regs::Config_Reset); reset(); regs.Config &= ~Regs::Config_Reset; } if ((changed & Regs::Config_IntEn)) { cpuIntrEnable = regs.Config & Regs::Config_IntEn; if (cpuIntrEnable) { if (regs.IntrStatus & regs.IntrMask) cpuIntrPost(curTick); } else { cpuIntrClear(); } } if ((changed & Regs::Config_TxEn)) { txEnable = regs.Config & Regs::Config_TxEn; if (txEnable) txKick(); } if ((changed & Regs::Config_RxEn)) { rxEnable = regs.Config & Regs::Config_RxEn; if (rxEnable) rxKick(); } } void Device::reset() { using namespace Regs; memset(®s, 0, sizeof(regs)); regs.RxMaxCopy = params()->rx_max_copy; regs.TxMaxCopy = params()->tx_max_copy; regs.IntrMask = Intr_TxFifo | Intr_RxFifo | Intr_RxData; rxState = rxIdle; txState = txIdle; rxFifo.clear(); txFifo.clear(); } void Device::rxDmaCopy() { assert(rxState == rxCopy); rxState = rxCopyDone; physmem->dma_write(rxDmaAddr, (uint8_t *)rxDmaData, rxDmaLen); DPRINTF(EthernetDMA, "rx dma write paddr=%#x len=%d\n", rxDmaAddr, rxDmaLen); DDUMP(EthernetDMA, rxDmaData, rxDmaLen); } void Device::rxDmaDone() { rxDmaCopy(); rxKick(); } void Device::rxKick() { DPRINTF(EthernetSM, "receive kick rxState=%s (rxFifo.size=%d)\n", RxStateStrings[rxState], rxFifo.size()); if (rxKickTick > curTick) { DPRINTF(EthernetSM, "receive kick exiting, can't run till %d\n", rxKickTick); return; } next: switch (rxState) { case rxIdle: if (rxPioRequest) { pioInterface->respond(rxPioRequest, curTick); rxPioRequest = 0; } goto exit; case rxFifoBlock: if (rxPacket) { rxState = rxBeginCopy; break; } if (rxFifo.empty()) { DPRINTF(EthernetSM, "receive waiting for data. Nothing to do.\n"); goto exit; } // Grab a new packet from the fifo. rxPacket = rxFifo.front(); rxPacketBufPtr = rxPacket->data; rxPktBytes = rxPacket->length; assert(rxPktBytes); rxDoneData = 0; /* scope for variables */ { IpPtr ip(rxPacket); if (ip) { rxDoneData |= Regs::RxDone_IpPacket; rxIpChecksums++; if (cksum(ip) != 0) { DPRINTF(EthernetCksum, "Rx IP Checksum Error\n"); rxDoneData |= Regs::RxDone_IpError; } TcpPtr tcp(ip); UdpPtr udp(ip); if (tcp) { rxDoneData |= Regs::RxDone_TcpPacket; rxTcpChecksums++; if (cksum(tcp) != 0) { DPRINTF(EthernetCksum, "Rx TCP Checksum Error\n"); rxDoneData |= Regs::RxDone_TcpError; } } else if (udp) { rxDoneData |= Regs::RxDone_UdpPacket; rxUdpChecksums++; if (cksum(udp) != 0) { DPRINTF(EthernetCksum, "Rx UDP Checksum Error\n"); rxDoneData |= Regs::RxDone_UdpError; } } } } rxState = rxBeginCopy; break; case rxBeginCopy: rxDmaAddr = plat->pciToDma(Regs::get_RxData_Addr(regs.RxData)); rxDmaLen = min(Regs::get_RxData_Len(regs.RxData), rxPktBytes); rxDmaData = rxPacketBufPtr; if (dmaInterface) { if (!dmaInterface->busy()) { dmaInterface->doDMA(WriteInvalidate, rxDmaAddr, rxDmaLen, curTick, &rxDmaEvent, true); rxState = rxCopy; } goto exit; } rxState = rxCopy; if (dmaWriteDelay != 0 || dmaWriteFactor != 0) { Tick factor = ((rxDmaLen + ULL(63)) >> ULL(6)) * dmaWriteFactor; Tick start = curTick + dmaWriteDelay + factor; rxDmaEvent.schedule(start); goto exit; } rxDmaCopy(); break; case rxCopy: DPRINTF(EthernetSM, "receive machine still copying\n"); goto exit; case rxCopyDone: regs.RxDone = rxDoneData | rxDmaLen; if (rxPktBytes == rxDmaLen) { rxPacket = NULL; rxFifo.pop(); } else { regs.RxDone |= Regs::RxDone_More; rxPktBytes -= rxDmaLen; rxPacketBufPtr += rxDmaLen; } regs.RxDone |= Regs::RxDone_Complete; devIntrPost(Regs::Intr_RxData); rxState = rxIdle; break; default: panic("Invalid rxState!"); } DPRINTF(EthernetSM, "entering next rxState=%s\n", RxStateStrings[rxState]); goto next; exit: /** * @todo do we want to schedule a future kick? */ DPRINTF(EthernetSM, "rx state machine exited rxState=%s\n", RxStateStrings[rxState]); } void Device::txDmaCopy() { assert(txState == txCopy); txState = txCopyDone; physmem->dma_read((uint8_t *)txDmaData, txDmaAddr, txDmaLen); DPRINTF(EthernetDMA, "tx dma read paddr=%#x len=%d\n", txDmaAddr, txDmaLen); DDUMP(EthernetDMA, txDmaData, txDmaLen); } void Device::txDmaDone() { txDmaCopy(); txKick(); } void Device::transmit() { if (txFifo.empty()) { DPRINTF(Ethernet, "nothing to transmit\n"); return; } PacketPtr packet = txFifo.front(); if (!interface->sendPacket(packet)) { DPRINTF(Ethernet, "Packet Transmit: failed txFifo available %d\n", txFifo.avail()); goto reschedule; } txFifo.pop(); #if TRACING_ON if (DTRACE(Ethernet)) { IpPtr ip(packet); if (ip) { DPRINTF(Ethernet, "ID is %d\n", ip->id()); TcpPtr tcp(ip); if (tcp) { DPRINTF(Ethernet, "Src Port=%d, Dest Port=%d\n", tcp->sport(), tcp->dport()); } } } #endif DDUMP(Ethernet, packet->data, packet->length); txBytes += packet->length; txPackets++; DPRINTF(Ethernet, "Packet Transmit: successful txFifo Available %d\n", txFifo.avail()); if (txFifo.size() <= params()->tx_fifo_threshold) devIntrPost(Regs::Intr_TxFifo); devIntrPost(Regs::Intr_TxDone); reschedule: if (!txFifo.empty() && !txEvent.scheduled()) { DPRINTF(Ethernet, "reschedule transmit\n"); txEvent.schedule(curTick + 1000); } } void Device::txKick() { DPRINTF(EthernetSM, "transmit kick txState=%s (txFifo.size=%d)\n", TxStateStrings[txState], txFifo.size()); if (txKickTick > curTick) { DPRINTF(EthernetSM, "transmit kick exiting, can't run till %d\n", txKickTick); return; } next: switch (txState) { case txIdle: if (txPioRequest) { pioInterface->respond(txPioRequest, curTick + pioLatency); txPioRequest = 0; } goto exit; case txFifoBlock: if (!txPacket) { // Grab a new packet from the fifo. txPacket = new PacketData(16384); txPacketBufPtr = txPacket->data; } if (txFifo.avail() - txPacket->length < Regs::get_TxData_Len(regs.TxData)) { DPRINTF(EthernetSM, "transmit fifo full. Nothing to do.\n"); goto exit; } txState = txBeginCopy; break; case txBeginCopy: txDmaAddr = plat->pciToDma(Regs::get_TxData_Addr(regs.TxData)); txDmaLen = Regs::get_TxData_Len(regs.TxData); txDmaData = txPacketBufPtr; if (dmaInterface) { if (!dmaInterface->busy()) { dmaInterface->doDMA(Read, txDmaAddr, txDmaLen, curTick, &txDmaEvent, true); txState = txCopy; } goto exit; } txState = txCopy; if (dmaReadDelay != 0 || dmaReadFactor != 0) { Tick factor = ((txDmaLen + ULL(63)) >> ULL(6)) * dmaReadFactor; Tick start = curTick + dmaReadDelay + factor; txDmaEvent.schedule(start); goto exit; } txDmaCopy(); break; case txCopy: DPRINTF(EthernetSM, "transmit machine still copying\n"); goto exit; case txCopyDone: txPacket->length += txDmaLen; if ((regs.TxData & Regs::TxData_More)) { txPacketBufPtr += txDmaLen; } else { assert(txPacket->length <= txFifo.avail()); if ((regs.TxData & Regs::TxData_Checksum)) { IpPtr ip(txPacket); if (ip) { TcpPtr tcp(ip); if (tcp) { tcp->sum(0); tcp->sum(cksum(tcp)); txTcpChecksums++; } UdpPtr udp(ip); if (udp) { udp->sum(0); udp->sum(cksum(udp)); txUdpChecksums++; } ip->sum(0); ip->sum(cksum(ip)); txIpChecksums++; } } txFifo.push(txPacket); txPacket = 0; transmit(); } regs.TxDone = txDmaLen | Regs::TxDone_Complete; devIntrPost(Regs::Intr_TxData); txState = txIdle; break; default: panic("Invalid txState!"); } DPRINTF(EthernetSM, "entering next txState=%s\n", TxStateStrings[txState]); goto next; exit: /** * @todo do we want to schedule a future kick? */ DPRINTF(EthernetSM, "tx state machine exited txState=%s\n", TxStateStrings[txState]); } void Device::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"); if (txEvent.scheduled()) txEvent.reschedule(curTick + 1); else txEvent.schedule(curTick + 1); } bool Device::rxFilter(const PacketPtr &packet) { if (!Regs::get_Config_Filter(regs.Config)) return false; panic("receive filter not implemented\n"); bool drop = true; #if 0 string type; EthHdr *eth = packet->eth(); if (eth->unicast()) { // If we're accepting all unicast addresses if (acceptUnicast) drop = false; // If we make a perfect match if (acceptPerfect && params->eaddr == eth.dst()) drop = false; if (acceptArp && eth->type() == ETH_TYPE_ARP) drop = false; } else if (eth->broadcast()) { // if we're accepting broadcasts if (acceptBroadcast) drop = false; } else if (eth->multicast()) { // if we're accepting all multicasts if (acceptMulticast) drop = false; } if (drop) { DPRINTF(Ethernet, "rxFilter drop\n"); DDUMP(EthernetData, packet->data, packet->length); } #endif return drop; } bool Device::recvPacket(PacketPtr packet) { rxBytes += packet->length; rxPackets++; DPRINTF(Ethernet, "Receiving packet from wire, rxFifo Available is %d\n", rxFifo.avail()); if (!rxEnable) { DPRINTF(Ethernet, "receive disabled...packet dropped\n"); interface->recvDone(); return true; } if (rxFilter(packet)) { DPRINTF(Ethernet, "packet filtered...dropped\n"); interface->recvDone(); return true; } if (rxFifo.size() >= params()->rx_fifo_threshold) devIntrPost(Regs::Intr_RxFifo); if (!rxFifo.push(packet)) { DPRINTF(Ethernet, "packet will not fit in receive buffer...packet dropped\n"); return false; } interface->recvDone(); devIntrPost(Regs::Intr_RxDone); rxKick(); return true; } //===================================================================== // // void Base::serialize(ostream &os) { // Serialize the PciDev base class PciDev::serialize(os); SERIALIZE_SCALAR(rxEnable); SERIALIZE_SCALAR(txEnable); SERIALIZE_SCALAR(cpuIntrEnable); /* * Keep track of pending interrupt status. */ SERIALIZE_SCALAR(intrTick); SERIALIZE_SCALAR(cpuPendingIntr); Tick intrEventTick = 0; if (intrEvent) intrEventTick = intrEvent->when(); SERIALIZE_SCALAR(intrEventTick); } void Base::unserialize(Checkpoint *cp, const std::string §ion) { // Unserialize the PciDev base class PciDev::unserialize(cp, section); UNSERIALIZE_SCALAR(rxEnable); UNSERIALIZE_SCALAR(txEnable); UNSERIALIZE_SCALAR(cpuIntrEnable); /* * 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); } } void Device::serialize(ostream &os) { // Serialize the PciDev base class Base::serialize(os); if (rxDmaEvent.scheduled()) rxDmaCopy(); if (txDmaEvent.scheduled()) txDmaCopy(); /* * Serialize the device registers */ SERIALIZE_SCALAR(regs.Config); SERIALIZE_SCALAR(regs.RxMaxCopy); SERIALIZE_SCALAR(regs.TxMaxCopy); SERIALIZE_SCALAR(regs.RxThreshold); SERIALIZE_SCALAR(regs.TxThreshold); SERIALIZE_SCALAR(regs.IntrStatus); SERIALIZE_SCALAR(regs.IntrMask); SERIALIZE_SCALAR(regs.RxData); SERIALIZE_SCALAR(regs.RxDone); SERIALIZE_SCALAR(regs.TxData); SERIALIZE_SCALAR(regs.TxDone); /* * Serialize rx state machine */ int rxState = this->rxState; SERIALIZE_SCALAR(rxState); rxFifo.serialize("rxFifo", os); bool rxPacketExists = rxPacket; SERIALIZE_SCALAR(rxPacketExists); if (rxPacketExists) { rxPacket->serialize("rxPacket", os); uint32_t rxPktBufPtr = (uint32_t) (rxPacketBufPtr - rxPacket->data); SERIALIZE_SCALAR(rxPktBufPtr); SERIALIZE_SCALAR(rxPktBytes); } SERIALIZE_SCALAR(rxDoneData); /* * Serialize tx state machine */ int txState = this->txState; SERIALIZE_SCALAR(txState); txFifo.serialize("txFifo", os); bool txPacketExists = txPacket; SERIALIZE_SCALAR(txPacketExists); if (txPacketExists) { txPacket->serialize("txPacket", os); uint32_t txPktBufPtr = (uint32_t) (txPacketBufPtr - txPacket->data); SERIALIZE_SCALAR(txPktBufPtr); SERIALIZE_SCALAR(txPktBytes); } /* * 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); } void Device::unserialize(Checkpoint *cp, const std::string §ion) { // Unserialize the PciDev base class Base::unserialize(cp, section); /* * Unserialize the device registers */ UNSERIALIZE_SCALAR(regs.Config); UNSERIALIZE_SCALAR(regs.RxMaxCopy); UNSERIALIZE_SCALAR(regs.TxMaxCopy); UNSERIALIZE_SCALAR(regs.RxThreshold); UNSERIALIZE_SCALAR(regs.TxThreshold); UNSERIALIZE_SCALAR(regs.IntrStatus); UNSERIALIZE_SCALAR(regs.IntrMask); UNSERIALIZE_SCALAR(regs.RxData); UNSERIALIZE_SCALAR(regs.RxDone); UNSERIALIZE_SCALAR(regs.TxData); UNSERIALIZE_SCALAR(regs.TxDone); /* * Unserialize rx state machine */ int rxState; UNSERIALIZE_SCALAR(rxState); this->rxState = (RxState) rxState; rxFifo.unserialize("rxFifo", cp, section); bool rxPacketExists; UNSERIALIZE_SCALAR(rxPacketExists); rxPacket = 0; if (rxPacketExists) { rxPacket = new PacketData; rxPacket->unserialize("rxPacket", cp, section); uint32_t rxPktBufPtr; UNSERIALIZE_SCALAR(rxPktBufPtr); this->rxPacketBufPtr = (uint8_t *) rxPacket->data + rxPktBufPtr; UNSERIALIZE_SCALAR(rxPktBytes); } UNSERIALIZE_SCALAR(rxDoneData); /* * Unserialize tx state machine */ int txState; UNSERIALIZE_SCALAR(txState); this->txState = (TxState) txState; txFifo.unserialize("txFifo", cp, section); bool txPacketExists; UNSERIALIZE_SCALAR(txPacketExists); txPacket = 0; if (txPacketExists) { txPacket = new PacketData; txPacket->unserialize("txPacket", cp, section); uint32_t txPktBufPtr; UNSERIALIZE_SCALAR(txPktBufPtr); this->txPacketBufPtr = (uint8_t *) txPacket->data + txPktBufPtr; UNSERIALIZE_SCALAR(txPktBytes); } /* * If there's a pending transmit, reschedule it now */ Tick transmitTick; UNSERIALIZE_SCALAR(transmitTick); if (transmitTick) txEvent.schedule(curTick + transmitTick); /* * re-add addrRanges to bus bridges */ if (pioInterface) { pioInterface->addAddrRange(RangeSize(BARAddrs[0], BARSize[0])); pioInterface->addAddrRange(RangeSize(BARAddrs[1], BARSize[1])); } } Tick Device::cacheAccess(MemReqPtr &req) { //The mask is to give you only the offset into the device register file Addr daddr = req->paddr - addr; DPRINTF(EthernetPIO, "timing access to paddr=%#x (daddr=%#x)\n", req->paddr, daddr); Tick when = curTick + pioLatency; switch (daddr) { case Regs::RxDone: if (rxState != rxIdle) { rxPioRequest = req; when = 0; } break; case Regs::TxDone: if (txState != txIdle) { txPioRequest = req; when = 0; } break; } return when; } BEGIN_DECLARE_SIM_OBJECT_PARAMS(Interface) SimObjectParam peer; SimObjectParam device; END_DECLARE_SIM_OBJECT_PARAMS(Interface) BEGIN_INIT_SIM_OBJECT_PARAMS(Interface) INIT_PARAM_DFLT(peer, "peer interface", NULL), INIT_PARAM(device, "Ethernet device of this interface") END_INIT_SIM_OBJECT_PARAMS(Interface) CREATE_SIM_OBJECT(Interface) { Interface *dev_int = new Interface(getInstanceName(), device); EtherInt *p = (EtherInt *)peer; if (p) { dev_int->setPeer(p); p->setPeer(dev_int); } return dev_int; } REGISTER_SIM_OBJECT("SinicInt", Interface) BEGIN_DECLARE_SIM_OBJECT_PARAMS(Device) Param tx_delay; Param rx_delay; Param intr_delay; SimObjectParam mmu; SimObjectParam physmem; Param rx_filter; Param hardware_address; SimObjectParam header_bus; SimObjectParam payload_bus; SimObjectParam hier; Param pio_latency; SimObjectParam configspace; SimObjectParam configdata; SimObjectParam platform; Param pci_bus; Param pci_dev; Param pci_func; Param rx_max_copy; Param tx_max_copy; Param rx_fifo_size; Param tx_fifo_size; Param rx_fifo_threshold; Param tx_fifo_threshold; Param dma_read_delay; Param dma_read_factor; Param dma_write_delay; Param dma_write_factor; END_DECLARE_SIM_OBJECT_PARAMS(Device) BEGIN_INIT_SIM_OBJECT_PARAMS(Device) INIT_PARAM_DFLT(tx_delay, "Transmit Delay", 1000), INIT_PARAM_DFLT(rx_delay, "Receive Delay", 1000), INIT_PARAM_DFLT(intr_delay, "Interrupt Delay in microseconds", 0), INIT_PARAM(mmu, "Memory Controller"), INIT_PARAM(physmem, "Physical Memory"), 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 in bus cycles", 1), INIT_PARAM(configspace, "PCI Configspace"), INIT_PARAM(configdata, "PCI Config data"), INIT_PARAM(platform, "Platform"), INIT_PARAM(pci_bus, "PCI bus"), INIT_PARAM(pci_dev, "PCI device number"), INIT_PARAM(pci_func, "PCI function code"), INIT_PARAM_DFLT(rx_max_copy, "rx max copy", 16*1024), INIT_PARAM_DFLT(tx_max_copy, "rx max copy", 16*1024), INIT_PARAM_DFLT(rx_fifo_size, "max size in bytes of rxFifo", 64*1024), INIT_PARAM_DFLT(tx_fifo_size, "max size in bytes of txFifo", 64*1024), INIT_PARAM_DFLT(rx_fifo_threshold, "max size in bytes of rxFifo", 48*1024), INIT_PARAM_DFLT(tx_fifo_threshold, "max size in bytes of txFifo", 16*1024), INIT_PARAM_DFLT(dma_read_delay, "fixed delay for dma reads", 0), INIT_PARAM_DFLT(dma_read_factor, "multiplier for dma reads", 0), INIT_PARAM_DFLT(dma_write_delay, "fixed delay for dma writes", 0), INIT_PARAM_DFLT(dma_write_factor, "multiplier for dma writes", 0) END_INIT_SIM_OBJECT_PARAMS(Device) CREATE_SIM_OBJECT(Device) { Device::Params *params = new Device::Params; params->name = getInstanceName(); params->intr_delay = intr_delay; params->physmem = physmem; params->tx_delay = tx_delay; params->rx_delay = rx_delay; params->mmu = mmu; params->hier = hier; params->header_bus = header_bus; params->payload_bus = payload_bus; params->pio_latency = pio_latency; params->configSpace = configspace; params->configData = configdata; params->plat = platform; params->busNum = pci_bus; params->deviceNum = pci_dev; params->functionNum = pci_func; params->rx_filter = rx_filter; params->eaddr = hardware_address; params->rx_max_copy = rx_max_copy; params->tx_max_copy = tx_max_copy; params->rx_fifo_size = rx_fifo_size; params->tx_fifo_size = tx_fifo_size; params->rx_fifo_threshold = rx_fifo_threshold; params->tx_fifo_threshold = tx_fifo_threshold; params->dma_read_delay = dma_read_delay; params->dma_read_factor = dma_read_factor; params->dma_write_delay = dma_write_delay; params->dma_write_factor = dma_write_factor; return new Device(params); } REGISTER_SIM_OBJECT("Sinic", Device) /* namespace Sinic */ }