ruby: Support for merging ALPHA_FS and ruby
authorBrad Beckmann <Brad.Beckmann@amd.com>
Wed, 18 Nov 2009 21:55:58 +0000 (13:55 -0800)
committerBrad Beckmann <Brad.Beckmann@amd.com>
Wed, 18 Nov 2009 21:55:58 +0000 (13:55 -0800)
Connects M5 cpu and dma ports directly to ruby sequencers and dma
sequencers.  Rubymem also includes a pio port so that pio requests
and be forwarded to a special pio bus connecting to device pio
ports.

configs/common/FSConfig.py
configs/example/ruby_fs.py [new file with mode: 0644]
src/mem/RubyMemory.py
src/mem/SConscript
src/mem/ruby/config/MI_example-homogeneous.rb
src/mem/ruby/system/System.hh
src/mem/rubymem.cc
src/mem/rubymem.hh
tests/configs/ruby_config.py

index bd20a2bacc7b3f0633b200c0697d97a24799fef7..7ab7319cda9f83d44dcec921e19099224439d794 100644 (file)
@@ -79,6 +79,50 @@ def makeLinuxAlphaSystem(mem_mode, mdesc = None):
 
     return self
 
+def makeLinuxAlphaRubySystem(mem_mode, rubymem, mdesc = None):
+    class BaseTsunami(Tsunami):
+        ethernet = NSGigE(pci_bus=0, pci_dev=1, pci_func=0)
+        ide = IdeController(disks=[Parent.disk0, Parent.disk2],
+                            pci_func=0, pci_dev=0, pci_bus=0)
+        
+
+    self = LinuxAlphaSystem(physmem = rubymem)
+    if not mdesc:
+        # generic system
+        mdesc = SysConfig()
+    self.readfile = mdesc.script()
+
+    # Create pio bus to connect all device pio ports to rubymem's pio port
+    self.piobus = Bus(bus_id=0)
+    
+    self.disk0 = CowIdeDisk(driveID='master')
+    self.disk2 = CowIdeDisk(driveID='master')
+    self.disk0.childImage(mdesc.disk())
+    self.disk2.childImage(disk('linux-bigswap2.img'))
+    self.tsunami = BaseTsunami()
+    self.tsunami.attachIO(self.piobus)
+    self.tsunami.ide.pio = self.piobus.port
+    self.tsunami.ethernet.pio = self.piobus.port
+
+    # connect the dma ports directly to ruby dma ports
+    self.tsunami.ide.dma = self.physmem.dma_port
+    self.tsunami.ethernet.dma = self.physmem.dma_port
+
+    # connect the pio bus to rubymem
+    self.physmem.pio_port = self.piobus.port
+    
+    self.simple_disk = SimpleDisk(disk=RawDiskImage(image_file = mdesc.disk(),
+                                               read_only = True))
+    self.intrctrl = IntrControl()
+    self.mem_mode = mem_mode
+    self.terminal = Terminal()
+    self.kernel = binary('vmlinux')
+    self.pal = binary('ts_osfpal')
+    self.console = binary('console')
+    self.boot_osflags = 'root=/dev/hda1 console=ttyS0'
+
+    return self
+
 def makeSparcSystem(mem_mode, mdesc = None):
     class CowMmDisk(MmDisk):
         image = CowDiskImage(child=RawDiskImage(read_only=True),
diff --git a/configs/example/ruby_fs.py b/configs/example/ruby_fs.py
new file mode 100644 (file)
index 0000000..d27eae5
--- /dev/null
@@ -0,0 +1,182 @@
+# 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: Brad Beckmann
+
+#
+# Full system configuraiton for ruby
+#
+
+import os
+import optparse
+import sys
+from os.path import join as joinpath
+
+import m5
+from m5.defines import buildEnv
+from m5.objects import *
+from m5.util import addToPath, panic
+
+if not buildEnv['FULL_SYSTEM']:
+    panic("This script requires full-system mode (*_FS).")
+
+addToPath('../../tests/configs/')
+addToPath('../common')
+
+import ruby_config
+
+from FSConfig import *
+from SysPaths import *
+from Benchmarks import *
+import Simulation
+from Caches import *
+
+# Get paths we might need.  It's expected this file is in m5/configs/example.
+config_path = os.path.dirname(os.path.abspath(__file__))
+config_root = os.path.dirname(config_path)
+m5_root = os.path.dirname(config_root)
+
+parser = optparse.OptionParser()
+
+# Benchmark options
+parser.add_option("-b", "--benchmark", action="store", type="string",
+                  dest="benchmark",
+                  help="Specify the benchmark to run. Available benchmarks: %s"\
+                  % DefinedBenchmarks)
+parser.add_option("-o", "--options", default="",
+    help='The options to pass to the binary, use " " around the entire string')
+parser.add_option("-i", "--input", default="", help="Read stdin from a file.")
+parser.add_option("--output", default="", help="Redirect stdout to a file.")
+parser.add_option("--errout", default="", help="Redirect stderr to a file.")
+parser.add_option("--ruby-debug", action="store_true")
+parser.add_option("--ruby-debug-file", default="", help="Ruby debug out file (stdout if blank)")
+
+# ruby host memory experimentation
+parser.add_option("--cache_size", type="int")
+parser.add_option("--cache_assoc", type="int")
+parser.add_option("--map_levels", type="int")
+
+execfile(os.path.join(config_root, "common", "Options.py"))
+
+(options, args) = parser.parse_args()
+
+if args:
+    print "Error: script doesn't take any positional arguments"
+    sys.exit(1)
+
+if options.benchmark:
+    try:
+        bm = Benchmarks[options.benchmark]
+    except KeyError:
+        print "Error benchmark %s has not been defined." % options.benchmark
+        print "Valid benchmarks are: %s" % DefinedBenchmarks
+        sys.exit(1)
+else:
+    bm = [SysConfig()]
+
+#
+# currently ruby fs only works in timing mode because ruby does not support
+# atomic accesses by devices
+#
+assert(options.timing)
+
+(CPUClass, test_mem_mode, FutureClass) = Simulation.setCPUClass(options)
+
+CPUClass.clock = '1GHz'
+
+#
+# Since we are running in timing mode, set the number of M5 ticks to ruby ticks
+# to the cpu clock frequency
+#
+M5_to_ruby_tick = '1000t'
+
+np = options.num_cpus
+
+# check for max instruction count
+if options.max_inst:
+    max_inst = options.max_inst
+else:
+    max_inst = 0
+    
+# set cache size
+if options.cache_size:
+    cache_size = options.cache_size
+else:
+    cache_size = 32768 # 32 kB is default
+    
+# set cache assoc
+if options.cache_assoc:
+    cache_assoc = options.cache_assoc
+else:
+    cache_assoc = 8 # 8 is default
+    
+# set map levels
+if options.map_levels:
+    map_levels = options.map_levels
+else:
+    map_levels = 4 # 4 levels is the default
+
+#
+# Currently, since ruby configuraiton is separate from m5, we need to manually
+# tell ruby that two dma ports are created by makeLinuxAlphaRubySystem().
+# Eventually, this will be fix with a unified configuration system.
+#
+rubymem = ruby_config.generate("MI_example-homogeneous.rb",
+                               np,
+                               np,
+                               128,
+                               False,
+                               cache_size,
+                               cache_assoc,
+                               map_levels,
+                               2,
+                               M5_to_ruby_tick)
+
+if options.ruby_debug == True:
+  rubymem.debug = True
+  rubymem.debug_file = options.ruby_debug_file
+
+system = makeLinuxAlphaRubySystem(test_mem_mode, rubymem, bm[0])
+
+system.cpu = [CPUClass(cpu_id=i) for i in xrange(np)]
+
+if options.l2cache:
+    print "Error: -l2cache incompatible with ruby, must configure it ruby-style"
+    sys.exit(1)
+
+if options.caches:
+    print "Error: -caches incompatible with ruby, must configure it ruby-style"
+    sys.exit(1)
+
+for i in xrange(np):
+    system.cpu[i].connectMemPorts(system.physmem)
+
+    if options.fastmem:
+        system.cpu[i].physmem_port = system.physmem.port
+
+root = Root(system = system)
+
+Simulation.run(options, root, system, FutureClass)
index fbbbeebe491280e637f0855a01127c51bf23b2a1..ddd97572c5a221ebc8a45ac6dd222fe3fd23577b 100644 (file)
@@ -42,4 +42,6 @@ class RubyMemory(PhysicalMemory):
     debug = Param.Bool(False, "Use ruby debug")
     debug_file = Param.String("ruby.debug",
         "path to the Ruby debug output file (stdout if blank)")
-
+    num_dmas = Param.Int(0, "Number of DMA ports connected to the Ruby memory")
+    dma_port = VectorPort("Ruby_dma_ports")
+    pio_port = Port("Ruby_pio_port")
index 21335a709aee74f1b0960771354b1d79f1445904..2188850e09b835546ec8c01e4f996bdb6a07789b 100644 (file)
@@ -63,3 +63,4 @@ TraceFlag('BusBridge')
 TraceFlag('LLSC')
 TraceFlag('MMU')
 TraceFlag('MemoryAccess')
+TraceFlag('Ruby')
index 2b416e647acc5943eb6f0ca68752c162309858e1..b7842aaafeb20735ee8bfa7fd8e91680afc3de62 100644 (file)
@@ -37,6 +37,9 @@ for i in 0..$*.size-1 do
   elsif $*[i] == "-s"
     memory_size_mb = $*[i+1].to_i
     i = i + 1
+  elsif $*[i] == "-D"
+    num_dma = $*[i+1].to_i
+    i = i + 1
   end
 end
 
index 38ef091775de46ca191acf0b3c95501a4803c146..1d36de878f0e63c87f407fe4a49acf72730f9087 100644 (file)
@@ -107,7 +107,10 @@ public:
     if (m_ports.count(name) != 1){
       cerr << "Port " << name << " has " << m_ports.count(name) << " instances" << endl;
     }
-    assert(m_ports.count(name) == 1); m_ports[name]->registerHitCallback(hit_callback); return m_ports[name]; }
+    assert(m_ports.count(name) == 1); 
+    m_ports[name]->registerHitCallback(hit_callback); 
+    return m_ports[name]; 
+  }
   static Network* getNetwork() { assert(m_network_ptr != NULL); return m_network_ptr; }
   static Topology* getTopology(const string & name) { assert(m_topologies.count(name) == 1); return m_topologies[name]; }
   static CacheMemory* getCache(const string & name) { assert(m_caches.count(name) == 1); return m_caches[name]; }
