mem-ruby: Fix Ruby handling of functional requests
authorTiago Muck <tiago.muck@arm.com>
Thu, 2 May 2019 23:40:08 +0000 (18:40 -0500)
committerTiago Mück <tiago.muck@arm.com>
Thu, 23 Apr 2020 00:23:30 +0000 (00:23 +0000)
This patch addresses multiple cases:

- When a controller has read/write permissions while others have read
  only permissions, the one with r/w permissions performs the read as
  the others may have stale data
- When controllers only have lines with stale or busy access permissions,
  a valid copy of the line may be in a message in transit in the network
  or in a message buffer (not seen by the controller yet). In this case,
  we forward the functional request accordingly.
- Sequencer messages should not accept functional reads
- Functional writes also update the packet data on the sequencer
  outstanding request lists and the cpu-side response queue.

Change-Id: I6b0656f1a2b81d41bdcf6c783dfa522a77393981
Signed-off-by: Tiago Mück <tiago.muck@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/22022
Tested-by: Gem5 Cloud Project GCB service account <345032938727@cloudbuild.gserviceaccount.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: John Alsop <johnathan.alsop@amd.com>
Reviewed-by: Jason Lowe-Power <power.jg@gmail.com>
Maintainer: Jason Lowe-Power <power.jg@gmail.com>

src/mem/ruby/protocol/RubySlicc_Exports.sm
src/mem/ruby/slicc_interface/AbstractController.hh
src/mem/ruby/slicc_interface/RubyRequest.cc
src/mem/ruby/system/RubyPort.cc
src/mem/ruby/system/RubyPort.hh
src/mem/ruby/system/RubySystem.cc
src/mem/ruby/system/Sequencer.cc
src/mem/ruby/system/Sequencer.hh
src/mem/slicc/symbols/StateMachine.py

index 8e17f98498af033d71812056b3d0dbb66f854e42..08d30cfee3b6556b3c7d33120ecf88af34386256 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2019 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 1999-2012 Mark D. Hill and David A. Wood
  * Copyright (c) 2011 Advanced Micro Devices, Inc.
  * All rights reserved.
@@ -291,7 +303,7 @@ structure(SequencerMsg, desc="...", interface="Message") {
   MessageSizeType MessageSize, default="MessageSizeType_Request_Control";
 
   bool functionalRead(Packet *pkt) {
-    return testAndRead(PhysicalAddress, DataBlk, pkt);
+    return false;
   }
 
   bool functionalWrite(Packet *pkt) {
index 48f9618ae8c3733fc4fd9d9e9f831f20623616bf..15aff12b1d6a0e82b5ee4a8d4fa0011dbf1ecfca 100644 (file)
@@ -62,6 +62,7 @@
 
 class Network;
 class GPUCoalescer;
+class DMASequencer;
 
 // used to communicate that an in_port peeked the wrong message type
 class RejectException: public std::exception
@@ -101,6 +102,7 @@ class AbstractController : public ClockedObject, public Consumer
 
     virtual void recordCacheTrace(int cntrl, CacheRecorder* tr) = 0;
     virtual Sequencer* getCPUSequencer() const = 0;
+    virtual DMASequencer* getDMASequencer() const = 0;
     virtual GPUCoalescer* getGPUCoalescer() const = 0;
 
     // This latency is used by the sequencer when enqueueing requests.
index dd26ad645489647ea9280c5a2f281d3d90777fdc..f30bde540f4f163053cb331a03da0b0a7cbf02be 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2019 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 2011 Mark D. Hill and David A. Wood
  * All rights reserved.
  *
@@ -30,6 +42,8 @@
 
 #include <iostream>
 
+#include "mem/ruby/slicc_interface/RubySlicc_Util.hh"
+
 using namespace std;
 
 void
@@ -67,6 +81,9 @@ RubyRequest::functionalWrite(Packet *pkt)
     // has to overwrite the data for the timing request, even if the
     // timing request has still not been ordered globally.
 
+    if (!data)
+      return false;
+
     Addr wBase = pkt->getAddr();
     Addr wTail = wBase + pkt->getSize();
     Addr mBase = m_PhysicalAddress;
@@ -81,5 +98,8 @@ RubyRequest::functionalWrite(Packet *pkt)
         data[i - mBase] = pktData[i - wBase];
     }
 
+    // also overwrite the WTData
+    testAndWrite(m_PhysicalAddress, m_WTData, pkt);
+
     return cBase < cTail;
 }
