Update copyright dates and author list
SConscript:
arch/alpha/alpha_linux_process.cc:
arch/alpha/alpha_linux_process.hh:
arch/alpha/alpha_memory.cc:
arch/alpha/alpha_memory.hh:
arch/alpha/alpha_tru64_process.cc:
arch/alpha/alpha_tru64_process.hh:
arch/alpha/aout_machdep.h:
arch/alpha/arguments.cc:
arch/alpha/arguments.hh:
arch/alpha/ev5.cc:
arch/alpha/ev5.hh:
arch/alpha/faults.cc:
arch/alpha/faults.hh:
arch/alpha/isa_desc:
arch/alpha/isa_traits.hh:
arch/alpha/osfpal.cc:
arch/alpha/osfpal.hh:
arch/alpha/pseudo_inst.cc:
arch/alpha/pseudo_inst.hh:
arch/alpha/vptr.hh:
arch/alpha/vtophys.cc:
arch/alpha/vtophys.hh:
base/bitfield.hh:
base/callback.hh:
base/circlebuf.cc:
base/circlebuf.hh:
base/cprintf.cc:
base/cprintf.hh:
base/cprintf_formats.hh:
base/crc.hh:
base/date.cc:
base/dbl_list.hh:
base/endian.hh:
base/fast_alloc.cc:
base/fast_alloc.hh:
base/fifo_buffer.cc:
base/fifo_buffer.hh:
base/hashmap.hh:
base/hostinfo.cc:
base/hostinfo.hh:
base/hybrid_pred.cc:
base/hybrid_pred.hh:
base/inet.cc:
base/inet.hh:
base/inifile.cc:
base/inifile.hh:
base/intmath.cc:
base/intmath.hh:
base/match.cc:
base/match.hh:
base/misc.cc:
base/misc.hh:
base/mod_num.hh:
base/mysql.cc:
base/mysql.hh:
base/output.cc:
base/output.hh:
base/pollevent.cc:
base/pollevent.hh:
base/predictor.hh:
base/random.cc:
base/random.hh:
base/range.cc:
base/range.hh:
base/refcnt.hh:
base/remote_gdb.cc:
base/remote_gdb.hh:
base/res_list.hh:
base/sat_counter.cc:
base/sat_counter.hh:
base/sched_list.hh:
base/socket.cc:
base/socket.hh:
base/statistics.cc:
base/statistics.hh:
base/compression/lzss_compression.cc:
base/compression/lzss_compression.hh:
base/compression/null_compression.hh:
base/loader/aout_object.cc:
base/loader/aout_object.hh:
base/loader/ecoff_object.cc:
base/loader/ecoff_object.hh:
base/loader/elf_object.cc:
base/loader/elf_object.hh:
base/loader/object_file.cc:
base/loader/object_file.hh:
base/loader/symtab.cc:
base/loader/symtab.hh:
base/stats/events.cc:
base/stats/events.hh:
base/stats/flags.hh:
base/stats/mysql.cc:
base/stats/mysql.hh:
base/stats/mysql_run.hh:
base/stats/output.hh:
base/stats/statdb.cc:
base/stats/statdb.hh:
base/stats/text.cc:
base/stats/text.hh:
base/stats/types.hh:
base/stats/visit.cc:
base/stats/visit.hh:
base/str.cc:
base/str.hh:
base/time.cc:
base/time.hh:
base/timebuf.hh:
base/trace.cc:
base/trace.hh:
base/userinfo.cc:
base/userinfo.hh:
build/SConstruct:
cpu/base.cc:
cpu/base.hh:
cpu/base_dyn_inst.cc:
cpu/base_dyn_inst.hh:
cpu/exec_context.cc:
cpu/exec_context.hh:
cpu/exetrace.cc:
cpu/exetrace.hh:
cpu/inst_seq.hh:
cpu/intr_control.cc:
cpu/intr_control.hh:
cpu/memtest/memtest.cc:
cpu/pc_event.cc:
cpu/pc_event.hh:
cpu/smt.hh:
cpu/static_inst.cc:
cpu/static_inst.hh:
cpu/memtest/memtest.hh:
cpu/o3/sat_counter.cc:
cpu/o3/sat_counter.hh:
cpu/ozone/cpu.hh:
cpu/simple/cpu.cc:
cpu/simple/cpu.hh:
cpu/trace/opt_cpu.cc:
cpu/trace/opt_cpu.hh:
cpu/trace/reader/ibm_reader.cc:
cpu/trace/reader/ibm_reader.hh:
cpu/trace/reader/itx_reader.cc:
cpu/trace/reader/itx_reader.hh:
cpu/trace/reader/m5_reader.cc:
cpu/trace/reader/m5_reader.hh:
cpu/trace/reader/mem_trace_reader.cc:
cpu/trace/reader/mem_trace_reader.hh:
cpu/trace/trace_cpu.cc:
cpu/trace/trace_cpu.hh:
dev/alpha_access.h:
dev/alpha_console.cc:
dev/alpha_console.hh:
dev/baddev.cc:
dev/baddev.hh:
dev/disk_image.cc:
dev/disk_image.hh:
dev/etherbus.cc:
dev/etherbus.hh:
dev/etherdump.cc:
dev/etherdump.hh:
dev/etherint.cc:
dev/etherint.hh:
dev/etherlink.cc:
dev/etherlink.hh:
dev/etherpkt.cc:
dev/etherpkt.hh:
dev/ethertap.cc:
dev/ethertap.hh:
dev/ide_ctrl.cc:
dev/ide_ctrl.hh:
dev/ide_disk.cc:
dev/ide_disk.hh:
dev/io_device.cc:
dev/io_device.hh:
dev/ns_gige.cc:
dev/ns_gige.hh:
dev/ns_gige_reg.h:
dev/pciconfigall.cc:
dev/pciconfigall.hh:
dev/pcidev.cc:
dev/pcidev.hh:
dev/pcireg.h:
dev/pktfifo.cc:
dev/pktfifo.hh:
dev/platform.cc:
dev/platform.hh:
dev/simconsole.cc:
dev/simconsole.hh:
dev/simple_disk.cc:
dev/simple_disk.hh:
dev/sinic.cc:
dev/sinic.hh:
dev/sinicreg.hh:
dev/tsunami.cc:
dev/tsunami.hh:
dev/tsunami_cchip.cc:
dev/tsunami_cchip.hh:
dev/tsunami_io.cc:
dev/tsunami_io.hh:
dev/tsunami_pchip.cc:
dev/tsunami_pchip.hh:
dev/tsunamireg.h:
dev/uart.cc:
dev/uart.hh:
dev/uart8250.cc:
dev/uart8250.hh:
docs/stl.hh:
encumbered/cpu/full/op_class.hh:
kern/kernel_stats.cc:
kern/kernel_stats.hh:
kern/linux/linux.hh:
kern/linux/linux_syscalls.cc:
kern/linux/linux_syscalls.hh:
kern/linux/linux_system.cc:
kern/linux/linux_system.hh:
kern/linux/linux_threadinfo.hh:
kern/linux/printk.cc:
kern/linux/printk.hh:
kern/system_events.cc:
kern/system_events.hh:
kern/tru64/dump_mbuf.cc:
kern/tru64/dump_mbuf.hh:
kern/tru64/mbuf.hh:
kern/tru64/printf.cc:
kern/tru64/printf.hh:
kern/tru64/tru64.hh:
kern/tru64/tru64_events.cc:
kern/tru64/tru64_events.hh:
kern/tru64/tru64_syscalls.cc:
kern/tru64/tru64_syscalls.hh:
kern/tru64/tru64_system.cc:
kern/tru64/tru64_system.hh:
python/SConscript:
python/m5/__init__.py:
python/m5/config.py:
python/m5/convert.py:
python/m5/multidict.py:
python/m5/smartdict.py:
sim/async.hh:
sim/builder.cc:
sim/builder.hh:
sim/debug.cc:
sim/debug.hh:
sim/eventq.cc:
sim/eventq.hh:
sim/host.hh:
sim/main.cc:
sim/param.cc:
sim/param.hh:
sim/process.cc:
sim/process.hh:
sim/root.cc:
sim/serialize.cc:
sim/serialize.hh:
sim/sim_events.cc:
sim/sim_events.hh:
sim/sim_exit.hh:
sim/sim_object.cc:
sim/sim_object.hh:
sim/startup.cc:
sim/startup.hh:
sim/stat_control.cc:
sim/stat_control.hh:
sim/stats.hh:
sim/syscall_emul.cc:
sim/syscall_emul.hh:
sim/system.cc:
sim/system.hh:
test/bitvectest.cc:
test/circletest.cc:
test/cprintftest.cc:
test/genini.py:
test/initest.cc:
test/lru_test.cc:
test/nmtest.cc:
test/offtest.cc:
test/paramtest.cc:
test/rangetest.cc:
test/sized_test.cc:
test/stattest.cc:
test/strnumtest.cc:
test/symtest.cc:
test/tokentest.cc:
test/tracetest.cc:
util/ccdrv/devtime.c:
util/m5/m5.c:
util/oprofile-top.py:
util/rundiff:
util/m5/m5op.h:
util/m5/m5op.s:
util/stats/db.py:
util/stats/dbinit.py:
util/stats/display.py:
util/stats/info.py:
util/stats/print.py:
util/stats/stats.py:
util/tap/tap.cc:
Update copyright dates and author list
--HG--
extra : convert_revision : 0faba08fc0fc0146f1efb7f61e4b043c020ff9e4
1472 lines
39 KiB
C++
1472 lines
39 KiB
C++
/*
|
|
* Copyright (c) 2004-2005 The Regents of The University of Michigan
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met: redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer;
|
|
* redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution;
|
|
* neither the name of the copyright holders nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <cstdio>
|
|
#include <deque>
|
|
#include <string>
|
|
|
|
#include "base/inet.hh"
|
|
#include "cpu/exec_context.hh"
|
|
#include "cpu/intr_control.hh"
|
|
#include "dev/etherlink.hh"
|
|
#include "dev/sinic.hh"
|
|
#include "dev/pciconfigall.hh"
|
|
#include "mem/bus/bus.hh"
|
|
#include "mem/bus/dma_interface.hh"
|
|
#include "mem/bus/pio_interface.hh"
|
|
#include "mem/bus/pio_interface_impl.hh"
|
|
#include "mem/functional/memory_control.hh"
|
|
#include "mem/functional/physical.hh"
|
|
#include "sim/builder.hh"
|
|
#include "sim/debug.hh"
|
|
#include "sim/eventq.hh"
|
|
#include "sim/host.hh"
|
|
#include "sim/stats.hh"
|
|
#include "targetarch/vtophys.hh"
|
|
|
|
using namespace Net;
|
|
|
|
namespace Sinic {
|
|
|
|
const char *RxStateStrings[] =
|
|
{
|
|
"rxIdle",
|
|
"rxFifoBlock",
|
|
"rxBeginCopy",
|
|
"rxCopy",
|
|
"rxCopyDone"
|
|
};
|
|
|
|
const char *TxStateStrings[] =
|
|
{
|
|
"txIdle",
|
|
"txFifoBlock",
|
|
"txBeginCopy",
|
|
"txCopy",
|
|
"txCopyDone"
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Sinic PCI Device
|
|
//
|
|
Base::Base(Params *p)
|
|
: PciDev(p), rxEnable(false), txEnable(false), cycleTime(p->cycle_time),
|
|
intrDelay(p->intr_delay), intrTick(0), cpuIntrEnable(false),
|
|
cpuPendingIntr(false), intrEvent(0), interface(NULL)
|
|
{
|
|
}
|
|
|
|
Device::Device(Params *p)
|
|
: Base(p), plat(p->plat), physmem(p->physmem),
|
|
rxFifo(p->rx_fifo_size), txFifo(p->tx_fifo_size),
|
|
rxKickTick(0), txKickTick(0),
|
|
txEvent(this), rxDmaEvent(this), txDmaEvent(this),
|
|
dmaReadDelay(p->dma_read_delay), dmaReadFactor(p->dma_read_factor),
|
|
dmaWriteDelay(p->dma_write_delay), dmaWriteFactor(p->dma_write_factor)
|
|
{
|
|
reset();
|
|
|
|
if (p->io_bus) {
|
|
pioInterface = newPioInterface(p->name, p->hier, p->io_bus, this,
|
|
&Device::cacheAccess);
|
|
|
|
pioLatency = p->pio_latency * p->io_bus->clockRate;
|
|
|
|
if (p->payload_bus)
|
|
dmaInterface = new DMAInterface<Bus>(p->name + ".dma", p->io_bus,
|
|
p->payload_bus, 1,
|
|
p->dma_no_allocate);
|
|
else
|
|
dmaInterface = new DMAInterface<Bus>(p->name + ".dma", p->io_bus,
|
|
p->io_bus, 1,
|
|
p->dma_no_allocate);
|
|
} else if (p->payload_bus) {
|
|
pioInterface = newPioInterface(p->name, p->hier, p->payload_bus, this,
|
|
&Device::cacheAccess);
|
|
|
|
pioLatency = p->pio_latency * p->payload_bus->clockRate;
|
|
|
|
dmaInterface = new DMAInterface<Bus>(p->name + ".dma", p->payload_bus,
|
|
p->payload_bus, 1,
|
|
p->dma_no_allocate);
|
|
}
|
|
}
|
|
|
|
Device::~Device()
|
|
{}
|
|
|
|
void
|
|
Device::regStats()
|
|
{
|
|
rxBytes
|
|
.name(name() + ".rxBytes")
|
|
.desc("Bytes Received")
|
|
.prereq(rxBytes)
|
|
;
|
|
|
|
rxBandwidth
|
|
.name(name() + ".rxBandwidth")
|
|
.desc("Receive Bandwidth (bits/s)")
|
|
.precision(0)
|
|
.prereq(rxBytes)
|
|
;
|
|
|
|
rxPackets
|
|
.name(name() + ".rxPackets")
|
|
.desc("Number of Packets Received")
|
|
.prereq(rxBytes)
|
|
;
|
|
|
|
rxPacketRate
|
|
.name(name() + ".rxPPS")
|
|
.desc("Packet Reception Rate (packets/s)")
|
|
.precision(0)
|
|
.prereq(rxBytes)
|
|
;
|
|
|
|
rxIpPackets
|
|
.name(name() + ".rxIpPackets")
|
|
.desc("Number of IP Packets Received")
|
|
.prereq(rxBytes)
|
|
;
|
|
|
|
rxTcpPackets
|
|
.name(name() + ".rxTcpPackets")
|
|
.desc("Number of Packets Received")
|
|
.prereq(rxBytes)
|
|
;
|
|
|
|
rxUdpPackets
|
|
.name(name() + ".rxUdpPackets")
|
|
.desc("Number of UDP Packets Received")
|
|
.prereq(rxBytes)
|
|
;
|
|
|
|
rxIpChecksums
|
|
.name(name() + ".rxIpChecksums")
|
|
.desc("Number of rx IP Checksums done by device")
|
|
.precision(0)
|
|
.prereq(rxBytes)
|
|
;
|
|
|
|
rxTcpChecksums
|
|
.name(name() + ".rxTcpChecksums")
|
|
.desc("Number of rx TCP Checksums done by device")
|
|
.precision(0)
|
|
.prereq(rxBytes)
|
|
;
|
|
|
|
rxUdpChecksums
|
|
.name(name() + ".rxUdpChecksums")
|
|
.desc("Number of rx UDP Checksums done by device")
|
|
.precision(0)
|
|
.prereq(rxBytes)
|
|
;
|
|
|
|
totBandwidth
|
|
.name(name() + ".totBandwidth")
|
|
.desc("Total Bandwidth (bits/s)")
|
|
.precision(0)
|
|
.prereq(totBytes)
|
|
;
|
|
|
|
totPackets
|
|
.name(name() + ".totPackets")
|
|
.desc("Total Packets")
|
|
.precision(0)
|
|
.prereq(totBytes)
|
|
;
|
|
|
|
totBytes
|
|
.name(name() + ".totBytes")
|
|
.desc("Total Bytes")
|
|
.precision(0)
|
|
.prereq(totBytes)
|
|
;
|
|
|
|
totPacketRate
|
|
.name(name() + ".totPPS")
|
|
.desc("Total Tranmission Rate (packets/s)")
|
|
.precision(0)
|
|
.prereq(totBytes)
|
|
;
|
|
|
|
txBytes
|
|
.name(name() + ".txBytes")
|
|
.desc("Bytes Transmitted")
|
|
.prereq(txBytes)
|
|
;
|
|
|
|
txBandwidth
|
|
.name(name() + ".txBandwidth")
|
|
.desc("Transmit Bandwidth (bits/s)")
|
|
.precision(0)
|
|
.prereq(txBytes)
|
|
;
|
|
|
|
txPackets
|
|
.name(name() + ".txPackets")
|
|
.desc("Number of Packets Transmitted")
|
|
.prereq(txBytes)
|
|
;
|
|
|
|
txPacketRate
|
|
.name(name() + ".txPPS")
|
|
.desc("Packet Tranmission Rate (packets/s)")
|
|
.precision(0)
|
|
.prereq(txBytes)
|
|
;
|
|
|
|
txIpPackets
|
|
.name(name() + ".txIpPackets")
|
|
.desc("Number of IP Packets Transmitted")
|
|
.prereq(txBytes)
|
|
;
|
|
|
|
txTcpPackets
|
|
.name(name() + ".txTcpPackets")
|
|
.desc("Number of TCP Packets Transmitted")
|
|
.prereq(txBytes)
|
|
;
|
|
|
|
txUdpPackets
|
|
.name(name() + ".txUdpPackets")
|
|
.desc("Number of Packets Transmitted")
|
|
.prereq(txBytes)
|
|
;
|
|
|
|
txIpChecksums
|
|
.name(name() + ".txIpChecksums")
|
|
.desc("Number of tx IP Checksums done by device")
|
|
.precision(0)
|
|
.prereq(txBytes)
|
|
;
|
|
|
|
txTcpChecksums
|
|
.name(name() + ".txTcpChecksums")
|
|
.desc("Number of tx TCP Checksums done by device")
|
|
.precision(0)
|
|
.prereq(txBytes)
|
|
;
|
|
|
|
txUdpChecksums
|
|
.name(name() + ".txUdpChecksums")
|
|
.desc("Number of tx UDP Checksums done by device")
|
|
.precision(0)
|
|
.prereq(txBytes)
|
|
;
|
|
|
|
txBandwidth = txBytes * Stats::constant(8) / simSeconds;
|
|
rxBandwidth = rxBytes * Stats::constant(8) / simSeconds;
|
|
totBandwidth = txBandwidth + rxBandwidth;
|
|
totBytes = txBytes + rxBytes;
|
|
totPackets = txPackets + rxPackets;
|
|
txPacketRate = txPackets / simSeconds;
|
|
rxPacketRate = rxPackets / simSeconds;
|
|
}
|
|
|
|
/**
|
|
* This is to write to the PCI general configuration registers
|
|
*/
|
|
void
|
|
Device::WriteConfig(int offset, int size, uint32_t data)
|
|
{
|
|
switch (offset) {
|
|
case PCI0_BASE_ADDR0:
|
|
// Need to catch writes to BARs to update the PIO interface
|
|
PciDev::WriteConfig(offset, size, data);
|
|
if (BARAddrs[0] != 0) {
|
|
if (pioInterface)
|
|
pioInterface->addAddrRange(RangeSize(BARAddrs[0], BARSize[0]));
|
|
|
|
BARAddrs[0] &= EV5::PAddrUncachedMask;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
PciDev::WriteConfig(offset, size, data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This reads the device registers, which are detailed in the NS83820
|
|
* spec sheet
|
|
*/
|
|
Fault
|
|
Device::read(MemReqPtr &req, uint8_t *data)
|
|
{
|
|
assert(config.hdr.command & PCI_CMD_MSE);
|
|
|
|
//The mask is to give you only the offset into the device register file
|
|
Addr daddr = req->paddr & 0xfff;
|
|
|
|
if (Regs::regSize(daddr) == 0)
|
|
panic("invalid address: da=%#x pa=%#x va=%#x size=%d",
|
|
daddr, req->paddr, req->vaddr, req->size);
|
|
|
|
if (req->size != Regs::regSize(daddr))
|
|
panic("invalid size for reg %s: da=%#x pa=%#x va=%#x size=%d",
|
|
Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size);
|
|
|
|
DPRINTF(EthernetPIO, "read reg=%s da=%#x pa=%#x va=%#x size=%d\n",
|
|
Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size);
|
|
|
|
uint32_t ®32 = *(uint32_t *)data;
|
|
uint64_t ®64 = *(uint64_t *)data;
|
|
|
|
switch (daddr) {
|
|
case Regs::Config:
|
|
reg32 = regs.Config;
|
|
break;
|
|
|
|
case Regs::RxMaxCopy:
|
|
reg32 = regs.RxMaxCopy;
|
|
break;
|
|
|
|
case Regs::TxMaxCopy:
|
|
reg32 = regs.TxMaxCopy;
|
|
break;
|
|
|
|
case Regs::RxThreshold:
|
|
reg32 = regs.RxThreshold;
|
|
break;
|
|
|
|
case Regs::TxThreshold:
|
|
reg32 = regs.TxThreshold;
|
|
break;
|
|
|
|
case Regs::IntrStatus:
|
|
reg32 = regs.IntrStatus;
|
|
devIntrClear();
|
|
break;
|
|
|
|
case Regs::IntrMask:
|
|
reg32 = regs.IntrMask;
|
|
break;
|
|
|
|
case Regs::RxData:
|
|
reg64 = regs.RxData;
|
|
break;
|
|
|
|
case Regs::RxDone:
|
|
case Regs::RxWait:
|
|
reg64 = Regs::set_RxDone_FifoLen(regs.RxDone,
|
|
min(rxFifo.packets(), 255));
|
|
break;
|
|
|
|
case Regs::TxData:
|
|
reg64 = regs.TxData;
|
|
break;
|
|
|
|
case Regs::TxDone:
|
|
case Regs::TxWait:
|
|
reg64 = Regs::set_TxDone_FifoLen(regs.TxDone,
|
|
min(txFifo.packets(), 255));
|
|
break;
|
|
|
|
case Regs::HwAddr:
|
|
reg64 = params()->eaddr;
|
|
break;
|
|
|
|
default:
|
|
panic("reading write only register %s: da=%#x pa=%#x va=%#x size=%d",
|
|
Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size);
|
|
}
|
|
|
|
DPRINTF(EthernetPIO, "read reg=%s done val=%#x\n", Regs::regName(daddr),
|
|
Regs::regSize(daddr) == 4 ? reg32 : reg64);
|
|
|
|
return No_Fault;
|
|
}
|
|
|
|
Fault
|
|
Device::write(MemReqPtr &req, const uint8_t *data)
|
|
{
|
|
assert(config.hdr.command & PCI_CMD_MSE);
|
|
Addr daddr = req->paddr & 0xfff;
|
|
|
|
if (Regs::regSize(daddr) == 0)
|
|
panic("invalid address: da=%#x pa=%#x va=%#x size=%d",
|
|
daddr, req->paddr, req->vaddr, req->size);
|
|
|
|
if (req->size != Regs::regSize(daddr))
|
|
panic("invalid size: reg=%s da=%#x pa=%#x va=%#x size=%d",
|
|
Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size);
|
|
|
|
uint32_t reg32 = *(uint32_t *)data;
|
|
uint64_t reg64 = *(uint64_t *)data;
|
|
|
|
DPRINTF(EthernetPIO, "write reg=%s val=%#x da=%#x pa=%#x va=%#x size=%d\n",
|
|
Regs::regName(daddr), Regs::regSize(daddr) == 4 ? reg32 : reg64,
|
|
daddr, req->paddr, req->vaddr, req->size);
|
|
|
|
|
|
switch (daddr) {
|
|
case Regs::Config:
|
|
changeConfig(reg32);
|
|
break;
|
|
|
|
case Regs::RxThreshold:
|
|
regs.RxThreshold = reg32;
|
|
break;
|
|
|
|
case Regs::TxThreshold:
|
|
regs.TxThreshold = reg32;
|
|
break;
|
|
|
|
case Regs::IntrMask:
|
|
devIntrChangeMask(reg32);
|
|
break;
|
|
|
|
case Regs::RxData:
|
|
if (rxState != rxIdle)
|
|
panic("receive machine busy with another request!");
|
|
|
|
regs.RxDone = 0;
|
|
regs.RxData = reg64;
|
|
if (rxEnable) {
|
|
rxState = rxFifoBlock;
|
|
rxKick();
|
|
}
|
|
break;
|
|
|
|
case Regs::TxData:
|
|
if (txState != txIdle)
|
|
panic("transmit machine busy with another request!");
|
|
|
|
regs.TxDone = 0;
|
|
regs.TxData = reg64;
|
|
if (txEnable) {
|
|
txState = txFifoBlock;
|
|
txKick();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
panic("writing read only register %s: da=%#x pa=%#x va=%#x size=%d",
|
|
Regs::regName(daddr), daddr, req->paddr, req->vaddr, req->size);
|
|
}
|
|
|
|
return No_Fault;
|
|
}
|
|
|
|
void
|
|
Device::devIntrPost(uint32_t interrupts)
|
|
{
|
|
if ((interrupts & Regs::Intr_Res))
|
|
panic("Cannot set a reserved interrupt");
|
|
|
|
regs.IntrStatus |= interrupts;
|
|
|
|
DPRINTF(EthernetIntr,
|
|
"interrupt written to intStatus: intr=%#x status=%#x mask=%#x\n",
|
|
interrupts, regs.IntrStatus, regs.IntrMask);
|
|
|
|
if ((regs.IntrStatus & regs.IntrMask)) {
|
|
Tick when = curTick;
|
|
if ((regs.IntrStatus & regs.IntrMask & Regs::Intr_NoDelay) == 0)
|
|
when += intrDelay;
|
|
cpuIntrPost(when);
|
|
}
|
|
}
|
|
|
|
void
|
|
Device::devIntrClear(uint32_t interrupts)
|
|
{
|
|
if ((interrupts & Regs::Intr_Res))
|
|
panic("Cannot clear a reserved interrupt");
|
|
|
|
regs.IntrStatus &= ~interrupts;
|
|
|
|
DPRINTF(EthernetIntr,
|
|
"interrupt cleared from intStatus: intr=%x status=%x mask=%x\n",
|
|
interrupts, regs.IntrStatus, regs.IntrMask);
|
|
|
|
if (!(regs.IntrStatus & regs.IntrMask))
|
|
cpuIntrClear();
|
|
}
|
|
|
|
void
|
|
Device::devIntrChangeMask(uint32_t newmask)
|
|
{
|
|
if (regs.IntrMask == newmask)
|
|
return;
|
|
|
|
regs.IntrMask = newmask;
|
|
|
|
DPRINTF(EthernetIntr,
|
|
"interrupt mask changed: intStatus=%x intMask=%x masked=%x\n",
|
|
regs.IntrStatus, regs.IntrMask, regs.IntrStatus & regs.IntrMask);
|
|
|
|
if (regs.IntrStatus & regs.IntrMask)
|
|
cpuIntrPost(curTick);
|
|
else
|
|
cpuIntrClear();
|
|
}
|
|
|
|
void
|
|
Base::cpuIntrPost(Tick when)
|
|
{
|
|
// If the interrupt you want to post is later than an interrupt
|
|
// already scheduled, just let it post in the coming one and don't
|
|
// schedule another.
|
|
// HOWEVER, must be sure that the scheduled intrTick is in the
|
|
// future (this was formerly the source of a bug)
|
|
/**
|
|
* @todo this warning should be removed and the intrTick code should
|
|
* be fixed.
|
|
*/
|
|
assert(when >= curTick);
|
|
assert(intrTick >= curTick || intrTick == 0);
|
|
if (!cpuIntrEnable) {
|
|
DPRINTF(EthernetIntr, "interrupts not enabled.\n",
|
|
intrTick);
|
|
return;
|
|
}
|
|
|
|
if (when > intrTick && intrTick != 0) {
|
|
DPRINTF(EthernetIntr, "don't need to schedule event...intrTick=%d\n",
|
|
intrTick);
|
|
return;
|
|
}
|
|
|
|
intrTick = when;
|
|
if (intrTick < curTick) {
|
|
debug_break();
|
|
intrTick = curTick;
|
|
}
|
|
|
|
DPRINTF(EthernetIntr, "going to schedule an interrupt for intrTick=%d\n",
|
|
intrTick);
|
|
|
|
if (intrEvent)
|
|
intrEvent->squash();
|
|
intrEvent = new IntrEvent(this, true);
|
|
intrEvent->schedule(intrTick);
|
|
}
|
|
|
|
void
|
|
Base::cpuInterrupt()
|
|
{
|
|
assert(intrTick == curTick);
|
|
|
|
// Whether or not there's a pending interrupt, we don't care about
|
|
// it anymore
|
|
intrEvent = 0;
|
|
intrTick = 0;
|
|
|
|
// Don't send an interrupt if there's already one
|
|
if (cpuPendingIntr) {
|
|
DPRINTF(EthernetIntr,
|
|
"would send an interrupt now, but there's already pending\n");
|
|
} else {
|
|
// Send interrupt
|
|
cpuPendingIntr = true;
|
|
|
|
DPRINTF(EthernetIntr, "posting interrupt\n");
|
|
intrPost();
|
|
}
|
|
}
|
|
|
|
void
|
|
Base::cpuIntrClear()
|
|
{
|
|
if (!cpuPendingIntr)
|
|
return;
|
|
|
|
if (intrEvent) {
|
|
intrEvent->squash();
|
|
intrEvent = 0;
|
|
}
|
|
|
|
intrTick = 0;
|
|
|
|
cpuPendingIntr = false;
|
|
|
|
DPRINTF(EthernetIntr, "clearing cchip interrupt\n");
|
|
intrClear();
|
|
}
|
|
|
|
bool
|
|
Base::cpuIntrPending() const
|
|
{ return cpuPendingIntr; }
|
|
|
|
void
|
|
Device::changeConfig(uint32_t newconf)
|
|
{
|
|
uint32_t changed = regs.Config ^ newconf;
|
|
if (!changed)
|
|
return;
|
|
|
|
regs.Config = newconf;
|
|
|
|
if ((changed & Regs::Config_Reset)) {
|
|
assert(regs.Config & Regs::Config_Reset);
|
|
reset();
|
|
regs.Config &= ~Regs::Config_Reset;
|
|
}
|
|
|
|
if ((changed & Regs::Config_IntEn)) {
|
|
cpuIntrEnable = regs.Config & Regs::Config_IntEn;
|
|
if (cpuIntrEnable) {
|
|
if (regs.IntrStatus & regs.IntrMask)
|
|
cpuIntrPost(curTick);
|
|
} else {
|
|
cpuIntrClear();
|
|
}
|
|
}
|
|
|
|
if ((changed & Regs::Config_TxEn)) {
|
|
txEnable = regs.Config & Regs::Config_TxEn;
|
|
if (txEnable)
|
|
txKick();
|
|
}
|
|
|
|
if ((changed & Regs::Config_RxEn)) {
|
|
rxEnable = regs.Config & Regs::Config_RxEn;
|
|
if (rxEnable)
|
|
rxKick();
|
|
}
|
|
}
|
|
|
|
void
|
|
Device::reset()
|
|
{
|
|
using namespace Regs;
|
|
memset(®s, 0, sizeof(regs));
|
|
regs.RxMaxCopy = params()->rx_max_copy;
|
|
regs.TxMaxCopy = params()->tx_max_copy;
|
|
regs.IntrMask = Intr_TxFifo | Intr_RxFifo | Intr_RxData;
|
|
|
|
rxState = rxIdle;
|
|
txState = txIdle;
|
|
|
|
rxFifo.clear();
|
|
txFifo.clear();
|
|
}
|
|
|
|
void
|
|
Device::rxDmaCopy()
|
|
{
|
|
assert(rxState == rxCopy);
|
|
rxState = rxCopyDone;
|
|
physmem->dma_write(rxDmaAddr, (uint8_t *)rxDmaData, rxDmaLen);
|
|
DPRINTF(EthernetDMA, "rx dma write paddr=%#x len=%d\n",
|
|
rxDmaAddr, rxDmaLen);
|
|
DDUMP(EthernetDMA, rxDmaData, rxDmaLen);
|
|
}
|
|
|
|
void
|
|
Device::rxDmaDone()
|
|
{
|
|
rxDmaCopy();
|
|
rxKick();
|
|
}
|
|
|
|
void
|
|
Device::rxKick()
|
|
{
|
|
DPRINTF(EthernetSM, "receive kick rxState=%s (rxFifo.size=%d)\n",
|
|
RxStateStrings[rxState], rxFifo.size());
|
|
|
|
if (rxKickTick > curTick) {
|
|
DPRINTF(EthernetSM, "receive kick exiting, can't run till %d\n",
|
|
rxKickTick);
|
|
return;
|
|
}
|
|
|
|
next:
|
|
switch (rxState) {
|
|
case rxIdle:
|
|
if (rxPioRequest) {
|
|
pioInterface->respond(rxPioRequest, curTick);
|
|
rxPioRequest = 0;
|
|
}
|
|
goto exit;
|
|
|
|
case rxFifoBlock:
|
|
if (rxPacket) {
|
|
rxState = rxBeginCopy;
|
|
break;
|
|
}
|
|
|
|
if (rxFifo.empty()) {
|
|
DPRINTF(EthernetSM, "receive waiting for data. Nothing to do.\n");
|
|
goto exit;
|
|
}
|
|
|
|
// Grab a new packet from the fifo.
|
|
rxPacket = rxFifo.front();
|
|
rxPacketBufPtr = rxPacket->data;
|
|
rxPktBytes = rxPacket->length;
|
|
assert(rxPktBytes);
|
|
|
|
rxDoneData = 0;
|
|
/* scope for variables */ {
|
|
IpPtr ip(rxPacket);
|
|
if (ip) {
|
|
rxDoneData |= Regs::RxDone_IpPacket;
|
|
rxIpChecksums++;
|
|
if (cksum(ip) != 0) {
|
|
DPRINTF(EthernetCksum, "Rx IP Checksum Error\n");
|
|
rxDoneData |= Regs::RxDone_IpError;
|
|
}
|
|
TcpPtr tcp(ip);
|
|
UdpPtr udp(ip);
|
|
if (tcp) {
|
|
rxDoneData |= Regs::RxDone_TcpPacket;
|
|
rxTcpChecksums++;
|
|
if (cksum(tcp) != 0) {
|
|
DPRINTF(EthernetCksum, "Rx TCP Checksum Error\n");
|
|
rxDoneData |= Regs::RxDone_TcpError;
|
|
}
|
|
} else if (udp) {
|
|
rxDoneData |= Regs::RxDone_UdpPacket;
|
|
rxUdpChecksums++;
|
|
if (cksum(udp) != 0) {
|
|
DPRINTF(EthernetCksum, "Rx UDP Checksum Error\n");
|
|
rxDoneData |= Regs::RxDone_UdpError;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
rxState = rxBeginCopy;
|
|
break;
|
|
|
|
case rxBeginCopy:
|
|
rxDmaAddr = plat->pciToDma(Regs::get_RxData_Addr(regs.RxData));
|
|
rxDmaLen = min<int>(Regs::get_RxData_Len(regs.RxData), rxPktBytes);
|
|
rxDmaData = rxPacketBufPtr;
|
|
|
|
if (dmaInterface) {
|
|
if (!dmaInterface->busy()) {
|
|
dmaInterface->doDMA(WriteInvalidate, rxDmaAddr, rxDmaLen,
|
|
curTick, &rxDmaEvent, true);
|
|
rxState = rxCopy;
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
rxState = rxCopy;
|
|
if (dmaWriteDelay != 0 || dmaWriteFactor != 0) {
|
|
Tick factor = ((rxDmaLen + ULL(63)) >> ULL(6)) * dmaWriteFactor;
|
|
Tick start = curTick + dmaWriteDelay + factor;
|
|
rxDmaEvent.schedule(start);
|
|
goto exit;
|
|
}
|
|
|
|
rxDmaCopy();
|
|
break;
|
|
|
|
case rxCopy:
|
|
DPRINTF(EthernetSM, "receive machine still copying\n");
|
|
goto exit;
|
|
|
|
case rxCopyDone:
|
|
regs.RxDone = rxDoneData | rxDmaLen;
|
|
|
|
if (rxPktBytes == rxDmaLen) {
|
|
rxPacket = NULL;
|
|
rxFifo.pop();
|
|
} else {
|
|
regs.RxDone |= Regs::RxDone_More;
|
|
rxPktBytes -= rxDmaLen;
|
|
rxPacketBufPtr += rxDmaLen;
|
|
}
|
|
|
|
regs.RxDone |= Regs::RxDone_Complete;
|
|
devIntrPost(Regs::Intr_RxData);
|
|
rxState = rxIdle;
|
|
break;
|
|
|
|
default:
|
|
panic("Invalid rxState!");
|
|
}
|
|
|
|
DPRINTF(EthernetSM, "entering next rxState=%s\n",
|
|
RxStateStrings[rxState]);
|
|
|
|
goto next;
|
|
|
|
exit:
|
|
/**
|
|
* @todo do we want to schedule a future kick?
|
|
*/
|
|
DPRINTF(EthernetSM, "rx state machine exited rxState=%s\n",
|
|
RxStateStrings[rxState]);
|
|
}
|
|
|
|
void
|
|
Device::txDmaCopy()
|
|
{
|
|
assert(txState == txCopy);
|
|
txState = txCopyDone;
|
|
physmem->dma_read((uint8_t *)txDmaData, txDmaAddr, txDmaLen);
|
|
DPRINTF(EthernetDMA, "tx dma read paddr=%#x len=%d\n",
|
|
txDmaAddr, txDmaLen);
|
|
DDUMP(EthernetDMA, txDmaData, txDmaLen);
|
|
}
|
|
|
|
void
|
|
Device::txDmaDone()
|
|
{
|
|
txDmaCopy();
|
|
txKick();
|
|
}
|
|
|
|
void
|
|
Device::transmit()
|
|
{
|
|
if (txFifo.empty()) {
|
|
DPRINTF(Ethernet, "nothing to transmit\n");
|
|
return;
|
|
}
|
|
|
|
PacketPtr packet = txFifo.front();
|
|
if (!interface->sendPacket(packet)) {
|
|
DPRINTF(Ethernet, "Packet Transmit: failed txFifo available %d\n",
|
|
txFifo.avail());
|
|
goto reschedule;
|
|
}
|
|
|
|
txFifo.pop();
|
|
|
|
#if TRACING_ON
|
|
if (DTRACE(Ethernet)) {
|
|
IpPtr ip(packet);
|
|
if (ip) {
|
|
DPRINTF(Ethernet, "ID is %d\n", ip->id());
|
|
TcpPtr tcp(ip);
|
|
if (tcp) {
|
|
DPRINTF(Ethernet, "Src Port=%d, Dest Port=%d\n",
|
|
tcp->sport(), tcp->dport());
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
DDUMP(Ethernet, packet->data, packet->length);
|
|
txBytes += packet->length;
|
|
txPackets++;
|
|
|
|
DPRINTF(Ethernet, "Packet Transmit: successful txFifo Available %d\n",
|
|
txFifo.avail());
|
|
|
|
if (txFifo.size() <= params()->tx_fifo_threshold)
|
|
devIntrPost(Regs::Intr_TxFifo);
|
|
|
|
devIntrPost(Regs::Intr_TxDone);
|
|
|
|
reschedule:
|
|
if (!txFifo.empty() && !txEvent.scheduled()) {
|
|
DPRINTF(Ethernet, "reschedule transmit\n");
|
|
txEvent.schedule(curTick + retryTime);
|
|
}
|
|
}
|
|
|
|
void
|
|
Device::txKick()
|
|
{
|
|
DPRINTF(EthernetSM, "transmit kick txState=%s (txFifo.size=%d)\n",
|
|
TxStateStrings[txState], txFifo.size());
|
|
|
|
if (txKickTick > curTick) {
|
|
DPRINTF(EthernetSM, "transmit kick exiting, can't run till %d\n",
|
|
txKickTick);
|
|
return;
|
|
}
|
|
|
|
next:
|
|
switch (txState) {
|
|
case txIdle:
|
|
if (txPioRequest) {
|
|
pioInterface->respond(txPioRequest, curTick + pioLatency);
|
|
txPioRequest = 0;
|
|
}
|
|
goto exit;
|
|
|
|
case txFifoBlock:
|
|
if (!txPacket) {
|
|
// Grab a new packet from the fifo.
|
|
txPacket = new PacketData(16384);
|
|
txPacketBufPtr = txPacket->data;
|
|
}
|
|
|
|
if (txFifo.avail() - txPacket->length <
|
|
Regs::get_TxData_Len(regs.TxData)) {
|
|
DPRINTF(EthernetSM, "transmit fifo full. Nothing to do.\n");
|
|
goto exit;
|
|
}
|
|
|
|
txState = txBeginCopy;
|
|
break;
|
|
|
|
case txBeginCopy:
|
|
txDmaAddr = plat->pciToDma(Regs::get_TxData_Addr(regs.TxData));
|
|
txDmaLen = Regs::get_TxData_Len(regs.TxData);
|
|
txDmaData = txPacketBufPtr;
|
|
|
|
if (dmaInterface) {
|
|
if (!dmaInterface->busy()) {
|
|
dmaInterface->doDMA(Read, txDmaAddr, txDmaLen,
|
|
curTick, &txDmaEvent, true);
|
|
txState = txCopy;
|
|
}
|
|
|
|
goto exit;
|
|
}
|
|
|
|
txState = txCopy;
|
|
if (dmaReadDelay != 0 || dmaReadFactor != 0) {
|
|
Tick factor = ((txDmaLen + ULL(63)) >> ULL(6)) * dmaReadFactor;
|
|
Tick start = curTick + dmaReadDelay + factor;
|
|
txDmaEvent.schedule(start);
|
|
goto exit;
|
|
}
|
|
|
|
txDmaCopy();
|
|
break;
|
|
|
|
case txCopy:
|
|
DPRINTF(EthernetSM, "transmit machine still copying\n");
|
|
goto exit;
|
|
|
|
case txCopyDone:
|
|
txPacket->length += txDmaLen;
|
|
if ((regs.TxData & Regs::TxData_More)) {
|
|
txPacketBufPtr += txDmaLen;
|
|
} else {
|
|
assert(txPacket->length <= txFifo.avail());
|
|
if ((regs.TxData & Regs::TxData_Checksum)) {
|
|
IpPtr ip(txPacket);
|
|
if (ip) {
|
|
TcpPtr tcp(ip);
|
|
if (tcp) {
|
|
tcp->sum(0);
|
|
tcp->sum(cksum(tcp));
|
|
txTcpChecksums++;
|
|
}
|
|
|
|
UdpPtr udp(ip);
|
|
if (udp) {
|
|
udp->sum(0);
|
|
udp->sum(cksum(udp));
|
|
txUdpChecksums++;
|
|
}
|
|
|
|
ip->sum(0);
|
|
ip->sum(cksum(ip));
|
|
txIpChecksums++;
|
|
}
|
|
}
|
|
txFifo.push(txPacket);
|
|
txPacket = 0;
|
|
transmit();
|
|
}
|
|
|
|
regs.TxDone = txDmaLen | Regs::TxDone_Complete;
|
|
devIntrPost(Regs::Intr_TxData);
|
|
txState = txIdle;
|
|
break;
|
|
|
|
default:
|
|
panic("Invalid txState!");
|
|
}
|
|
|
|
DPRINTF(EthernetSM, "entering next txState=%s\n",
|
|
TxStateStrings[txState]);
|
|
|
|
goto next;
|
|
|
|
exit:
|
|
/**
|
|
* @todo do we want to schedule a future kick?
|
|
*/
|
|
DPRINTF(EthernetSM, "tx state machine exited txState=%s\n",
|
|
TxStateStrings[txState]);
|
|
}
|
|
|
|
void
|
|
Device::transferDone()
|
|
{
|
|
if (txFifo.empty()) {
|
|
DPRINTF(Ethernet, "transfer complete: txFifo empty...nothing to do\n");
|
|
return;
|
|
}
|
|
|
|
DPRINTF(Ethernet, "transfer complete: data in txFifo...schedule xmit\n");
|
|
|
|
if (txEvent.scheduled())
|
|
txEvent.reschedule(curTick + cycles(1));
|
|
else
|
|
txEvent.schedule(curTick + cycles(1));
|
|
}
|
|
|
|
bool
|
|
Device::rxFilter(const PacketPtr &packet)
|
|
{
|
|
if (!Regs::get_Config_Filter(regs.Config))
|
|
return false;
|
|
|
|
panic("receive filter not implemented\n");
|
|
bool drop = true;
|
|
|
|
#if 0
|
|
string type;
|
|
|
|
EthHdr *eth = packet->eth();
|
|
if (eth->unicast()) {
|
|
// If we're accepting all unicast addresses
|
|
if (acceptUnicast)
|
|
drop = false;
|
|
|
|
// If we make a perfect match
|
|
if (acceptPerfect && params->eaddr == eth.dst())
|
|
drop = false;
|
|
|
|
if (acceptArp && eth->type() == ETH_TYPE_ARP)
|
|
drop = false;
|
|
|
|
} else if (eth->broadcast()) {
|
|
// if we're accepting broadcasts
|
|
if (acceptBroadcast)
|
|
drop = false;
|
|
|
|
} else if (eth->multicast()) {
|
|
// if we're accepting all multicasts
|
|
if (acceptMulticast)
|
|
drop = false;
|
|
|
|
}
|
|
|
|
if (drop) {
|
|
DPRINTF(Ethernet, "rxFilter drop\n");
|
|
DDUMP(EthernetData, packet->data, packet->length);
|
|
}
|
|
#endif
|
|
return drop;
|
|
}
|
|
|
|
bool
|
|
Device::recvPacket(PacketPtr packet)
|
|
{
|
|
rxBytes += packet->length;
|
|
rxPackets++;
|
|
|
|
DPRINTF(Ethernet, "Receiving packet from wire, rxFifo Available is %d\n",
|
|
rxFifo.avail());
|
|
|
|
if (!rxEnable) {
|
|
DPRINTF(Ethernet, "receive disabled...packet dropped\n");
|
|
interface->recvDone();
|
|
return true;
|
|
}
|
|
|
|
if (rxFilter(packet)) {
|
|
DPRINTF(Ethernet, "packet filtered...dropped\n");
|
|
interface->recvDone();
|
|
return true;
|
|
}
|
|
|
|
if (rxFifo.size() >= params()->rx_fifo_threshold)
|
|
devIntrPost(Regs::Intr_RxFifo);
|
|
|
|
if (!rxFifo.push(packet)) {
|
|
DPRINTF(Ethernet,
|
|
"packet will not fit in receive buffer...packet dropped\n");
|
|
return false;
|
|
}
|
|
|
|
interface->recvDone();
|
|
devIntrPost(Regs::Intr_RxDone);
|
|
rxKick();
|
|
return true;
|
|
}
|
|
|
|
//=====================================================================
|
|
//
|
|
//
|
|
void
|
|
Base::serialize(ostream &os)
|
|
{
|
|
// Serialize the PciDev base class
|
|
PciDev::serialize(os);
|
|
|
|
SERIALIZE_SCALAR(rxEnable);
|
|
SERIALIZE_SCALAR(txEnable);
|
|
SERIALIZE_SCALAR(cpuIntrEnable);
|
|
|
|
/*
|
|
* Keep track of pending interrupt status.
|
|
*/
|
|
SERIALIZE_SCALAR(intrTick);
|
|
SERIALIZE_SCALAR(cpuPendingIntr);
|
|
Tick intrEventTick = 0;
|
|
if (intrEvent)
|
|
intrEventTick = intrEvent->when();
|
|
SERIALIZE_SCALAR(intrEventTick);
|
|
}
|
|
|
|
void
|
|
Base::unserialize(Checkpoint *cp, const std::string §ion)
|
|
{
|
|
// Unserialize the PciDev base class
|
|
PciDev::unserialize(cp, section);
|
|
|
|
UNSERIALIZE_SCALAR(rxEnable);
|
|
UNSERIALIZE_SCALAR(txEnable);
|
|
UNSERIALIZE_SCALAR(cpuIntrEnable);
|
|
|
|
/*
|
|
* Keep track of pending interrupt status.
|
|
*/
|
|
UNSERIALIZE_SCALAR(intrTick);
|
|
UNSERIALIZE_SCALAR(cpuPendingIntr);
|
|
Tick intrEventTick;
|
|
UNSERIALIZE_SCALAR(intrEventTick);
|
|
if (intrEventTick) {
|
|
intrEvent = new IntrEvent(this, true);
|
|
intrEvent->schedule(intrEventTick);
|
|
}
|
|
}
|
|
|
|
void
|
|
Device::serialize(ostream &os)
|
|
{
|
|
// Serialize the PciDev base class
|
|
Base::serialize(os);
|
|
|
|
if (rxDmaEvent.scheduled())
|
|
rxDmaCopy();
|
|
|
|
if (txDmaEvent.scheduled())
|
|
txDmaCopy();
|
|
|
|
/*
|
|
* Serialize the device registers
|
|
*/
|
|
SERIALIZE_SCALAR(regs.Config);
|
|
SERIALIZE_SCALAR(regs.RxMaxCopy);
|
|
SERIALIZE_SCALAR(regs.TxMaxCopy);
|
|
SERIALIZE_SCALAR(regs.RxThreshold);
|
|
SERIALIZE_SCALAR(regs.TxThreshold);
|
|
SERIALIZE_SCALAR(regs.IntrStatus);
|
|
SERIALIZE_SCALAR(regs.IntrMask);
|
|
SERIALIZE_SCALAR(regs.RxData);
|
|
SERIALIZE_SCALAR(regs.RxDone);
|
|
SERIALIZE_SCALAR(regs.TxData);
|
|
SERIALIZE_SCALAR(regs.TxDone);
|
|
|
|
/*
|
|
* Serialize rx state machine
|
|
*/
|
|
int rxState = this->rxState;
|
|
SERIALIZE_SCALAR(rxState);
|
|
rxFifo.serialize("rxFifo", os);
|
|
bool rxPacketExists = rxPacket;
|
|
SERIALIZE_SCALAR(rxPacketExists);
|
|
if (rxPacketExists) {
|
|
rxPacket->serialize("rxPacket", os);
|
|
uint32_t rxPktBufPtr = (uint32_t) (rxPacketBufPtr - rxPacket->data);
|
|
SERIALIZE_SCALAR(rxPktBufPtr);
|
|
SERIALIZE_SCALAR(rxPktBytes);
|
|
}
|
|
SERIALIZE_SCALAR(rxDoneData);
|
|
|
|
/*
|
|
* Serialize tx state machine
|
|
*/
|
|
int txState = this->txState;
|
|
SERIALIZE_SCALAR(txState);
|
|
txFifo.serialize("txFifo", os);
|
|
bool txPacketExists = txPacket;
|
|
SERIALIZE_SCALAR(txPacketExists);
|
|
if (txPacketExists) {
|
|
txPacket->serialize("txPacket", os);
|
|
uint32_t txPktBufPtr = (uint32_t) (txPacketBufPtr - txPacket->data);
|
|
SERIALIZE_SCALAR(txPktBufPtr);
|
|
SERIALIZE_SCALAR(txPktBytes);
|
|
}
|
|
|
|
/*
|
|
* If there's a pending transmit, store the time so we can
|
|
* reschedule it later
|
|
*/
|
|
Tick transmitTick = txEvent.scheduled() ? txEvent.when() - curTick : 0;
|
|
SERIALIZE_SCALAR(transmitTick);
|
|
}
|
|
|
|
void
|
|
Device::unserialize(Checkpoint *cp, const std::string §ion)
|
|
{
|
|
// Unserialize the PciDev base class
|
|
Base::unserialize(cp, section);
|
|
|
|
/*
|
|
* Unserialize the device registers
|
|
*/
|
|
UNSERIALIZE_SCALAR(regs.Config);
|
|
UNSERIALIZE_SCALAR(regs.RxMaxCopy);
|
|
UNSERIALIZE_SCALAR(regs.TxMaxCopy);
|
|
UNSERIALIZE_SCALAR(regs.RxThreshold);
|
|
UNSERIALIZE_SCALAR(regs.TxThreshold);
|
|
UNSERIALIZE_SCALAR(regs.IntrStatus);
|
|
UNSERIALIZE_SCALAR(regs.IntrMask);
|
|
UNSERIALIZE_SCALAR(regs.RxData);
|
|
UNSERIALIZE_SCALAR(regs.RxDone);
|
|
UNSERIALIZE_SCALAR(regs.TxData);
|
|
UNSERIALIZE_SCALAR(regs.TxDone);
|
|
|
|
/*
|
|
* Unserialize rx state machine
|
|
*/
|
|
int rxState;
|
|
UNSERIALIZE_SCALAR(rxState);
|
|
this->rxState = (RxState) rxState;
|
|
rxFifo.unserialize("rxFifo", cp, section);
|
|
bool rxPacketExists;
|
|
UNSERIALIZE_SCALAR(rxPacketExists);
|
|
rxPacket = 0;
|
|
if (rxPacketExists) {
|
|
rxPacket = new PacketData(16384);
|
|
rxPacket->unserialize("rxPacket", cp, section);
|
|
uint32_t rxPktBufPtr;
|
|
UNSERIALIZE_SCALAR(rxPktBufPtr);
|
|
this->rxPacketBufPtr = (uint8_t *) rxPacket->data + rxPktBufPtr;
|
|
UNSERIALIZE_SCALAR(rxPktBytes);
|
|
}
|
|
UNSERIALIZE_SCALAR(rxDoneData);
|
|
|
|
/*
|
|
* Unserialize tx state machine
|
|
*/
|
|
int txState;
|
|
UNSERIALIZE_SCALAR(txState);
|
|
this->txState = (TxState) txState;
|
|
txFifo.unserialize("txFifo", cp, section);
|
|
bool txPacketExists;
|
|
UNSERIALIZE_SCALAR(txPacketExists);
|
|
txPacket = 0;
|
|
if (txPacketExists) {
|
|
txPacket = new PacketData(16384);
|
|
txPacket->unserialize("txPacket", cp, section);
|
|
uint32_t txPktBufPtr;
|
|
UNSERIALIZE_SCALAR(txPktBufPtr);
|
|
this->txPacketBufPtr = (uint8_t *) txPacket->data + txPktBufPtr;
|
|
UNSERIALIZE_SCALAR(txPktBytes);
|
|
}
|
|
|
|
/*
|
|
* If there's a pending transmit, reschedule it now
|
|
*/
|
|
Tick transmitTick;
|
|
UNSERIALIZE_SCALAR(transmitTick);
|
|
if (transmitTick)
|
|
txEvent.schedule(curTick + transmitTick);
|
|
|
|
/*
|
|
* re-add addrRanges to bus bridges
|
|
*/
|
|
if (pioInterface)
|
|
pioInterface->addAddrRange(RangeSize(BARAddrs[0], BARSize[0]));
|
|
}
|
|
|
|
Tick
|
|
Device::cacheAccess(MemReqPtr &req)
|
|
{
|
|
//The mask is to give you only the offset into the device register file
|
|
Addr daddr = req->paddr - addr;
|
|
|
|
DPRINTF(EthernetPIO, "timing access to paddr=%#x (daddr=%#x)\n",
|
|
req->paddr, daddr);
|
|
|
|
Tick when = curTick + pioLatency;
|
|
|
|
switch (daddr) {
|
|
case Regs::RxDone:
|
|
if (rxState != rxIdle) {
|
|
rxPioRequest = req;
|
|
when = 0;
|
|
}
|
|
break;
|
|
|
|
case Regs::TxDone:
|
|
if (txState != txIdle) {
|
|
txPioRequest = req;
|
|
when = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return when;
|
|
}
|
|
|
|
BEGIN_DECLARE_SIM_OBJECT_PARAMS(Interface)
|
|
|
|
SimObjectParam<EtherInt *> peer;
|
|
SimObjectParam<Device *> device;
|
|
|
|
END_DECLARE_SIM_OBJECT_PARAMS(Interface)
|
|
|
|
BEGIN_INIT_SIM_OBJECT_PARAMS(Interface)
|
|
|
|
INIT_PARAM_DFLT(peer, "peer interface", NULL),
|
|
INIT_PARAM(device, "Ethernet device of this interface")
|
|
|
|
END_INIT_SIM_OBJECT_PARAMS(Interface)
|
|
|
|
CREATE_SIM_OBJECT(Interface)
|
|
{
|
|
Interface *dev_int = new Interface(getInstanceName(), device);
|
|
|
|
EtherInt *p = (EtherInt *)peer;
|
|
if (p) {
|
|
dev_int->setPeer(p);
|
|
p->setPeer(dev_int);
|
|
}
|
|
|
|
return dev_int;
|
|
}
|
|
|
|
REGISTER_SIM_OBJECT("SinicInt", Interface)
|
|
|
|
|
|
BEGIN_DECLARE_SIM_OBJECT_PARAMS(Device)
|
|
|
|
Param<Addr> addr;
|
|
Param<Tick> cycle_time;
|
|
Param<Tick> tx_delay;
|
|
Param<Tick> rx_delay;
|
|
Param<Tick> intr_delay;
|
|
SimObjectParam<MemoryController *> mmu;
|
|
SimObjectParam<PhysicalMemory *> physmem;
|
|
Param<bool> rx_filter;
|
|
Param<string> hardware_address;
|
|
SimObjectParam<Bus*> io_bus;
|
|
SimObjectParam<Bus*> payload_bus;
|
|
SimObjectParam<HierParams *> hier;
|
|
Param<Tick> pio_latency;
|
|
SimObjectParam<PciConfigAll *> configspace;
|
|
SimObjectParam<PciConfigData *> configdata;
|
|
SimObjectParam<Platform *> platform;
|
|
Param<uint32_t> pci_bus;
|
|
Param<uint32_t> pci_dev;
|
|
Param<uint32_t> pci_func;
|
|
Param<uint32_t> rx_max_copy;
|
|
Param<uint32_t> tx_max_copy;
|
|
Param<uint32_t> rx_fifo_size;
|
|
Param<uint32_t> tx_fifo_size;
|
|
Param<uint32_t> rx_fifo_threshold;
|
|
Param<uint32_t> tx_fifo_threshold;
|
|
Param<Tick> dma_read_delay;
|
|
Param<Tick> dma_read_factor;
|
|
Param<Tick> dma_write_delay;
|
|
Param<Tick> dma_write_factor;
|
|
Param<bool> dma_no_allocate;
|
|
|
|
END_DECLARE_SIM_OBJECT_PARAMS(Device)
|
|
|
|
BEGIN_INIT_SIM_OBJECT_PARAMS(Device)
|
|
|
|
INIT_PARAM(addr, "Device Address"),
|
|
INIT_PARAM(cycle_time, "State machine cycle time"),
|
|
INIT_PARAM_DFLT(tx_delay, "Transmit Delay", 1000),
|
|
INIT_PARAM_DFLT(rx_delay, "Receive Delay", 1000),
|
|
INIT_PARAM_DFLT(intr_delay, "Interrupt Delay in microseconds", 0),
|
|
INIT_PARAM(mmu, "Memory Controller"),
|
|
INIT_PARAM(physmem, "Physical Memory"),
|
|
INIT_PARAM_DFLT(rx_filter, "Enable Receive Filter", true),
|
|
INIT_PARAM_DFLT(hardware_address, "Ethernet Hardware Address",
|
|
"00:99:00:00:00:01"),
|
|
INIT_PARAM_DFLT(io_bus, "The IO Bus to attach to for headers", NULL),
|
|
INIT_PARAM_DFLT(payload_bus, "The IO Bus to attach to for payload", NULL),
|
|
INIT_PARAM_DFLT(hier, "Hierarchy global variables", &defaultHierParams),
|
|
INIT_PARAM_DFLT(pio_latency, "Programmed IO latency in bus cycles", 1),
|
|
INIT_PARAM(configspace, "PCI Configspace"),
|
|
INIT_PARAM(configdata, "PCI Config data"),
|
|
INIT_PARAM(platform, "Platform"),
|
|
INIT_PARAM(pci_bus, "PCI bus"),
|
|
INIT_PARAM(pci_dev, "PCI device number"),
|
|
INIT_PARAM(pci_func, "PCI function code"),
|
|
INIT_PARAM_DFLT(rx_max_copy, "rx max copy", 16*1024),
|
|
INIT_PARAM_DFLT(tx_max_copy, "rx max copy", 16*1024),
|
|
INIT_PARAM_DFLT(rx_fifo_size, "max size in bytes of rxFifo", 64*1024),
|
|
INIT_PARAM_DFLT(tx_fifo_size, "max size in bytes of txFifo", 64*1024),
|
|
INIT_PARAM_DFLT(rx_fifo_threshold, "max size in bytes of rxFifo", 48*1024),
|
|
INIT_PARAM_DFLT(tx_fifo_threshold, "max size in bytes of txFifo", 16*1024),
|
|
INIT_PARAM_DFLT(dma_read_delay, "fixed delay for dma reads", 0),
|
|
INIT_PARAM_DFLT(dma_read_factor, "multiplier for dma reads", 0),
|
|
INIT_PARAM_DFLT(dma_write_delay, "fixed delay for dma writes", 0),
|
|
INIT_PARAM_DFLT(dma_write_factor, "multiplier for dma writes", 0),
|
|
INIT_PARAM_DFLT(dma_no_allocate, "Should we allocat on read in cache", true)
|
|
|
|
END_INIT_SIM_OBJECT_PARAMS(Device)
|
|
|
|
|
|
CREATE_SIM_OBJECT(Device)
|
|
{
|
|
Device::Params *params = new Device::Params;
|
|
params->name = getInstanceName();
|
|
params->intr_delay = intr_delay;
|
|
params->physmem = physmem;
|
|
params->cycle_time = cycle_time;
|
|
params->tx_delay = tx_delay;
|
|
params->rx_delay = rx_delay;
|
|
params->mmu = mmu;
|
|
params->hier = hier;
|
|
params->io_bus = io_bus;
|
|
params->payload_bus = payload_bus;
|
|
params->pio_latency = pio_latency;
|
|
params->configSpace = configspace;
|
|
params->configData = configdata;
|
|
params->plat = platform;
|
|
params->busNum = pci_bus;
|
|
params->deviceNum = pci_dev;
|
|
params->functionNum = pci_func;
|
|
params->rx_filter = rx_filter;
|
|
params->eaddr = hardware_address;
|
|
params->rx_max_copy = rx_max_copy;
|
|
params->tx_max_copy = tx_max_copy;
|
|
params->rx_fifo_size = rx_fifo_size;
|
|
params->tx_fifo_size = tx_fifo_size;
|
|
params->rx_fifo_threshold = rx_fifo_threshold;
|
|
params->tx_fifo_threshold = tx_fifo_threshold;
|
|
params->dma_read_delay = dma_read_delay;
|
|
params->dma_read_factor = dma_read_factor;
|
|
params->dma_write_delay = dma_write_delay;
|
|
params->dma_write_factor = dma_write_factor;
|
|
params->dma_no_allocate = dma_no_allocate;
|
|
return new Device(params);
|
|
}
|
|
|
|
REGISTER_SIM_OBJECT("Sinic", Device)
|
|
|
|
/* namespace Sinic */ }
|