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 + -
显示快捷键?