sinic.cc
来自「M5,一个功能强大的多处理器系统模拟器.很多针对处理器架构,性能的研究都使用它作」· CC 代码 · 共 1,617 行 · 第 1/3 页
CC
1,617 行
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); 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 ((interrupts & Regs::Intr_NoDelay) == 0) when += intrDelay; cpuIntrPost(when); }}voidDevice::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();}voidDevice::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();}voidBase::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, intrTick, true);}voidBase::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(); }}voidBase::cpuIntrClear(){ if (!cpuPendingIntr) return; if (intrEvent) { intrEvent->squash(); intrEvent = 0; } intrTick = 0; cpuPendingIntr = false; DPRINTF(EthernetIntr, "clearing cchip interrupt\n"); intrClear();}boolBase::cpuIntrPending() const{ return cpuPendingIntr; }voidDevice::changeConfig(uint32_t newconf){ uint32_t changed = regs.Config ^ newconf; if (!changed) return; regs.Config = newconf; 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(); }}voidDevice::command(uint32_t command){ if (command & Regs::Command_Intr) devIntrPost(Regs::Intr_Soft); if (command & Regs::Command_Reset) reset();}voidDevice::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; if (params()->rss) regs.Config |= Config_RSS; if (params()->zero_copy) regs.Config |= Config_ZeroCopy; if (params()->delay_copy) regs.Config |= Config_DelayCopy; if (params()->virtual_addr) regs.Config |= Config_Vaddr; if (params()->delay_copy && params()->zero_copy) panic("Can't delay copy and zero copy"); regs.IntrMask = Intr_Soft | Intr_RxHigh | Intr_RxPacket | Intr_TxLow; regs.RxMaxCopy = params()->rx_max_copy; regs.TxMaxCopy = params()->tx_max_copy; regs.RxMaxIntr = params()->rx_max_intr; regs.VirtualCount = params()->virtual_count; 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()->hardware_address; rxList.clear(); rxBusy.clear(); rxActive = -1; txList.clear(); rxState = rxIdle; txState = txIdle; rxFifo.clear(); rxFifoPtr = rxFifo.end(); txFifo.clear(); rxEmpty = false; rxLow = true; txFull = false; int size = virtualRegs.size(); virtualRegs.clear(); virtualRegs.resize(size); for (int i = 0; i < size; ++i) virtualRegs[i].rxPacket = rxFifo.end();}voidDevice::rxDmaDone(){ assert(rxState == rxCopy); rxState = rxCopyDone; DPRINTF(EthernetDMA, "end rx dma write paddr=%#x len=%d\n", rxDmaAddr, rxDmaLen); DDUMP(EthernetData, rxDmaData, rxDmaLen); // If the transmit state machine has a pending DMA, let it go first if (txState == txBeginCopy) txKick(); rxKick();}voidDevice::rxKick(){ VirtualReg *vnic = NULL; DPRINTF(EthernetSM, "rxKick: rxState=%s (rxFifo.size=%d)\n", RxStateStrings[rxState], rxFifo.size()); if (rxKickTick > curTick) { DPRINTF(EthernetSM, "rxKick: exiting, can't run till %d\n", rxKickTick); return; } next: if (rxState == rxIdle) goto exit; if (rxActive == -1) { if (rxState != rxFifoBlock) panic("no active vnic while in state %s", RxStateStrings[rxState]); DPRINTF(EthernetSM, "processing rxState=%s\n", RxStateStrings[rxState]); } else { vnic = &virtualRegs[rxActive]; DPRINTF(EthernetSM, "processing rxState=%s for vnic %d (rxunique %d)\n", RxStateStrings[rxState], rxActive, vnic->rxUnique); } switch (rxState) { case rxFifoBlock: if (DTRACE(EthernetSM)) { PacketFifo::iterator end = rxFifo.end(); int size = virtualRegs.size(); for (int i = 0; i < size; ++i) { VirtualReg *vn = &virtualRegs[i]; if (vn->rxPacket != end && !Regs::get_RxDone_Busy(vn->RxDone)) { DPRINTF(EthernetSM, "vnic %d (rxunique %d), has outstanding packet %d\n", i, vn->rxUnique, rxFifo.countPacketsBefore(vn->rxPacket)); } } } if (!rxBusy.empty()) { rxActive = rxBusy.front(); rxBusy.pop_front(); vnic = &virtualRegs[rxActive]; if (vnic->rxPacket == rxFifo.end()) panic("continuing vnic without packet\n"); DPRINTF(EthernetSM, "continue processing for vnic %d (rxunique %d)\n", rxActive, vnic->rxUnique); rxState = rxBeginCopy; break; } if (rxFifoPtr == rxFifo.end()) { DPRINTF(EthernetSM, "receive waiting for data. Nothing to do.\n"); goto exit; } if (rxList.empty()) panic("Not idle, but nothing to do!"); assert(!rxFifo.empty()); rxActive = rxList.front(); rxList.pop_front(); vnic = &virtualRegs[rxActive]; DPRINTF(EthernetSM, "processing new packet for vnic %d (rxunique %d)\n", rxActive, vnic->rxUnique); // Grab a new packet from the fifo. vnic->rxPacket = rxFifoPtr++; vnic->rxPacketOffset = 0; vnic->rxPacketBytes = (*vnic->rxPacket)->length; assert(vnic->rxPacketBytes); vnic->rxDoneData = 0; /* scope for variables */ { IpPtr ip(*vnic->rxPacket); if (ip) { DPRINTF(Ethernet, "ID is %d\n", ip->id()); vnic->rxDoneData |= Regs::RxDone_IpPacket; rxIpChecksums++; if (cksum(ip) != 0) { DPRINTF(EthernetCksum, "Rx IP Checksum Error\n"); vnic->rxDoneData |= Regs::RxDone_IpError; } TcpPtr tcp(ip); UdpPtr udp(ip); if (tcp) { DPRINTF(Ethernet, "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n", tcp->sport(), tcp->dport(), tcp->seq(), tcp->ack()); vnic->rxDoneData |= Regs::RxDone_TcpPacket; rxTcpChecksums++; if (cksum(tcp) != 0) { DPRINTF(EthernetCksum, "Rx TCP Checksum Error\n"); vnic->rxDoneData |= Regs::RxDone_TcpError; } } else if (udp) { vnic->rxDoneData |= Regs::RxDone_UdpPacket; rxUdpChecksums++; if (cksum(udp) != 0) { DPRINTF(EthernetCksum, "Rx UDP Checksum Error\n"); vnic->rxDoneData |= Regs::RxDone_UdpError; } } } } rxState = rxBeginCopy; break; case rxBeginCopy: if (dmaPending() || getState() != Running) goto exit; rxDmaAddr = params()->platform->pciToDma( Regs::get_RxData_Addr(vnic->RxData)); rxDmaLen = std::min<int>(Regs::get_RxData_Len(vnic->RxData), vnic->rxPacketBytes); rxDmaData = (*vnic->rxPacket)->data + vnic->rxPacketOffset; rxState = rxCopy; if (rxDmaAddr == 1LL) { rxState = rxCopyDone; break; } dmaWrite(rxDmaAddr, rxDmaLen, &rxDmaEvent, rxDmaData); break; case rxCopy: DPRINTF(EthernetSM, "receive machine still copying\n"); goto exit; case rxCopyDone: vnic->RxDone = vnic->rxDoneData; vnic->RxDone |= Regs::RxDone_Complete; if (vnic->rxPacketBytes == rxDmaLen) { // Packet is complete. Indicate how many bytes were copied vnic->RxDone = Regs::set_RxDone_CopyLen(vnic->RxDone, rxDmaLen); DPRINTF(EthernetSM, "rxKick: packet complete on vnic %d (rxunique %d)\n", rxActive, vnic->rxUnique); rxFifo.remove(vnic->rxPacket); vnic->rxPacket = rxFifo.end(); } else { vnic->rxPacketBytes -= rxDmaLen; vnic->rxPacketOffset += rxDmaLen; vnic->RxDone |= Regs::RxDone_More; vnic->RxDone = Regs::set_RxDone_CopyLen(vnic->RxDone, vnic->rxPacketBytes); DPRINTF(EthernetSM, "rxKick: packet not complete on vnic %d (rxunique %d): " "%d bytes left\n", rxActive, vnic->rxUnique, vnic->rxPacketBytes); } rxActive = -1; rxState = rxBusy.empty() && rxList.empty() ? rxIdle : rxFifoBlock; if (rxFifo.empty()) { devIntrPost(Regs::Intr_RxEmpty); rxEmpty = true; } if (rxFifo.size() < params()->rx_fifo_low_mark) rxLow = true; if (rxFifo.size() > params()->rx_fifo_threshold) rxLow = false; devIntrPost(Regs::Intr_RxDMA); 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]);}voidDevice::txDmaDone(){ assert(txState == txCopy); txState = txCopyDone; DPRINTF(EthernetDMA, "tx dma read paddr=%#x len=%d\n", txDmaAddr, txDmaLen); DDUMP(EthernetData, txDmaData, txDmaLen); // If the receive state machine has a pending DMA, let it go first if (rxState == rxBeginCopy) rxKick(); txKick();}voidDevice::transmit(){ if (txFifo.empty()) { DPRINTF(Ethernet, "nothing to transmit\n"); return; } uint32_t interrupts; EthPacketPtr 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, Seq=%d, Ack=%d\n", tcp->sport(), tcp->dport(), tcp->seq(), tcp->ack()); } } }#endif DDUMP(EthernetData, packet->data, packet->length); txBytes += packet->length; txPackets++; DPRINTF(Ethernet, "Packet Transmit: successful txFifo Available %d\n", txFifo.avail()); interrupts = Regs::Intr_TxPacket; if (txFifo.size() < regs.TxFifoMark) interrupts |= Regs::Intr_TxLow; devIntrPost(interrupts);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?