summaryrefslogtreecommitdiff
path: root/src/mem/rubymem.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/mem/rubymem.cc')
-rw-r--r--src/mem/rubymem.cc240
1 files changed, 204 insertions, 36 deletions
diff --git a/src/mem/rubymem.cc b/src/mem/rubymem.cc
index 4d9f8051f..74a6e390e 100644
--- a/src/mem/rubymem.cc
+++ b/src/mem/rubymem.cc
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2001-2005 The Regents of The University of Michigan
+ * Copyright (c) 2009 Advanced Micro Devices, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,6 +27,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Daniel Sanchez
+ * Brad Beckmann
*/
#include <iostream>
@@ -35,6 +37,7 @@
#include "base/output.hh"
#include "base/str.hh"
#include "base/types.hh"
+#include "config/the_isa.hh"
#include "mem/ruby/common/Debug.hh"
#include "mem/ruby/libruby.hh"
#include "mem/ruby/system/RubyPort.hh"
@@ -55,13 +58,23 @@ RubyMemory::RubyMemory(const Params *p)
ruby_clock = p->clock;
ruby_phase = p->phase;
+ ports_per_cpu = p->ports_per_core;
+
+ DPRINTF(Ruby, "creating Ruby Memory from file %s\n",
+ p->config_file.c_str());
+
ifstream config(p->config_file.c_str());
+ if (config.good() == false) {
+ fatal("Did not successfully open %s.\n", p->config_file.c_str());
+ }
+
vector<RubyObjConf> sys_conf;
while (!config.eof()) {
- char buffer[4096];
+ char buffer[65536];
config.getline(buffer, sizeof(buffer));
string line = buffer;
+ DPRINTF(Ruby, "%s %d\n", line, line.empty());
if (line.empty())
continue;
vector<string> tokens;
@@ -80,11 +93,27 @@ RubyMemory::RubyMemory(const Params *p)
RubySystem::create(sys_conf);
+ //
+ // Create the necessary ruby_ports to connect to the sequencers.
+ // This code should be fixed when the configuration systems are unified
+ // and the ruby configuration text files no longer exist. Also,
+ // it would be great to remove the single ruby_hit_callback func with
+ // separate pointers to particular ports to rubymem. However, functional
+ // access currently prevent the improvement.
+ //
for (int i = 0; i < params()->num_cpus; i++) {
RubyPort *p = RubySystem::getPort(csprintf("Sequencer_%d", i),
ruby_hit_callback);
ruby_ports.push_back(p);
}
+
+ for (int i = 0; i < params()->num_dmas; i++) {
+ RubyPort *p = RubySystem::getPort(csprintf("DMASequencer_%d", i),
+ ruby_hit_callback);
+ ruby_dma_ports.push_back(p);
+ }
+
+ pio_port = NULL;
}
void
@@ -105,7 +134,6 @@ RubyMemory::init()
//g_debug_ptr->setDebugTime(1);
//g_debug_ptr->setDebugOutputFile("ruby.debug");
-
g_system_ptr->clearStats();
if (ports.size() == 0) {
@@ -117,9 +145,18 @@ RubyMemory::init()
(*pi)->sendStatusChange(Port::RangeChange);
}
+ for (PortIterator pi = dma_ports.begin(); pi != dma_ports.end(); ++pi) {
+ if (*pi)
+ (*pi)->sendStatusChange(Port::RangeChange);
+ }
+
+ if (pio_port != NULL) {
+ pio_port->sendStatusChange(Port::RangeChange);
+ }
+
//Print stats at exit
- RubyExitCallback* rc = new RubyExitCallback(this);
- registerExitCallback(rc);
+ rubyExitCB = new RubyExitCallback(this);
+ registerExitCallback(rubyExitCB);
//Sched RubyEvent, automatically reschedules to advance ruby cycles
rubyTickEvent = new RubyEvent(this);
@@ -137,37 +174,57 @@ RubyMemory::tick()
RubyMemory::~RubyMemory()
{
-}
-
-void
-RubyMemory::hitCallback(PacketPtr pkt, Port *port)
-{
- DPRINTF(MemoryAccess, "Hit callback\n");
-
- bool needsResponse = pkt->needsResponse();
- doAtomicAccess(pkt);
-
- // turn packet around to go back to requester if response expected
- if (needsResponse) {
- // recvAtomic() should already have turned packet into
- // atomic response
- assert(pkt->isResponse());
- DPRINTF(MemoryAccess, "Sending packet back over port\n");
- port->sendTiming(pkt);
- } else {
- delete pkt;
- }
- DPRINTF(MemoryAccess, "Hit callback done!\n");
+ delete g_system_ptr;
}
Port *
RubyMemory::getPort(const std::string &if_name, int idx)
{
+ DPRINTF(Ruby, "getting port %d %s\n", idx, if_name);
+ DPRINTF(Ruby,
+ "number of ruby ports %d and dma ports %d\n",
+ ruby_ports.size(),
+ ruby_dma_ports.size());
+
+ //
+ // By default, getPort will be passed an idx of -1. Of course this is an
+ // invalid ruby port index and must be a modified
+ //
+ if (idx == -1) {
+ idx = 0;
+ }
+
// Accept request for "functional" port for backwards compatibility
// with places where this function is called from C++. I'd prefer
// to move all these into Python someday.
if (if_name == "functional") {
- return new Port(csprintf("%s-functional", name()), this);
+ assert(idx < ruby_ports.size());
+ return new Port(csprintf("%s-functional", name()),
+ this,
+ ruby_ports[idx]);
+ }
+
+ //
+ // if dma port request, allocate the appropriate prot
+ //
+ if (if_name == "dma_port") {
+ assert(idx < ruby_dma_ports.size());
+ RubyMemory::Port* dma_port =
+ new Port(csprintf("%s-dma_port%d", name(), idx),
+ this,
+ ruby_dma_ports[idx]);
+ dma_ports.push_back(dma_port);
+ return dma_port;
+ }
+
+ //
+ // if pio port, ensure that there is only one
+ //
+ if (if_name == "pio_port") {
+ assert(pio_port == NULL);
+ pio_port =
+ new RubyMemory::Port("ruby_pio_port", this, NULL);
+ return pio_port;
}
if (if_name != "port") {
@@ -182,30 +239,68 @@ RubyMemory::getPort(const std::string &if_name, int idx)
panic("RubyMemory::getPort: port %d already assigned", idx);
}
- Port *port = new Port(csprintf("%s-port%d", name(), idx), this);
+ //
+ // Currently this code assumes that each cpu has both a
+ // icache and dcache port and therefore divides by ports per cpu. This will
+ // be fixed once we unify the configuration systems and Ruby sequencers
+ // directly support M5 ports.
+ //
+ assert(idx/ports_per_cpu < ruby_ports.size());
+ Port *port = new Port(csprintf("%s-port%d", name(), idx),
+ this,
+ ruby_ports[idx/ports_per_cpu]);
ports[idx] = port;
return port;
}
-RubyMemory::Port::Port(const std::string &_name, RubyMemory *_memory)
+RubyMemory::Port::Port(const std::string &_name,
+ RubyMemory *_memory,
+ RubyPort *_port)
: PhysicalMemory::MemoryPort::MemoryPort(_name, _memory)
{
+ DPRINTF(Ruby, "creating port to ruby memory %s\n", _name);
ruby_mem = _memory;
+ ruby_port = _port;
}
bool
RubyMemory::Port::recvTiming(PacketPtr pkt)
{
- DPRINTF(MemoryAccess, "Timing access caught\n");
+ DPRINTF(MemoryAccess,
+ "Timing access caught for address %#x\n",
+ pkt->getAddr());
//dsm: based on SimpleTimingPort::recvTiming(pkt);
- // If the device is only a slave, it should only be sending
- // responses, which should never get nacked. There used to be
- // code to hanldle nacks here, but I'm pretty sure it didn't work
- // correctly with the drain code, so that would need to be fixed
- // if we ever added it back.
+ //
+ // In FS mode, ruby memory will receive pio responses from devices and
+ // it must forward these responses back to the particular CPU.
+ //
+ if (pkt->isResponse() != false && isPioAddress(pkt->getAddr()) != false) {
+ DPRINTF(MemoryAccess,
+ "Pio Response callback %#x\n",
+ pkt->getAddr());
+ RubyMemory::SenderState *senderState =
+ safe_cast<RubyMemory::SenderState *>(pkt->senderState);
+ RubyMemory::Port *port = senderState->port;
+
+ // pop the sender state from the packet
+ pkt->senderState = senderState->saved;
+ delete senderState;
+
+ port->sendTiming(pkt);
+
+ return true;
+ }
+
+ //
+ // After checking for pio responses, the remainder of packets
+ // received by ruby should only be M5 requests, which should never
+ // get nacked. There used to be code to hanldle nacks here, but
+ // I'm pretty sure it didn't work correctly with the drain code,
+ // so that would need to be fixed if we ever added it back.
+ //
assert(pkt->isRequest());
if (pkt->memInhibitAsserted()) {
@@ -219,6 +314,18 @@ RubyMemory::Port::recvTiming(PacketPtr pkt)
// Save the port in the sender state object
pkt->senderState = new SenderState(this, pkt->senderState);
+ //
+ // Check for pio requests and directly send them to the dedicated
+ // pio_port.
+ //
+ if (isPioAddress(pkt->getAddr()) != false) {
+ return ruby_mem->pio_port->sendTiming(pkt);
+ }
+
+ //
+ // For DMA and CPU requests, translate them to ruby requests before
+ // sending them to our assigned ruby port.
+ //
RubyRequestType type = RubyRequestType_NULL;
Addr pc = 0;
if (pkt->isRead()) {
@@ -239,7 +346,6 @@ RubyMemory::Port::recvTiming(PacketPtr pkt)
RubyAccessMode_Supervisor);
// Submit the ruby request
- RubyPort *ruby_port = ruby_mem->ruby_ports[pkt->req->contextId()];
int64_t req_id = ruby_port->makeRequest(ruby_request);
if (req_id == -1) {
RubyMemory::SenderState *senderState =
@@ -260,6 +366,12 @@ RubyMemory::Port::recvTiming(PacketPtr pkt)
void
ruby_hit_callback(int64_t req_id)
{
+ //
+ // Note: This single fuction can be called by cpu and dma ports,
+ // as well as the functional port. The functional port prevents
+ // us from replacing this single function with separate port
+ // functions.
+ //
typedef map<int64_t, PacketPtr> map_t;
map_t &prm = RubyMemory::pending_requests;
@@ -278,13 +390,58 @@ ruby_hit_callback(int64_t req_id)
pkt->senderState = senderState->saved;
delete senderState;
- port->ruby_mem->hitCallback(pkt, port);
+ port->hitCallback(pkt);
}
void
+RubyMemory::Port::hitCallback(PacketPtr pkt)
+{
+
+ bool needsResponse = pkt->needsResponse();
+
+ DPRINTF(MemoryAccess, "Hit callback needs response %d\n",
+ needsResponse);
+
+ ruby_mem->doAtomicAccess(pkt);
+
+ // turn packet around to go back to requester if response expected
+ if (needsResponse) {
+ // recvAtomic() should already have turned packet into
+ // atomic response
+ assert(pkt->isResponse());
+ DPRINTF(MemoryAccess, "Sending packet back over port\n");
+ sendTiming(pkt);
+ } else {
+ delete pkt;
+ }
+ DPRINTF(MemoryAccess, "Hit callback done!\n");
+}
+
+bool
RubyMemory::Port::sendTiming(PacketPtr pkt)
{
schedSendTiming(pkt, curTick + 1); //minimum latency, must be > 0
+ return true;
+}
+
+bool
+RubyMemory::Port::isPioAddress(Addr addr)
+{
+ AddrRangeList pioAddrList;
+ bool snoop = false;
+ if (ruby_mem->pio_port == NULL) {
+ return false;
+ }
+
+ ruby_mem->pio_port->getPeerAddressRanges(pioAddrList, snoop);
+ for(AddrRangeIter iter = pioAddrList.begin(); iter != pioAddrList.end(); iter++) {
+ if (addr >= iter->start && addr <= iter->end) {
+ DPRINTF(MemoryAccess, "Pio request found in %#llx - %#llx range\n",
+ iter->start, iter->end);
+ return true;
+ }
+ }
+ return false;
}
void RubyMemory::printConfigStats()
@@ -311,6 +468,17 @@ void RubyMemory::printConfig(std::ostream & out) const {
//g_system_ptr->printConfig(out);
}
+void RubyMemory::serialize(ostream &os)
+{
+ PhysicalMemory::serialize(os);
+}
+
+void RubyMemory::unserialize(Checkpoint *cp, const string &section)
+{
+ DPRINTF(Config, "Ruby memory being restored\n");
+ reschedule(rubyTickEvent, curTick + ruby_clock + ruby_phase);
+ PhysicalMemory::unserialize(cp, section);
+}
//Python-interface code
RubyMemory *