+++ /dev/null
-/*
- * 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
- * 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
- * Brad Beckmann
- */
-
-#include <iostream>
-#include <fstream>
-
-#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<int64_t, PacketPtr> RubyMemory::pending_requests;
-
-RubyMemory::RubyMemory(const Params *p)
- : PhysicalMemory(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[65536];
- config.getline(buffer, sizeof(buffer));
- string line = buffer;
- DPRINTF(Ruby, "%s %d\n", line, line.empty());
- 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);
-
- //
- // 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
-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);
- }
-
- 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
- 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;
-}
-
-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") {
- 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") {
- 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);
- }
-
- //
- // 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,
- 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 for address %#x\n",
- pkt->getAddr());
-
- //dsm: based on SimpleTimingPort::recvTiming(pkt);
-
- //
- // 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()) {
- 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);
-
- //
- // 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()) {
- 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
- 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)
-{
- //
- // 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;
-
- 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->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()
-{
- 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);
-}
-
-void RubyMemory::serialize(ostream &os)
-{
- PhysicalMemory::serialize(os);
-}
-
-void RubyMemory::unserialize(Checkpoint *cp, const string §ion)
-{
- DPRINTF(Config, "Ruby memory being restored\n");
- reschedule(rubyTickEvent, curTick + ruby_clock + ruby_phase);
- PhysicalMemory::unserialize(cp, section);
-}
-
-//Python-interface code
-RubyMemory *
-RubyMemoryParams::create()
-{
- return new RubyMemory(this);
-}
-
+++ /dev/null
-/*
- * 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
- * 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
- */
-
-#ifndef __RUBY_MEMORY_HH__
-#define __RUBY_MEMORY_HH__
-
-#include <map>
-#include <vector>
-
-#include "base/callback.hh"
-#include "mem/packet.hh"
-#include "mem/physical.hh"
-#include "mem/ruby/system/RubyPort.hh"
-#include "params/RubyMemory.hh"
-#include "mem/port.hh"
-
-class RubyExitCallback;
-
-class RubyMemory : public PhysicalMemory
-{
- public:
- std::vector<RubyPort *> ruby_ports;
- std::vector<RubyPort *> ruby_dma_ports;
- class Port : public MemoryPort
- {
- friend void ruby_hit_callback(int64_t req_id);
-
- RubyMemory *ruby_mem;
- RubyPort *ruby_port;
-
- public:
- Port(const std::string &_name,
- RubyMemory *_memory,
- RubyPort *_port);
- bool sendTiming(PacketPtr pkt);
- void hitCallback(PacketPtr pkt);
-
- protected:
- virtual bool recvTiming(PacketPtr pkt);
-
- private:
- bool isPioAddress(Addr addr);
- };
-
- class RubyEvent : public Event
- {
- RubyMemory *ruby_ptr;
- public:
- RubyEvent(RubyMemory *p)
- : Event(), ruby_ptr(p) {}
-
- virtual void process() { ruby_ptr->tick(); }
-
- virtual const char *description() const { return "ruby tick"; }
- };
-
- struct SenderState : public Packet::SenderState
- {
- Port *port;
- Packet::SenderState *saved;
-
- SenderState(Port *p, Packet::SenderState *s = NULL)
- : port(p), saved(s)
- {}
- };
-
- private:
- // prevent copying of a RubyMemory object
- RubyMemory(const RubyMemory &specmem);
- const RubyMemory &operator=(const RubyMemory &specmem);
-
- RubyEvent* rubyTickEvent;
-
- public:
- typedef RubyMemoryParams Params;
- RubyMemory(const Params *p);
- virtual ~RubyMemory();
-
- const Params *
- params() const
- {
- return safe_cast<const Params *>(_params);
- }
-
- public:
- virtual ::Port *getPort(const std::string &if_name, int idx = -1);
- void virtual init();
-
- //Ruby-related specifics
- void printConfigStats(); //dsm: Maybe this function should
- //disappear once the configuration
- //options change & M5 determines the
- //stats file to use
-
- void printStats(std::ostream & out) const;
- void clearStats();
- void printConfig(std::ostream & out) const;
-
- void tick();
-
- private:
- Tick ruby_clock;
- Tick ruby_phase;
- RubyExitCallback* rubyExitCB;
- int ports_per_cpu;
-
- public:
- static std::map<int64_t, PacketPtr> pending_requests;
- RubyMemory::Port* pio_port;
-
- protected:
- std::vector<MemoryPort*> dma_ports;
-
- public:
- virtual void serialize(std::ostream &os);
- virtual void unserialize(Checkpoint *cp, const std::string §ion);
-};
-
-void ruby_hit_callback(int64_t);
-
-class RubyExitCallback : public Callback
-{
- private:
- RubyMemory* ruby;
-
- public:
- /**
- * virtualize the destructor to make sure that the correct one
- * gets called.
- */
-
- virtual ~RubyExitCallback() {};
-
- RubyExitCallback(RubyMemory* rm) {ruby=rm;};
-
- /**
- * virtual process function that is invoked when the callback
- * queue is executed.
- */
- virtual void process() {ruby->printConfigStats(); /*delete ruby; was doing double delete...*/};
-};
-
-
-#endif //__RUBY_MEMORY_HH__
-