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(&regs, 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 + -
显示快捷键?