index 14b2048a04dd3bdb2f15a822b5870ffb096a8d09..70077e7da5f5cdcef8f5c983a563a97e4e77201a 100644 (file)
@@ -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>
@@ -63,6 +65,7 @@ RubyMemory::RubyMemory(const Params *p)
         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;
@@ -81,11 +84,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
@@ -106,7 +125,6 @@ RubyMemory::init()
     //g_debug_ptr->setDebugTime(1);
     //g_debug_ptr->setDebugOutputFile("ruby.debug");
 
-
     g_system_ptr->clearStats();
 
     if (ports.size() == 0) {
@@ -118,6 +136,15 @@ 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
     rubyExitCB = new RubyExitCallback(this);
     registerExitCallback(rubyExitCB);
@@ -141,35 +168,45 @@ 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)
 {
+    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());
+
     // 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);
+        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") {
@@ -184,30 +221,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 two.  This will be
+    // fixed once we unify the configuration systems and Ruby sequencers
+    // directly support M5 ports.
+    //
+    assert(idx/2 < ruby_ports.size());
+    Port *port = new Port(csprintf("%s-port%d", name(), idx), 
+                          this, 
+                          ruby_ports[idx/2]);
 
     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()) {
@@ -221,6 +296,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()) {
@@ -241,7 +328,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 =
@@ -262,6 +348,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;
 
@@ -280,13 +372,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()
@@ -313,6 +450,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 *
index 1bfb135e8cb1cfe7befbf3f952f4d781959f1388..dd0a492f5b2314d0b346d99d187e1d480b50a53b 100644 (file)
@@ -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
@@ -39,6 +40,7 @@
 #include "mem/physical.hh"
 #include "mem/ruby/system/RubyPort.hh"
 #include "params/RubyMemory.hh"
+#include "mem/port.hh"
 
 class RubyExitCallback;
 
@@ -46,18 +48,26 @@ 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);
-        void sendTiming(PacketPtr pkt);
+        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
@@ -110,8 +120,6 @@ class RubyMemory : public PhysicalMemory
                              //options change & M5 determines the
                              //stats file to use
 
