X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fdev%2Fi8254xGBe.cc;h=0fbda1897a67a37699202450f273bbbf48e66775;hb=27e8f3c98a4b1c56e16ce5f80c7ad992083ccef9;hp=c38a9e873d2374c78138b014515f50fe57d5dd9e;hpb=e8dc1723ee132f3bc1cb9bb74c0cb0f3e9051cc2;p=gem5.git diff --git a/src/dev/i8254xGBe.cc b/src/dev/i8254xGBe.cc index c38a9e873..0fbda1897 100644 --- a/src/dev/i8254xGBe.cc +++ b/src/dev/i8254xGBe.cc @@ -40,28 +40,35 @@ * @todo really there are multiple dma engines.. we should implement them. */ +#include + #include "base/inet.hh" #include "base/trace.hh" #include "dev/i8254xGBe.hh" #include "mem/packet.hh" #include "mem/packet_access.hh" -#include "sim/builder.hh" +#include "params/IGbE.hh" #include "sim/stats.hh" #include "sim/system.hh" -#include - using namespace iGbReg; using namespace Net; -IGbE::IGbE(Params *p) - : PciDev(p), etherInt(NULL), useFlowControl(p->use_flow_control), +IGbE::IGbE(const Params *p) + : EtherDevice(p), etherInt(NULL), drainEvent(NULL), useFlowControl(p->use_flow_control), rxFifo(p->rx_fifo_size), txFifo(p->tx_fifo_size), rxTick(false), - txTick(false), rdtrEvent(this), radvEvent(this), tadvEvent(this), - tidvEvent(this), tickEvent(this), interEvent(this), + txTick(false), txFifoTick(false), rxDmaPacket(false), + fetchDelay(p->fetch_delay), wbDelay(p->wb_delay), + fetchCompDelay(p->fetch_comp_delay), wbCompDelay(p->wb_comp_delay), + rxWriteDelay(p->rx_write_delay), txReadDelay(p->tx_read_delay), + rdtrEvent(this), radvEvent(this), + tadvEvent(this), tidvEvent(this), tickEvent(this), interEvent(this), rxDescCache(this, name()+".RxDesc", p->rx_desc_cache_size), - txDescCache(this, name()+".TxDesc", p->tx_desc_cache_size), clock(p->clock) + txDescCache(this, name()+".TxDesc", p->tx_desc_cache_size), + clock(p->clock), lastInterrupt(0) { + etherInt = new IGbEInt(name() + ".int", this); + // Initialized internal registers per Intel documentation // All registers intialized to 0 by per register constructor regs.ctrl.fd(1); @@ -107,6 +114,17 @@ IGbE::IGbE(Params *p) txFifo.clear(); } +EtherInt* +IGbE::getEthPort(const std::string &if_name, int idx) +{ + + if (if_name == "interface") { + if (etherInt->getPeer()) + panic("Port already connected to\n"); + return etherInt; + } + return NULL; +} Tick IGbE::writeConfig(PacketPtr pkt) @@ -223,6 +241,7 @@ IGbE::read(PacketPtr pkt) pkt->set(regs.rdtr()); if (regs.rdtr.fpd()) { rxDescCache.writeback(0); + DPRINTF(EthernetIntr, "Posting interrupt because of RDTR.FPD write\n"); postInterrupt(IT_RXT); regs.rdtr.fpd(0); } @@ -270,7 +289,7 @@ IGbE::read(PacketPtr pkt) pkt->set(0); }; - pkt->result = Packet::Success; + pkt->makeAtomicResponse(); return pioDelay; } @@ -411,6 +430,7 @@ IGbE::write(PacketPtr pkt) regs.itr = val; break; case REG_ICS: + DPRINTF(EthernetIntr, "Posting interrupt because of ICS write\n"); postInterrupt((IntTypes)val); break; case REG_IMS: @@ -429,6 +449,7 @@ IGbE::write(PacketPtr pkt) regs.rctl = val; if (regs.rctl.rst()) { rxDescCache.reset(); + DPRINTF(EthernetSM, "RXS: Got RESET!\n"); rxFifo.clear(); regs.rctl.rst(0); } @@ -488,8 +509,13 @@ IGbE::write(PacketPtr pkt) break; case REG_RDT: regs.rdt = val; - rxTick = true; - restartClock(); + DPRINTF(EthernetSM, "RXS: RDT Updated.\n"); + if (getState() == SimObject::Running) { + DPRINTF(EthernetSM, "RXS: RDT Fetching Descriptors!\n"); + rxDescCache.fetchDescriptors(); + } else { + DPRINTF(EthernetSM, "RXS: RDT NOT Fetching Desc b/c draining!\n"); + } break; case REG_RDTR: regs.rdtr = val; @@ -515,8 +541,13 @@ IGbE::write(PacketPtr pkt) break; case REG_TDT: regs.tdt = val; - txTick = true; - restartClock(); + DPRINTF(EthernetSM, "TXS: TX Tail pointer updated\n"); + if (getState() == SimObject::Running) { + DPRINTF(EthernetSM, "TXS: TDT Fetching Descriptors!\n"); + txDescCache.fetchDescriptors(); + } else { + DPRINTF(EthernetSM, "TXS: TDT NOT Fetching Desc b/c draining!\n"); + } break; case REG_TIDV: regs.tidv = val; @@ -540,7 +571,7 @@ IGbE::write(PacketPtr pkt) panic("Write request to unknown register number: %#x\n", daddr); }; - pkt->result = Packet::Success; + pkt->makeAtomicResponse(); return pioDelay; } @@ -550,54 +581,80 @@ IGbE::postInterrupt(IntTypes t, bool now) assert(t); // Interrupt is already pending - if (t & regs.icr()) + if (t & regs.icr() && !now) return; - if (regs.icr() & regs.imr) - { - regs.icr = regs.icr() | t; - if (!interEvent.scheduled()) - interEvent.schedule(curTick + Clock::Int::ns * 256 * - regs.itr.interval()); - } else { - regs.icr = regs.icr() | t; - if (regs.itr.interval() == 0 || now) { - if (interEvent.scheduled()) - interEvent.deschedule(); - cpuPostInt(); - } else { - DPRINTF(EthernetIntr, "EINT: Scheduling timer interrupt for %d ticks\n", - Clock::Int::ns * 256 * regs.itr.interval()); - assert(!interEvent.scheduled()); - interEvent.schedule(curTick + Clock::Int::ns * 256 * regs.itr.interval()); + regs.icr = regs.icr() | t; + + Tick itr_interval = Clock::Int::ns * 256 * regs.itr.interval(); + DPRINTF(EthernetIntr, "EINT: postInterrupt() curTick: %d itr: %d interval: %d\n", + curTick, regs.itr.interval(), itr_interval); + + if (regs.itr.interval() == 0 || now || lastInterrupt + itr_interval <= curTick) { + if (interEvent.scheduled()) { + deschedule(interEvent); } + cpuPostInt(); + } else { + Tick int_time = lastInterrupt + itr_interval; + assert(int_time > 0); + DPRINTF(EthernetIntr, "EINT: Scheduling timer interrupt for tick %d\n", + int_time); + if (!interEvent.scheduled()) { + schedule(interEvent, int_time); + } } } +void +IGbE::delayIntEvent() +{ + cpuPostInt(); +} + + void IGbE::cpuPostInt() { + + postedInterrupts++; + + if (!(regs.icr() & regs.imr)) { + DPRINTF(Ethernet, "Interrupt Masked. Not Posting\n"); + return; + } + + DPRINTF(Ethernet, "Posting Interrupt\n"); + + + if (interEvent.scheduled()) { + deschedule(interEvent); + } + if (rdtrEvent.scheduled()) { regs.icr.rxt0(1); - rdtrEvent.deschedule(); + deschedule(rdtrEvent); } if (radvEvent.scheduled()) { regs.icr.rxt0(1); - radvEvent.deschedule(); + deschedule(radvEvent); } if (tadvEvent.scheduled()) { regs.icr.txdw(1); - tadvEvent.deschedule(); + deschedule(tadvEvent); } if (tidvEvent.scheduled()) { regs.icr.txdw(1); - tidvEvent.deschedule(); + deschedule(tidvEvent); } regs.icr.int_assert(1); DPRINTF(EthernetIntr, "EINT: Posting interrupt to CPU now. Vector %#x\n", regs.icr()); + intrPost(); + + lastInterrupt = curTick; } void @@ -614,20 +671,29 @@ IGbE::cpuClearInt() void IGbE::chkInterrupt() { + DPRINTF(Ethernet, "Checking interrupts icr: %#x imr: %#x\n", regs.icr(), + regs.imr); // Check if we need to clear the cpu interrupt if (!(regs.icr() & regs.imr)) { + DPRINTF(Ethernet, "Mask cleaned all interrupts\n"); if (interEvent.scheduled()) - interEvent.deschedule(); + deschedule(interEvent); if (regs.icr.int_assert()) cpuClearInt(); } + DPRINTF(Ethernet, "ITR = %#X itr.interval = %#X\n", regs.itr(), regs.itr.interval()); if (regs.icr() & regs.imr) { if (regs.itr.interval() == 0) { cpuPostInt(); } else { - if (!interEvent.scheduled()) - interEvent.schedule(curTick + Clock::Int::ns * 256 * regs.itr.interval()); + DPRINTF(Ethernet, "Possibly scheduling interrupt because of imr write\n"); + if (!interEvent.scheduled()) { + DPRINTF(Ethernet, "Scheduling for %d\n", curTick + Clock::Int::ns + * 256 * regs.itr.interval()); + schedule(interEvent, + curTick + Clock::Int::ns * 256 * regs.itr.interval()); + } } } @@ -641,7 +707,7 @@ IGbE::RxDescCache::RxDescCache(IGbE *i, const std::string n, int s) { } -bool +void IGbE::RxDescCache::writePacket(EthPacketPtr packet) { // We shouldn't have to deal with any of these yet @@ -649,14 +715,14 @@ IGbE::RxDescCache::writePacket(EthPacketPtr packet) packet->length, igbe->regs.rctl.descSize()); assert(packet->length < igbe->regs.rctl.descSize()); - if (!unusedCache.size()) - return false; + assert(unusedCache.size()); + //if (!unusedCache.size()) + // return false; pktPtr = packet; - + pktDone = false; igbe->dmaWrite(igbe->platform->pciToDma(unusedCache.front()->buf), - packet->length, &pktEvent, packet->data); - return true; + packet->length, &pktEvent, packet->data, igbe->rxWriteDelay); } void @@ -676,43 +742,53 @@ IGbE::RxDescCache::pktComplete() // no support for anything but starting at 0 assert(igbe->regs.rxcsum.pcss() == 0); - DPRINTF(EthernetDesc, "RxDesc: Packet written to memory updating Descriptor\n"); + DPRINTF(EthernetDesc, "Packet written to memory updating Descriptor\n"); uint8_t status = RXDS_DD | RXDS_EOP; uint8_t err = 0; + IpPtr ip(pktPtr); + if (ip) { + DPRINTF(EthernetDesc, "Proccesing Ip packet with Id=%d\n", ip->id()); + if (igbe->regs.rxcsum.ipofld()) { - DPRINTF(EthernetDesc, "RxDesc: Checking IP checksum\n"); + DPRINTF(EthernetDesc, "Checking IP checksum\n"); status |= RXDS_IPCS; desc->csum = htole(cksum(ip)); + igbe->rxIpChecksums++; if (cksum(ip) != 0) { err |= RXDE_IPE; - DPRINTF(EthernetDesc, "RxDesc: Checksum is bad!!\n"); + DPRINTF(EthernetDesc, "Checksum is bad!!\n"); } } TcpPtr tcp(ip); if (tcp && igbe->regs.rxcsum.tuofld()) { - DPRINTF(EthernetDesc, "RxDesc: Checking TCP checksum\n"); + DPRINTF(EthernetDesc, "Checking TCP checksum\n"); status |= RXDS_TCPCS; desc->csum = htole(cksum(tcp)); + igbe->rxTcpChecksums++; if (cksum(tcp) != 0) { - DPRINTF(EthernetDesc, "RxDesc: Checksum is bad!!\n"); + DPRINTF(EthernetDesc, "Checksum is bad!!\n"); err |= RXDE_TCPE; } } UdpPtr udp(ip); if (udp && igbe->regs.rxcsum.tuofld()) { - DPRINTF(EthernetDesc, "RxDesc: Checking UDP checksum\n"); + DPRINTF(EthernetDesc, "Checking UDP checksum\n"); status |= RXDS_UDPCS; desc->csum = htole(cksum(udp)); - if (cksum(tcp) != 0) { - DPRINTF(EthernetDesc, "RxDesc: Checksum is bad!!\n"); + igbe->rxUdpChecksums++; + if (cksum(udp) != 0) { + DPRINTF(EthernetDesc, "Checksum is bad!!\n"); err |= RXDE_TCPE; } } - } // if ip + } else { // if ip + DPRINTF(EthernetSM, "Proccesing Non-Ip packet\n"); + } + desc->status = htole(status); desc->errors = htole(err); @@ -724,46 +800,51 @@ IGbE::RxDescCache::pktComplete() if (igbe->regs.rdtr.delay()) { DPRINTF(EthernetSM, "RXS: Scheduling DTR for %d\n", igbe->regs.rdtr.delay() * igbe->intClock()); - if (igbe->rdtrEvent.scheduled()) - igbe->rdtrEvent.reschedule(curTick + igbe->regs.rdtr.delay() * - igbe->intClock()); - else - igbe->rdtrEvent.schedule(curTick + igbe->regs.rdtr.delay() * - igbe->intClock()); + igbe->reschedule(igbe->rdtrEvent, + curTick + igbe->regs.rdtr.delay() * igbe->intClock(), true); } - if (igbe->regs.radv.idv() && igbe->regs.rdtr.delay()) { + if (igbe->regs.radv.idv()) { DPRINTF(EthernetSM, "RXS: Scheduling ADV for %d\n", igbe->regs.radv.idv() * igbe->intClock()); - if (!igbe->radvEvent.scheduled()) - igbe->radvEvent.schedule(curTick + igbe->regs.radv.idv() * - igbe->intClock()); + if (!igbe->radvEvent.scheduled()) { + igbe->schedule(igbe->radvEvent, + curTick + igbe->regs.radv.idv() * igbe->intClock()); + } } // if neither radv or rdtr, maybe itr is set... - if (!igbe->regs.rdtr.delay()) { + if (!igbe->regs.rdtr.delay() && !igbe->regs.radv.idv()) { DPRINTF(EthernetSM, "RXS: Receive interrupt delay disabled, posting IT_RXT\n"); igbe->postInterrupt(IT_RXT); } // If the packet is small enough, interrupt appropriately // I wonder if this is delayed or not?! - if (pktPtr->length <= igbe->regs.rsrpd.idv()) + if (pktPtr->length <= igbe->regs.rsrpd.idv()) { + DPRINTF(EthernetSM, "RXS: Posting IT_SRPD beacuse small packet received\n"); igbe->postInterrupt(IT_SRPD); + } - DPRINTF(EthernetDesc, "RxDesc: Processing of this descriptor complete\n"); + DPRINTF(EthernetDesc, "Processing of this descriptor complete\n"); unusedCache.pop_front(); usedCache.push_back(desc); + + pktPtr = NULL; enableSm(); pktDone = true; + igbe->checkDrain(); + } void IGbE::RxDescCache::enableSm() { - igbe->rxTick = true; - igbe->restartClock(); + if (!igbe->drainEvent) { + igbe->rxTick = true; + igbe->restartClock(); + } } bool @@ -776,11 +857,33 @@ IGbE::RxDescCache::packetDone() return false; } +bool +IGbE::RxDescCache::hasOutstandingEvents() +{ + return pktEvent.scheduled() || wbEvent.scheduled() || + fetchEvent.scheduled(); +} + +void +IGbE::RxDescCache::serialize(std::ostream &os) +{ + DescCache::serialize(os); + SERIALIZE_SCALAR(pktDone); +} + +void +IGbE::RxDescCache::unserialize(Checkpoint *cp, const std::string §ion) +{ + DescCache::unserialize(cp, section); + UNSERIALIZE_SCALAR(pktDone); +} + + ///////////////////////////////////// IGbE::TxDesc ///////////////////////////////// IGbE::TxDescCache::TxDescCache(IGbE *i, const std::string n, int s) : DescCache(i,n, s), pktDone(false), isTcp(false), pktWaiting(false), - hLen(0), pktEvent(this) + pktEvent(this) { } @@ -792,18 +895,20 @@ IGbE::TxDescCache::getPacketSize() TxDesc *desc; - DPRINTF(EthernetDesc, "TxDesc: Starting processing of descriptor\n"); + DPRINTF(EthernetDesc, "Starting processing of descriptor\n"); while (unusedCache.size() && TxdOp::isContext(unusedCache.front())) { - DPRINTF(EthernetDesc, "TxDesc: Got context descriptor type... skipping\n"); + DPRINTF(EthernetDesc, "Got context descriptor type... skipping\n"); // I think we can just ignore these for now? desc = unusedCache.front(); + DPRINTF(EthernetDesc, "Descriptor upper: %#x lower: %#X\n", desc->d1, + desc->d2); // is this going to be a tcp or udp packet? isTcp = TxdOp::tcp(desc) ? true : false; // make sure it's ipv4 - assert(TxdOp::ip(desc)); + //assert(TxdOp::ip(desc)); TxdOp::setDd(desc); unusedCache.pop_front(); @@ -813,7 +918,7 @@ IGbE::TxDescCache::getPacketSize() if (!unusedCache.size()) return -1; - DPRINTF(EthernetDesc, "TxDesc: Next TX packet is %d bytes\n", + DPRINTF(EthernetDesc, "Next TX packet is %d bytes\n", TxdOp::getLen(unusedCache.front())); return TxdOp::getLen(unusedCache.front()); @@ -833,9 +938,9 @@ IGbE::TxDescCache::getPacketData(EthPacketPtr p) pktWaiting = true; - DPRINTF(EthernetDesc, "TxDesc: Starting DMA of packet\n"); + DPRINTF(EthernetDesc, "Starting DMA of packet at offset %d\n", p->length); igbe->dmaRead(igbe->platform->pciToDma(TxdOp::getBuf(desc)), - TxdOp::getLen(desc), &pktEvent, p->data + hLen); + TxdOp::getLen(desc), &pktEvent, p->data + p->length, igbe->txReadDelay); } @@ -848,7 +953,7 @@ IGbE::TxDescCache::pktComplete() assert(unusedCache.size()); assert(pktPtr); - DPRINTF(EthernetDesc, "TxDesc: DMA of packet complete\n"); + DPRINTF(EthernetDesc, "DMA of packet complete\n"); desc = unusedCache.front(); @@ -857,20 +962,25 @@ IGbE::TxDescCache::pktComplete() DPRINTF(EthernetDesc, "TxDescriptor data d1: %#llx d2: %#llx\n", desc->d1, desc->d2); if (!TxdOp::eop(desc)) { - assert(hLen == 0); - hLen = TxdOp::getLen(desc); + pktPtr->length += TxdOp::getLen(desc); unusedCache.pop_front(); usedCache.push_back(desc); pktDone = true; pktWaiting = false; + pktMultiDesc = true; + + DPRINTF(EthernetDesc, "Partial Packet Descriptor of %d bytes Done\n", + pktPtr->length); pktPtr = NULL; - DPRINTF(EthernetDesc, "TxDesc: Partial Packet Descriptor Done\n"); + enableSm(); + igbe->checkDrain(); return; } + pktMultiDesc = false; // Set the length of the data in the EtherPacket - pktPtr->length = TxdOp::getLen(desc) + hLen; + pktPtr->length += TxdOp::getLen(desc); // no support for vlans assert(!TxdOp::vle(desc)); @@ -886,48 +996,61 @@ IGbE::TxDescCache::pktComplete() DPRINTF(EthernetDesc, "TxDescriptor data d1: %#llx d2: %#llx\n", desc->d1, desc->d2); + if (DTRACE(EthernetDesc)) { + IpPtr ip(pktPtr); + if (ip) + DPRINTF(EthernetDesc, "Proccesing Ip packet with Id=%d\n", + ip->id()); + else + DPRINTF(EthernetSM, "Proccesing Non-Ip packet\n"); + } + // Checksums are only ofloaded for new descriptor types if (TxdOp::isData(desc) && ( TxdOp::ixsm(desc) || TxdOp::txsm(desc)) ) { - DPRINTF(EthernetDesc, "TxDesc: Calculating checksums for packet\n"); + DPRINTF(EthernetDesc, "Calculating checksums for packet\n"); IpPtr ip(pktPtr); + assert(ip); if (TxdOp::ixsm(desc)) { ip->sum(0); ip->sum(cksum(ip)); - DPRINTF(EthernetDesc, "TxDesc: Calculated IP checksum\n"); + igbe->txIpChecksums++; + DPRINTF(EthernetDesc, "Calculated IP checksum\n"); } - if (TxdOp::txsm(desc)) { - if (isTcp) { - TcpPtr tcp(ip); - tcp->sum(0); - tcp->sum(cksum(tcp)); - DPRINTF(EthernetDesc, "TxDesc: Calculated TCP checksum\n"); - } else { - UdpPtr udp(ip); - udp->sum(0); - udp->sum(cksum(udp)); - DPRINTF(EthernetDesc, "TxDesc: Calculated UDP checksum\n"); - } + if (TxdOp::txsm(desc)) { + TcpPtr tcp(ip); + UdpPtr udp(ip); + if (tcp) { + tcp->sum(0); + tcp->sum(cksum(tcp)); + igbe->txTcpChecksums++; + DPRINTF(EthernetDesc, "Calculated TCP checksum\n"); + } else if (udp) { + assert(udp); + udp->sum(0); + udp->sum(cksum(udp)); + igbe->txUdpChecksums++; + DPRINTF(EthernetDesc, "Calculated UDP checksum\n"); + } else { + panic("Told to checksum, but don't know how\n"); + } } } if (TxdOp::ide(desc)) { // Deal with the rx timer interrupts - DPRINTF(EthernetDesc, "TxDesc: Descriptor had IDE set\n"); + DPRINTF(EthernetDesc, "Descriptor had IDE set\n"); if (igbe->regs.tidv.idv()) { - DPRINTF(EthernetDesc, "TxDesc: setting tidv\n"); - if (igbe->tidvEvent.scheduled()) - igbe->tidvEvent.reschedule(curTick + igbe->regs.tidv.idv() * - igbe->intClock()); - else - igbe->tidvEvent.schedule(curTick + igbe->regs.tidv.idv() * - igbe->intClock()); + DPRINTF(EthernetDesc, "setting tidv\n"); + igbe->reschedule(igbe->tidvEvent, + curTick + igbe->regs.tidv.idv() * igbe->intClock(), true); } if (igbe->regs.tadv.idv() && igbe->regs.tidv.idv()) { - DPRINTF(EthernetDesc, "TxDesc: setting tadv\n"); - if (!igbe->tadvEvent.scheduled()) - igbe->tadvEvent.schedule(curTick + igbe->regs.tadv.idv() * - igbe->intClock()); + DPRINTF(EthernetDesc, "setting tadv\n"); + if (!igbe->tadvEvent.scheduled()) { + igbe->schedule(igbe->tadvEvent, + curTick + igbe->regs.tadv.idv() * igbe->intClock()); + } } } @@ -939,17 +1062,37 @@ IGbE::TxDescCache::pktComplete() pktWaiting = false; pktPtr = NULL; - hLen = 0; - DPRINTF(EthernetDesc, "TxDesc: Descriptor Done\n"); + DPRINTF(EthernetDesc, "Descriptor Done\n"); if (igbe->regs.txdctl.wthresh() == 0) { - DPRINTF(EthernetDesc, "TxDesc: WTHRESH == 0, writing back descriptor\n"); + DPRINTF(EthernetDesc, "WTHRESH == 0, writing back descriptor\n"); writeback(0); } else if (igbe->regs.txdctl.wthresh() >= usedCache.size()) { - DPRINTF(EthernetDesc, "TxDesc: used > WTHRESH, writing back descriptor\n"); + DPRINTF(EthernetDesc, "used > WTHRESH, writing back descriptor\n"); writeback((igbe->cacheBlockSize()-1)>>4); } + enableSm(); + igbe->checkDrain(); +} +void +IGbE::TxDescCache::serialize(std::ostream &os) +{ + DescCache::serialize(os); + SERIALIZE_SCALAR(pktDone); + SERIALIZE_SCALAR(isTcp); + SERIALIZE_SCALAR(pktWaiting); + SERIALIZE_SCALAR(pktMultiDesc); +} + +void +IGbE::TxDescCache::unserialize(Checkpoint *cp, const std::string §ion) +{ + DescCache::unserialize(cp, section); + UNSERIALIZE_SCALAR(pktDone); + UNSERIALIZE_SCALAR(isTcp); + UNSERIALIZE_SCALAR(pktWaiting); + UNSERIALIZE_SCALAR(pktMultiDesc); } bool @@ -965,11 +1108,18 @@ IGbE::TxDescCache::packetAvailable() void IGbE::TxDescCache::enableSm() { - igbe->txTick = true; - igbe->restartClock(); + if (!igbe->drainEvent) { + igbe->txTick = true; + igbe->restartClock(); + } } - +bool +IGbE::TxDescCache::hasOutstandingEvents() +{ + return pktEvent.scheduled() || wbEvent.scheduled() || + fetchEvent.scheduled(); +} ///////////////////////////////////// IGbE ///////////////////////////////// @@ -977,10 +1127,64 @@ IGbE::TxDescCache::enableSm() void IGbE::restartClock() { - if (!tickEvent.scheduled() && (rxTick || txTick)) - tickEvent.schedule((curTick/cycles(1)) * cycles(1) + cycles(1)); + if (!tickEvent.scheduled() && (rxTick || txTick || txFifoTick) && + getState() == SimObject::Running) + schedule(tickEvent, (curTick / ticks(1)) * ticks(1) + ticks(1)); } +unsigned int +IGbE::drain(Event *de) +{ + unsigned int count; + count = pioPort->drain(de) + dmaPort->drain(de); + if (rxDescCache.hasOutstandingEvents() || + txDescCache.hasOutstandingEvents()) { + count++; + drainEvent = de; + } + + txFifoTick = false; + txTick = false; + rxTick = false; + + if (tickEvent.scheduled()) + deschedule(tickEvent); + + if (count) + changeState(Draining); + else + changeState(Drained); + + return count; +} + +void +IGbE::resume() +{ + SimObject::resume(); + + txFifoTick = true; + txTick = true; + rxTick = true; + + restartClock(); +} + +void +IGbE::checkDrain() +{ + if (!drainEvent) + return; + + txFifoTick = false; + txTick = false; + rxTick = false; + if (!rxDescCache.hasOutstandingEvents() && + !txDescCache.hasOutstandingEvents()) { + drainEvent->process(); + drainEvent = NULL; + } +} void IGbE::txStateMachine() @@ -994,12 +1198,16 @@ IGbE::txStateMachine() // If we have a packet available and it's length is not 0 (meaning it's not // a multidescriptor packet) put it in the fifo, otherwise an the next // iteration we'll get the rest of the data - if (txPacket && txDescCache.packetAvailable() && txPacket->length) { + if (txPacket && txDescCache.packetAvailable() + && !txDescCache.packetMultiDesc() && txPacket->length) { bool success; + DPRINTF(EthernetSM, "TXS: packet placed in TX FIFO\n"); success = txFifo.push(txPacket); + txFifoTick = true && !drainEvent; assert(success); txPacket = NULL; + txDescCache.writeback((cacheBlockSize()-1)>>4); return; } @@ -1016,21 +1224,24 @@ IGbE::txStateMachine() if (!txDescCache.packetWaiting()) { if (txDescCache.descLeft() == 0) { + postInterrupt(IT_TXQE); + txDescCache.writeback(0); + txDescCache.fetchDescriptors(); DPRINTF(EthernetSM, "TXS: No descriptors left in ring, forcing " "writeback stopping ticking and posting TXQE\n"); - txDescCache.writeback(0); txTick = false; - postInterrupt(IT_TXQE, true); + return; } if (!(txDescCache.descUnused())) { + txDescCache.fetchDescriptors(); DPRINTF(EthernetSM, "TXS: No descriptors available in cache, fetching and stopping ticking\n"); txTick = false; - txDescCache.fetchDescriptors(); return; } + int size; size = txDescCache.getPacketSize(); if (size > 0 && txFifo.avail() > size) { @@ -1038,26 +1249,38 @@ IGbE::txStateMachine() "DMA of next packet\n", size); txFifo.reserve(size); txDescCache.getPacketData(txPacket); - } else { + } else if (size <= 0) { + DPRINTF(EthernetSM, "TXS: getPacketSize returned: %d\n", size); DPRINTF(EthernetSM, "TXS: No packets to get, writing back used descriptors\n"); txDescCache.writeback(0); + } else { + DPRINTF(EthernetSM, "TXS: FIFO full, stopping ticking until space " + "available in FIFO\n"); + txTick = false; } + return; } + DPRINTF(EthernetSM, "TXS: Nothing to do, stopping ticking\n"); + txTick = false; } bool IGbE::ethRxPkt(EthPacketPtr pkt) { + rxBytes += pkt->length; + rxPackets++; + DPRINTF(Ethernet, "RxFIFO: Receiving pcakte from wire\n"); + if (!regs.rctl.en()) { DPRINTF(Ethernet, "RxFIFO: RX not enabled, dropping\n"); return true; } // restart the state machines if they are stopped - rxTick = true; + rxTick = true && !drainEvent; if ((rxTick || txTick) && !tickEvent.scheduled()) { DPRINTF(EthernetSM, "RXS: received packet into fifo, starting ticking\n"); restartClock(); @@ -1068,6 +1291,7 @@ IGbE::ethRxPkt(EthPacketPtr pkt) postInterrupt(IT_RXO, true); return false; } + return true; } @@ -1083,6 +1307,7 @@ IGbE::rxStateMachine() // If the packet is done check for interrupts/descriptors/etc if (rxDescCache.packetDone()) { + rxDmaPacket = false; DPRINTF(EthernetSM, "RXS: Packet completed DMA to memory\n"); int descLeft = rxDescCache.descLeft(); switch (regs.rctl.rdmts()) { @@ -1095,9 +1320,9 @@ IGbE::rxStateMachine() } if (descLeft == 0) { - DPRINTF(EthernetSM, "RXS: No descriptors left in ring, forcing writeback\n"); rxDescCache.writeback(0); - DPRINTF(EthernetSM, "RXS: No descriptors left, stopping ticking\n"); + DPRINTF(EthernetSM, "RXS: No descriptors left in ring, forcing" + " writeback and stopping ticking\n"); rxTick = false; } @@ -1119,19 +1344,25 @@ IGbE::rxStateMachine() } if (rxDescCache.descUnused() == 0) { - DPRINTF(EthernetSM, "RXS: No descriptors available in cache, stopping ticking\n"); - rxTick = false; - DPRINTF(EthernetSM, "RXS: Fetching descriptors because none available\n"); rxDescCache.fetchDescriptors(); + DPRINTF(EthernetSM, "RXS: No descriptors available in cache, " + "fetching descriptors and stopping ticking\n"); + rxTick = false; } return; } + if (rxDmaPacket) { + DPRINTF(EthernetSM, "RXS: stopping ticking until packet DMA completes\n"); + rxTick = false; + return; + } + if (!rxDescCache.descUnused()) { + rxDescCache.fetchDescriptors(); DPRINTF(EthernetSM, "RXS: No descriptors available in cache, stopping ticking\n"); rxTick = false; DPRINTF(EthernetSM, "RXS: No descriptors available, fetching\n"); - rxDescCache.fetchDescriptors(); return; } @@ -1144,32 +1375,46 @@ IGbE::rxStateMachine() EthPacketPtr pkt; pkt = rxFifo.front(); - DPRINTF(EthernetSM, "RXS: Writing packet into memory\n"); - if (!rxDescCache.writePacket(pkt)) { - return; - } + rxDescCache.writePacket(pkt); + DPRINTF(EthernetSM, "RXS: Writing packet into memory\n"); DPRINTF(EthernetSM, "RXS: Removing packet from FIFO\n"); rxFifo.pop(); DPRINTF(EthernetSM, "RXS: stopping ticking until packet DMA completes\n"); rxTick = false; + rxDmaPacket = true; } void IGbE::txWire() { if (txFifo.empty()) { + txFifoTick = false; return; } - txTick = true; if (etherInt->sendPacket(txFifo.front())) { - DPRINTF(Ethernet, "TxFIFO: Successful transmit, bytes in fifo: %d\n", + if (DTRACE(EthernetSM)) { + IpPtr ip(txFifo.front()); + if (ip) + DPRINTF(EthernetSM, "Transmitting Ip packet with Id=%d\n", + ip->id()); + else + DPRINTF(EthernetSM, "Transmitting Non-Ip packet\n"); + } + DPRINTF(EthernetSM, "TxFIFO: Successful transmit, bytes available in fifo: %d\n", txFifo.avail()); + + txBytes += txFifo.front()->length; + txPackets++; + txFifoTick = false; + txFifo.pop(); + } else { + // We'll get woken up when the packet ethTxDone() gets called + txFifoTick = false; } - } void @@ -1180,132 +1425,141 @@ IGbE::tick() if (rxTick) rxStateMachine(); - if (txTick) { + if (txTick) txStateMachine(); + + if (txFifoTick) txWire(); - } - if (rxTick || txTick) - tickEvent.schedule(curTick + cycles(1)); + + if (rxTick || txTick || txFifoTick) + schedule(tickEvent, curTick + ticks(1)); } void IGbE::ethTxDone() { - // restart the state machines if they are stopped - txTick = true; + // restart the tx state machines if they are stopped + // fifo to send another packet + // tx sm to put more data into the fifo + txFifoTick = true && !drainEvent; + if (txDescCache.descLeft() != 0 && !drainEvent) + txTick = true; + restartClock(); - DPRINTF(Ethernet, "TxFIFO: Transmission complete\n"); + txWire(); + DPRINTF(EthernetSM, "TxFIFO: Transmission complete\n"); } void IGbE::serialize(std::ostream &os) { - panic("Need to implemenet\n"); -} + PciDev::serialize(os); -void -IGbE::unserialize(Checkpoint *cp, const std::string §ion) -{ - panic("Need to implemenet\n"); -} + regs.serialize(os); + SERIALIZE_SCALAR(eeOpBits); + SERIALIZE_SCALAR(eeAddrBits); + SERIALIZE_SCALAR(eeDataBits); + SERIALIZE_SCALAR(eeOpcode); + SERIALIZE_SCALAR(eeAddr); + SERIALIZE_SCALAR(lastInterrupt); + SERIALIZE_ARRAY(flash,iGbReg::EEPROM_SIZE); + rxFifo.serialize("rxfifo", os); + txFifo.serialize("txfifo", os); -BEGIN_DECLARE_SIM_OBJECT_PARAMS(IGbEInt) + bool txPktExists = txPacket; + SERIALIZE_SCALAR(txPktExists); + if (txPktExists) + txPacket->serialize("txpacket", os); - SimObjectParam peer; - SimObjectParam device; + Tick rdtr_time = 0, radv_time = 0, tidv_time = 0, tadv_time = 0, + inter_time = 0; -END_DECLARE_SIM_OBJECT_PARAMS(IGbEInt) + if (rdtrEvent.scheduled()) + rdtr_time = rdtrEvent.when(); + SERIALIZE_SCALAR(rdtr_time); -BEGIN_INIT_SIM_OBJECT_PARAMS(IGbEInt) + if (radvEvent.scheduled()) + radv_time = radvEvent.when(); + SERIALIZE_SCALAR(radv_time); - INIT_PARAM_DFLT(peer, "peer interface", NULL), - INIT_PARAM(device, "Ethernet device of this interface") + if (tidvEvent.scheduled()) + tidv_time = tidvEvent.when(); + SERIALIZE_SCALAR(tidv_time); -END_INIT_SIM_OBJECT_PARAMS(IGbEInt) + if (tadvEvent.scheduled()) + tadv_time = tadvEvent.when(); + SERIALIZE_SCALAR(tadv_time); -CREATE_SIM_OBJECT(IGbEInt) -{ - IGbEInt *dev_int = new IGbEInt(getInstanceName(), device); + if (interEvent.scheduled()) + inter_time = interEvent.when(); + SERIALIZE_SCALAR(inter_time); - EtherInt *p = (EtherInt *)peer; - if (p) { - dev_int->setPeer(p); - p->setPeer(dev_int); - } + nameOut(os, csprintf("%s.TxDescCache", name())); + txDescCache.serialize(os); - return dev_int; + nameOut(os, csprintf("%s.RxDescCache", name())); + rxDescCache.serialize(os); } -REGISTER_SIM_OBJECT("IGbEInt", IGbEInt) +void +IGbE::unserialize(Checkpoint *cp, const std::string §ion) +{ + PciDev::unserialize(cp, section); + + regs.unserialize(cp, section); + UNSERIALIZE_SCALAR(eeOpBits); + UNSERIALIZE_SCALAR(eeAddrBits); + UNSERIALIZE_SCALAR(eeDataBits); + UNSERIALIZE_SCALAR(eeOpcode); + UNSERIALIZE_SCALAR(eeAddr); + UNSERIALIZE_SCALAR(lastInterrupt); + UNSERIALIZE_ARRAY(flash,iGbReg::EEPROM_SIZE); + + rxFifo.unserialize("rxfifo", cp, section); + txFifo.unserialize("txfifo", cp, section); + + bool txPktExists; + UNSERIALIZE_SCALAR(txPktExists); + if (txPktExists) { + txPacket = new EthPacketData(16384); + txPacket->unserialize("txpacket", cp, section); + } + rxTick = true; + txTick = true; + txFifoTick = true; -BEGIN_DECLARE_SIM_OBJECT_PARAMS(IGbE) + Tick rdtr_time, radv_time, tidv_time, tadv_time, inter_time; + UNSERIALIZE_SCALAR(rdtr_time); + UNSERIALIZE_SCALAR(radv_time); + UNSERIALIZE_SCALAR(tidv_time); + UNSERIALIZE_SCALAR(tadv_time); + UNSERIALIZE_SCALAR(inter_time); - SimObjectParam system; - SimObjectParam platform; - SimObjectParam configdata; - Param pci_bus; - Param pci_dev; - Param pci_func; - Param pio_latency; - Param config_latency; - Param hardware_address; - Param use_flow_control; - Param rx_fifo_size; - Param tx_fifo_size; - Param rx_desc_cache_size; - Param tx_desc_cache_size; - Param clock; + if (rdtr_time) + schedule(rdtrEvent, rdtr_time); + if (radv_time) + schedule(radvEvent, radv_time); -END_DECLARE_SIM_OBJECT_PARAMS(IGbE) + if (tidv_time) + schedule(tidvEvent, tidv_time); -BEGIN_INIT_SIM_OBJECT_PARAMS(IGbE) + if (tadv_time) + schedule(tadvEvent, tadv_time); - INIT_PARAM(system, "System pointer"), - INIT_PARAM(platform, "Platform pointer"), - INIT_PARAM(configdata, "PCI Config data"), - INIT_PARAM(pci_bus, "PCI bus ID"), - INIT_PARAM(pci_dev, "PCI device number"), - INIT_PARAM(pci_func, "PCI function code"), - INIT_PARAM_DFLT(pio_latency, "Programmed IO latency in bus cycles", 1), - INIT_PARAM(config_latency, "Number of cycles for a config read or write"), - INIT_PARAM(hardware_address, "Ethernet Hardware Address"), - INIT_PARAM(use_flow_control,"Should the device use xon/off packets"), - INIT_PARAM(rx_fifo_size,"Size of the RX FIFO"), - INIT_PARAM(tx_fifo_size,"Size of the TX FIFO"), - INIT_PARAM(rx_desc_cache_size,"Size of the RX descriptor cache"), - INIT_PARAM(tx_desc_cache_size,"Size of the TX descriptor cache"), - INIT_PARAM(clock,"Clock rate for the device to tick at") + if (inter_time) + schedule(interEvent, inter_time); -END_INIT_SIM_OBJECT_PARAMS(IGbE) + txDescCache.unserialize(cp, csprintf("%s.TxDescCache", section)); + rxDescCache.unserialize(cp, csprintf("%s.RxDescCache", section)); +} -CREATE_SIM_OBJECT(IGbE) +IGbE * +IGbEParams::create() { - IGbE::Params *params = new IGbE::Params; - - params->name = getInstanceName(); - params->platform = platform; - params->system = system; - params->configData = configdata; - params->busNum = pci_bus; - params->deviceNum = pci_dev; - params->functionNum = pci_func; - params->pio_delay = pio_latency; - params->config_delay = config_latency; - params->hardware_address = hardware_address; - params->use_flow_control = use_flow_control; - params->rx_fifo_size = rx_fifo_size; - params->tx_fifo_size = tx_fifo_size; - params->rx_desc_cache_size = rx_desc_cache_size; - params->tx_desc_cache_size = tx_desc_cache_size; - params->clock = clock; - - - return new IGbE(params); + return new IGbE(this); } - -REGISTER_SIM_OBJECT("IGbE", IGbE)