X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fdev%2Fi8254xGBe.cc;h=0fbda1897a67a37699202450f273bbbf48e66775;hb=27e8f3c98a4b1c56e16ce5f80c7ad992083ccef9;hp=77c7318991f72dff40ae3eadc8b7cf8425a0f8ed;hpb=17b0e9714d4bde7462d4663899bb9498027f6b40;p=gem5.git diff --git a/src/dev/i8254xGBe.cc b/src/dev/i8254xGBe.cc index 77c731899..0fbda1897 100644 --- a/src/dev/i8254xGBe.cc +++ b/src/dev/i8254xGBe.cc @@ -30,24 +30,104 @@ /* @file * Device model for Intel's 8254x line of gigabit ethernet controllers. + * In particular an 82547 revision 2 (82547GI) MAC because it seems to have the + * fewest workarounds in the driver. It will probably work with most of the + * other MACs with slight modifications. */ + +/* + * @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 "sim/builder.hh" +#include "mem/packet_access.hh" +#include "params/IGbE.hh" #include "sim/stats.hh" #include "sim/system.hh" -IGbE::IGbE(Params *p) - : PciDev(p), etherInt(NULL) +using namespace iGbReg; +using namespace Net; + +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), 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), 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); + regs.ctrl.lrst(1); + regs.ctrl.speed(2); + regs.ctrl.frcspd(1); + regs.sts.speed(3); // Say we're 1000Mbps + regs.sts.fd(1); // full duplex + regs.sts.lu(1); // link up + regs.eecd.fwe(1); + regs.eecd.ee_type(1); + regs.imr = 0; + regs.iam = 0; + regs.rxdctl.gran(1); + regs.rxdctl.wthresh(1); + regs.fcrth(1); + + regs.pba.rxa(0x30); + regs.pba.txa(0x10); + + eeOpBits = 0; + eeAddrBits = 0; + eeDataBits = 0; + eeOpcode = 0; + + // clear all 64 16 bit words of the eeprom + memset(&flash, 0, EEPROM_SIZE*2); + + // Set the MAC address + memcpy(flash, p->hardware_address.bytes(), ETH_ADDR_LEN); + for (int x = 0; x < ETH_ADDR_LEN/2; x++) + flash[x] = htobe(flash[x]); + uint16_t csum = 0; + for (int x = 0; x < EEPROM_SIZE; x++) + csum += htobe(flash[x]); + + + // Magic happy checksum value + flash[EEPROM_SIZE-1] = htobe((uint16_t)(EEPROM_CSUM - csum)); + + rxFifo.clear(); + 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(Packet *pkt) +IGbE::writeConfig(PacketPtr pkt) { int offset = pkt->getAddr() & PCI_CONFIG_SIZE; if (offset < PCI_DEVICE_SPECIFIC) @@ -63,7 +143,7 @@ IGbE::writeConfig(Packet *pkt) } Tick -IGbE::read(Packet *pkt) +IGbE::read(PacketPtr pkt) { int bar; Addr daddr; @@ -74,140 +154,1412 @@ IGbE::read(Packet *pkt) // Only Memory register BAR is allowed assert(bar == 0); - DPRINTF(Ethernet, "Accessed devie register %#X\n", daddr); + // Only 32bit accesses allowed + assert(pkt->getSize() == 4); - pkt->allocate(); + DPRINTF(Ethernet, "Read device register %#X\n", daddr); + pkt->allocate(); /// /// Handle read of register here /// - pkt->result = Packet::Success; + + switch (daddr) { + case REG_CTRL: + pkt->set(regs.ctrl()); + break; + case REG_STATUS: + pkt->set(regs.sts()); + break; + case REG_EECD: + pkt->set(regs.eecd()); + break; + case REG_EERD: + pkt->set(regs.eerd()); + break; + case REG_CTRL_EXT: + pkt->set(regs.ctrl_ext()); + break; + case REG_MDIC: + pkt->set(regs.mdic()); + break; + case REG_ICR: + DPRINTF(Ethernet, "Reading ICR. ICR=%#x IMR=%#x IAM=%#x IAME=%d\n", regs.icr(), + regs.imr, regs.iam, regs.ctrl_ext.iame()); + pkt->set(regs.icr()); + if (regs.icr.int_assert() || regs.imr == 0) { + regs.icr = regs.icr() & ~mask(30); + DPRINTF(Ethernet, "Cleared ICR. ICR=%#x\n", regs.icr()); + } + if (regs.ctrl_ext.iame() && regs.icr.int_assert()) + regs.imr &= ~regs.iam; + chkInterrupt(); + break; + case REG_ITR: + pkt->set(regs.itr()); + break; + case REG_RCTL: + pkt->set(regs.rctl()); + break; + case REG_FCTTV: + pkt->set(regs.fcttv()); + break; + case REG_TCTL: + pkt->set(regs.tctl()); + break; + case REG_PBA: + pkt->set(regs.pba()); + break; + case REG_WUC: + case REG_LEDCTL: + pkt->set(0); // We don't care, so just return 0 + break; + case REG_FCRTL: + pkt->set(regs.fcrtl()); + break; + case REG_FCRTH: + pkt->set(regs.fcrth()); + break; + case REG_RDBAL: + pkt->set(regs.rdba.rdbal()); + break; + case REG_RDBAH: + pkt->set(regs.rdba.rdbah()); + break; + case REG_RDLEN: + pkt->set(regs.rdlen()); + break; + case REG_RDH: + pkt->set(regs.rdh()); + break; + case REG_RDT: + pkt->set(regs.rdt()); + break; + case REG_RDTR: + 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); + } + break; + case REG_RADV: + pkt->set(regs.radv()); + break; + case REG_TDBAL: + pkt->set(regs.tdba.tdbal()); + break; + case REG_TDBAH: + pkt->set(regs.tdba.tdbah()); + break; + case REG_TDLEN: + pkt->set(regs.tdlen()); + break; + case REG_TDH: + pkt->set(regs.tdh()); + break; + case REG_TDT: + pkt->set(regs.tdt()); + break; + case REG_TIDV: + pkt->set(regs.tidv()); + break; + case REG_TXDCTL: + pkt->set(regs.txdctl()); + break; + case REG_TADV: + pkt->set(regs.tadv()); + break; + case REG_RXCSUM: + pkt->set(regs.rxcsum()); + break; + case REG_MANC: + pkt->set(regs.manc()); + break; + default: + if (!(daddr >= REG_VFTA && daddr < (REG_VFTA + VLAN_FILTER_TABLE_SIZE*4)) && + !(daddr >= REG_RAL && daddr < (REG_RAL + RCV_ADDRESS_TABLE_SIZE*8)) && + !(daddr >= REG_MTA && daddr < (REG_MTA + MULTICAST_TABLE_SIZE*4)) && + !(daddr >= REG_CRCERRS && daddr < (REG_CRCERRS + STATS_REGS_SIZE))) + panic("Read request to unknown register number: %#x\n", daddr); + else + pkt->set(0); + }; + + pkt->makeAtomicResponse(); return pioDelay; } Tick -IGbE::write(Packet *pkt) +IGbE::write(PacketPtr pkt) { int bar; Addr daddr; + if (!getBAR(pkt->getAddr(), bar, daddr)) panic("Invalid PCI memory access to unmapped memory.\n"); // Only Memory register BAR is allowed assert(bar == 0); - DPRINTF(Ethernet, "Accessed devie register %#X\n", daddr); + // Only 32bit accesses allowed + assert(pkt->getSize() == sizeof(uint32_t)); + + DPRINTF(Ethernet, "Wrote device register %#X value %#X\n", daddr, pkt->get()); /// /// Handle write of register here /// + uint32_t val = pkt->get(); + + Regs::RCTL oldrctl; + Regs::TCTL oldtctl; + + switch (daddr) { + case REG_CTRL: + regs.ctrl = val; + if (regs.ctrl.tfce()) + warn("TX Flow control enabled, should implement\n"); + if (regs.ctrl.rfce()) + warn("RX Flow control enabled, should implement\n"); + break; + case REG_CTRL_EXT: + regs.ctrl_ext = val; + break; + case REG_STATUS: + regs.sts = val; + break; + case REG_EECD: + int oldClk; + oldClk = regs.eecd.sk(); + regs.eecd = val; + // See if this is a eeprom access and emulate accordingly + if (!oldClk && regs.eecd.sk()) { + if (eeOpBits < 8) { + eeOpcode = eeOpcode << 1 | regs.eecd.din(); + eeOpBits++; + } else if (eeAddrBits < 8 && eeOpcode == EEPROM_READ_OPCODE_SPI) { + eeAddr = eeAddr << 1 | regs.eecd.din(); + eeAddrBits++; + } else if (eeDataBits < 16 && eeOpcode == EEPROM_READ_OPCODE_SPI) { + assert(eeAddr>>1 < EEPROM_SIZE); + DPRINTF(EthernetEEPROM, "EEPROM bit read: %d word: %#X\n", + flash[eeAddr>>1] >> eeDataBits & 0x1, flash[eeAddr>>1]); + regs.eecd.dout((flash[eeAddr>>1] >> (15-eeDataBits)) & 0x1); + eeDataBits++; + } else if (eeDataBits < 8 && eeOpcode == EEPROM_RDSR_OPCODE_SPI) { + regs.eecd.dout(0); + eeDataBits++; + } else + panic("What's going on with eeprom interface? opcode:" + " %#x:%d addr: %#x:%d, data: %d\n", (uint32_t)eeOpcode, + (uint32_t)eeOpBits, (uint32_t)eeAddr, + (uint32_t)eeAddrBits, (uint32_t)eeDataBits); - pkt->result = Packet::Success; + // Reset everything for the next command + if ((eeDataBits == 16 && eeOpcode == EEPROM_READ_OPCODE_SPI) || + (eeDataBits == 8 && eeOpcode == EEPROM_RDSR_OPCODE_SPI)) { + eeOpBits = 0; + eeAddrBits = 0; + eeDataBits = 0; + eeOpcode = 0; + eeAddr = 0; + } + + DPRINTF(EthernetEEPROM, "EEPROM: opcode: %#X:%d addr: %#X:%d\n", + (uint32_t)eeOpcode, (uint32_t) eeOpBits, + (uint32_t)eeAddr>>1, (uint32_t)eeAddrBits); + if (eeOpBits == 8 && !(eeOpcode == EEPROM_READ_OPCODE_SPI || + eeOpcode == EEPROM_RDSR_OPCODE_SPI )) + panic("Unknown eeprom opcode: %#X:%d\n", (uint32_t)eeOpcode, + (uint32_t)eeOpBits); + + + } + // If driver requests eeprom access, immediately give it to it + regs.eecd.ee_gnt(regs.eecd.ee_req()); + break; + case REG_EERD: + regs.eerd = val; + break; + case REG_MDIC: + regs.mdic = val; + if (regs.mdic.i()) + panic("No support for interrupt on mdic complete\n"); + if (regs.mdic.phyadd() != 1) + panic("No support for reading anything but phy\n"); + DPRINTF(Ethernet, "%s phy address %x\n", regs.mdic.op() == 1 ? "Writing" + : "Reading", regs.mdic.regadd()); + switch (regs.mdic.regadd()) { + case PHY_PSTATUS: + regs.mdic.data(0x796D); // link up + break; + case PHY_PID: + regs.mdic.data(0x02A8); + break; + case PHY_EPID: + regs.mdic.data(0x0380); + break; + case PHY_GSTATUS: + regs.mdic.data(0x7C00); + break; + case PHY_EPSTATUS: + regs.mdic.data(0x3000); + break; + case PHY_AGC: + regs.mdic.data(0x180); // some random length + break; + default: + regs.mdic.data(0); + } + regs.mdic.r(1); + break; + case REG_ICR: + DPRINTF(Ethernet, "Writing ICR. ICR=%#x IMR=%#x IAM=%#x IAME=%d\n", regs.icr(), + regs.imr, regs.iam, regs.ctrl_ext.iame()); + if (regs.ctrl_ext.iame()) + regs.imr &= ~regs.iam; + regs.icr = ~bits(val,30,0) & regs.icr(); + chkInterrupt(); + break; + case REG_ITR: + regs.itr = val; + break; + case REG_ICS: + DPRINTF(EthernetIntr, "Posting interrupt because of ICS write\n"); + postInterrupt((IntTypes)val); + break; + case REG_IMS: + regs.imr |= val; + chkInterrupt(); + break; + case REG_IMC: + regs.imr &= ~val; + chkInterrupt(); + break; + case REG_IAM: + regs.iam = val; + break; + case REG_RCTL: + oldrctl = regs.rctl; + regs.rctl = val; + if (regs.rctl.rst()) { + rxDescCache.reset(); + DPRINTF(EthernetSM, "RXS: Got RESET!\n"); + rxFifo.clear(); + regs.rctl.rst(0); + } + if (regs.rctl.en()) + rxTick = true; + restartClock(); + break; + case REG_FCTTV: + regs.fcttv = val; + break; + case REG_TCTL: + regs.tctl = val; + oldtctl = regs.tctl; + regs.tctl = val; + if (regs.tctl.en()) + txTick = true; + restartClock(); + if (regs.tctl.en() && !oldtctl.en()) { + txDescCache.reset(); + } + break; + case REG_PBA: + regs.pba.rxa(val); + regs.pba.txa(64 - regs.pba.rxa()); + break; + case REG_WUC: + case REG_LEDCTL: + case REG_FCAL: + case REG_FCAH: + case REG_FCT: + case REG_VET: + case REG_AIFS: + case REG_TIPG: + ; // We don't care, so don't store anything + break; + case REG_FCRTL: + regs.fcrtl = val; + break; + case REG_FCRTH: + regs.fcrth = val; + break; + case REG_RDBAL: + regs.rdba.rdbal( val & ~mask(4)); + rxDescCache.areaChanged(); + break; + case REG_RDBAH: + regs.rdba.rdbah(val); + rxDescCache.areaChanged(); + break; + case REG_RDLEN: + regs.rdlen = val & ~mask(7); + rxDescCache.areaChanged(); + break; + case REG_RDH: + regs.rdh = val; + rxDescCache.areaChanged(); + break; + case REG_RDT: + regs.rdt = val; + 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; + break; + case REG_RADV: + regs.radv = val; + break; + case REG_TDBAL: + regs.tdba.tdbal( val & ~mask(4)); + txDescCache.areaChanged(); + break; + case REG_TDBAH: + regs.tdba.tdbah(val); + txDescCache.areaChanged(); + break; + case REG_TDLEN: + regs.tdlen = val & ~mask(7); + txDescCache.areaChanged(); + break; + case REG_TDH: + regs.tdh = val; + txDescCache.areaChanged(); + break; + case REG_TDT: + regs.tdt = val; + 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; + break; + case REG_TXDCTL: + regs.txdctl = val; + break; + case REG_TADV: + regs.tadv = val; + break; + case REG_RXCSUM: + regs.rxcsum = val; + break; + case REG_MANC: + regs.manc = val; + break; + default: + if (!(daddr >= REG_VFTA && daddr < (REG_VFTA + VLAN_FILTER_TABLE_SIZE*4)) && + !(daddr >= REG_RAL && daddr < (REG_RAL + RCV_ADDRESS_TABLE_SIZE*8)) && + !(daddr >= REG_MTA && daddr < (REG_MTA + MULTICAST_TABLE_SIZE*4))) + panic("Write request to unknown register number: %#x\n", daddr); + }; + + pkt->makeAtomicResponse(); return pioDelay; } +void +IGbE::postInterrupt(IntTypes t, bool now) +{ + assert(t); + + // Interrupt is already pending + if (t & regs.icr() && !now) + return; + + 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); + deschedule(rdtrEvent); + } + if (radvEvent.scheduled()) { + regs.icr.rxt0(1); + deschedule(radvEvent); + } + if (tadvEvent.scheduled()) { + regs.icr.txdw(1); + deschedule(tadvEvent); + } + if (tidvEvent.scheduled()) { + regs.icr.txdw(1); + deschedule(tidvEvent); + } + + regs.icr.int_assert(1); + DPRINTF(EthernetIntr, "EINT: Posting interrupt to CPU now. Vector %#x\n", + regs.icr()); + + intrPost(); + + lastInterrupt = curTick; +} + +void +IGbE::cpuClearInt() +{ + if (regs.icr.int_assert()) { + regs.icr.int_assert(0); + DPRINTF(EthernetIntr, "EINT: Clearing interrupt to CPU now. Vector %#x\n", + regs.icr()); + intrClear(); + } +} + +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()) + 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 { + 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()); + } + } + } + + +} + + +IGbE::RxDescCache::RxDescCache(IGbE *i, const std::string n, int s) + : DescCache(i, n, s), pktDone(false), pktEvent(this) + +{ +} + +void +IGbE::RxDescCache::writePacket(EthPacketPtr packet) +{ + // We shouldn't have to deal with any of these yet + DPRINTF(EthernetDesc, "Packet Length: %d Desc Size: %d\n", + packet->length, igbe->regs.rctl.descSize()); + assert(packet->length < igbe->regs.rctl.descSize()); + + 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, igbe->rxWriteDelay); +} + +void +IGbE::RxDescCache::pktComplete() +{ + assert(unusedCache.size()); + RxDesc *desc; + desc = unusedCache.front(); + + uint16_t crcfixup = igbe->regs.rctl.secrc() ? 0 : 4 ; + desc->len = htole((uint16_t)(pktPtr->length + crcfixup)); + DPRINTF(EthernetDesc, "pktPtr->length: %d stripcrc offset: %d value written: %d %d\n", + pktPtr->length, crcfixup, + htole((uint16_t)(pktPtr->length + crcfixup)), + (uint16_t)(pktPtr->length + crcfixup)); + + // no support for anything but starting at 0 + assert(igbe->regs.rxcsum.pcss() == 0); + + 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, "Checking IP checksum\n"); + status |= RXDS_IPCS; + desc->csum = htole(cksum(ip)); + igbe->rxIpChecksums++; + if (cksum(ip) != 0) { + err |= RXDE_IPE; + DPRINTF(EthernetDesc, "Checksum is bad!!\n"); + } + } + TcpPtr tcp(ip); + if (tcp && igbe->regs.rxcsum.tuofld()) { + DPRINTF(EthernetDesc, "Checking TCP checksum\n"); + status |= RXDS_TCPCS; + desc->csum = htole(cksum(tcp)); + igbe->rxTcpChecksums++; + if (cksum(tcp) != 0) { + DPRINTF(EthernetDesc, "Checksum is bad!!\n"); + err |= RXDE_TCPE; + } + } + + UdpPtr udp(ip); + if (udp && igbe->regs.rxcsum.tuofld()) { + DPRINTF(EthernetDesc, "Checking UDP checksum\n"); + status |= RXDS_UDPCS; + desc->csum = htole(cksum(udp)); + igbe->rxUdpChecksums++; + if (cksum(udp) != 0) { + DPRINTF(EthernetDesc, "Checksum is bad!!\n"); + err |= RXDE_TCPE; + } + } + } else { // if ip + DPRINTF(EthernetSM, "Proccesing Non-Ip packet\n"); + } + + + desc->status = htole(status); + desc->errors = htole(err); + + // No vlan support at this point... just set it to 0 + desc->vlan = 0; + + // Deal with the rx timer interrupts + if (igbe->regs.rdtr.delay()) { + DPRINTF(EthernetSM, "RXS: Scheduling DTR for %d\n", + igbe->regs.rdtr.delay() * igbe->intClock()); + igbe->reschedule(igbe->rdtrEvent, + curTick + igbe->regs.rdtr.delay() * igbe->intClock(), true); + } + + if (igbe->regs.radv.idv()) { + DPRINTF(EthernetSM, "RXS: Scheduling ADV for %d\n", + 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() && !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()) { + DPRINTF(EthernetSM, "RXS: Posting IT_SRPD beacuse small packet received\n"); + igbe->postInterrupt(IT_SRPD); + } + + 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() +{ + if (!igbe->drainEvent) { + igbe->rxTick = true; + igbe->restartClock(); + } +} + +bool +IGbE::RxDescCache::packetDone() +{ + if (pktDone) { + pktDone = false; + return true; + } + return false; +} bool -IGbE::ethRxPkt(EthPacketPtr packet) +IGbE::RxDescCache::hasOutstandingEvents() { - panic("Need to implemenet\n"); + return pktEvent.scheduled() || wbEvent.scheduled() || + fetchEvent.scheduled(); } +void +IGbE::RxDescCache::serialize(std::ostream &os) +{ + DescCache::serialize(os); + SERIALIZE_SCALAR(pktDone); +} void -IGbE::ethTxDone() +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), + pktEvent(this) + +{ +} + +int +IGbE::TxDescCache::getPacketSize() { - panic("Need to implemenet\n"); + assert(unusedCache.size()); + + TxDesc *desc; + + DPRINTF(EthernetDesc, "Starting processing of descriptor\n"); + + while (unusedCache.size() && TxdOp::isContext(unusedCache.front())) { + 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)); + + TxdOp::setDd(desc); + unusedCache.pop_front(); + usedCache.push_back(desc); + } + + if (!unusedCache.size()) + return -1; + + DPRINTF(EthernetDesc, "Next TX packet is %d bytes\n", + TxdOp::getLen(unusedCache.front())); + + return TxdOp::getLen(unusedCache.front()); } void -IGbE::serialize(std::ostream &os) +IGbE::TxDescCache::getPacketData(EthPacketPtr p) { - panic("Need to implemenet\n"); + assert(unusedCache.size()); + + TxDesc *desc; + desc = unusedCache.front(); + + assert((TxdOp::isLegacy(desc) || TxdOp::isData(desc)) && TxdOp::getLen(desc)); + + pktPtr = p; + + pktWaiting = true; + + 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 + p->length, igbe->txReadDelay); + + } void -IGbE::unserialize(Checkpoint *cp, const std::string §ion) +IGbE::TxDescCache::pktComplete() +{ + + TxDesc *desc; + assert(unusedCache.size()); + assert(pktPtr); + + DPRINTF(EthernetDesc, "DMA of packet complete\n"); + + + desc = unusedCache.front(); + assert((TxdOp::isLegacy(desc) || TxdOp::isData(desc)) && TxdOp::getLen(desc)); + + DPRINTF(EthernetDesc, "TxDescriptor data d1: %#llx d2: %#llx\n", desc->d1, desc->d2); + + if (!TxdOp::eop(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; + + enableSm(); + igbe->checkDrain(); + return; + } + pktMultiDesc = false; + + // Set the length of the data in the EtherPacket + pktPtr->length += TxdOp::getLen(desc); + + // no support for vlans + assert(!TxdOp::vle(desc)); + + // we alway report status + assert(TxdOp::rs(desc)); + + // we only support single packet descriptors at this point + assert(TxdOp::eop(desc)); + + // set that this packet is done + TxdOp::setDd(desc); + + 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, "Calculating checksums for packet\n"); + IpPtr ip(pktPtr); + assert(ip); + if (TxdOp::ixsm(desc)) { + ip->sum(0); + ip->sum(cksum(ip)); + igbe->txIpChecksums++; + DPRINTF(EthernetDesc, "Calculated IP 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, "Descriptor had IDE set\n"); + if (igbe->regs.tidv.idv()) { + 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, "setting tadv\n"); + if (!igbe->tadvEvent.scheduled()) { + igbe->schedule(igbe->tadvEvent, + curTick + igbe->regs.tadv.idv() * igbe->intClock()); + } + } + } + + + + unusedCache.pop_front(); + usedCache.push_back(desc); + pktDone = true; + pktWaiting = false; + pktPtr = NULL; + + DPRINTF(EthernetDesc, "Descriptor Done\n"); + + if (igbe->regs.txdctl.wthresh() == 0) { + DPRINTF(EthernetDesc, "WTHRESH == 0, writing back descriptor\n"); + writeback(0); + } else if (igbe->regs.txdctl.wthresh() >= usedCache.size()) { + DPRINTF(EthernetDesc, "used > WTHRESH, writing back descriptor\n"); + writeback((igbe->cacheBlockSize()-1)>>4); + } + enableSm(); + igbe->checkDrain(); +} + +void +IGbE::TxDescCache::serialize(std::ostream &os) { - panic("Need to implemenet\n"); + 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); +} -BEGIN_DECLARE_SIM_OBJECT_PARAMS(IGbEInt) +bool +IGbE::TxDescCache::packetAvailable() +{ + if (pktDone) { + pktDone = false; + return true; + } + return false; +} - SimObjectParam peer; - SimObjectParam device; +void +IGbE::TxDescCache::enableSm() +{ + if (!igbe->drainEvent) { + igbe->txTick = true; + igbe->restartClock(); + } +} -END_DECLARE_SIM_OBJECT_PARAMS(IGbEInt) +bool +IGbE::TxDescCache::hasOutstandingEvents() +{ + return pktEvent.scheduled() || wbEvent.scheduled() || + fetchEvent.scheduled(); +} + + +///////////////////////////////////// IGbE ///////////////////////////////// + +void +IGbE::restartClock() +{ + 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; -BEGIN_INIT_SIM_OBJECT_PARAMS(IGbEInt) + restartClock(); +} - INIT_PARAM_DFLT(peer, "peer interface", NULL), - INIT_PARAM(device, "Ethernet device of this interface") +void +IGbE::checkDrain() +{ + if (!drainEvent) + return; -END_INIT_SIM_OBJECT_PARAMS(IGbEInt) + txFifoTick = false; + txTick = false; + rxTick = false; + if (!rxDescCache.hasOutstandingEvents() && + !txDescCache.hasOutstandingEvents()) { + drainEvent->process(); + drainEvent = NULL; + } +} -CREATE_SIM_OBJECT(IGbEInt) +void +IGbE::txStateMachine() { - IGbEInt *dev_int = new IGbEInt(getInstanceName(), device); + if (!regs.tctl.en()) { + txTick = false; + DPRINTF(EthernetSM, "TXS: TX disabled, stopping ticking\n"); + return; + } + + // 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() + && !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; + } - EtherInt *p = (EtherInt *)peer; - if (p) { - dev_int->setPeer(p); - p->setPeer(dev_int); + // Only support descriptor granularity + assert(regs.txdctl.gran()); + if (regs.txdctl.lwthresh() && txDescCache.descLeft() < (regs.txdctl.lwthresh() * 8)) { + DPRINTF(EthernetSM, "TXS: LWTHRESH caused posting of TXDLOW\n"); + postInterrupt(IT_TXDLOW); } - return dev_int; + if (!txPacket) { + txPacket = new EthPacketData(16384); + } + + 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"); + txTick = false; + return; + } + + + if (!(txDescCache.descUnused())) { + txDescCache.fetchDescriptors(); + DPRINTF(EthernetSM, "TXS: No descriptors available in cache, fetching and stopping ticking\n"); + txTick = false; + return; + } + + + int size; + size = txDescCache.getPacketSize(); + if (size > 0 && txFifo.avail() > size) { + DPRINTF(EthernetSM, "TXS: Reserving %d bytes in FIFO and begining " + "DMA of next packet\n", size); + txFifo.reserve(size); + txDescCache.getPacketData(txPacket); + } 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; } -REGISTER_SIM_OBJECT("IGbEInt", IGbEInt) +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 && !drainEvent; + if ((rxTick || txTick) && !tickEvent.scheduled()) { + DPRINTF(EthernetSM, "RXS: received packet into fifo, starting ticking\n"); + restartClock(); + } + + if (!rxFifo.push(pkt)) { + DPRINTF(Ethernet, "RxFIFO: Packet won't fit in fifo... dropped\n"); + postInterrupt(IT_RXO, true); + return false; + } + + return true; +} + + +void +IGbE::rxStateMachine() +{ + if (!regs.rctl.en()) { + rxTick = false; + DPRINTF(EthernetSM, "RXS: RX disabled, stopping ticking\n"); + return; + } + + // 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()) { + case 2: if (descLeft > .125 * regs.rdlen()) break; + case 1: if (descLeft > .250 * regs.rdlen()) break; + case 0: if (descLeft > .500 * regs.rdlen()) break; + DPRINTF(Ethernet, "RXS: Interrupting (RXDMT) because of descriptors left\n"); + postInterrupt(IT_RXDMT); + break; + } + + if (descLeft == 0) { + rxDescCache.writeback(0); + DPRINTF(EthernetSM, "RXS: No descriptors left in ring, forcing" + " writeback and stopping ticking\n"); + rxTick = false; + } + + // only support descriptor granulaties + assert(regs.rxdctl.gran()); + + if (regs.rxdctl.wthresh() >= rxDescCache.descUsed()) { + DPRINTF(EthernetSM, "RXS: Writing back because WTHRESH >= descUsed\n"); + if (regs.rxdctl.wthresh() < (cacheBlockSize()>>4)) + rxDescCache.writeback(regs.rxdctl.wthresh()-1); + else + rxDescCache.writeback((cacheBlockSize()-1)>>4); + } + + if ((rxDescCache.descUnused() < regs.rxdctl.pthresh()) && + ((rxDescCache.descLeft() - rxDescCache.descUnused()) > regs.rxdctl.hthresh())) { + DPRINTF(EthernetSM, "RXS: Fetching descriptors because descUnused < PTHRESH\n"); + rxDescCache.fetchDescriptors(); + } + + if (rxDescCache.descUnused() == 0) { + 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"); + return; + } + + if (rxFifo.empty()) { + DPRINTF(EthernetSM, "RXS: RxFIFO empty, stopping ticking\n"); + rxTick = false; + return; + } + + EthPacketPtr pkt; + pkt = rxFifo.front(); + + + 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; + } + + + if (etherInt->sendPacket(txFifo.front())) { + 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 +IGbE::tick() +{ + DPRINTF(EthernetSM, "IGbE: -------------- Cycle --------------\n"); + + if (rxTick) + rxStateMachine(); + + if (txTick) + txStateMachine(); + + if (txFifoTick) + txWire(); + + + if (rxTick || txTick || txFifoTick) + schedule(tickEvent, curTick + ticks(1)); +} + +void +IGbE::ethTxDone() +{ + // 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(); + txWire(); + DPRINTF(EthernetSM, "TxFIFO: Transmission complete\n"); +} + +void +IGbE::serialize(std::ostream &os) +{ + PciDev::serialize(os); + + 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); + bool txPktExists = txPacket; + SERIALIZE_SCALAR(txPktExists); + if (txPktExists) + txPacket->serialize("txpacket", os); -BEGIN_DECLARE_SIM_OBJECT_PARAMS(IGbE) + Tick rdtr_time = 0, radv_time = 0, tidv_time = 0, tadv_time = 0, + inter_time = 0; - SimObjectParam system; - SimObjectParam platform; - SimObjectParam configdata; - Param pci_bus; - Param pci_dev; - Param pci_func; - Param pio_latency; - Param config_latency; + if (rdtrEvent.scheduled()) + rdtr_time = rdtrEvent.when(); + SERIALIZE_SCALAR(rdtr_time); -END_DECLARE_SIM_OBJECT_PARAMS(IGbE) + if (radvEvent.scheduled()) + radv_time = radvEvent.when(); + SERIALIZE_SCALAR(radv_time); -BEGIN_INIT_SIM_OBJECT_PARAMS(IGbE) + if (tidvEvent.scheduled()) + tidv_time = tidvEvent.when(); + SERIALIZE_SCALAR(tidv_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") + if (tadvEvent.scheduled()) + tadv_time = tadvEvent.when(); + SERIALIZE_SCALAR(tadv_time); -END_INIT_SIM_OBJECT_PARAMS(IGbE) + if (interEvent.scheduled()) + inter_time = interEvent.when(); + SERIALIZE_SCALAR(inter_time); + nameOut(os, csprintf("%s.TxDescCache", name())); + txDescCache.serialize(os); + + nameOut(os, csprintf("%s.RxDescCache", name())); + rxDescCache.serialize(os); +} -CREATE_SIM_OBJECT(IGbE) +void +IGbE::unserialize(Checkpoint *cp, const std::string §ion) { - IGbE::Params *params = new IGbE::Params; + 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; + + 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); + + if (rdtr_time) + schedule(rdtrEvent, rdtr_time); + + if (radv_time) + schedule(radvEvent, radv_time); - 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; + if (tidv_time) + schedule(tidvEvent, tidv_time); - return new IGbE(params); + if (tadv_time) + schedule(tadvEvent, tadv_time); + + if (inter_time) + schedule(interEvent, inter_time); + + txDescCache.unserialize(cp, csprintf("%s.TxDescCache", section)); + + rxDescCache.unserialize(cp, csprintf("%s.RxDescCache", section)); } -REGISTER_SIM_OBJECT("IGbE", IGbE) +IGbE * +IGbEParams::create() +{ + return new IGbE(this); +}