Merge zizzer:/bk/newmem

into  zeep.pool:/z/saidi/work/m5.newmem

--HG--
extra : convert_revision : fd590d464359d8ae859998a9f446b960781a5e33
This commit is contained in:
Ali Saidi 2007-03-22 18:39:51 -04:00
commit 2c47413a7a
4 changed files with 1243 additions and 243 deletions

View file

@ -35,7 +35,13 @@
* other MACs with slight modifications. * other MACs with slight modifications.
*/ */
/*
* @todo really there are multiple dma engines.. we should implement them.
*/
#include "base/inet.hh" #include "base/inet.hh"
#include "base/trace.hh"
#include "dev/i8254xGBe.hh" #include "dev/i8254xGBe.hh"
#include "mem/packet.hh" #include "mem/packet.hh"
#include "mem/packet_access.hh" #include "mem/packet_access.hh"
@ -43,32 +49,34 @@
#include "sim/stats.hh" #include "sim/stats.hh"
#include "sim/system.hh" #include "sim/system.hh"
#include <algorithm>
using namespace iGbReg; using namespace iGbReg;
using namespace Net;
IGbE::IGbE(Params *p) IGbE::IGbE(Params *p)
: PciDev(p), etherInt(NULL), useFlowControl(p->use_flow_control) : PciDev(p), etherInt(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),
rxDescCache(this, name()+".TxDesc", p->rx_desc_cache_size),
txDescCache(this, name()+".RxDesc", p->tx_desc_cache_size), clock(p->clock)
{ {
// Initialized internal registers per Intel documentation // Initialized internal registers per Intel documentation
regs.tctl(0); // All registers intialized to 0 by per register constructor
regs.rctl(0);
regs.ctrl(0);
regs.ctrl.fd(1); regs.ctrl.fd(1);
regs.ctrl.lrst(1); regs.ctrl.lrst(1);
regs.ctrl.speed(2); regs.ctrl.speed(2);
regs.ctrl.frcspd(1); regs.ctrl.frcspd(1);
regs.sts(0);
regs.sts.speed(3); // Say we're 1000Mbps regs.sts.speed(3); // Say we're 1000Mbps
regs.sts.fd(1); // full duplex regs.sts.fd(1); // full duplex
regs.eecd(0);
regs.eecd.fwe(1); regs.eecd.fwe(1);
regs.eecd.ee_type(1); regs.eecd.ee_type(1);
regs.eerd(0); regs.imr = 0;
regs.icr(0); regs.iam = 0;
regs.rctl(0); regs.rxdctl.gran(1);
regs.tctl(0); regs.rxdctl.wthresh(1);
regs.fcrtl(0);
regs.fcrth(1); regs.fcrth(1);
regs.manc(0);
regs.pba.rxa(0x30); regs.pba.rxa(0x30);
regs.pba.txa(0x10); regs.pba.txa(0x10);
@ -92,6 +100,9 @@ IGbE::IGbE(Params *p)
// Magic happy checksum value // Magic happy checksum value
flash[EEPROM_SIZE-1] = htobe((uint16_t)(EEPROM_CSUM - csum)); flash[EEPROM_SIZE-1] = htobe((uint16_t)(EEPROM_CSUM - csum));
rxFifo.clear();
txFifo.clear();
} }
@ -156,7 +167,12 @@ IGbE::read(PacketPtr pkt)
break; break;
case REG_ICR: case REG_ICR:
pkt->set<uint32_t>(regs.icr()); pkt->set<uint32_t>(regs.icr());
// handle auto setting mask from IAM if (regs.icr.int_assert())
regs.imr &= regs.iam;
if (regs.imr == 0 || (regs.icr.int_assert() && regs.ctrl_ext.iame())) {
regs.icr(0);
cpuClearInt();
}
break; break;
case REG_ITR: case REG_ITR:
pkt->set<uint32_t>(regs.itr()); pkt->set<uint32_t>(regs.itr());
@ -200,6 +216,18 @@ IGbE::read(PacketPtr pkt)
break; break;
case REG_RDTR: case REG_RDTR:
pkt->set<uint32_t>(regs.rdtr()); pkt->set<uint32_t>(regs.rdtr());
if (regs.rdtr.fpd()) {
rxDescCache.writeback(0);
postInterrupt(IT_RXT);
regs.rdtr.fpd(0);
}
if (regs.rdtr.delay()) {
Tick t = regs.rdtr.delay() * Clock::Int::ns * 1024;
if (rdtrEvent.scheduled())
rdtrEvent.reschedule(curTick + t);
else
rdtrEvent.schedule(curTick + t);
}
break; break;
case REG_RADV: case REG_RADV:
pkt->set<uint32_t>(regs.radv()); pkt->set<uint32_t>(regs.radv());
@ -271,6 +299,9 @@ IGbE::write(PacketPtr pkt)
/// ///
uint32_t val = pkt->get<uint32_t>(); uint32_t val = pkt->get<uint32_t>();
Regs::RCTL oldrctl;
Regs::TCTL oldtctl;
switch (daddr) { switch (daddr) {
case REG_CTRL: case REG_CTRL:
regs.ctrl = val; regs.ctrl = val;
@ -372,36 +403,60 @@ IGbE::write(PacketPtr pkt)
regs.mdic.r(1); regs.mdic.r(1);
break; break;
case REG_ICR: case REG_ICR:
regs.icr = val; if (regs.icr.int_assert())
// handle auto setting mask from IAM regs.imr &= regs.iam;
regs.icr = ~bits(val,30,0) & regs.icr();
// if no more bits are set clear the int_asserted bit
if (!bits(regs.icr(),31,31))
cpuClearInt();
break; break;
case REG_ITR: case REG_ITR:
regs.itr = val; regs.itr = val;
break; break;
case REG_ICS: case REG_ICS:
regs.icr = val | regs.icr(); postInterrupt((IntTypes)val);
// generate an interrupt if needed here
break; break;
case REG_IMS: case REG_IMS:
regs.imr |= val; regs.imr |= val;
// handle interrupts if needed here chkInterrupt();
break; break;
case REG_IMC: case REG_IMC:
regs.imr |= ~val; regs.imr &= ~val;
// handle interrupts if needed here chkInterrupt();
break; break;
case REG_IAM: case REG_IAM:
regs.iam = val; regs.iam = val;
break; break;
case REG_RCTL: case REG_RCTL:
oldrctl = regs.rctl;
regs.rctl = val; regs.rctl = val;
if (regs.rctl.rst()) {
rxDescCache.reset();
rxFifo.clear();
regs.rctl.rst(0);
}
if (regs.rctl.en())
rxTick = true;
if ((rxTick || txTick) && !tickEvent.scheduled())
tickEvent.schedule(curTick + cycles(1));
break; break;
case REG_FCTTV: case REG_FCTTV:
regs.fcttv = val; regs.fcttv = val;
break; break;
case REG_TCTL: case REG_TCTL:
regs.tctl = val; regs.tctl = val;
break; oldtctl = regs.tctl;
regs.tctl = val;
if (regs.tctl.en())
txTick = true;
if ((rxTick || txTick) && !tickEvent.scheduled())
tickEvent.schedule(curTick + cycles(1));
if (regs.tctl.en() && !oldtctl.en()) {
txDescCache.reset();
}
break;
case REG_PBA: case REG_PBA:
regs.pba.rxa(val); regs.pba.rxa(val);
regs.pba.txa(64 - regs.pba.rxa()); regs.pba.txa(64 - regs.pba.rxa());
@ -424,18 +479,25 @@ IGbE::write(PacketPtr pkt)
break; break;
case REG_RDBAL: case REG_RDBAL:
regs.rdba.rdbal( val & ~mask(4)); regs.rdba.rdbal( val & ~mask(4));
rxDescCache.areaChanged();
break; break;
case REG_RDBAH: case REG_RDBAH:
regs.rdba.rdbah(val); regs.rdba.rdbah(val);
rxDescCache.areaChanged();
break; break;
case REG_RDLEN: case REG_RDLEN:
regs.rdlen = val & ~mask(7); regs.rdlen = val & ~mask(7);
rxDescCache.areaChanged();
break; break;
case REG_RDH: case REG_RDH:
regs.rdh = val; regs.rdh = val;
rxDescCache.areaChanged();
break; break;
case REG_RDT: case REG_RDT:
regs.rdt = val; regs.rdt = val;
rxTick = true;
if ((rxTick || txTick) && !tickEvent.scheduled())
tickEvent.schedule(curTick + cycles(1));
break; break;
case REG_RDTR: case REG_RDTR:
regs.rdtr = val; regs.rdtr = val;
@ -445,18 +507,25 @@ IGbE::write(PacketPtr pkt)
break; break;
case REG_TDBAL: case REG_TDBAL:
regs.tdba.tdbal( val & ~mask(4)); regs.tdba.tdbal( val & ~mask(4));
txDescCache.areaChanged();
break; break;
case REG_TDBAH: case REG_TDBAH:
regs.tdba.tdbah(val); regs.tdba.tdbah(val);
txDescCache.areaChanged();
break; break;
case REG_TDLEN: case REG_TDLEN:
regs.tdlen = val & ~mask(7); regs.tdlen = val & ~mask(7);
txDescCache.areaChanged();
break; break;
case REG_TDH: case REG_TDH:
regs.tdh = val; regs.tdh = val;
txDescCache.areaChanged();
break; break;
case REG_TDT: case REG_TDT:
regs.tdt = val; regs.tdt = val;
txTick = true;
if ((rxTick || txTick) && !tickEvent.scheduled())
tickEvent.schedule(curTick + cycles(1));
break; break;
case REG_TIDV: case REG_TIDV:
regs.tidv = val; regs.tidv = val;
@ -484,18 +553,585 @@ IGbE::write(PacketPtr pkt)
return pioDelay; return pioDelay;
} }
void
IGbE::postInterrupt(IntTypes t, bool now)
{
// Interrupt is already pending
if (t & regs.icr())
return;
if (regs.icr() & regs.imr)
{
// already in an interrupt state, set new int and done
regs.icr = regs.icr() | t;
} else {
regs.icr = regs.icr() | t;
if (regs.itr.interval() == 0 || now) {
if (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());
}
}
}
void
IGbE::cpuPostInt()
{
if (rdtrEvent.scheduled()) {
regs.icr.rxt0(1);
rdtrEvent.deschedule();
}
if (radvEvent.scheduled()) {
regs.icr.rxt0(1);
radvEvent.deschedule();
}
if (tadvEvent.scheduled()) {
regs.icr.txdw(1);
tadvEvent.deschedule();
}
if (tidvEvent.scheduled()) {
regs.icr.txdw(1);
tidvEvent.deschedule();
}
regs.icr.int_assert(1);
DPRINTF(EthernetIntr, "EINT: Posting interrupt to CPU now. Vector %#x\n",
regs.icr());
intrPost();
}
void
IGbE::cpuClearInt()
{
regs.icr.int_assert(0);
DPRINTF(EthernetIntr, "EINT: Clearing interrupt to CPU now. Vector %#x\n",
regs.icr());
intrClear();
}
void
IGbE::chkInterrupt()
{
// Check if we need to clear the cpu interrupt
if (!(regs.icr() & regs.imr))
cpuClearInt();
// Check if we need to set the cpu interupt
postInterrupt(IT_NONE);
}
IGbE::RxDescCache::RxDescCache(IGbE *i, const std::string n, int s)
: DescCache<RxDesc>(i, n, s), pktDone(false), pktEvent(this)
{
}
bool bool
IGbE::ethRxPkt(EthPacketPtr packet) IGbE::RxDescCache::writePacket(EthPacketPtr packet)
{ {
panic("Need to implemenet\n"); // We shouldn't have to deal with any of these yet
assert(packet->length < igbe->regs.rctl.descSize());
if (!unusedCache.size())
return false;
pktPtr = packet;
igbe->dmaWrite(unusedCache.front()->buf, packet->length, &pktEvent, packet->data);
return true;
}
void
IGbE::RxDescCache::pktComplete()
{
assert(unusedCache.size());
RxDesc *desc;
desc = unusedCache.front();
desc->len = pktPtr->length;
// no support for anything but starting at 0
assert(igbe->regs.rxcsum.pcss() == 0);
DPRINTF(EthernetDesc, "RxDesc: Packet written to memory updating Descriptor\n");
uint8_t status = RXDS_DD | RXDS_EOP;
uint8_t err = 0;
IpPtr ip(pktPtr);
if (ip) {
if (igbe->regs.rxcsum.ipofld()) {
DPRINTF(EthernetDesc, "RxDesc: Checking IP checksum\n");
status |= RXDS_IPCS;
desc->csum = cksum(ip);
if (cksum(ip) != 0) {
err |= RXDE_IPE;
DPRINTF(EthernetDesc, "RxDesc: Checksum is bad!!\n");
}
}
TcpPtr tcp(ip);
if (tcp && igbe->regs.rxcsum.tuofld()) {
DPRINTF(EthernetDesc, "RxDesc: Checking TCP checksum\n");
status |= RXDS_TCPCS;
desc->csum = cksum(tcp);
if (cksum(tcp) != 0) {
DPRINTF(EthernetDesc, "RxDesc: Checksum is bad!!\n");
err |= RXDE_TCPE;
}
}
UdpPtr udp(ip);
if (udp && igbe->regs.rxcsum.tuofld()) {
DPRINTF(EthernetDesc, "RxDesc: Checking UDP checksum\n");
status |= RXDS_UDPCS;
desc->csum = cksum(udp);
if (cksum(tcp) != 0) {
DPRINTF(EthernetDesc, "RxDesc: Checksum is bad!!\n");
err |= RXDE_TCPE;
}
}
} // if ip
desc->status = status;
desc->errors = 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());
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());
}
if (igbe->regs.radv.idv() && igbe->regs.rdtr.delay()) {
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 the packet is small enough, interrupt appropriately
if (pktPtr->length <= igbe->regs.rsrpd.idv())
igbe->postInterrupt(IT_SRPD);
DPRINTF(EthernetDesc, "RxDesc: Processing of this descriptor complete\n");
unusedCache.pop_front();
usedCache.push_back(desc);
pktPtr = NULL;
enableSm();
pktDone = true;
}
void
IGbE::RxDescCache::enableSm()
{
igbe->rxTick = true;
if ((igbe->rxTick || igbe->txTick) && !igbe->tickEvent.scheduled())
igbe->tickEvent.schedule((curTick/igbe->cycles(1)) * igbe->cycles(1) +
igbe->cycles(1));
}
bool
IGbE::RxDescCache::packetDone()
{
if (pktDone) {
pktDone = false;
return true;
}
return false;
}
///////////////////////////////////// IGbE::TxDesc /////////////////////////////////
IGbE::TxDescCache::TxDescCache(IGbE *i, const std::string n, int s)
: DescCache<TxDesc>(i,n, s), pktDone(false), isTcp(false), pktWaiting(false),
pktEvent(this)
{
}
int
IGbE::TxDescCache::getPacketSize()
{
assert(unusedCache.size());
TxDesc *desc;
DPRINTF(EthernetDesc, "TxDesc: Starting processing of descriptor\n");
while (unusedCache.size() && TxdOp::isContext(unusedCache.front())) {
DPRINTF(EthernetDesc, "TxDesc: Got context descriptor type... skipping\n");
// I think we can just ignore these for now?
desc = unusedCache.front();
// 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, "TxDesc: Next TX packet is %d bytes\n",
TxdOp::getLen(unusedCache.front()));
return TxdOp::getLen(unusedCache.front());
}
void
IGbE::TxDescCache::getPacketData(EthPacketPtr p)
{
assert(unusedCache.size());
TxDesc *desc;
desc = unusedCache.front();
assert((TxdOp::isLegacy(desc) || TxdOp::isData(desc)) && TxdOp::getLen(desc));
pktPtr = p;
pktWaiting = true;
DPRINTF(EthernetDesc, "TxDesc: Starting DMA of packet\n");
igbe->dmaRead(TxdOp::getBuf(desc), TxdOp::getLen(desc), &pktEvent, p->data);
}
void
IGbE::TxDescCache::pktComplete()
{
TxDesc *desc;
assert(unusedCache.size());
assert(pktPtr);
DPRINTF(EthernetDesc, "TxDesc: DMA of packet complete\n");
desc = unusedCache.front();
assert((TxdOp::isLegacy(desc) || TxdOp::isData(desc)) && 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);
// 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");
IpPtr ip(pktPtr);
if (TxdOp::ixsm(desc)) {
ip->sum(0);
ip->sum(cksum(ip));
DPRINTF(EthernetDesc, "TxDesc: 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::ide(desc)) {
// Deal with the rx timer interrupts
DPRINTF(EthernetDesc, "TxDesc: 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());
}
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());
}
}
unusedCache.pop_front();
usedCache.push_back(desc);
pktDone = true;
pktWaiting = false;
pktPtr = NULL;
DPRINTF(EthernetDesc, "TxDesc: Descriptor Done\n");
}
bool
IGbE::TxDescCache::packetAvailable()
{
if (pktDone) {
pktDone = false;
return true;
}
return false;
}
void
IGbE::TxDescCache::enableSm()
{
igbe->txTick = true;
if ((igbe->rxTick || igbe->txTick) && !igbe->tickEvent.scheduled())
igbe->tickEvent.schedule((curTick/igbe->cycles(1)) * igbe->cycles(1) +
igbe->cycles(1));
}
///////////////////////////////////// IGbE /////////////////////////////////
void
IGbE::txStateMachine()
{
if (!regs.tctl.en()) {
txTick = false;
DPRINTF(EthernetSM, "TXS: RX disabled, stopping ticking\n");
return;
}
if (txPacket && txDescCache.packetAvailable()) {
bool success;
DPRINTF(EthernetSM, "TXS: packet placed in TX FIFO\n");
success = txFifo.push(txPacket);
assert(success);
txPacket = NULL;
return;
}
// 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);
}
if (!txPacket) {
txPacket = new EthPacketData(16384);
}
if (!txDescCache.packetWaiting()) {
if (txDescCache.descLeft() == 0) {
DPRINTF(EthernetSM, "TXS: No descriptors left in ring, forcing writeback\n");
txDescCache.writeback(0);
DPRINTF(EthernetSM, "TXS: No descriptors left, stopping ticking\n");
txTick = false;
}
if (!(txDescCache.descUnused())) {
DPRINTF(EthernetSM, "TXS: No descriptors available in cache, stopping ticking\n");
txTick = false;
DPRINTF(EthernetSM, "TXS: No descriptors left, fetching\n");
txDescCache.fetchDescriptors();
return;
}
int size;
size = txDescCache.getPacketSize();
if (size > 0 && rxFifo.avail() > size) {
DPRINTF(EthernetSM, "TXS: Reserving %d bytes in FIFO and begining DMA of next packet\n");
rxFifo.reserve(size);
txDescCache.getPacketData(txPacket);
} else {
DPRINTF(EthernetSM, "TXS: No packets to get, writing back used descriptors\n");
txDescCache.writeback(0);
}
return;
}
}
bool
IGbE::ethRxPkt(EthPacketPtr pkt)
{
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;
if ((rxTick || txTick) && !tickEvent.scheduled()) {
DPRINTF(EthernetSM, "RXS: received packet into fifo, starting ticking\n");
tickEvent.schedule(curTick/cycles(1) + cycles(1));
}
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()) {
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) {
DPRINTF(EthernetSM, "RXS: No descriptors left in ring, forcing writeback\n");
rxDescCache.writeback(0);
DPRINTF(EthernetSM, "RXS: No descriptors left, 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");
rxDescCache.writeback(cacheBlockSize()-1);
}
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) {
DPRINTF(EthernetSM, "RXS: No descriptors available in cache, stopping ticking\n");
rxTick = false;
DPRINTF(EthernetSM, "RXS: Fetching descriptors because none available\n");
rxDescCache.fetchDescriptors();
}
return;
}
if (!rxDescCache.descUnused()) {
DPRINTF(EthernetSM, "RXS: No descriptors available in cache, stopping ticking\n");
rxTick = false;
DPRINTF(EthernetSM, "RXS: No descriptors available, fetching\n");
rxDescCache.fetchDescriptors();
return;
}
if (rxFifo.empty()) {
DPRINTF(EthernetSM, "RXS: RxFIFO empty, stopping ticking\n");
rxTick = false;
return;
}
EthPacketPtr pkt;
pkt = rxFifo.front();
DPRINTF(EthernetSM, "RXS: Writing packet into memory\n");
if (!rxDescCache.writePacket(pkt)) {
return;
}
DPRINTF(EthernetSM, "RXS: Removing packet from FIFO\n");
rxFifo.pop();
DPRINTF(EthernetSM, "RXS: stopping ticking until packet DMA completes\n");
rxTick = false;
}
void
IGbE::txWire()
{
if (txFifo.empty()) {
return;
}
txTick = true;
if (etherInt->sendPacket(txFifo.front())) {
DPRINTF(Ethernet, "TxFIFO: Successful transmit, bytes in fifo: %d\n",
txFifo.avail());
txFifo.pop();
}
if (txFifo.empty()) {
postInterrupt(IT_TXQE);
DPRINTF(Ethernet, "TxFIFO: Empty, posting interruppt\n");
}
}
void
IGbE::tick()
{
DPRINTF(EthernetSM, "IGbE: -------------- Cycle -------------- ");
if (rxTick)
rxStateMachine();
if (txTick) {
txStateMachine();
txWire();
}
if (rxTick || txTick)
tickEvent.schedule(curTick + cycles(1));
}
void void
IGbE::ethTxDone() IGbE::ethTxDone()
{ {
panic("Need to implemenet\n"); // restart the state machines if they are stopped
txTick = true;
if ((rxTick || txTick) && !tickEvent.scheduled())
tickEvent.schedule(curTick/cycles(1) + cycles(1));
DPRINTF(Ethernet, "TxFIFO: Transmission complete\n");
} }
void void

