rubytest: seperated read and write ports.
authorBrad Beckmann <Brad.Beckmann@amd.com>
Fri, 6 Apr 2012 20:47:06 +0000 (13:47 -0700)
committerBrad Beckmann <Brad.Beckmann@amd.com>
Fri, 6 Apr 2012 20:47:06 +0000 (13:47 -0700)
This patch allows the ruby tester to support protocols where the i-cache and d-cache
are managed by seperate controllers.

configs/example/ruby_random_test.py
src/cpu/testers/rubytest/Check.cc
src/cpu/testers/rubytest/Check.hh
src/cpu/testers/rubytest/CheckTable.cc
src/cpu/testers/rubytest/CheckTable.hh
src/cpu/testers/rubytest/RubyTester.cc
src/cpu/testers/rubytest/RubyTester.hh
src/cpu/testers/rubytest/RubyTester.py
src/mem/ruby/system/Sequencer.py

index cb6a7477613f3865d02b09671a7fab594f5a87b3..b4d32b72cca3b625d01a8c92787abce40fd247c5 100644 (file)
@@ -89,7 +89,8 @@ if buildEnv['PROTOCOL'] == 'MOESI_hammer':
 
 tester = RubyTester(check_flush = check_flush,
                     checks_to_complete = options.checks,
-                    wakeup_frequency = options.wakeup_freq)
+                    wakeup_frequency = options.wakeup_freq,
+                    num_cpus = options.num_cpus)
 
 #
 # Create the M5 system.  Note that the Memory Object isn't
@@ -110,9 +111,12 @@ system.ruby.randomization = True
 
 for ruby_port in system.ruby._cpu_ruby_ports:
     #
-    # Tie the ruby tester ports to the ruby cpu ports
+    # Tie the ruby tester ports to the ruby cpu read and write ports
     #
-    tester.cpuPort = ruby_port.slave
+    if ruby_port.support_data_reqs:
+         tester.cpuDataPort = ruby_port.slave
+    if ruby_port.support_inst_reqs:
+         tester.cpuInstPort = ruby_port.slave
 
     #
     # Tell each sequencer this is the ruby tester so that it
index 2444a14ab72c447fd92e64500d30ede7627fe7f5..6f119af06737eb13cb6bcd59e17738a181fcfa4c 100644 (file)
@@ -36,8 +36,9 @@
 typedef RubyTester::SenderState SenderState;
 
 Check::Check(const Address& address, const Address& pc,
-             int _num_cpu_sequencers, RubyTester* _tester)
-    : m_num_cpu_sequencers(_num_cpu_sequencers), m_tester_ptr(_tester)
+             int _num_writers, int _num_readers, RubyTester* _tester)
+    : m_num_writers(_num_writers), m_num_readers(_num_readers),
+      m_tester_ptr(_tester)
 {
     m_status = TesterStatus_Idle;
 
@@ -80,9 +81,9 @@ Check::initiatePrefetch()
 {
     DPRINTF(RubyTest, "initiating prefetch\n");
 
-    int index = random() % m_num_cpu_sequencers;
+    int index = random() % m_num_readers;
     RubyTester::CpuPort* port =
-        safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));
+      safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getReadableCpuPort(index));
 
     Request::Flags flags;
     flags.set(Request::PREFETCH);
@@ -93,8 +94,8 @@ Check::initiatePrefetch()
     if ((random() & 0x7) != 0) {
         cmd = MemCmd::ReadReq;
 
-        // 50% chance that the request will be an instruction fetch
-        if ((random() & 0x1) == 0) {
+        // if necessary, make the request an instruction fetch
+        if (port->type == RubyTester::CpuPort::InstOnly) {
             flags.set(Request::INST_FETCH);
         }
     } else {
@@ -135,9 +136,9 @@ Check::initiateFlush()
 
     DPRINTF(RubyTest, "initiating Flush\n");
 
-    int index = random() % m_num_cpu_sequencers;
+    int index = random() % m_num_writers;
     RubyTester::CpuPort* port =
-        safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));
+      safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getWritableCpuPort(index));
 
     Request::Flags flags;
 
@@ -166,9 +167,9 @@ Check::initiateAction()
     DPRINTF(RubyTest, "initiating Action\n");
     assert(m_status == TesterStatus_Idle);
 
-    int index = random() % m_num_cpu_sequencers;
+    int index = random() % m_num_writers;
     RubyTester::CpuPort* port =