-    void hitCallback(PacketPtr pkt, Port *port);
-
     void printStats(std::ostream & out) const;
     void clearStats();
     void printConfig(std::ostream & out) const;
@@ -125,6 +133,14 @@ class RubyMemory : public PhysicalMemory
 
   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 &section);
 };
 
 void ruby_hit_callback(int64_t);
index 7b8e2761365773f8f848ca3fc027193f31e90e2a..000b7d23f9a6f98f3e7900c7371d3181183203d3 100644 (file)
@@ -4,17 +4,25 @@ import subprocess
 from os.path import dirname, join as joinpath
 
 import m5
+from m5.params import *
 
-def generate(config_file, cores=1, memories=1, memory_size=1024):
+def generate(config_file, cores=1, memories=1, memory_size=1024, \
+             cache_size=32768, cache_assoc=8, dmas=1,
+             ruby_tick='1t'):
     default = joinpath(dirname(__file__), '../../src/mem/ruby/config')
     ruby_config = os.environ.get('RUBY_CONFIG', default)
     args = [ "ruby", "-I", ruby_config, joinpath(ruby_config, "print_cfg.rb"),
              "-r", joinpath(ruby_config, config_file), "-p", str(cores),
-             "-m", str(memories), "-s", str(memory_size)]
+             "-m", str(memories), "-s", str(memory_size), "-C", str(cache_size),
+             "-A", str(cache_assoc), "-D", str(dmas)]
 
     temp_config = joinpath(m5.options.outdir, "ruby.config")
     ret = subprocess.call(args, stdout=file(temp_config, "w"))
     if ret != 0:
         raise RuntimeError, "subprocess failed!"
 
-    return m5.objects.RubyMemory(config_file=temp_config, num_cpus=cores)
+    return m5.objects.RubyMemory(clock = ruby_tick,
+                                 config_file = temp_config,
+                                 num_cpus = cores,
+                                 range = AddrRange(str(memory_size)+"MB"),
+                                 num_dmas = dmas)