index a6cee054a63ba81d341635ea750f52fdfc03289d..7632bbb4eac52d305cc5f64f34b35f6e97629e3f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013 ARM Limited
+ * Copyright (c) 2012-2013,2019 ARM Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -408,6 +408,12 @@ RubyPort::MemSlavePort::recvFunctional(PacketPtr pkt)
 
         // turn packet around to go back to requester if response expected
         if (needsResponse) {
+            // The pkt is already turned into a reponse if the directory
+            // forwarded the request to the memory controller (see
+            // AbstractController::functionalMemoryWrite and
+            // AbstractMemory::functionalAccess)
+            if (!pkt->isResponse())
+                pkt->makeResponse();
             pkt->setFunctionalResponseStatus(accessSucceeded);
         }
 
@@ -620,3 +626,16 @@ RubyPort::PioMasterPort::recvRangeChange()
         r.pioSlavePort.sendRangeChange();
     }
 }
+
+
+int
+RubyPort::functionalWrite(Packet *func_pkt)
+{
+    int num_written = 0;
+    for (auto port : slave_ports) {
+        if (port->trySatisfyFunctional(func_pkt)) {
+            num_written += 1;
+        }
+    }
+    return num_written;
+}
\ No newline at end of file
index b57828d5872b19b63d073bda9568307d38fbf484..b14e707465d7b41fdd912eb5ef41950334f8856d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013 ARM Limited
+ * Copyright (c) 2012-2013,2019 ARM Limited
  * All rights reserved.
  *
  * The license below extends only to copyright in the software and shall
@@ -166,6 +166,8 @@ class RubyPort : public ClockedObject
 
     bool isCPUSequencer() { return m_isCPUSequencer; }
 
+    virtual int functionalWrite(Packet *func_pkt);
+
   protected:
     void trySendRetries();
     void ruby_hit_callback(PacketPtr pkt);
index 83fa4c7ce6277701977a76cab49b3ae4afd446eb..57d49667e7da11777e43b1eedcd6e31c39f0d7a3 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2019 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 1999-2011 Mark D. Hill and David A. Wood
  * All rights reserved.
  *
@@ -40,6 +52,8 @@
 #include "debug/RubySystem.hh"
 #include "mem/ruby/common/Address.hh"
 #include "mem/ruby/network/Network.hh"
+#include "mem/ruby/system/DMASequencer.hh"
+#include "mem/ruby/system/Sequencer.hh"
 #include "mem/simple_mem.hh"
 #include "sim/eventq.hh"
 #include "sim/simulate.hh"
@@ -409,25 +423,39 @@ RubySystem::functionalRead(PacketPtr pkt)
     unsigned int num_ro = 0;
     unsigned int num_rw = 0;
     unsigned int num_busy = 0;
+    unsigned int num_maybe_stale = 0;
     unsigned int num_backing_store = 0;
     unsigned int num_invalid = 0;
 
