From 7e4229fb8fa70cd91f831ad056186e9fc43b9f80 Mon Sep 17 00:00:00 2001 From: Nathan Binkert Date: Sat, 13 Nov 2004 17:10:48 -0500 Subject: [PATCH] Add the Simple Integrated Network Interface Controller --HG-- extra : convert_revision : 2bce25881a104e8282a5ed819769c6a7de414fb2 --- SConscript | 1 + dev/sinic.cc | 1435 +++++++++++++++++++++++++++++++++++++++++++++++ dev/sinic.hh | 340 +++++++++++ dev/sinicreg.hh | 187 ++++++ 4 files changed, 1963 insertions(+) create mode 100644 dev/sinic.cc create mode 100644 dev/sinic.hh create mode 100644 dev/sinicreg.hh diff --git a/SConscript b/SConscript index 4fedf25ba..7769d0708 100644 --- a/SConscript +++ b/SConscript @@ -276,6 +276,7 @@ full_system_sources = Split(''' dev/scsi_ctrl.cc dev/scsi_disk.cc dev/scsi_none.cc + dev/sinic.cc dev/simple_disk.cc dev/tlaser_clock.cc dev/tlaser_ipi.cc diff --git a/dev/sinic.cc b/dev/sinic.cc new file mode 100644 index 000000000..32a3825fc --- /dev/null +++ b/dev/sinic.cc @@ -0,0 +1,1435 @@ +/* + * 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/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 */ } diff --git a/dev/sinic.hh b/dev/sinic.hh new file mode 100644 index 000000000..ef515ffad --- /dev/null +++ b/dev/sinic.hh @@ -0,0 +1,340 @@ +/* + * 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. + */ + +#ifndef __DEV_SINIC_HH__ +#define __DEV_SINIC_HH__ + +#include "base/inet.hh" +#include "base/statistics.hh" +#include "dev/etherint.hh" +#include "dev/etherpkt.hh" +#include "dev/io_device.hh" +#include "dev/pcidev.hh" +#include "dev/pktfifo.hh" +#include "dev/sinicreg.hh" +#include "mem/bus/bus.hh" +#include "sim/eventq.hh" + +namespace Sinic { + +class Interface; +class Base : public PciDev +{ + protected: + bool rxEnable; + bool txEnable; + + protected: + Tick intrDelay; + Tick intrTick; + bool cpuIntrEnable; + bool cpuPendingIntr; + void cpuIntrPost(Tick when); + void cpuInterrupt(); + void cpuIntrClear(); + + typedef EventWrapper IntrEvent; + friend class IntrEvent; + IntrEvent *intrEvent; + Interface *interface; + + bool cpuIntrPending() const; + void cpuIntrAck() { cpuIntrClear(); } + +/** + * Serialization stuff + */ + public: + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +/** + * Construction/Destruction/Parameters + */ + public: + struct Params : public PciDev::Params + { + Tick intr_delay; + }; + + Base(Params *p); +}; + +class Device : public Base +{ + protected: + Platform *plat; + PhysicalMemory *physmem; + + protected: + /** Receive State Machine States */ + enum RxState { + rxIdle, + rxFifoBlock, + rxBeginCopy, + rxCopy, + rxCopyDone + }; + + /** Transmit State Machine states */ + enum TxState { + txIdle, + txFifoBlock, + txBeginCopy, + txCopy, + txCopyDone + }; + + /** device register file */ + struct { + uint32_t Config; + uint32_t RxMaxCopy; + uint32_t TxMaxCopy; + uint32_t RxThreshold; + uint32_t TxThreshold; + uint32_t IntrStatus; + uint32_t IntrMask; + uint64_t RxData; + uint64_t RxDone; + uint64_t TxData; + uint64_t TxDone; + } regs; + + private: + Addr addr; + static const Addr size = Regs::Size; + + protected: + RxState rxState; + PacketFifo rxFifo; + PacketPtr rxPacket; + uint8_t *rxPacketBufPtr; + int rxPktBytes; + uint64_t rxDoneData; + Addr rxDmaAddr; + uint8_t *rxDmaData; + int rxDmaLen; + + TxState txState; + PacketFifo txFifo; + PacketPtr txPacket; + uint8_t *txPacketBufPtr; + int txPktBytes; + Addr txDmaAddr; + uint8_t *txDmaData; + int txDmaLen; + + protected: + void reset(); + + void rxKick(); + Tick rxKickTick; + typedef EventWrapper RxKickEvent; + friend class RxKickEvent; + + void txKick(); + Tick txKickTick; + typedef EventWrapper TxKickEvent; + friend class TxKickEvent; + + /** + * Retransmit event + */ + void transmit(); + void txEventTransmit() + { + transmit(); + if (txState == txFifoBlock) + txKick(); + } + typedef EventWrapper TxEvent; + friend class TxEvent; + TxEvent txEvent; + + void txDump() const; + void rxDump() const; + + /** + * receive address filter + */ + bool rxFilter(const PacketPtr &packet); + +/** + * device configuration + */ + void changeConfig(uint32_t newconfig); + +/** + * device ethernet interface + */ + public: + bool recvPacket(PacketPtr packet); + void transferDone(); + void setInterface(Interface *i) { assert(!interface); interface = i; } + +/** + * DMA parameters + */ + protected: + void rxDmaCopy(); + void rxDmaDone(); + friend class EventWrapper; + EventWrapper rxDmaEvent; + + void txDmaCopy(); + void txDmaDone(); + friend class EventWrapper; + EventWrapper txDmaEvent; + + Tick dmaReadDelay; + Tick dmaReadFactor; + Tick dmaWriteDelay; + Tick dmaWriteFactor; + +/** + * PIO parameters + */ + protected: + MemReqPtr rxPioRequest; + MemReqPtr txPioRequest; + +/** + * Interrupt management + */ + protected: + void devIntrPost(uint32_t interrupts); + void devIntrClear(uint32_t interrupts = Regs::Intr_All); + void devIntrChangeMask(uint32_t newmask); + +/** + * PCI Configuration interface + */ + public: + virtual void WriteConfig(int offset, int size, uint32_t data); + +/** + * Memory Interface + */ + public: + virtual Fault read(MemReqPtr &req, uint8_t *data); + virtual Fault write(MemReqPtr &req, const uint8_t *data); + Tick cacheAccess(MemReqPtr &req); + +/** + * Statistics + */ + private: + Stats::Scalar<> rxBytes; + Stats::Formula rxBandwidth; + Stats::Scalar<> rxPackets; + Stats::Formula rxPacketRate; + Stats::Scalar<> rxIpPackets; + Stats::Scalar<> rxTcpPackets; + Stats::Scalar<> rxUdpPackets; + Stats::Scalar<> rxIpChecksums; + Stats::Scalar<> rxTcpChecksums; + Stats::Scalar<> rxUdpChecksums; + + Stats::Scalar<> txBytes; + Stats::Formula txBandwidth; + Stats::Scalar<> txPackets; + Stats::Formula txPacketRate; + Stats::Scalar<> txIpPackets; + Stats::Scalar<> txTcpPackets; + Stats::Scalar<> txUdpPackets; + Stats::Scalar<> txIpChecksums; + Stats::Scalar<> txTcpChecksums; + Stats::Scalar<> txUdpChecksums; + + public: + virtual void regStats(); + +/** + * Serialization stuff + */ + public: + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +/** + * Construction/Destruction/Parameters + */ + public: + struct Params : public Base::Params + { + IntrControl *i; + PhysicalMemory *pmem; + Tick tx_delay; + Tick rx_delay; + HierParams *hier; + Bus *header_bus; + Bus *payload_bus; + Tick pio_latency; + PhysicalMemory *physmem; + IntrControl *intctrl; + bool rx_filter; + Net::EthAddr eaddr; + uint32_t rx_max_copy; + uint32_t tx_max_copy; + uint32_t rx_fifo_size; + uint32_t tx_fifo_size; + uint32_t rx_fifo_threshold; + uint32_t tx_fifo_threshold; + Tick dma_read_delay; + Tick dma_read_factor; + Tick dma_write_delay; + Tick dma_write_factor; + }; + + protected: + const Params *params() const { return (const Params *)_params; } + + public: + Device(Params *params); + ~Device(); +}; + +/* + * Ethernet Interface for an Ethernet Device + */ +class Interface : public EtherInt +{ + private: + Device *dev; + + public: + Interface(const std::string &name, Device *d) + : EtherInt(name), dev(d) { dev->setInterface(this); } + + virtual bool recvPacket(PacketPtr pkt) { return dev->recvPacket(pkt); } + virtual void sendDone() { dev->transferDone(); } +}; + +/* namespace Sinic */ } + +#endif // __DEV_SINIC_HH__ diff --git a/dev/sinicreg.hh b/dev/sinicreg.hh new file mode 100644 index 000000000..9f3412a31 --- /dev/null +++ b/dev/sinicreg.hh @@ -0,0 +1,187 @@ +/* + * 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. + */ + +#ifndef __DEV_SINICREG_HH__ +#define __DEV_SINICREG_HH__ + +#define __SINIC_REG32(NAME, VAL) static const uint32_t NAME = (VAL) +#define __SINIC_REG64(NAME, VAL) static const uint64_t NAME = (VAL) + +#define __SINIC_VAL32(NAME, OFFSET, WIDTH) \ + static const uint32_t NAME##_width = WIDTH; \ + static const uint32_t NAME##_offset = OFFSET; \ + static const uint32_t NAME##_mask = (1 << WIDTH) - 1; \ + static const uint32_t NAME = ((1 << WIDTH) - 1) << OFFSET; \ + static inline uint32_t get_##NAME(uint32_t reg) \ + { return (reg & NAME) >> OFFSET; } \ + static inline uint32_t set_##NAME(uint32_t reg, uint32_t val) \ + { return (reg & ~NAME) | ((val << OFFSET) & NAME); } + +#define __SINIC_VAL64(NAME, OFFSET, WIDTH) \ + static const uint64_t NAME##_width = WIDTH; \ + static const uint64_t NAME##_offset = OFFSET; \ + static const uint64_t NAME##_mask = (ULL(1) << WIDTH) - 1; \ + static const uint64_t NAME = ((ULL(1) << WIDTH) - 1) << OFFSET; \ + static inline uint64_t get_##NAME(uint64_t reg) \ + { return (reg & NAME) >> OFFSET; } \ + static inline uint64_t set_##NAME(uint64_t reg, uint64_t val) \ + { return (reg & ~NAME) | ((val << OFFSET) & NAME); } + +namespace Sinic { +namespace Regs { + +// Registers +__SINIC_REG32(Config, 0x00); // 32: configuration register +__SINIC_REG32(RxMaxCopy, 0x04); // 32: max rx copy +__SINIC_REG32(TxMaxCopy, 0x08); // 32: max tx copy +__SINIC_REG32(RxThreshold, 0x0c); // 32: receive fifo threshold +__SINIC_REG32(TxThreshold, 0x10); // 32: transmit fifo threshold +__SINIC_REG32(IntrStatus, 0x14); // 32: interrupt status +__SINIC_REG32(IntrMask, 0x18); // 32: interrupt mask +__SINIC_REG32(RxData, 0x20); // 64: receive data +__SINIC_REG32(RxDone, 0x28); // 64: receive done +__SINIC_REG32(RxWait, 0x30); // 64: receive done (busy wait) +__SINIC_REG32(TxData, 0x38); // 64: transmit data +__SINIC_REG32(TxDone, 0x40); // 64: transmit done +__SINIC_REG32(TxWait, 0x48); // 64: transmit done (busy wait) +__SINIC_REG32(HwAddr, 0x50); // 64: mac address +__SINIC_REG32(Size, 0x58); + +// Config register bits +__SINIC_VAL32(Config_Reset, 31, 1); // reset chip +__SINIC_VAL32(Config_Filter, 7, 1); // enable receive filter +__SINIC_VAL32(Config_Vlan, 6, 1); // enable vlan tagging +__SINIC_VAL32(Config_Virtual, 5, 1); // enable virtual addressing +__SINIC_VAL32(Config_Desc, 4, 1); // enable tx/rx descriptors +__SINIC_VAL32(Config_Poll, 3, 1); // enable polling +__SINIC_VAL32(Config_IntEn, 2, 1); // enable interrupts +__SINIC_VAL32(Config_TxEn, 1, 1); // enable transmit +__SINIC_VAL32(Config_RxEn, 0, 1); // enable receive + +// Interrupt register bits +__SINIC_VAL32(Intr_TxFifo, 5, 1); // Fifo oflow/uflow/threshold +__SINIC_VAL32(Intr_TxData, 4, 1); // DMA Completed w/ interrupt +__SINIC_VAL32(Intr_TxDone, 3, 1); // Packet transmitted +__SINIC_VAL32(Intr_RxFifo, 2, 1); // Fifo oflow/uflow/threshold +__SINIC_VAL32(Intr_RxData, 1, 1); // DMA Completed w/ interrupt +__SINIC_VAL32(Intr_RxDone, 0, 1); // Packet received +__SINIC_REG32(Intr_All, 0x3f); +__SINIC_REG32(Intr_NoDelay, 0x24); +__SINIC_REG32(Intr_Res, ~0x3f); + +// RX Data Description +__SINIC_VAL64(RxData_Len, 40, 20); // 0 - 1M +__SINIC_VAL64(RxData_Addr, 0, 40); // Address 1TB + +// TX Data Description +__SINIC_VAL64(TxData_More, 63, 1); +__SINIC_VAL64(TxData_Checksum, 62, 1); +__SINIC_VAL64(TxData_Len, 40, 20); // 0 - 1M +__SINIC_VAL64(TxData_Addr, 0, 40); // Address 1TB + +// RX Done/Busy Information +__SINIC_VAL64(RxDone_Complete, 63, 1); +__SINIC_VAL64(RxDone_IpPacket, 45, 1); +__SINIC_VAL64(RxDone_TcpPacket, 44, 1); +__SINIC_VAL64(RxDone_UdpPacket, 43, 1); +__SINIC_VAL64(RxDone_IpError, 42, 1); +__SINIC_VAL64(RxDone_TcpError, 41, 1); +__SINIC_VAL64(RxDone_UdpError, 40, 1); +__SINIC_VAL64(RxDone_More, 32, 1); +__SINIC_VAL64(RxDone_FifoLen, 20, 8); // up to 255 packets +__SINIC_VAL64(RxDone_CopyLen, 0, 20); // up to 256k + +// TX Done/Busy Information +__SINIC_VAL64(TxDone_Complete, 63, 1); +__SINIC_VAL64(TxDone_FifoLen, 20, 8); // up to 255 packets +__SINIC_VAL64(TxDone_CopyLen, 0, 20); // up to 256k + +inline int +regSize(int offset) +{ + static const char sizes[] = { + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 0, + 8, 0, + 8, 0, + 8, 0, + 8, 0, + 8, 0, + 8, 0, + 8, 0 + }; + + if (offset & 0x3) + return 0; + + if (offset >= Size) + return 0; + + return sizes[offset / 4]; +} + +inline const char * +regName(int offset) +{ + static const char *names[] = { + "Config", + "RxMaxCopy", + "TxMaxCopy", + "RxThreshold", + "TxThreshold", + "IntrStatus", + "IntrMask", + "invalid", + "RxData", "invalid", + "RxDone", "invalid", + "RxWait", "invalid", + "TxData", "invalid", + "TxDone", "invalid", + "TxWait", "invalid", + "HwAddr", "invalid" + }; + + if (offset & 0x3) + return "invalid"; + + if (offset >= Size) + return "invalid"; + + return names[offset / 4]; +} + +/* namespace Regs */ } +/* namespace Sinic */ } + +#endif // __DEV_SINICREG_HH__