View file

@ -35,6 +35,9 @@
#ifndef __DEV_I8254XGBE_HH__ #ifndef __DEV_I8254XGBE_HH__
#define __DEV_I8254XGBE_HH__ #define __DEV_I8254XGBE_HH__
#include <deque>
#include <string>
#include "base/inet.hh" #include "base/inet.hh"
#include "base/statistics.hh" #include "base/statistics.hh"
#include "dev/etherint.hh" #include "dev/etherint.hh"
@ -50,24 +53,434 @@ class IGbE : public PciDev
{ {
private: private:
IGbEInt *etherInt; IGbEInt *etherInt;
// device registers
iGbReg::Regs regs; iGbReg::Regs regs;
// eeprom data, status and control bits
int eeOpBits, eeAddrBits, eeDataBits; int eeOpBits, eeAddrBits, eeDataBits;
uint8_t eeOpcode, eeAddr; uint8_t eeOpcode, eeAddr;
bool useFlowControl;
uint16_t flash[iGbReg::EEPROM_SIZE]; uint16_t flash[iGbReg::EEPROM_SIZE];
// cached parameters from params struct
Tick tickRate;
bool useFlowControl;
// packet fifos
PacketFifo rxFifo;
PacketFifo txFifo;
// Packet that we are currently putting into the txFifo
EthPacketPtr txPacket;
// Should to Rx/Tx State machine tick?
bool rxTick;
bool txTick;
// Event and function to deal with RDTR timer expiring
void rdtrProcess() { postInterrupt(iGbReg::IT_RXDMT, true); }
//friend class EventWrapper<IGbE, &IGbE::rdtrProcess>;
EventWrapper<IGbE, &IGbE::rdtrProcess> rdtrEvent;
// Event and function to deal with RADV timer expiring
void radvProcess() { postInterrupt(iGbReg::IT_RXDMT, true); }
//friend class EventWrapper<IGbE, &IGbE::radvProcess>;
EventWrapper<IGbE, &IGbE::radvProcess> radvEvent;
// Event and function to deal with TADV timer expiring
void tadvProcess() { postInterrupt(iGbReg::IT_TXDW, true); }
//friend class EventWrapper<IGbE, &IGbE::tadvProcess>;
EventWrapper<IGbE, &IGbE::tadvProcess> tadvEvent;
// Event and function to deal with TIDV timer expiring
void tidvProcess() { postInterrupt(iGbReg::IT_TXDW, true); };
//friend class EventWrapper<IGbE, &IGbE::tidvProcess>;
EventWrapper<IGbE, &IGbE::tidvProcess> tidvEvent;
// Main event to tick the device
void tick();
//friend class EventWrapper<IGbE, &IGbE::tick>;
EventWrapper<IGbE, &IGbE::tick> tickEvent;
void rxStateMachine();
void txStateMachine();
void txWire();
/** Write an interrupt into the interrupt pending register and check mask
* and interrupt limit timer before sending interrupt to CPU
* @param t the type of interrupt we are posting
* @param now should we ignore the interrupt limiting timer
*/
void postInterrupt(iGbReg::IntTypes t, bool now = false);
/** Check and see if changes to the mask register have caused an interrupt
* to need to be sent or perhaps removed an interrupt cause.
*/
void chkInterrupt();
/** Send an interrupt to the cpu
*/
void cpuPostInt();
// Event to moderate interrupts
EventWrapper<IGbE, &IGbE::cpuPostInt> interEvent;
/** Clear the interupt line to the cpu
*/
void cpuClearInt();
Tick intClock() { return Clock::Int::ns * 1024; }
template<class T>
class DescCache
{
protected:
virtual Addr descBase() const = 0;
virtual long descHead() const = 0;
virtual long descTail() const = 0;
virtual long descLen() const = 0;
virtual void updateHead(long h) = 0;
virtual void enableSm() = 0;
std::deque<T*> usedCache;
std::deque<T*> unusedCache;
T *fetchBuf;
T *wbBuf;
// Pointer to the device we cache for
IGbE *igbe;
// Name of this descriptor cache
std::string _name;
// How far we've cached
int cachePnt;
// The size of the descriptor cache
int size;
// How many descriptors we are currently fetching
int curFetching;
// How many descriptors we are currently writing back
int wbOut;
// if the we wrote back to the end of the descriptor ring and are going
// to have to wrap and write more
bool moreToWb;
// What the alignment is of the next descriptor writeback
Addr wbAlignment;
/** The packet that is currently being dmad to memory if any
*/
EthPacketPtr pktPtr;
public:
DescCache(IGbE *i, const std::string n, int s)
: igbe(i), _name(n), cachePnt(0), size(s), curFetching(0), wbOut(0),
pktPtr(NULL), fetchEvent(this), wbEvent(this)
{
fetchBuf = new T[size];
wbBuf = new T[size];
}
virtual ~DescCache()
{
reset();
}
std::string name() { return _name; }
/** If the address/len/head change when we've got descriptors that are
* dirty that is very bad. This function checks that we don't and if we
* do panics.
*/
void areaChanged()
{
if (usedCache.size() > 0 || unusedCache.size() > 0)
panic("Descriptor Address, Length or Head changed. Bad\n");
}
void writeback(Addr aMask)
{
int curHead = descHead();
int max_to_wb = usedCache.size() + curHead;
DPRINTF(EthernetDesc, "Writing back descriptors head: %d tail: "
"%d len: %d cachePnt: %d max_to_wb: %d descleft: %d\n",
curHead, descTail(), descLen(), cachePnt, max_to_wb,
descLeft());
// Check if this writeback is less restrictive that the previous
// and if so setup another one immediately following it
if (wbOut && (aMask < wbAlignment)) {
moreToWb = true;
wbAlignment = aMask;
DPRINTF(EthernetDesc, "Writing back already in process, returning\n");
return;
}
moreToWb = false;
wbAlignment = aMask;
if (max_to_wb > descLen()) {
max_to_wb = descLen() - curHead;
moreToWb = true;
// this is by definition aligned correctly
} else if (aMask != 0) {
// align the wb point to the mask
max_to_wb = max_to_wb & ~(aMask>>4);
}
DPRINTF(EthernetDesc, "Writing back %d descriptors\n", max_to_wb);
if (max_to_wb <= 0 || wbOut)
return;
wbOut = max_to_wb - curHead;
for (int x = 0; x < wbOut; x++)
memcpy(&wbBuf[x], usedCache[x], sizeof(T));
for (int x = 0; x < wbOut; x++) {
assert(usedCache.size());
delete usedCache[0];
usedCache.pop_front();
};
igbe->dmaWrite(descBase() + curHead * sizeof(T), wbOut * sizeof(T),
&wbEvent, (uint8_t*)wbBuf);
}
/** Fetch a chunk of descriptors into the descriptor cache.
* Calls fetchComplete when the memory system returns the data
*/
void fetchDescriptors()
{
size_t max_to_fetch = cachePnt - descTail();
if (max_to_fetch < 0)
max_to_fetch = descLen() - cachePnt;
max_to_fetch = std::min(max_to_fetch, (size - usedCache.size() -
unusedCache.size()));
DPRINTF(EthernetDesc, "Fetching descriptors head: %d tail: "
"%d len: %d cachePnt: %d max_to_wb: %d descleft: %d\n",
descHead(), descTail(), descLen(), cachePnt,
max_to_fetch, descLeft());
// Nothing to do
if (max_to_fetch == 0 || curFetching)
return;
// So we don't have two descriptor fetches going on at once
curFetching = max_to_fetch;
igbe->dmaRead(descBase() + cachePnt * sizeof(T),
curFetching * sizeof(T), &fetchEvent, (uint8_t*)fetchBuf);
}
/** Called by event when dma to read descriptors is completed
*/
void fetchComplete()
{
T *newDesc;
for (int x = 0; x < curFetching; x++) {
newDesc = new T;
memcpy(newDesc, &fetchBuf[x], sizeof(T));
unusedCache.push_back(newDesc);
}
#ifndef NDEBUG
int oldCp = cachePnt;
#endif
cachePnt += curFetching;
if (cachePnt > descLen())
cachePnt -= descLen();
DPRINTF(EthernetDesc, "Fetching complete cachePnt %d -> %d\n",
oldCp, cachePnt);
enableSm();
}
EventWrapper<DescCache, &DescCache::fetchComplete> fetchEvent;
/** Called by event when dma to writeback descriptors is completed
*/
void wbComplete()
{
long curHead = descHead();
#ifndef NDEBUG
long oldHead = curHead;
#endif
curHead += wbOut;
wbOut = 0;
if (curHead > descLen())
curHead = 0;
// Update the head
updateHead(curHead);
DPRINTF(EthernetDesc, "Writeback complete cachePnt %d -> %d\n",
oldHead, curHead);
// If we still have more to wb, call wb now
if (moreToWb) {
DPRINTF(EthernetDesc, "Writeback has more todo\n");
writeback(wbAlignment);
}
}
EventWrapper<DescCache, &DescCache::wbComplete> wbEvent;
/* Return the number of descriptors left in the ring, so the device has
* a way to figure out if it needs to interrupt.
*/
int descLeft() const
{
int left = unusedCache.size();
if (cachePnt - descTail() >= 0)
left += (cachePnt - descTail());
else
left += (descLen() - cachePnt);
return left;
}
/* Return the number of descriptors used and not written back.
*/
int descUsed() const { return usedCache.size(); }
/* Return the number of cache unused descriptors we have. */
int descUnused() const {return unusedCache.size(); }
/* Get into a state where the descriptor address/head/etc colud be
* changed */
void reset()
{
DPRINTF(EthernetDesc, "Reseting descriptor cache\n");
for (int x = 0; x < usedCache.size(); x++)
delete usedCache[x];
for (int x = 0; x < unusedCache.size(); x++)
delete unusedCache[x];
usedCache.clear();
unusedCache.clear();
}
};
class RxDescCache : public DescCache<iGbReg::RxDesc>
{
protected:
virtual Addr descBase() const { return igbe->regs.rdba(); }
virtual long descHead() const { return igbe->regs.rdh(); }
virtual long descLen() const { return igbe->regs.rdlen() >> 4; }
virtual long descTail() const { return igbe->regs.rdt(); }
virtual void updateHead(long h) { igbe->regs.rdh(h); }
virtual void enableSm();
bool pktDone;
public:
RxDescCache(IGbE *i, std::string n, int s);
/** Write the given packet into the buffer(s) pointed to by the
* descriptor and update the book keeping. Should only be called when
* there are no dma's pending.
* @param packet ethernet packet to write
* @return if the packet could be written (there was a free descriptor)
*/
bool writePacket(EthPacketPtr packet);
/** Called by event when dma to write packet is completed
*/
void pktComplete();
/** Check if the dma on the packet has completed.
*/
bool packetDone();
EventWrapper<RxDescCache, &RxDescCache::pktComplete> pktEvent;
};
friend class RxDescCache;
RxDescCache rxDescCache;
class TxDescCache : public DescCache<iGbReg::TxDesc>
{
protected:
virtual Addr descBase() const { return igbe->regs.tdba(); }
virtual long descHead() const { return igbe->regs.tdh(); }
virtual long descTail() const { return igbe->regs.tdt(); }
virtual long descLen() const { return igbe->regs.tdlen() >> 4; }
virtual void updateHead(long h) { igbe->regs.tdh(h); }
virtual void enableSm();
bool pktDone;
bool isTcp;
bool pktWaiting;
public:
TxDescCache(IGbE *i, std::string n, int s);
/** Tell the cache to DMA a packet from main memory into its buffer and
* return the size the of the packet to reserve space in tx fifo.
* @return size of the packet
*/
int getPacketSize();
void getPacketData(EthPacketPtr p);
/** Ask if the packet has been transfered so the state machine can give
* it to the fifo.
* @return packet available in descriptor cache
*/
bool packetAvailable();
/** Ask if we are still waiting for the packet to be transfered.
* @return packet still in transit.
*/
bool packetWaiting() { return pktWaiting; }
/** Called by event when dma to write packet is completed
*/
void pktComplete();
EventWrapper<TxDescCache, &TxDescCache::pktComplete> pktEvent;
};
friend class TxDescCache;
TxDescCache txDescCache;
public: public:
struct Params : public PciDev::Params struct Params : public PciDev::Params
{ {
bool use_flow_control; bool use_flow_control;
int rx_fifo_size;
int tx_fifo_size;
int rx_desc_cache_size;
int tx_desc_cache_size;
Tick clock;
}; };
IGbE(Params *params); IGbE(Params *params);
~IGbE() {;} ~IGbE() {;}
Tick clock;
inline Tick cycles(int numCycles) const { return numCycles * clock; }
virtual Tick read(PacketPtr pkt); virtual Tick read(PacketPtr pkt);
virtual Tick write(PacketPtr pkt); virtual Tick write(PacketPtr pkt);
@ -78,6 +491,7 @@ class IGbE : public PciDev
void setEthInt(IGbEInt *i) { assert(!etherInt); etherInt = i; } void setEthInt(IGbEInt *i) { assert(!etherInt); etherInt = i; }
const Params *params() const {return (const Params *)_params; } const Params *params() const {return (const Params *)_params; }
virtual void serialize(std::ostream &os); virtual void serialize(std::ostream &os);

View file

@ -35,59 +35,58 @@
namespace iGbReg { namespace iGbReg {
const uint32_t REG_CTRL = 0x00000; //*
const uint32_t REG_STATUS = 0x00008; //* // Registers used by the Intel GbE NIC
const uint32_t REG_EECD = 0x00010; //* const uint32_t REG_CTRL = 0x00000;
const uint32_t REG_EERD = 0x00014; //* const uint32_t REG_STATUS = 0x00008;
const uint32_t REG_CTRL_EXT = 0x00018; //*- const uint32_t REG_EECD = 0x00010;
const uint32_t REG_MDIC = 0x00020; //* const uint32_t REG_EERD = 0x00014;
const uint32_t REG_FCAL = 0x00028; //* const uint32_t REG_CTRL_EXT = 0x00018;
const uint32_t REG_FCAH = 0x0002C; //* const uint32_t REG_MDIC = 0x00020;
const uint32_t REG_FCT = 0x00030; //* const uint32_t REG_FCAL = 0x00028;
const uint32_t REG_VET = 0x00038; //* const uint32_t REG_FCAH = 0x0002C;
const uint32_t REG_PBA = 0x01000; //* const uint32_t REG_FCT = 0x00030;
const uint32_t REG_ICR = 0x000C0; //* const uint32_t REG_VET = 0x00038;
const uint32_t REG_ITR = 0x000C4; //* const uint32_t REG_PBA = 0x01000;
const uint32_t REG_ICS = 0x000C8; //* const uint32_t REG_ICR = 0x000C0;
const uint32_t REG_IMS = 0x000D0; //* const uint32_t REG_ITR = 0x000C4;
const uint32_t REG_IMC = 0x000D8; //* const uint32_t REG_ICS = 0x000C8;
const uint32_t REG_IAM = 0x000E0; //* const uint32_t REG_IMS = 0x000D0;
const uint32_t REG_RCTL = 0x00100; //* const uint32_t REG_IMC = 0x000D8;
const uint32_t REG_FCTTV = 0x00170; //* const uint32_t REG_IAM = 0x000E0;
const uint32_t REG_TIPG = 0x00410; //* const uint32_t REG_RCTL = 0x00100;
const uint32_t REG_AIFS = 0x00458; //* const uint32_t REG_FCTTV = 0x00170;
const uint32_t REG_LEDCTL = 0x00e00; //* const uint32_t REG_TIPG = 0x00410;
const uint32_t REG_FCRTL = 0x02160; //* const uint32_t REG_AIFS = 0x00458;
const uint32_t REG_FCRTH = 0x02168; //* const uint32_t REG_LEDCTL = 0x00e00;
const uint32_t REG_RDBAL = 0x02800; //*- const uint32_t REG_FCRTL = 0x02160;
const uint32_t REG_RDBAH = 0x02804; //*- const uint32_t REG_FCRTH = 0x02168;
const uint32_t REG_RDLEN = 0x02808; //*- const uint32_t REG_RDBAL = 0x02800;
const uint32_t REG_RDH = 0x02810; //*- const uint32_t REG_RDBAH = 0x02804;
const uint32_t REG_RDT = 0x02818; //*- const uint32_t REG_RDLEN = 0x02808;
const uint32_t REG_RDTR = 0x02820; //*- const uint32_t REG_RDH = 0x02810;
const uint32_t REG_RXDCTL = 0x02828; //* const uint32_t REG_RDT = 0x02818;
const uint32_t REG_RADV = 0x0282C; //*- const uint32_t REG_RDTR = 0x02820;
const uint32_t REG_RSRPD = 0x02C00; const uint32_t REG_RXDCTL = 0x02828;
const uint32_t REG_TCTL = 0x00400; //* const uint32_t REG_RADV = 0x0282C;
const uint32_t REG_TDBAL = 0x03800; //* const uint32_t REG_TCTL = 0x00400;
const uint32_t REG_TDBAH = 0x03804; //* const uint32_t REG_TDBAL = 0x03800;
const uint32_t REG_TDLEN = 0x03808; //* const uint32_t REG_TDBAH = 0x03804;
const uint32_t REG_TDH = 0x03810; //* const uint32_t REG_TDLEN = 0x03808;
const uint32_t REG_TDT = 0x03818; //* const uint32_t REG_TDH = 0x03810;
const uint32_t REG_TIDV = 0x03820; //* const uint32_t REG_TDT = 0x03818;
const uint32_t REG_TXDMAC = 0x03000; const uint32_t REG_TIDV = 0x03820;
const uint32_t REG_TXDCTL = 0x03828; //* const uint32_t REG_TXDCTL = 0x03828;
const uint32_t REG_TADV = 0x0382C; //* const uint32_t REG_TADV = 0x0382C;
const uint32_t REG_TSPMT = 0x03830;
const uint32_t REG_CRCERRS = 0x04000; const uint32_t REG_CRCERRS = 0x04000;
const uint32_t REG_RXCSUM = 0x05000; //*- const uint32_t REG_RXCSUM = 0x05000;
const uint32_t REG_MTA = 0x05200; const uint32_t REG_MTA = 0x05200;
const uint32_t REG_RAL = 0x05400; const uint32_t REG_RAL = 0x05400;
const uint32_t REG_RAH = 0x05404; const uint32_t REG_RAH = 0x05404;
const uint32_t REG_VFTA = 0x05600; const uint32_t REG_VFTA = 0x05600;
const uint32_t REG_WUC = 0x05800;//* const uint32_t REG_WUC = 0x05800;
const uint32_t REG_MANC = 0x05820;//* const uint32_t REG_MANC = 0x05820;
const uint8_t EEPROM_READ_OPCODE_SPI = 0x03; const uint8_t EEPROM_READ_OPCODE_SPI = 0x03;
const uint8_t EEPROM_RDSR_OPCODE_SPI = 0x05; const uint8_t EEPROM_RDSR_OPCODE_SPI = 0x05;
@ -99,6 +98,8 @@ const uint8_t RCV_ADDRESS_TABLE_SIZE = 16;
const uint8_t MULTICAST_TABLE_SIZE = 128; const uint8_t MULTICAST_TABLE_SIZE = 128;
const uint32_t STATS_REGS_SIZE = 0x124; const uint32_t STATS_REGS_SIZE = 0x124;
// Registers in that are accessed in the PHY
const uint8_t PHY_PSTATUS = 0x1; const uint8_t PHY_PSTATUS = 0x1;
const uint8_t PHY_PID = 0x2; const uint8_t PHY_PID = 0x2;
const uint8_t PHY_EPID = 0x3; const uint8_t PHY_EPID = 0x3;
@ -106,179 +107,102 @@ const uint8_t PHY_GSTATUS = 10;
const uint8_t PHY_EPSTATUS = 15; const uint8_t PHY_EPSTATUS = 15;
const uint8_t PHY_AGC = 18; const uint8_t PHY_AGC = 18;
// Receive Descriptor Status Flags
const uint8_t RXDS_PIF = 0x80;
const uint8_t RXDS_IPCS = 0x40;
const uint8_t RXDS_TCPCS = 0x20;
const uint8_t RXDS_UDPCS = 0x10;
const uint8_t RXDS_VP = 0x08;
const uint8_t RXDS_IXSM = 0x04;
const uint8_t RXDS_EOP = 0x02;
const uint8_t RXDS_DD = 0x01;
// Receive Descriptor Error Flags
const uint8_t RXDE_RXE = 0x80;
const uint8_t RXDE_IPE = 0x40;
const uint8_t RXDE_TCPE = 0x20;
const uint8_t RXDE_SEQ = 0x04;
const uint8_t RXDE_SE = 0x02;
const uint8_t RXDE_CE = 0x01;
// Interrupt types
enum IntTypes
{
IT_NONE = 0x00000, //dummy value
IT_TXDW = 0x00001,
IT_TXQE = 0x00002,
IT_LSC = 0x00004,
IT_RXSEQ = 0x00008,
IT_RXDMT = 0x00010,
IT_RXO = 0x00040,
IT_RXT = 0x00080,
IT_MADC = 0x00200,
IT_RXCFG = 0x00400,
IT_GPI0 = 0x02000,
IT_GPI1 = 0x04000,
IT_TXDLOW = 0x08000,
IT_SRPD = 0x10000,
IT_ACK = 0x20000
};
// Receive Descriptor struct
struct RxDesc { struct RxDesc {
Addr buf; Addr buf;
uint16_t len; uint16_t len;
uint16_t csum; uint16_t csum;
union { uint8_t status;
uint8_t status; uint8_t errors;
struct { // these may be in the worng order uint16_t vlan;
uint8_t dd:1; // descriptor done (hw is done when 1)
uint8_t eop:1; // end of packet
uint8_t xism:1; // ignore checksum
uint8_t vp:1; // packet is vlan packet
uint8_t rsv:1; // reserved
uint8_t tcpcs:1; // TCP checksum done
uint8_t ipcs:1; // IP checksum done
uint8_t pif:1; // passed in-exact filter
} st;
};
union {
uint8_t errors;
struct {
uint8_t ce:1; // crc error or alignment error
uint8_t se:1; // symbol error
uint8_t seq:1; // sequence error
uint8_t rsv:1; // reserved
uint8_t cxe:1; // carrier extension error
uint8_t tcpe:1; // tcp checksum error
uint8_t ipe:1; // ip checksum error
uint8_t rxe:1; // PX data error
} er;
};
union {
uint16_t special;
struct {
uint16_t vlan:12; //vlan id
uint16_t cfi:1; // canocial form id
uint16_t pri:3; // user priority
} sp;
};
}; };
union TxDesc { struct TxDesc {
uint8_t data[16]; uint64_t d1;
struct { uint64_t d2;
Addr buf;
uint16_t len;
uint8_t cso;
union {
uint8_t command;
struct {
uint8_t eop:1; // end of packet
uint8_t ifcs:1; // insert crc
uint8_t ic:1; // insert checksum
uint8_t rs:1; // report status
uint8_t rps:1; // report packet sent
uint8_t dext:1; // extension
uint8_t vle:1; // vlan enable
uint8_t ide:1; // interrupt delay enable
} cmd;
};
union {
uint8_t status:4;
struct {
uint8_t dd:1; // descriptor done
uint8_t ec:1; // excess collisions
uint8_t lc:1; // late collision
uint8_t tu:1; // transmit underrun
} st;
};
uint8_t reserved:4;
uint8_t css;
union {
uint16_t special;
struct {
uint16_t vlan:12; //vlan id
uint16_t cfi:1; // canocial form id
uint16_t pri:3; // user priority
} sp;
};
} legacy;
// Type 0000 descriptor
struct {
uint8_t ipcss;
uint8_t ipcso;
uint16_t ipcse;
uint8_t tucss;
uint8_t tucso;
uint16_t tucse;
uint32_t paylen:20;
uint8_t dtype:4;
union {
uint8_t tucommand;
struct {
uint8_t tcp:1; // tcp/udp
uint8_t ip:1; // ip ipv4/ipv6
uint8_t tse:1; // tcp segment enbale
uint8_t rs:1; // report status
uint8_t rsv0:1; // reserved
uint8_t dext:1; // descriptor extension
uint8_t rsv1:1; // reserved
uint8_t ide:1; // interrupt delay enable
} tucmd;
};
union {
uint8_t status:4;
struct {
uint8_t dd:1;
uint8_t rsvd:3;
} sta;
};
uint8_t reserved:4;
uint8_t hdrlen;
uint16_t mss;
} t0;
// Type 0001 descriptor
struct {
Addr buf;
uint32_t dtalen:20;
uint8_t dtype:4;
union {
uint8_t dcommand;
struct {
uint8_t eop:1; // end of packet
uint8_t ifcs:1; // insert crc
uint8_t tse:1; // segmentation enable
uint8_t rs:1; // report status
uint8_t rps:1; // report packet sent
uint8_t dext:1; // extension
uint8_t vle:1; // vlan enable
uint8_t ide:1; // interrupt delay enable
} dcmd;
};
union {
uint8_t status:4;
struct {
uint8_t dd:1; // descriptor done
uint8_t ec:1; // excess collisions
uint8_t lc:1; // late collision
uint8_t tu:1; // transmit underrun
} sta;
};
union {
uint8_t pktopts;
struct {
uint8_t ixsm:1; // insert ip checksum
uint8_t txsm:1; // insert tcp checksum
};
};
union {
uint16_t special;
struct {
uint16_t vlan:12; //vlan id
uint16_t cfi:1; // canocial form id
uint16_t pri:3; // user priority
} sp;
};
} t1;
// Junk to test descriptor type!
struct {
uint64_t junk;
uint32_t junk1:20;
uint8_t dtype;
uint8_t junk2:5;
uint8_t dext:1;
uint8_t junk3:2;
uint8_t junk4:4;
uint32_t junk5;
} type;
}; };
namespace TxdOp {
const uint8_t TXD_CNXT = 0x0;
const uint8_t TXD_DATA = 0x0;
bool isLegacy(TxDesc *d) { return !bits(d->d2,29,29); }
uint8_t getType(TxDesc *d) { return bits(d->d2, 23,20); }
bool isContext(TxDesc *d) { return !isLegacy(d) && getType(d) == TXD_CNXT; }
bool isData(TxDesc *d) { return !isLegacy(d) && getType(d) == TXD_DATA; }
Addr getBuf(TxDesc *d) { assert(isLegacy(d) || isData(d)); return d->d1; }
Addr getLen(TxDesc *d) { if (isLegacy(d)) return bits(d->d2,15,0); else return bits(d->d2, 19,0); }
void setDd(TxDesc *d)
{
replaceBits(d->d1, 35, 32, 1);
}
bool ide(TxDesc *d) { return bits(d->d2, 31,31); }
bool vle(TxDesc *d) { assert(isLegacy(d) || isData(d)); return bits(d->d2, 30,30); }
bool rs(TxDesc *d) { return bits(d->d2, 28,28); }
bool ic(TxDesc *d) { assert(isLegacy(d) || isData(d)); return isLegacy(d) && bits(d->d2, 27,27); }
bool tse(TxDesc *d) { return (isData(d) || isContext(d)) && bits(d->d2, 27,27); }
bool ifcs(TxDesc *d) { assert(isLegacy(d) || isData(d)); return bits(d->d2, 26,26); }
bool eop(TxDesc *d) { assert(isLegacy(d) || isData(d)); return bits(d->d2, 25,25); }
bool ip(TxDesc *d) { assert(isContext(d)); return bits(d->d2, 26,26); }
bool tcp(TxDesc *d) { assert(isContext(d)); return bits(d->d2, 25,25); }
uint8_t getCso(TxDesc *d) { assert(isLegacy(d)); return bits(d->d2, 23,16); }
uint8_t getCss(TxDesc *d) { assert(isLegacy(d)); return bits(d->d2, 47,40); }
bool ixsm(TxDesc *d) { return isData(d) && bits(d->d2, 40,40); }
bool txsm(TxDesc *d) { return isData(d) && bits(d->d2, 41,41); }
int tucse(TxDesc *d) { assert(isContext(d)); return bits(d->d1,63,48); }
int tucso(TxDesc *d) { assert(isContext(d)); return bits(d->d1,47,40); }
int tucss(TxDesc *d) { assert(isContext(d)); return bits(d->d1,39,32); }
int ipcse(TxDesc *d) { assert(isContext(d)); return bits(d->d1,31,16); }
int ipcso(TxDesc *d) { assert(isContext(d)); return bits(d->d1,15,8); }
int ipcss(TxDesc *d) { assert(isContext(d)); return bits(d->d1,7,0); }
int mss(TxDesc *d) { assert(isContext(d)); return bits(d->d2,63,48); }
int hdrlen(TxDesc *d) { assert(isContext(d)); return bits(d->d2,47,40); }
} // namespace TxdOp
#define ADD_FIELD32(NAME, OFFSET, BITS) \ #define ADD_FIELD32(NAME, OFFSET, BITS) \
inline uint32_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \ inline uint32_t NAME() { return bits(_data, OFFSET+BITS-1, OFFSET); } \
inline void NAME(uint32_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); } inline void NAME(uint32_t d) { replaceBits(_data, OFFSET+BITS-1, OFFSET,d); }
@ -295,6 +219,7 @@ struct Regs {
const Reg<T> &operator=(T d) { _data = d; return *this;} const Reg<T> &operator=(T d) { _data = d; return *this;}
bool operator==(T d) { return d == _data; } bool operator==(T d) { return d == _data; }
void operator()(T d) { _data = d; } void operator()(T d) { _data = d; }
Reg() { _data = 0; }
}; };
struct CTRL : public Reg<uint32_t> { // 0x0000 CTRL Register struct CTRL : public Reg<uint32_t> { // 0x0000 CTRL Register
@ -454,7 +379,6 @@ struct Regs {
ADD_FIELD32(lpe,5,1); // long packet reception enabled ADD_FIELD32(lpe,5,1); // long packet reception enabled
ADD_FIELD32(lbm,6,2); // ADD_FIELD32(lbm,6,2); //
ADD_FIELD32(rdmts,8,2); // ADD_FIELD32(rdmts,8,2); //
ADD_FIELD32(rsvd,10,2); //
ADD_FIELD32(mo,12,2); // ADD_FIELD32(mo,12,2); //
ADD_FIELD32(mdr,14,1); // ADD_FIELD32(mdr,14,1); //
ADD_FIELD32(bam,15,1); // ADD_FIELD32(bam,15,1); //
@ -462,11 +386,21 @@ struct Regs {
ADD_FIELD32(vfe,18,1); // ADD_FIELD32(vfe,18,1); //
ADD_FIELD32(cfien,19,1); // ADD_FIELD32(cfien,19,1); //
ADD_FIELD32(cfi,20,1); // ADD_FIELD32(cfi,20,1); //
ADD_FIELD32(rsvd2,21,1); //
ADD_FIELD32(dpf,22,1); // discard pause frames ADD_FIELD32(dpf,22,1); // discard pause frames
ADD_FIELD32(pmcf,23,1); // pass mac control frames ADD_FIELD32(pmcf,23,1); // pass mac control frames
ADD_FIELD32(bsex,25,1); // buffer size extension ADD_FIELD32(bsex,25,1); // buffer size extension
ADD_FIELD32(secrc,26,1); // strip ethernet crc from incoming packet ADD_FIELD32(secrc,26,1); // strip ethernet crc from incoming packet
int descSize()
{
switch(bsize()) {
case 0: return bsex() ? 2048 : -1;
case 1: return bsex() ? 1024 : 16384;
case 2: return bsex() ? 512 : 8192;
case 3: return bsex() ? 256 : 4096;
default:
return -1;
}
}
}; };
RCTL rctl; RCTL rctl;
@ -543,10 +477,21 @@ struct Regs {
struct RDTR : public Reg<uint32_t> { // 0x2820 RDTR Register struct RDTR : public Reg<uint32_t> { // 0x2820 RDTR Register
using Reg<uint32_t>::operator=; using Reg<uint32_t>::operator=;
ADD_FIELD32(delay,0,16); // receive delay timer ADD_FIELD32(delay,0,16); // receive delay timer
ADD_FIELD32(fpd, 31,); // flush partial descriptor block ?? ADD_FIELD32(fpd, 31,1); // flush partial descriptor block ??
}; };
RDTR rdtr; RDTR rdtr;
struct RXDCTL : public Reg<uint32_t> { // 0x2828 RXDCTL Register
using Reg<uint32_t>::operator=;
ADD_FIELD32(pthresh,0,6); // prefetch threshold, less that this
// consider prefetch
ADD_FIELD32(hthresh,8,6); // number of descriptors in host mem to
// consider prefetch
ADD_FIELD32(wthresh,16,6); // writeback threshold
ADD_FIELD32(gran,24,1); // granularity 0 = desc, 1 = cacheline
};
RXDCTL rxdctl;
struct RADV : public Reg<uint32_t> { // 0x282C RADV Register struct RADV : public Reg<uint32_t> { // 0x282C RADV Register
using Reg<uint32_t>::operator=; using Reg<uint32_t>::operator=;
ADD_FIELD32(idv,0,16); // absolute interrupt delay ADD_FIELD32(idv,0,16); // absolute interrupt delay

View file

@ -132,6 +132,7 @@ class DmaPort : public Port
bool dmaPending() { return pendingCount > 0; } bool dmaPending() { return pendingCount > 0; }
int cacheBlockSize() { return peerBlockSize(); }
unsigned int drain(Event *de); unsigned int drain(Event *de);
}; };
@ -261,13 +262,17 @@ class DmaDevice : public PioDevice
addr, size, event, data); addr, size, event, data);
} }
void dmaRead(Addr addr, int size, Event *event, uint8_t *data = NULL) void dmaRead(Addr addr, int size, Event *event, uint8_t *data)
{ dmaPort->dmaAction(MemCmd::ReadReq, addr, size, event, data); } {
dmaPort->dmaAction(MemCmd::ReadReq, addr, size, event, data);
}
bool dmaPending() { return dmaPort->dmaPending(); } bool dmaPending() { return dmaPort->dmaPending(); }
virtual unsigned int drain(Event *de); virtual unsigned int drain(Event *de);
int cacheBlockSize() { return dmaPort->cacheBlockSize(); }
virtual Port *getPort(const std::string &if_name, int idx = -1) virtual Port *getPort(const std::string &if_name, int idx = -1)
{ {
if (if_name == "pio") { if (if_name == "pio") {