+    AbstractController *ctrl_ro = nullptr;
+    AbstractController *ctrl_rw = nullptr;
+    AbstractController *ctrl_backing_store = nullptr;
+
     // In this loop we count the number of controllers that have the given
     // address in read only, read write and busy states.
     for (unsigned int i = 0; i < num_controllers; ++i) {
         access_perm = m_abs_cntrl_vec[i]-> getAccessPermission(line_address);
-        if (access_perm == AccessPermission_Read_Only)
+        if (access_perm == AccessPermission_Read_Only){
             num_ro++;
-        else if (access_perm == AccessPermission_Read_Write)
+            if (ctrl_ro == nullptr) ctrl_ro = m_abs_cntrl_vec[i];
+        }
+        else if (access_perm == AccessPermission_Read_Write){
             num_rw++;
+            if (ctrl_rw == nullptr) ctrl_rw = m_abs_cntrl_vec[i];
+        }
         else if (access_perm == AccessPermission_Busy)
             num_busy++;
-        else if (access_perm == AccessPermission_Backing_Store)
+        else if (access_perm == AccessPermission_Maybe_Stale)
+            num_maybe_stale++;
+        else if (access_perm == AccessPermission_Backing_Store) {
             // See RubySlicc_Exports.sm for details, but Backing_Store is meant
             // to represent blocks in memory *for Broadcast/Snooping protocols*,
             // where memory has no idea whether it has an exclusive copy of data
             // or not.
             num_backing_store++;
+            if (ctrl_backing_store == nullptr)
+                ctrl_backing_store = m_abs_cntrl_vec[i];
+        }
         else if (access_perm == AccessPermission_Invalid ||
                  access_perm == AccessPermission_NotPresent)
             num_invalid++;
@@ -443,13 +471,8 @@ RubySystem::functionalRead(PacketPtr pkt)
     // it only if it's not in the cache hierarchy at all.
     if (num_invalid == (num_controllers - 1) && num_backing_store == 1) {
         DPRINTF(RubySystem, "only copy in Backing_Store memory, read from it\n");
-        for (unsigned int i = 0; i < num_controllers; ++i) {
-            access_perm = m_abs_cntrl_vec[i]->getAccessPermission(line_address);
-            if (access_perm == AccessPermission_Backing_Store) {
-                m_abs_cntrl_vec[i]->functionalRead(line_address, pkt);
-                return true;
-            }
-        }
+        ctrl_backing_store->functionalRead(line_address, pkt);
+        return true;
     } else if (num_ro > 0 || num_rw >= 1) {
         if (num_rw > 1) {
             // We iterate over the vector of abstract controllers, and return
@@ -462,19 +485,34 @@ RubySystem::functionalRead(PacketPtr pkt)
         // exists somewhere in the caching hierarchy, then you want to read any
         // valid RO or RW block.  In directory protocols, same thing, you want
         // to read any valid readable copy of the block.
-        DPRINTF(RubySystem, "num_busy = %d, num_ro = %d, num_rw = %d\n",
-                num_busy, num_ro, num_rw);
-        // In this loop, we try to figure which controller has a read only or
-        // a read write copy of the given address. Any valid copy would suffice
-        // for a functional read.
-        for (unsigned int i = 0;i < num_controllers;++i) {
-            access_perm = m_abs_cntrl_vec[i]->getAccessPermission(line_address);
-            if (access_perm == AccessPermission_Read_Only ||
-                access_perm == AccessPermission_Read_Write) {
-                m_abs_cntrl_vec[i]->functionalRead(line_address, pkt);
+        DPRINTF(RubySystem, "num_maybe_stale=%d, num_busy = %d, num_ro = %d, "
+                            "num_rw = %d\n",
+                num_maybe_stale, num_busy, num_ro, num_rw);
+        // Use the copy from the controller with read/write permission (if
+        // any), otherwise use get the first read only found
+        if (ctrl_rw) {
+            ctrl_rw->functionalRead(line_address, pkt);
+        } else {
+            assert(ctrl_ro);
+            ctrl_ro->functionalRead(line_address, pkt);
+        }
+        return true;
+    } else if ((num_busy + num_maybe_stale) > 0) {
+        // No controller has a valid copy of the block, but a transient or
+        // stale state indicates a valid copy should be in transit in the
+        // network or in a message buffer waiting to be handled
+        DPRINTF(RubySystem, "Controllers functionalRead lookup "
+                            "(num_maybe_stale=%d, num_busy = %d)\n",
+                num_maybe_stale, num_busy);
+        for (unsigned int i = 0; i < num_controllers;++i) {
+            if (m_abs_cntrl_vec[i]->functionalReadBuffers(pkt))
                 return true;
-            }
         }
+        DPRINTF(RubySystem, "Network functionalRead lookup "
+                            "(num_maybe_stale=%d, num_busy = %d)\n",
+                num_maybe_stale, num_busy);
+        if (m_network->functionalRead(pkt))
+            return true;
     }
 
     return false;
@@ -506,6 +544,17 @@ RubySystem::functionalWrite(PacketPtr pkt)
             num_functional_writes +=
                 m_abs_cntrl_vec[i]->functionalWrite(line_addr, pkt);
         }
+
+        // Also updates requests pending in any sequencer associated
+        // with the controller
+        if (m_abs_cntrl_vec[i]->getCPUSequencer()) {
+            num_functional_writes +=
+                m_abs_cntrl_vec[i]->getCPUSequencer()->functionalWrite(pkt);
+        }
+        if (m_abs_cntrl_vec[i]->getDMASequencer()) {
+            num_functional_writes +=
+                m_abs_cntrl_vec[i]->getDMASequencer()->functionalWrite(pkt);
+        }
     }
 
     num_functional_writes += m_network->functionalWrite(pkt);
index f81578700372f67b5813f02d7b09f9b44ac27838..1f538c37581879546f92d8fb3abcacd6fd1a2e82 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2019 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
  * Copyright (c) 2013 Advanced Micro Devices, Inc.
  * All rights reserved.
@@ -112,6 +124,21 @@ Sequencer::wakeup()
     }
 }
 