-        safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));
+      safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getWritableCpuPort(index));
 
     Request::Flags flags;
 
@@ -231,14 +232,14 @@ Check::initiateCheck()
     DPRINTF(RubyTest, "Initiating Check\n");
     assert(m_status == TesterStatus_Ready);
 
-    int index = random() % m_num_cpu_sequencers;
+    int index = random() % m_num_readers;
     RubyTester::CpuPort* port =
-        safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getCpuPort(index));
+      safe_cast<RubyTester::CpuPort*>(m_tester_ptr->getReadableCpuPort(index));
 
     Request::Flags flags;
 
-    // 50% chance that the request will be an instruction fetch
-    if ((random() & 0x1) == 0) {
+    // If necessary, make the request an instruction fetch
+    if (port->type == RubyTester::CpuPort::InstOnly) {
         flags.set(Request::INST_FETCH);
     }
 
@@ -363,7 +364,7 @@ Check::pickInitiatingNode()
 {
     assert(m_status == TesterStatus_Idle || m_status == TesterStatus_Ready);
     m_status = TesterStatus_Idle;
-    m_initiatingNode = (random() % m_num_cpu_sequencers);
+    m_initiatingNode = (random() % m_num_writers);
     DPRINTF(RubyTest, "picked initiating node %d\n", m_initiatingNode);
     m_store_count = 0;
 }
index db1485548ac90d7aba27d0ae83727463646bb89e..1d84b446b3bb984f8e09db6abb95c6127528e4c2 100644 (file)
@@ -46,8 +46,8 @@ const int CHECK_SIZE = (1 << CHECK_SIZE_BITS);
 class Check
 {
   public:
-    Check(const Address& address, const Address& pc, int _num_cpu_sequencer,
-          RubyTester* _tester);
+    Check(const Address& address, const Address& pc, int _num_writers,
+          int _num_readers, RubyTester* _tester);
 
     void initiate(); // Does Action or Check or nether
     void performCallback(NodeID proc, SubBlock* data);
@@ -74,7 +74,8 @@ class Check
     Address m_address;
     Address m_pc;
     RubyAccessMode m_access_mode;
-    int m_num_cpu_sequencers;
+    int m_num_writers;
+    int m_num_readers;
     RubyTester* m_tester_ptr;
 };
 
index f3335b48c7e5010b0adb7470e8ab4e12608ab80d..b4860b62b425eb54241a7cf91c38826fbcf26f21 100644 (file)
@@ -32,8 +32,9 @@
 #include "cpu/testers/rubytest/CheckTable.hh"
 #include "debug/RubyTest.hh"
 
-CheckTable::CheckTable(int _num_cpu_sequencers, RubyTester* _tester)
-    : m_num_cpu_sequencers(_num_cpu_sequencers), m_tester_ptr(_tester)
+CheckTable::CheckTable(int _num_writers, int _num_readers, RubyTester* _tester)
+    : m_num_writers(_num_writers), m_num_readers(_num_readers),
+      m_tester_ptr(_tester)
 {
     physical_address_t physical = 0;
     Address address;
@@ -94,7 +95,7 @@ CheckTable::addCheck(const Address& address)
     }
 
     Check* check_ptr = new Check(address, Address(100 + m_check_vector.size()),
-                                 m_num_cpu_sequencers, m_tester_ptr);
+                                 m_num_writers, m_num_readers, m_tester_ptr);
     for (int i = 0; i < CHECK_SIZE; i++) {
         // Insert it once per byte
         m_lookup_map[Address(address.getAddress() + i)] = check_ptr;
index 5a4ead337a4af61b4c7ac5163d0c0fb6d265bf56..35ea7440a13a4cc107f00021e6de12f29d0b1f34 100644 (file)
@@ -43,7 +43,7 @@ class RubyTester;
 class CheckTable
 {
   public:
-    CheckTable(int _num_cpu_sequencers, RubyTester* _tester);
+    CheckTable(int _num_writers, int _num_readers, RubyTester* _tester);
     ~CheckTable();
 
     Check* getRandomCheck();
@@ -66,7 +66,8 @@ class CheckTable
     std::vector<Check*> m_check_vector;
     m5::hash_map<Address, Check*> m_lookup_map;
 
-    int m_num_cpu_sequencers;
+    int m_num_writers;
+    int m_num_readers;
     RubyTester* m_tester_ptr;
 };
 
index e1942cf6162b7bbe386e7a034843c2c7581dd789..7cc892166c7a52611995e3137f0e5387cd28617c 100644 (file)
 RubyTester::RubyTester(const Params *p)
   : MemObject(p), checkStartEvent(this),
     _masterId(p->system->getMasterId(name())),
+    m_num_cpus(p->num_cpus),
     m_checks_to_complete(p->checks_to_complete),
     m_deadlock_threshold(p->deadlock_threshold),
     m_wakeup_frequency(p->wakeup_frequency),
-    m_check_flush(p->check_flush)
+    m_check_flush(p->check_flush),
+    m_num_inst_ports(p->port_cpuInstPort_connection_count)
 {
     m_checks_completed = 0;
 
-    // create the ports
-    for (int i = 0; i < p->port_cpuPort_connection_count; ++i) {
-        ports.push_back(new CpuPort(csprintf("%s-port%d", name(), i),
-                                    this, i));
+    //
+    // Create the requested inst and data ports and place them on the
+    // appropriate read and write port lists.  The reason for the subtle
+    // difference between inst and data ports vs. read and write ports is
+    // from the tester's perspective, it only needs to know whether a port
+    // supports reads (checks) or writes (actions).  Meanwhile, the protocol
+    // controllers have data ports (support read and writes) or inst ports
+    // (support only reads).
+    // Note: the inst ports are the lowest elements of the readPort vector,
+    // then the data ports are added to the readPort vector
+    //
+    for (int i = 0; i < p->port_cpuInstPort_connection_count; ++i) {
+        readPorts.push_back(new CpuPort(csprintf("%s-instPort%d", name(), i),
+                                        this, i,
+                                        RubyTester::CpuPort::InstOnly));
+    }
+    for (int i = 0; i < p->port_cpuDataPort_connection_count; ++i) {
+        CpuPort *port = NULL;
+        port = new CpuPort(csprintf("%s-dataPort%d", name(), i), this, i,
+                           RubyTester::CpuPort::DataOnly);
+        readPorts.push_back(port);
+        writePorts.push_back(port);
     }
 
     // add the check start event to the event queue
@@ -73,37 +93,57 @@ RubyTester::RubyTester(const Params *p)
 RubyTester::~RubyTester()
 {
     delete m_checkTable_ptr;
-    for (int i = 0; i < ports.size(); i++)
-        delete ports[i];
+    // Only delete the readPorts since the writePorts are just a subset
+    for (int i = 0; i < readPorts.size(); i++)
+        delete readPorts[i];
 }
 
 void
 RubyTester::init()
 {
-    assert(ports.size() > 0);
+    assert(writePorts.size() > 0 && readPorts.size() > 0);
 
-    m_last_progress_vector.resize(ports.size());
+    m_last_progress_vector.resize(m_num_cpus);
     for (int i = 0; i < m_last_progress_vector.size(); i++) {
         m_last_progress_vector[i] = 0;
     }
 
-    m_num_cpu_sequencers = ports.size();
+    m_num_writers = writePorts.size();
+    m_num_readers = readPorts.size();
 
-    m_checkTable_ptr = new CheckTable(m_num_cpu_sequencers, this);
+    m_checkTable_ptr = new CheckTable(m_num_writers, m_num_readers, this);
 }
 
 MasterPort &
 RubyTester::getMasterPort(const std::string &if_name, int idx)
 {
-    if (if_name != "cpuPort") {
+    if (if_name != "cpuInstPort" && if_name != "cpuDataPort") {
         // pass it along to our super class
         return MemObject::getMasterPort(if_name, idx);
     } else {
-        if (idx >= static_cast<int>(ports.size())) {
-            panic("RubyTester::getMasterPort: unknown index %d\n", idx);
+        if (if_name == "cpuInstPort") {
+            printf("print getting inst port %d\n", idx);
+            if (idx > m_num_inst_ports) {
+                panic("RubyTester::getMasterPort: unknown inst port idx %d\n",
+                      idx);
+            }
+            //
+            // inst ports directly map to the lowest readPort elements
+            //
+            return *readPorts[idx];
+        } else {
+            assert(if_name == "cpuDataPort");
+            //
+            // add the inst port offset to translate to the correct read port
+            // index
+            //
+            int read_idx = idx + m_num_inst_ports;
+            if (read_idx >= static_cast<int>(readPorts.size())) {
+                panic("RubyTester::getMasterPort: unknown data port idx %d\n",
+                      idx);
+            }
+            return *readPorts[read_idx];
         }
-
-        return *ports[idx];
     }
 }
 
@@ -137,11 +177,19 @@ RubyTester::CpuPort::recvTiming(PacketPtr pkt)
 }
 
 MasterPort*
-RubyTester::getCpuPort(int idx)
+RubyTester::getReadableCpuPort(int idx)
+{
+    assert(idx >= 0 && idx < readPorts.size());
+
+    return readPorts[idx];
+}
+
+MasterPort*
+RubyTester::getWritableCpuPort(int idx)
 {
-    assert(idx >= 0 && idx < ports.size());
+    assert(idx >= 0 && idx < writePorts.size());
 
-    return ports[idx];
+    return writePorts[idx];
 }
 
 void
index b24dddd836242b429ae84f9a4a8fcb91854ba813..82698f201a803e6040b7f21182ccf61a7af56e7f 100644 (file)
@@ -51,11 +51,28 @@ class RubyTester : public MemObject
         RubyTester *tester;
 
       public:
-        CpuPort(const std::string &_name, RubyTester *_tester, int _idx)
-            : MasterPort(_name, _tester), tester(_tester), idx(_idx)
+        //
+        // Currently, each instatiation of the RubyTester::CpuPort supports
+        // only instruction or data requests, not both.  However, for those
+        // RubyPorts that support both types of requests, separate InstOnly
+        // and DataOnly CpuPorts will map to that RubyPort
+        //
+        enum Type
+        {
+            // Port supports only instruction requests
+            InstOnly,
+            // Port supports only data requests
+            DataOnly
+        };
+
+        CpuPort(const std::string &_name, RubyTester *_tester, int _idx,
+                Type _type)
+            : MasterPort(_name, _tester), tester(_tester), idx(_idx),
+              type(_type)
         {}
 
         int idx;
+        Type type;
 
       protected:
         virtual bool recvTiming(PacketPtr pkt);
@@ -90,7 +107,8 @@ class RubyTester : public MemObject
     virtual MasterPort &getMasterPort(const std::string &if_name,
                                       int idx = -1);
 
-    MasterPort* getCpuPort(int idx);
+    MasterPort* getReadableCpuPort(int idx);
+    MasterPort* getWritableCpuPort(int idx);
 
     virtual void init();
 
@@ -136,13 +154,17 @@ class RubyTester : public MemObject
     CheckTable* m_checkTable_ptr;
     std::vector<Time> m_last_progress_vector;
 
+    int m_num_cpus;
     uint64 m_checks_completed;
-    std::vector<CpuPort*> ports;
+    std::vector<CpuPort*> writePorts;
+    std::vector<CpuPort*> readPorts;
     uint64 m_checks_to_complete;
     int m_deadlock_threshold;
-    int m_num_cpu_sequencers;
+    int m_num_writers;
+    int m_num_readers;
     int m_wakeup_frequency;
     bool m_check_flush;
+    int m_num_inst_ports;
 };
 
 inline std::ostream&
index 6518862e9287ad9c4c83f3b4913bc1a25ec1bb2f..2eaeb8efd5c93fa348541faa861532a2e0e0c403 100644 (file)
@@ -32,7 +32,9 @@ from m5.proxy import *
 
 class RubyTester(MemObject):
     type = 'RubyTester'
-    cpuPort = VectorMasterPort("the cpu ports")
+    num_cpus = Param.Int("number of cpus / RubyPorts")
+    cpuDataPort = VectorMasterPort("the cpu data cache ports")
+    cpuInstPort = VectorMasterPort("the cpu inst cache ports")
     checks_to_complete = Param.Int(100, "checks to complete")
     deadlock_threshold = Param.Int(50000, "how often to check for deadlock")
     wakeup_frequency = Param.Int(10, "number of cycles between wakeups")
index 02686d33f88ffa8aa294a082aae7ada8110bb372..79cf9709eed681bd0b126d2273dd50d191a7c660 100644 (file)
@@ -44,6 +44,9 @@ class RubyPort(MemObject):
         "should the rubyport atomically update phys_mem")
     ruby_system = Param.RubySystem("")
     system = Param.System(Parent.any, "system object")
+    support_data_reqs = Param.Bool(True, "data cache requests supported")
+    support_inst_reqs = Param.Bool(True, "inst cache requests supported")
+
 
 class RubyPortProxy(RubyPort):
     type = 'RubyPortProxy'