i8254xgbe.cc
来自「M5,一个功能强大的多处理器系统模拟器.很多针对处理器架构,性能的研究都使用它作」· CC 代码 · 共 1,528 行 · 第 1/3 页
CC
1,528 行
/* * Copyright (c) 2006 * The Regents of The University of Michigan * All Rights Reserved * * This code is part of the M5 simulator. * * Permission is granted to use, copy, create derivative works and * redistribute this software and such derivative works for any * purpose, so long as the copyright notice above, this grant of * permission, and the disclaimer below appear in all copies made; and * so long as the name of The University of Michigan is not used in * any advertising or publicity pertaining to the use or distribution * of this software without specific, written prior authorization. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE * UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND * WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE * LIABLE FOR ANY DAMAGES, INCLUDING DIRECT, SPECIAL, INDIRECT, * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM * ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH * DAMAGES. * * Authors: Ali G. Saidi *//* @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 <algorithm>#include "base/inet.hh"#include "base/trace.hh"#include "dev/i8254xGBe.hh"#include "mem/packet.hh"#include "mem/packet_access.hh"#include "params/IGbE.hh"#include "sim/stats.hh"#include "sim/system.hh"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), 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){ 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;}TickIGbE::writeConfig(PacketPtr pkt){ int offset = pkt->getAddr() & PCI_CONFIG_SIZE; if (offset < PCI_DEVICE_SPECIFIC) PciDev::writeConfig(pkt); else panic("Device specific PCI config space not implemented.\n"); /// /// Some work may need to be done here based for the pci COMMAND bits. /// return pioDelay;}TickIGbE::read(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); // Only 32bit accesses allowed assert(pkt->getSize() == 4); DPRINTF(Ethernet, "Read device register %#X\n", daddr); pkt->allocate(); /// /// Handle read of register here /// switch (daddr) { case REG_CTRL: pkt->set<uint32_t>(regs.ctrl()); break; case REG_STATUS: pkt->set<uint32_t>(regs.sts()); break; case REG_EECD: pkt->set<uint32_t>(regs.eecd()); break; case REG_EERD: pkt->set<uint32_t>(regs.eerd()); break; case REG_CTRL_EXT: pkt->set<uint32_t>(regs.ctrl_ext()); break; case REG_MDIC: pkt->set<uint32_t>(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<uint32_t>(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<uint32_t>(regs.itr()); break; case REG_RCTL: pkt->set<uint32_t>(regs.rctl()); break; case REG_FCTTV: pkt->set<uint32_t>(regs.fcttv()); break; case REG_TCTL: pkt->set<uint32_t>(regs.tctl()); break; case REG_PBA: pkt->set<uint32_t>(regs.pba()); break; case REG_WUC: case REG_LEDCTL: pkt->set<uint32_t>(0); // We don't care, so just return 0 break; case REG_FCRTL: pkt->set<uint32_t>(regs.fcrtl()); break; case REG_FCRTH: pkt->set<uint32_t>(regs.fcrth()); break; case REG_RDBAL: pkt->set<uint32_t>(regs.rdba.rdbal()); break; case REG_RDBAH: pkt->set<uint32_t>(regs.rdba.rdbah()); break; case REG_RDLEN: pkt->set<uint32_t>(regs.rdlen()); break; case REG_RDH: pkt->set<uint32_t>(regs.rdh()); break; case REG_RDT: pkt->set<uint32_t>(regs.rdt()); break; case REG_RDTR: pkt->set<uint32_t>(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<uint32_t>(regs.radv()); break; case REG_TDBAL: pkt->set<uint32_t>(regs.tdba.tdbal()); break; case REG_TDBAH: pkt->set<uint32_t>(regs.tdba.tdbah()); break; case REG_TDLEN: pkt->set<uint32_t>(regs.tdlen()); break; case REG_TDH: pkt->set<uint32_t>(regs.tdh()); break; case REG_TDT: pkt->set<uint32_t>(regs.tdt()); break; case REG_TIDV: pkt->set<uint32_t>(regs.tidv()); break; case REG_TXDCTL: pkt->set<uint32_t>(regs.txdctl()); break; case REG_TADV: pkt->set<uint32_t>(regs.tadv()); break; case REG_RXCSUM: pkt->set<uint32_t>(regs.rxcsum()); break; case REG_MANC: pkt->set<uint32_t>(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<uint32_t>(0); }; pkt->makeAtomicResponse(); return pioDelay;}TickIGbE::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); // Only 32bit accesses allowed assert(pkt->getSize() == sizeof(uint32_t)); DPRINTF(Ethernet, "Wrote device register %#X value %#X\n", daddr, pkt->get<uint32_t>()); /// /// Handle write of register here /// uint32_t val = pkt->get<uint32_t>(); 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); // 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();
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?