X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=dev%2Fsinic.cc;h=a9363954b21c26996a6adf8be14c9cfd5a4e87e1;hb=d207168eda13483a2990cdf060c1a7ead42cc9da;hp=0619a63dd1680939f71cd2f78491b93a0ecb2256;hpb=a51565f6ae19206ff8814d2cdfd13b8f5277c6ef;p=gem5.git diff --git a/dev/sinic.cc b/dev/sinic.cc index 0619a63dd..a9363954b 100644 --- a/dev/sinic.cc +++ b/dev/sinic.cc @@ -50,6 +50,7 @@ #include "targetarch/vtophys.hh" using namespace Net; +using namespace TheISA; namespace Sinic { @@ -93,30 +94,25 @@ Device::Device(Params *p) { reset(); - if (p->io_bus) { - pioInterface = newPioInterface(p->name, p->hier, p->io_bus, this, - &Device::cacheAccess); - - pioLatency = p->pio_latency * p->io_bus->clockRate; + if (p->pio_bus) { + pioInterface = newPioInterface(p->name + ".pio", p->hier, p->pio_bus, + this, &Device::cacheAccess); + pioLatency = p->pio_latency * p->pio_bus->clockRate; + } + if (p->header_bus) { if (p->payload_bus) - dmaInterface = new DMAInterface(p->name + ".dma", p->io_bus, + dmaInterface = new DMAInterface(p->name + ".dma", + p->header_bus, p->payload_bus, 1, p->dma_no_allocate); else - dmaInterface = new DMAInterface(p->name + ".dma", p->io_bus, - p->io_bus, 1, + dmaInterface = new DMAInterface(p->name + ".dma", + p->header_bus, + p->header_bus, 1, p->dma_no_allocate); - } else if (p->payload_bus) { - pioInterface = newPioInterface(p->name, p->hier, p->payload_bus, this, - &Device::cacheAccess); - - pioLatency = p->pio_latency * p->payload_bus->clockRate; - - dmaInterface = new DMAInterface(p->name + ".dma", p->payload_bus, - p->payload_bus, 1, - p->dma_no_allocate); - } + } else if (p->payload_bus) + panic("must define a header bus if defining a payload bus"); } Device::~Device() @@ -315,130 +311,206 @@ Device::writeConfig(int offset, int size, const uint8_t *data) } } +void +Device::prepareIO(int cpu, int index) +{ + int size = virtualRegs.size(); + if (index < size) + return; + + virtualRegs.resize(index + 1); + for (int i = size; i <= index; ++i) + virtualRegs[i].rxPacket = rxFifo.end(); +} + +void +Device::prepareRead(int cpu, int index) +{ + using namespace Regs; + prepareIO(cpu, index); + + VirtualReg &vnic = virtualRegs[index]; + + // update rx registers + uint64_t rxdone = vnic.RxDone; + rxdone = set_RxDone_Packets(rxdone, rxFifo.packets()); + regs.RxData = vnic.RxData; + regs.RxDone = rxdone; + regs.RxWait = rxdone; + + // update tx regsiters + uint64_t txdone = vnic.TxDone; + txdone = set_TxDone_Packets(txdone, txFifo.packets()); + txdone = set_TxDone_Full(txdone, txFifo.avail() < regs.TxMaxCopy); + txdone = set_TxDone_Low(txdone, txFifo.size() < regs.TxFifoMark); + regs.TxData = vnic.TxData; + regs.TxDone = txdone; + regs.TxWait = txdone; +} + +void +Device::prepareWrite(int cpu, int index) +{ + prepareIO(cpu, index); +} + /** - * This reads the device registers, which are detailed in the NS83820 - * spec sheet + * I/O read of device register */ Fault Device::read(MemReqPtr &req, uint8_t *data) { assert(config.command & PCI_CMD_MSE); + Fault fault = readBar(req, data); - //The mask is to give you only the offset into the device register file - Addr daddr = req->paddr & 0xfff; + if (fault == MachineCheckFault) { + panic("address does not map to a BAR pa=%#x va=%#x size=%d", + req->paddr, req->vaddr, req->size); - if (Regs::regSize(daddr) == 0) - panic("invalid address: da=%#x pa=%#x va=%#x size=%d", - daddr, req->paddr, req->vaddr, req->size); + return MachineCheckFault; + } - 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); + return fault; +} - DPRINTF(EthernetPIO, "read reg=%s da=%#x pa=%#x va=%#x size=%d\n", - Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size); +Fault +Device::readBar0(MemReqPtr &req, Addr daddr, uint8_t *data) +{ + int cpu = (req->xc->regs.ipr[TheISA::IPR_PALtemp16] >> 8) & 0xff; + Addr index = daddr >> Regs::VirtualShift; + Addr raddr = daddr & Regs::VirtualMask; + + if (!regValid(raddr)) + panic("invalid register: cpu=%d, da=%#x pa=%#x va=%#x size=%d", + cpu, daddr, req->paddr, req->vaddr, req->size); + + const Regs::Info &info = regInfo(raddr); + if (!info.read) + panic("reading %s (write only): cpu=%d da=%#x pa=%#x va=%#x size=%d", + info.name, cpu, daddr, req->paddr, req->vaddr, req->size); + + if (req->size != info.size) + panic("invalid size for reg %s: cpu=%d da=%#x pa=%#x va=%#x size=%d", + info.name, cpu, daddr, req->paddr, req->vaddr, req->size); + + prepareRead(cpu, index); + + uint64_t value = 0; + if (req->size == 4) { + uint32_t ® = *(uint32_t *)data; + reg = regData32(raddr); + value = reg; + } - uint32_t ®32 = *(uint32_t *)data; - uint64_t ®64 = *(uint64_t *)data; + if (req->size == 8) { + uint64_t ® = *(uint64_t *)data; + reg = regData64(raddr); + value = reg; + } - switch (daddr) { - case Regs::Config: - reg32 = regs.Config; - break; + DPRINTF(EthernetPIO, + "read %s cpu=%d da=%#x pa=%#x va=%#x size=%d val=%#x\n", + info.name, cpu, daddr, req->paddr, req->vaddr, req->size, value); - case Regs::RxMaxCopy: - reg32 = regs.RxMaxCopy; - break; + // reading the interrupt status register has the side effect of + // clearing it + if (raddr == Regs::IntrStatus) + devIntrClear(); - case Regs::TxMaxCopy: - reg32 = regs.TxMaxCopy; - break; + return NoFault; +} - case Regs::RxThreshold: - reg32 = regs.RxThreshold; - break; +/** + * IPR read of device register + */ +Fault +Device::iprRead(Addr daddr, int cpu, uint64_t &result) +{ + if (!regValid(daddr)) + panic("invalid address: da=%#x", daddr); - case Regs::TxThreshold: - reg32 = regs.TxThreshold; - break; + const Regs::Info &info = regInfo(daddr); + if (!info.read) + panic("reading %s (write only): cpu=%d da=%#x", info.name, cpu, daddr); - case Regs::IntrStatus: - reg32 = regs.IntrStatus; - devIntrClear(); - break; + DPRINTF(EthernetPIO, "IPR read %s: cpu=%d da=%#x\n", + info.name, cpu, daddr); - case Regs::IntrMask: - reg32 = regs.IntrMask; - break; + prepareRead(cpu, 0); - case Regs::RxData: - reg64 = regs.RxData; - break; + if (info.size == 4) + result = regData32(daddr); - case Regs::RxDone: - case Regs::RxWait: - reg64 = Regs::set_RxDone_FifoLen(regs.RxDone, - min(rxFifo.packets(), 255)); - break; + if (info.size == 8) + result = regData64(daddr); - case Regs::TxData: - reg64 = regs.TxData; - break; + DPRINTF(EthernetPIO, "IPR read %s: cpu=%s da=%#x val=%#x\n", + info.name, cpu, result); - case Regs::TxDone: - case Regs::TxWait: - reg64 = Regs::set_TxDone_FifoLen(regs.TxDone, - min(txFifo.packets(), 255)); - break; + return NoFault; +} - case Regs::HwAddr: - reg64 = params()->eaddr; - break; +/** + * I/O write of device register + */ +Fault +Device::write(MemReqPtr &req, const uint8_t *data) +{ + assert(config.command & PCI_CMD_MSE); + Fault fault = writeBar(req, data); - 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); - } + if (fault == MachineCheckFault) { + panic("address does not map to a BAR pa=%#x va=%#x size=%d", + 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 MachineCheckFault; + } - return No_Fault; + return fault; } Fault -Device::write(MemReqPtr &req, const uint8_t *data) +Device::writeBar0(MemReqPtr &req, Addr daddr, const uint8_t *data) { - assert(config.command & PCI_CMD_MSE); - Addr daddr = req->paddr & 0xfff; + int cpu = (req->xc->regs.ipr[TheISA::IPR_PALtemp16] >> 8) & 0xff; + Addr index = daddr >> Regs::VirtualShift; + Addr raddr = daddr & Regs::VirtualMask; + + if (!regValid(raddr)) + panic("invalid address: cpu=%d da=%#x pa=%#x va=%#x size=%d", + cpu, daddr, req->paddr, req->vaddr, req->size); - if (Regs::regSize(daddr) == 0) - panic("invalid address: da=%#x pa=%#x va=%#x size=%d", - daddr, req->paddr, req->vaddr, req->size); + const Regs::Info &info = regInfo(raddr); + if (!info.write) + panic("writing %s (read only): cpu=%d da=%#x", + info.name, cpu, daddr); - 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); + if (req->size != info.size) + panic("invalid size for %s: cpu=%d da=%#x pa=%#x va=%#x size=%d", + info.name, cpu, daddr, req->paddr, req->vaddr, req->size); uint32_t reg32 = *(uint32_t *)data; uint64_t reg64 = *(uint64_t *)data; + VirtualReg &vnic = virtualRegs[index]; - 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); + DPRINTF(EthernetPIO, + "write %s: cpu=%d val=%#x da=%#x pa=%#x va=%#x size=%d\n", + info.name, cpu, info.size == 4 ? reg32 : reg64, daddr, + req->paddr, req->vaddr, req->size); + prepareWrite(cpu, index); - switch (daddr) { + switch (raddr) { case Regs::Config: changeConfig(reg32); break; - case Regs::RxThreshold: - regs.RxThreshold = reg32; + case Regs::Command: + command(reg32); break; - case Regs::TxThreshold: - regs.TxThreshold = reg32; + case Regs::IntrStatus: + devIntrClear(regs.IntrStatus & reg32); break; case Regs::IntrMask: @@ -446,35 +518,36 @@ Device::write(MemReqPtr &req, const uint8_t *data) break; case Regs::RxData: - if (rxState != rxIdle) - panic("receive machine busy with another request!"); - - regs.RxDone = 0; - regs.RxData = reg64; - if (rxEnable) { + if (Regs::get_RxDone_Busy(vnic.RxDone)) + panic("receive machine busy with another request! rxState=%s", + RxStateStrings[rxState]); + + vnic.RxDone = Regs::RxDone_Busy; + vnic.RxData = reg64; + rxList.push_back(index); + if (rxEnable && rxState == rxIdle) { 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) { + if (Regs::get_TxDone_Busy(vnic.TxDone)) + panic("transmit machine busy with another request! txState=%s", + TxStateStrings[txState]); + + vnic.TxDone = Regs::TxDone_Busy; + vnic.TxData = reg64; + if (txList.empty() || txList.front() != index) + txList.push_back(index); + if (txEnable && txState == txIdle && txList.front() == index) { 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; + return NoFault; } void @@ -489,9 +562,25 @@ Device::devIntrPost(uint32_t interrupts) "interrupt written to intStatus: intr=%#x status=%#x mask=%#x\n", interrupts, regs.IntrStatus, regs.IntrMask); - if ((regs.IntrStatus & regs.IntrMask)) { + interrupts = regs.IntrStatus & regs.IntrMask; + + // Intr_RxHigh is special, we only signal it if we've emptied the fifo + // and then filled it above the high watermark + if (rxEmpty) + rxEmpty = false; + else + interrupts &= ~Regs::Intr_RxHigh; + + // Intr_TxLow is special, we only signal it if we've filled up the fifo + // and then dropped below the low watermark + if (txFull) + txFull = false; + else + interrupts &= ~Regs::Intr_TxLow; + + if (interrupts) { Tick when = curTick; - if ((regs.IntrStatus & regs.IntrMask & Regs::Intr_NoDelay) == 0) + if ((interrupts & Regs::Intr_NoDelay) == 0) when += intrDelay; cpuIntrPost(when); } @@ -627,12 +716,6 @@ Device::changeConfig(uint32_t newconf) 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) { @@ -656,20 +739,55 @@ Device::changeConfig(uint32_t newconf) } } +void +Device::command(uint32_t command) +{ + if (command & Regs::Command_Intr) + devIntrPost(Regs::Intr_Soft); + + if (command & Regs::Command_Reset) + reset(); +} + void Device::reset() { using namespace Regs; + memset(®s, 0, sizeof(regs)); + + regs.Config = 0; + if (params()->rx_thread) + regs.Config |= Config_RxThread; + if (params()->tx_thread) + regs.Config |= Config_TxThread; + regs.IntrMask = Intr_Soft | Intr_RxHigh | Intr_RxPacket | Intr_TxLow; regs.RxMaxCopy = params()->rx_max_copy; regs.TxMaxCopy = params()->tx_max_copy; - regs.IntrMask = Intr_TxFifo | Intr_RxFifo | Intr_RxData; + regs.RxMaxIntr = params()->rx_max_intr; + regs.RxFifoSize = params()->rx_fifo_size; + regs.TxFifoSize = params()->tx_fifo_size; + regs.RxFifoMark = params()->rx_fifo_threshold; + regs.TxFifoMark = params()->tx_fifo_threshold; + regs.HwAddr = params()->eaddr; + + rxList.clear(); + txList.clear(); rxState = rxIdle; txState = txIdle; rxFifo.clear(); + rxFifoPtr = rxFifo.end(); txFifo.clear(); + rxEmpty = false; + txFull = false; + + int size = virtualRegs.size(); + virtualRegs.clear(); + virtualRegs.resize(size); + for (int i = 0; i < size; ++i) + virtualRegs[i].rxPacket = rxFifo.end(); } void @@ -680,19 +798,26 @@ Device::rxDmaCopy() physmem->dma_write(rxDmaAddr, (uint8_t *)rxDmaData, rxDmaLen); DPRINTF(EthernetDMA, "rx dma write paddr=%#x len=%d\n", rxDmaAddr, rxDmaLen); - DDUMP(EthernetDMA, rxDmaData, rxDmaLen); + DDUMP(EthernetData, rxDmaData, rxDmaLen); } void Device::rxDmaDone() { rxDmaCopy(); + + // If the transmit state machine has a pending DMA, let it go first + if (txState == txBeginCopy) + txKick(); + rxKick(); } void Device::rxKick() { + VirtualReg *vnic; + DPRINTF(EthernetSM, "receive kick rxState=%s (rxFifo.size=%d)\n", RxStateStrings[rxState], rxFifo.size()); @@ -703,56 +828,60 @@ Device::rxKick() } next: - switch (rxState) { - case rxIdle: - if (rxPioRequest) { - pioInterface->respond(rxPioRequest, curTick); - rxPioRequest = 0; - } + if (rxState == rxIdle) goto exit; + assert(!rxList.empty()); + vnic = &virtualRegs[rxList.front()]; + + DPRINTF(EthernetSM, "processing rxState=%s for virtual nic %d\n", + RxStateStrings[rxState], rxList.front()); + + switch (rxState) { case rxFifoBlock: - if (rxPacket) { + if (vnic->rxPacket != rxFifo.end()) { rxState = rxBeginCopy; break; } - if (rxFifo.empty()) { + if (rxFifoPtr == rxFifo.end()) { DPRINTF(EthernetSM, "receive waiting for data. Nothing to do.\n"); goto exit; } + assert(!rxFifo.empty()); + // Grab a new packet from the fifo. - rxPacket = rxFifo.front(); - rxPacketBufPtr = rxPacket->data; - rxPktBytes = rxPacket->length; - assert(rxPktBytes); + vnic->rxPacket = rxFifoPtr++; + vnic->rxPacketOffset = 0; + vnic->rxPacketBytes = (*vnic->rxPacket)->length; + assert(vnic->rxPacketBytes); - rxDoneData = 0; + vnic->rxDoneData = 0; /* scope for variables */ { - IpPtr ip(rxPacket); + IpPtr ip(*vnic->rxPacket); if (ip) { - rxDoneData |= Regs::RxDone_IpPacket; + vnic->rxDoneData |= Regs::RxDone_IpPacket; rxIpChecksums++; if (cksum(ip) != 0) { DPRINTF(EthernetCksum, "Rx IP Checksum Error\n"); - rxDoneData |= Regs::RxDone_IpError; + vnic->rxDoneData |= Regs::RxDone_IpError; } TcpPtr tcp(ip); UdpPtr udp(ip); if (tcp) { - rxDoneData |= Regs::RxDone_TcpPacket; + vnic->rxDoneData |= Regs::RxDone_TcpPacket; rxTcpChecksums++; if (cksum(tcp) != 0) { DPRINTF(EthernetCksum, "Rx TCP Checksum Error\n"); - rxDoneData |= Regs::RxDone_TcpError; + vnic->rxDoneData |= Regs::RxDone_TcpError; } } else if (udp) { - rxDoneData |= Regs::RxDone_UdpPacket; + vnic->rxDoneData |= Regs::RxDone_UdpPacket; rxUdpChecksums++; if (cksum(udp) != 0) { DPRINTF(EthernetCksum, "Rx UDP Checksum Error\n"); - rxDoneData |= Regs::RxDone_UdpError; + vnic->rxDoneData |= Regs::RxDone_UdpError; } } } @@ -761,20 +890,21 @@ Device::rxKick() 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 && dmaInterface->busy()) + goto exit; + + rxDmaAddr = plat->pciToDma(Regs::get_RxData_Addr(vnic->RxData)); + rxDmaLen = min(Regs::get_RxData_Len(vnic->RxData), + vnic->rxPacketBytes); + rxDmaData = (*vnic->rxPacket)->data + vnic->rxPacketOffset; + rxState = rxCopy; if (dmaInterface) { - if (!dmaInterface->busy()) { - dmaInterface->doDMA(WriteInvalidate, rxDmaAddr, rxDmaLen, - curTick, &rxDmaEvent, true); - rxState = rxCopy; - } + dmaInterface->doDMA(WriteInvalidate, rxDmaAddr, rxDmaLen, + curTick, &rxDmaEvent, true); goto exit; } - rxState = rxCopy; if (dmaWriteDelay != 0 || dmaWriteFactor != 0) { Tick factor = ((rxDmaLen + ULL(63)) >> ULL(6)) * dmaWriteFactor; Tick start = curTick + dmaWriteDelay + factor; @@ -790,20 +920,32 @@ Device::rxKick() goto exit; case rxCopyDone: - regs.RxDone = rxDoneData | rxDmaLen; - - if (rxPktBytes == rxDmaLen) { - rxPacket = NULL; - rxFifo.pop(); + vnic->RxDone = vnic->rxDoneData | rxDmaLen; + vnic->RxDone |= Regs::RxDone_Complete; + + if (vnic->rxPacketBytes == rxDmaLen) { + DPRINTF(EthernetSM, "rxKick: packet complete on vnic %d\n", + rxList.front()); + rxFifo.remove(vnic->rxPacket); + vnic->rxPacket = rxFifo.end(); } else { - regs.RxDone |= Regs::RxDone_More; - rxPktBytes -= rxDmaLen; - rxPacketBufPtr += rxDmaLen; + vnic->RxDone |= Regs::RxDone_More; + vnic->rxPacketBytes -= rxDmaLen; + vnic->rxPacketOffset += rxDmaLen; + DPRINTF(EthernetSM, + "rxKick: packet not complete on vnic %d: %d bytes left\n", + rxList.front(), vnic->rxPacketBytes); } - regs.RxDone |= Regs::RxDone_Complete; - devIntrPost(Regs::Intr_RxData); - rxState = rxIdle; + rxList.pop_front(); + rxState = rxList.empty() ? rxIdle : rxFifoBlock; + + if (rxFifo.empty()) { + devIntrPost(Regs::Intr_RxEmpty); + rxEmpty = true; + } + + devIntrPost(Regs::Intr_RxDMA); break; default: @@ -831,13 +973,18 @@ Device::txDmaCopy() physmem->dma_read((uint8_t *)txDmaData, txDmaAddr, txDmaLen); DPRINTF(EthernetDMA, "tx dma read paddr=%#x len=%d\n", txDmaAddr, txDmaLen); - DDUMP(EthernetDMA, txDmaData, txDmaLen); + DDUMP(EthernetData, txDmaData, txDmaLen); } void Device::txDmaDone() { txDmaCopy(); + + // If the receive state machine has a pending DMA, let it go first + if (rxState == rxBeginCopy) + rxKick(); + txKick(); } @@ -849,6 +996,7 @@ Device::transmit() return; } + uint32_t interrupts; PacketPtr packet = txFifo.front(); if (!interface->sendPacket(packet)) { DPRINTF(Ethernet, "Packet Transmit: failed txFifo available %d\n", @@ -857,7 +1005,6 @@ Device::transmit() } txFifo.pop(); - #if TRACING_ON if (DTRACE(Ethernet)) { IpPtr ip(packet); @@ -872,17 +1019,17 @@ Device::transmit() } #endif - DDUMP(Ethernet, packet->data, packet->length); + DDUMP(EthernetData, 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); + interrupts = Regs::Intr_TxPacket; + if (txFifo.size() < regs.TxFifoMark) + interrupts |= Regs::Intr_TxLow; + devIntrPost(interrupts); reschedule: if (!txFifo.empty() && !txEvent.scheduled()) { @@ -894,6 +1041,7 @@ Device::transmit() void Device::txKick() { + VirtualReg *vnic; DPRINTF(EthernetSM, "transmit kick txState=%s (txFifo.size=%d)\n", TxStateStrings[txState], txFifo.size()); @@ -904,23 +1052,23 @@ Device::txKick() } next: - switch (txState) { - case txIdle: - if (txPioRequest) { - pioInterface->respond(txPioRequest, curTick + pioLatency); - txPioRequest = 0; - } + if (txState == txIdle) goto exit; + assert(!txList.empty()); + vnic = &virtualRegs[txList.front()]; + + switch (txState) { case txFifoBlock: + assert(Regs::get_TxDone_Busy(vnic->TxData)); if (!txPacket) { // Grab a new packet from the fifo. txPacket = new PacketData(16384); - txPacketBufPtr = txPacket->data; + txPacketOffset = 0; } if (txFifo.avail() - txPacket->length < - Regs::get_TxData_Len(regs.TxData)) { + Regs::get_TxData_Len(vnic->TxData)) { DPRINTF(EthernetSM, "transmit fifo full. Nothing to do.\n"); goto exit; } @@ -929,21 +1077,20 @@ Device::txKick() break; case txBeginCopy: - txDmaAddr = plat->pciToDma(Regs::get_TxData_Addr(regs.TxData)); - txDmaLen = Regs::get_TxData_Len(regs.TxData); - txDmaData = txPacketBufPtr; + if (dmaInterface && dmaInterface->busy()) + goto exit; - if (dmaInterface) { - if (!dmaInterface->busy()) { - dmaInterface->doDMA(Read, txDmaAddr, txDmaLen, - curTick, &txDmaEvent, true); - txState = txCopy; - } + txDmaAddr = plat->pciToDma(Regs::get_TxData_Addr(vnic->TxData)); + txDmaLen = Regs::get_TxData_Len(vnic->TxData); + txDmaData = txPacket->data + txPacketOffset; + txState = txCopy; + if (dmaInterface) { + dmaInterface->doDMA(Read, txDmaAddr, txDmaLen, + curTick, &txDmaEvent, true); goto exit; } - txState = txCopy; if (dmaReadDelay != 0 || dmaReadFactor != 0) { Tick factor = ((txDmaLen + ULL(63)) >> ULL(6)) * dmaReadFactor; Tick start = curTick + dmaReadDelay + factor; @@ -959,41 +1106,49 @@ Device::txKick() goto exit; case txCopyDone: + vnic->TxDone = txDmaLen | Regs::TxDone_Complete; 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++; - } + if ((vnic->TxData & Regs::TxData_More)) { + txPacketOffset += txDmaLen; + txState = txIdle; + devIntrPost(Regs::Intr_TxDMA); + break; + } - UdpPtr udp(ip); - if (udp) { - udp->sum(0); - udp->sum(cksum(udp)); - txUdpChecksums++; - } + assert(txPacket->length <= txFifo.avail()); + if ((vnic->TxData & Regs::TxData_Checksum)) { + IpPtr ip(txPacket); + if (ip) { + TcpPtr tcp(ip); + if (tcp) { + tcp->sum(0); + tcp->sum(cksum(tcp)); + txTcpChecksums++; + } - ip->sum(0); - ip->sum(cksum(ip)); - txIpChecksums++; + 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; + txFifo.push(txPacket); + if (txFifo.avail() < regs.TxMaxCopy) { + devIntrPost(Regs::Intr_TxFull); + txFull = true; + } + txPacket = 0; + transmit(); + txList.pop_front(); + txState = txList.empty() ? txIdle : txFifoBlock; + devIntrPost(Regs::Intr_TxDMA); break; default: @@ -1093,8 +1248,8 @@ Device::recvPacket(PacketPtr packet) return true; } - if (rxFifo.size() >= params()->rx_fifo_threshold) - devIntrPost(Regs::Intr_RxFifo); + if (rxFifo.size() >= regs.RxFifoMark) + devIntrPost(Regs::Intr_RxHigh); if (!rxFifo.push(packet)) { DPRINTF(Ethernet, @@ -1102,7 +1257,12 @@ Device::recvPacket(PacketPtr packet) return false; } - devIntrPost(Regs::Intr_RxDone); + // If we were at the last element, back up one ot go to the new + // last element of the list. + if (rxFifoPtr == rxFifo.end()) + --rxFifoPtr; + + devIntrPost(Regs::Intr_RxPacket); rxKick(); return true; } @@ -1160,56 +1320,96 @@ Device::serialize(ostream &os) // Serialize the PciDev base class Base::serialize(os); - if (rxDmaEvent.scheduled()) - rxDmaCopy(); + if (rxState == rxCopy) + panic("can't serialize with an in flight dma request rxState=%s", + RxStateStrings[rxState]); - if (txDmaEvent.scheduled()) - txDmaCopy(); + if (txState == txCopy) + panic("can't serialize with an in flight dma request txState=%s", + TxStateStrings[txState]); /* * 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.RxMaxCopy); + SERIALIZE_SCALAR(regs.TxMaxCopy); + SERIALIZE_SCALAR(regs.RxMaxIntr); SERIALIZE_SCALAR(regs.RxData); SERIALIZE_SCALAR(regs.RxDone); SERIALIZE_SCALAR(regs.TxData); SERIALIZE_SCALAR(regs.TxDone); + /* + * Serialize the virtual nic state + */ + int virtualRegsSize = virtualRegs.size(); + SERIALIZE_SCALAR(virtualRegsSize); + for (int i = 0; i < virtualRegsSize; ++i) { + VirtualReg *vnic = &virtualRegs[i]; + + string reg = csprintf("vnic%d", i); + paramOut(os, reg + ".RxData", vnic->RxData); + paramOut(os, reg + ".RxDone", vnic->RxDone); + paramOut(os, reg + ".TxData", vnic->TxData); + paramOut(os, reg + ".TxDone", vnic->TxDone); + + PacketFifo::iterator rxFifoPtr; + + bool rxPacketExists = vnic->rxPacket != rxFifo.end(); + paramOut(os, reg + ".rxPacketExists", rxPacketExists); + if (rxPacketExists) { + int rxPacket = 0; + PacketFifo::iterator i = rxFifo.begin(); + while (i != vnic->rxPacket) { + assert(i != rxFifo.end()); + ++i; + ++rxPacket; + } + + paramOut(os, reg + ".rxPacket", rxPacket); + paramOut(os, reg + ".rxPacketOffset", vnic->rxPacketOffset); + paramOut(os, reg + ".rxPacketBytes", vnic->rxPacketBytes); + } + paramOut(os, reg + ".rxDoneData", vnic->rxDoneData); + } + + VirtualList::iterator i, end; + int count; + + int rxListSize = rxList.size(); + SERIALIZE_SCALAR(rxListSize); + for (count = 0, i = rxList.begin(), end = rxList.end(); i != end; ++i) + paramOut(os, csprintf("rxList%d", count++), *i); + + int txListSize = txList.size(); + SERIALIZE_SCALAR(txListSize); + for (count = 0, i = txList.begin(), end = txList.end(); i != end; ++i) + paramOut(os, csprintf("txList%d", count++), *i); + /* * Serialize rx state machine */ int rxState = this->rxState; SERIALIZE_SCALAR(rxState); + SERIALIZE_SCALAR(rxEmpty); 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); + SERIALIZE_SCALAR(txFull); 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); + SERIALIZE_SCALAR(txPacketOffset); + SERIALIZE_SCALAR(txPacketBytes); } /* @@ -1230,42 +1430,49 @@ Device::unserialize(Checkpoint *cp, const std::string §ion) * 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.RxMaxCopy); + UNSERIALIZE_SCALAR(regs.TxMaxCopy); + UNSERIALIZE_SCALAR(regs.RxMaxIntr); UNSERIALIZE_SCALAR(regs.RxData); UNSERIALIZE_SCALAR(regs.RxDone); UNSERIALIZE_SCALAR(regs.TxData); UNSERIALIZE_SCALAR(regs.TxDone); + int rxListSize; + UNSERIALIZE_SCALAR(rxListSize); + rxList.clear(); + for (int i = 0; i < rxListSize; ++i) { + int value; + paramIn(cp, section, csprintf("rxList%d", i), value); + rxList.push_back(value); + } + + int txListSize; + UNSERIALIZE_SCALAR(txListSize); + txList.clear(); + for (int i = 0; i < txListSize; ++i) { + int value; + paramIn(cp, section, csprintf("txList%d", i), value); + txList.push_back(value); + } + /* * Unserialize rx state machine */ int rxState; UNSERIALIZE_SCALAR(rxState); + UNSERIALIZE_SCALAR(rxEmpty); this->rxState = (RxState) rxState; rxFifo.unserialize("rxFifo", cp, section); - bool rxPacketExists; - UNSERIALIZE_SCALAR(rxPacketExists); - rxPacket = 0; - if (rxPacketExists) { - rxPacket = new PacketData(16384); - 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); + UNSERIALIZE_SCALAR(txFull); this->txState = (TxState) txState; txFifo.unserialize("txFifo", cp, section); bool txPacketExists; @@ -1274,10 +1481,45 @@ Device::unserialize(Checkpoint *cp, const std::string §ion) if (txPacketExists) { txPacket = new PacketData(16384); txPacket->unserialize("txPacket", cp, section); - uint32_t txPktBufPtr; - UNSERIALIZE_SCALAR(txPktBufPtr); - this->txPacketBufPtr = (uint8_t *) txPacket->data + txPktBufPtr; - UNSERIALIZE_SCALAR(txPktBytes); + UNSERIALIZE_SCALAR(txPacketOffset); + UNSERIALIZE_SCALAR(txPacketBytes); + } + + /* + * unserialize the virtual nic registers/state + * + * this must be done after the unserialization of the rxFifo + * because the packet iterators depend on the fifo being populated + */ + int virtualRegsSize; + UNSERIALIZE_SCALAR(virtualRegsSize); + virtualRegs.clear(); + virtualRegs.resize(virtualRegsSize); + for (int i = 0; i < virtualRegsSize; ++i) { + VirtualReg *vnic = &virtualRegs[i]; + string reg = csprintf("vnic%d", i); + + paramIn(cp, section, reg + ".RxData", vnic->RxData); + paramIn(cp, section, reg + ".RxDone", vnic->RxDone); + paramIn(cp, section, reg + ".TxData", vnic->TxData); + paramIn(cp, section, reg + ".TxDone", vnic->TxDone); + + bool rxPacketExists; + paramIn(cp, section, reg + ".rxPacketExists", rxPacketExists); + if (rxPacketExists) { + int rxPacket; + paramIn(cp, section, reg + ".rxPacket", rxPacket); + vnic->rxPacket = rxFifo.begin(); + while (rxPacket--) + ++vnic->rxPacket; + + paramIn(cp, section, reg + ".rxPacketOffset", + vnic->rxPacketOffset); + paramIn(cp, section, reg + ".rxPacketBytes", vnic->rxPacketBytes); + } else { + vnic->rxPacket = rxFifo.end(); + } + paramIn(cp, section, reg + ".rxDoneData", vnic->rxDoneData); } /* @@ -1291,38 +1533,25 @@ Device::unserialize(Checkpoint *cp, const std::string §ion) /* * re-add addrRanges to bus bridges */ - if (pioInterface) + 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; + Addr daddr; + int bar; + if (!getBAR(req->paddr, daddr, bar)) + panic("address does not map to a BAR pa=%#x va=%#x size=%d", + req->paddr, req->vaddr, req->size); - case Regs::TxDone: - if (txState != txIdle) { - txPioRequest = req; - when = 0; - } - break; - } + DPRINTF(EthernetPIO, "timing %s to paddr=%#x bar=%d daddr=%#x\n", + req->cmd.toString(), req->paddr, bar, daddr); - return when; + return curTick + pioLatency; } BEGIN_DECLARE_SIM_OBJECT_PARAMS(Interface) @@ -1370,7 +1599,8 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(Device) Param pci_func; SimObjectParam hier; - SimObjectParam io_bus; + SimObjectParam pio_bus; + SimObjectParam dma_bus; SimObjectParam payload_bus; Param dma_read_delay; Param dma_read_factor; @@ -1384,6 +1614,7 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(Device) Param tx_delay; Param rx_max_copy; Param tx_max_copy; + Param rx_max_intr; Param rx_fifo_size; Param tx_fifo_size; Param rx_fifo_threshold; @@ -1391,6 +1622,8 @@ BEGIN_DECLARE_SIM_OBJECT_PARAMS(Device) Param rx_filter; Param hardware_address; + Param rx_thread; + Param tx_thread; END_DECLARE_SIM_OBJECT_PARAMS(Device) @@ -1409,7 +1642,8 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(Device) INIT_PARAM(pci_func, "PCI function code"), INIT_PARAM(hier, "Hierarchy global variables"), - INIT_PARAM(io_bus, "The IO Bus to attach to for headers"), + INIT_PARAM(pio_bus, ""), + INIT_PARAM(dma_bus, ""), INIT_PARAM(payload_bus, "The IO Bus to attach to for payload"), INIT_PARAM(dma_read_delay, "fixed delay for dma reads"), INIT_PARAM(dma_read_factor, "multiplier for dma reads"), @@ -1423,13 +1657,16 @@ BEGIN_INIT_SIM_OBJECT_PARAMS(Device) INIT_PARAM(tx_delay, "Transmit Delay"), INIT_PARAM(rx_max_copy, "rx max copy"), INIT_PARAM(tx_max_copy, "rx max copy"), + INIT_PARAM(rx_max_intr, "rx max intr"), INIT_PARAM(rx_fifo_size, "max size in bytes of rxFifo"), INIT_PARAM(tx_fifo_size, "max size in bytes of txFifo"), INIT_PARAM(rx_fifo_threshold, "max size in bytes of rxFifo"), INIT_PARAM(tx_fifo_threshold, "max size in bytes of txFifo"), INIT_PARAM(rx_filter, "Enable Receive Filter"), - INIT_PARAM(hardware_address, "Ethernet Hardware Address") + INIT_PARAM(hardware_address, "Ethernet Hardware Address"), + INIT_PARAM(rx_thread, ""), + INIT_PARAM(tx_thread, "") END_INIT_SIM_OBJECT_PARAMS(Device) @@ -1441,6 +1678,7 @@ CREATE_SIM_OBJECT(Device) params->name = getInstanceName(); params->clock = clock; + params->mmu = mmu; params->physmem = physmem; params->configSpace = configspace; @@ -1451,7 +1689,8 @@ CREATE_SIM_OBJECT(Device) params->functionNum = pci_func; params->hier = hier; - params->io_bus = io_bus; + params->pio_bus = pio_bus; + params->header_bus = dma_bus; params->payload_bus = payload_bus; params->dma_read_delay = dma_read_delay; params->dma_read_factor = dma_read_factor; @@ -1465,6 +1704,7 @@ CREATE_SIM_OBJECT(Device) params->rx_delay = rx_delay; params->rx_max_copy = rx_max_copy; params->tx_max_copy = tx_max_copy; + params->rx_max_intr = rx_max_intr; params->rx_fifo_size = rx_fifo_size; params->tx_fifo_size = tx_fifo_size; params->rx_fifo_threshold = rx_fifo_threshold; @@ -1472,6 +1712,8 @@ CREATE_SIM_OBJECT(Device) params->rx_filter = rx_filter; params->eaddr = hardware_address; + params->rx_thread = rx_thread; + params->tx_thread = tx_thread; return new Device(params); }