+int
+Sequencer::functionalWrite(Packet *func_pkt)
+{
+    int num_written = RubyPort::functionalWrite(func_pkt);
+
+    for (const auto &table_entry : m_RequestTable) {
+        for (const auto& seq_req : table_entry.second) {
+            if (seq_req.functionalWrite(func_pkt))
+                ++num_written;
+        }
+    }
+
+    return num_written;
+}
+
 void Sequencer::resetStats()
 {
     m_outstandReqHist.reset();
index 71ffa99bf204d0cdd9e79017aefd555ef67a04ff..05694788eb5848e85eb4ab25bba5bfe42c1c086f 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2019 ARM Limited
+ * All rights reserved.
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
  * All rights reserved.
  *
@@ -52,6 +64,15 @@ struct SequencerRequest
                 : pkt(_pkt), m_type(_m_type), m_second_type(_m_second_type),
                   issue_time(_issue_time)
     {}
+
+    bool functionalWrite(Packet *func_pkt) const
+    {
+        // Follow-up on RubyRequest::functionalWrite
+        // This makes sure the hitCallback won't overrite the value we
+        // expect to find
+        assert(func_pkt->isWrite());
+        return func_pkt->trySatisfyFunctional(pkt);
+    }
 };
 
 std::ostream& operator<<(std::ostream& out, const SequencerRequest& obj);
@@ -103,6 +124,8 @@ class Sequencer : public RubyPort
     void invalidateSC(Addr address);
     int coreId() const { return m_coreId; }
 
+    virtual int functionalWrite(Packet *func_pkt) override;
+
     void recordRequestType(SequencerRequestType requestType);
     Stats::Histogram& getOutstandReqHist() { return m_outstandReqHist; }
 
index ee6b5fb7d5820803a0726a3e8fde7b1442f45f8b..0904ac63fc658f28b89d1000d9b96a3da195987b 100644 (file)
@@ -323,6 +323,7 @@ class $c_ident : public AbstractController
 
     void recordCacheTrace(int cntrl, CacheRecorder* tr);
     Sequencer* getCPUSequencer() const;
+    DMASequencer* getDMASequencer() const;
     GPUCoalescer* getGPUCoalescer() const;
 
     bool functionalReadBuffers(PacketPtr&);
@@ -702,6 +703,12 @@ $c_ident::init()
                 assert(param.pointer)
                 seq_ident = "m_%s_ptr" % param.ident
 
+        dma_seq_ident = "NULL"
+        for param in self.config_parameters:
+            if param.ident == "dma_sequencer":
+                assert(param.pointer)
+                dma_seq_ident = "m_%s_ptr" % param.ident
+
         coal_ident = "NULL"
         for param in self.config_parameters:
             if param.ident == "coalescer":
@@ -728,6 +735,28 @@ $c_ident::getCPUSequencer() const
 {
     return NULL;
 }
+''')
+
+        if dma_seq_ident != "NULL":
+            code('''
+DMASequencer*
+$c_ident::getDMASequencer() const
+{
+    if (NULL != $dma_seq_ident) {
+        return $dma_seq_ident;
+    } else {
+        return NULL;
+    }
+}
+''')
+        else:
+            code('''
+
+DMASequencer*
+$c_ident::getDMASequencer() const
+{
+    return NULL;
+}
 ''')
 
         if coal_ident != "NULL":