/* * 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 #include #include "arch/isa_traits.hh" #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" #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 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 sys_conf; while (!config.eof()) { char buffer[65536]; config.getline(buffer, sizeof(buffer)); string line = buffer; if (line.empty()) continue; vector tokens; tokenize(tokens, line, ' '); assert(tokens.size() >= 2); vector argv; for (size_t i=2; inum_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 rubyExitCB = new RubyExitCallback(this); registerExitCallback(rubyExitCB); //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() { delete g_system_ptr; } 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(), 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(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 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(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); }