321 lines
9.4 KiB
C++
321 lines
9.4 KiB
C++
/*
|
|
* Copyright (c) 2001-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.
|
|
*
|
|
* Authors: Daniel Sanchez
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
|
|
#include "arch/isa_traits.hh"
|
|
#include "base/output.hh"
|
|
#include "base/str.hh"
|
|
#include "base/types.hh"
|
|
#include "mem/ruby/common/Debug.hh"
|
|
#include "mem/ruby/libruby.hh"
|
|
#include "mem/ruby/system/RubyPort.hh"
|
|
#include "mem/ruby/system/Sequencer.hh"
|
|
#include "mem/ruby/system/System.hh"
|
|
#include "mem/rubymem.hh"
|
|
#include "sim/eventq.hh"
|
|
#include "sim/sim_exit.hh"
|
|
|
|
using namespace std;
|
|
using namespace TheISA;
|
|
|
|
map<int64_t, PacketPtr> RubyMemory::pending_requests;
|
|
|
|
RubyMemory::RubyMemory(const Params *p)
|
|
: PhysicalMemory(p)
|
|
{
|
|
ruby_clock = p->clock;
|
|
ruby_phase = p->phase;
|
|
|
|
ifstream config(p->config_file.c_str());
|
|
|
|
vector<RubyObjConf> sys_conf;
|
|
while (!config.eof()) {
|
|
char buffer[4096];
|
|
config.getline(buffer, sizeof(buffer));
|
|
string line = buffer;
|
|
if (line.empty())
|
|
continue;
|
|
vector<string> tokens;
|
|
tokenize(tokens, line, ' ');
|
|
assert(tokens.size() >= 2);
|
|
vector<string> argv;
|
|
for (size_t i=2; i<tokens.size(); i++) {
|
|
std::replace(tokens[i].begin(), tokens[i].end(), '%', ' ');
|
|
std::replace(tokens[i].begin(), tokens[i].end(), '#', '\n');
|
|
argv.push_back(tokens[i]);
|
|
}
|
|
sys_conf.push_back(RubyObjConf(tokens[0], tokens[1], argv));
|
|
tokens.clear();
|
|
argv.clear();
|
|
}
|
|
|
|
RubySystem::create(sys_conf);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void
|
|
RubyMemory::init()
|
|
{
|
|
if (params()->debug) {
|
|
g_debug_ptr->setVerbosityString("high");
|
|
g_debug_ptr->setDebugTime(1);
|
|
if (!params()->debug_file.empty()) {
|
|
g_debug_ptr->setDebugOutputFile(params()->debug_file.c_str());
|
|
}
|
|
}
|
|
|
|
//You may want to set some other options...
|
|
//g_debug_ptr->setVerbosityString("med");
|
|
//g_debug_ptr->setFilterString("lsNqST");
|
|
//g_debug_ptr->setFilterString("lsNST");
|
|
//g_debug_ptr->setDebugTime(1);
|
|
//g_debug_ptr->setDebugOutputFile("ruby.debug");
|
|
|
|
|
|
g_system_ptr->clearStats();
|
|
|
|
if (ports.size() == 0) {
|
|
fatal("RubyMemory object %s is unconnected!", name());
|
|
}
|
|
|
|
for (PortIterator pi = ports.begin(); pi != ports.end(); ++pi) {
|
|
if (*pi)
|
|
(*pi)->sendStatusChange(Port::RangeChange);
|
|
}
|
|
|
|
//Print stats at exit
|
|
RubyExitCallback* rc = new RubyExitCallback(this);
|
|
registerExitCallback(rc);
|
|
|
|
//Sched RubyEvent, automatically reschedules to advance ruby cycles
|
|
rubyTickEvent = new RubyEvent(this);
|
|
schedule(rubyTickEvent, curTick + ruby_clock + ruby_phase);
|
|
}
|
|
|
|
//called by rubyTickEvent
|
|
void
|
|
RubyMemory::tick()
|
|
{
|
|
RubyEventQueue *eq = RubySystem::getEventQueue();
|
|
eq->triggerEvents(eq->getTime() + 1);
|
|
schedule(rubyTickEvent, curTick + ruby_clock);
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
Port *
|
|
RubyMemory::getPort(const std::string &if_name, int idx)
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
if (if_name != "port") {
|
|
panic("RubyMemory::getPort: unknown port %s requested", if_name);
|
|
}
|
|
|
|
if (idx >= (int)ports.size()) {
|
|
ports.resize(idx+1);
|
|
}
|
|
|
|
if (ports[idx] != NULL) {
|
|
panic("RubyMemory::getPort: port %d already assigned", idx);
|
|
}
|
|
|
|
Port *port = new Port(csprintf("%s-port%d", name(), idx), this);
|
|
|
|
ports[idx] = port;
|
|
return port;
|
|
}
|
|
|
|
RubyMemory::Port::Port(const std::string &_name, RubyMemory *_memory)
|
|
: PhysicalMemory::MemoryPort::MemoryPort(_name, _memory)
|
|
{
|
|
ruby_mem = _memory;
|
|
}
|
|
|
|
bool
|
|
RubyMemory::Port::recvTiming(PacketPtr pkt)
|
|
{
|
|
DPRINTF(MemoryAccess, "Timing access caught\n");
|
|
|
|
//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.
|
|
assert(pkt->isRequest());
|
|
|
|
if (pkt->memInhibitAsserted()) {
|
|
warn("memInhibitAsserted???");
|
|
// snooper will supply based on copy of packet
|
|
// still target's responsibility to delete packet
|
|
delete pkt;
|
|
return true;
|
|
}
|
|
|
|
// Save the port in the sender state object
|
|
pkt->senderState = new SenderState(this, pkt->senderState);
|
|
|
|
RubyRequestType type = RubyRequestType_NULL;
|
|
Addr pc = 0;
|
|
if (pkt->isRead()) {
|
|
if (pkt->req->isInstFetch()) {
|
|
type = RubyRequestType_IFETCH;
|
|
pc = pkt->req->getPC();
|
|
} else {
|
|
type = RubyRequestType_LD;
|
|
}
|
|
} else if (pkt->isWrite()) {
|
|
type = RubyRequestType_ST;
|
|
} else if (pkt->isReadWrite()) {
|
|
// type = RubyRequestType_RMW;
|
|
}
|
|
|
|
RubyRequest ruby_request(pkt->getAddr(), pkt->getPtr<uint8_t>(),
|
|
pkt->getSize(), pc, type,
|
|
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 =
|
|
safe_cast<RubyMemory::SenderState *>(pkt->senderState);
|
|
|
|
// pop the sender state from the packet
|
|
pkt->senderState = senderState->saved;
|
|
delete senderState;
|
|
return false;
|
|
}
|
|
|
|
// Save the request for the callback
|
|
RubyMemory::pending_requests[req_id] = pkt;
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
ruby_hit_callback(int64_t req_id)
|
|
{
|
|
typedef map<int64_t, PacketPtr> map_t;
|
|
map_t &prm = RubyMemory::pending_requests;
|
|
|
|
map_t::iterator i = prm.find(req_id);
|
|
if (i == prm.end())
|
|
panic("could not find pending request %d\n", req_id);
|
|
|
|
PacketPtr pkt = i->second;
|
|
prm.erase(i);
|
|
|
|
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->ruby_mem->hitCallback(pkt, port);
|
|
}
|
|
|
|
void
|
|
RubyMemory::Port::sendTiming(PacketPtr pkt)
|
|
{
|
|
schedSendTiming(pkt, curTick + 1); //minimum latency, must be > 0
|
|
}
|
|
|
|
void RubyMemory::printConfigStats()
|
|
{
|
|
std::ostream *os = simout.create(params()->stats_file);
|
|
RubySystem::printConfig(*os);
|
|
*os << endl;
|
|
RubySystem::printStats(*os);
|
|
}
|
|
|
|
|
|
//Right now these functions seem to be called by RubySystem. If they do calls
|
|
// to RubySystem perform it intended actions, you'll get into an inf loop
|
|
//FIXME what's the purpose of these here?
|
|
void RubyMemory::printStats(std::ostream & out) const {
|
|
//g_system_ptr->printConfig(out);
|
|
}
|
|
|
|
void RubyMemory::clearStats() {
|
|
//g_system_ptr->clearStats();
|
|
}
|
|
|
|
void RubyMemory::printConfig(std::ostream & out) const {
|
|
//g_system_ptr->printConfig(out);
|
|
}
|
|
|
|
|
|
//Python-interface code
|
|
RubyMemory *
|
|
RubyMemoryParams::create()
|
|
{
|
|
return new RubyMemory(this);
|
|
}
|
|
|