mem: write combining for ruby protocols
authorTony Gutierrez <anthony.gutierrez@amd.com>
Tue, 19 Jan 2016 19:05:03 +0000 (14:05 -0500)
committerTony Gutierrez <anthony.gutierrez@amd.com>
Tue, 19 Jan 2016 19:05:03 +0000 (14:05 -0500)
This patch adds support for write-combining in ruby.

src/mem/protocol/RubySlicc_Exports.sm
src/mem/protocol/RubySlicc_Types.sm
src/mem/ruby/SConscript
src/mem/ruby/common/DataBlock.cc
src/mem/ruby/common/DataBlock.hh
src/mem/ruby/common/SConscript
src/mem/ruby/common/WriteMask.cc [new file with mode: 0644]
src/mem/ruby/common/WriteMask.hh [new file with mode: 0644]
src/mem/ruby/slicc_interface/RubyRequest.hh
src/mem/ruby/slicc_interface/RubySlicc_Util.hh

index 882102923919cdd140fc7879d7c7cd92c45acb4b..5ee26d65ca2102f96608b0002cadc4e91d06cd62 100644 (file)
@@ -39,12 +39,25 @@ external_type(Addr, primitive="yes");
 external_type(Cycles, primitive="yes", default="Cycles(0)");
 external_type(Tick, primitive="yes", default="0");
 
+structure(WriteMask, external="yes", desc="...") {
+  void clear();
+  bool cmpMask(WriteMask);
+  bool isEmpty();
+  bool isFull();
+  bool isOverlap(WriteMask);
+  void orMask(WriteMask);
+  void fillMask();
+}
+
 structure(DataBlock, external = "yes", desc="..."){
   void clear();
+  void copyPartial(DataBlock, int, int);
+  void copyPartial(DataBlock, WriteMask);
   void atomicPartial(DataBlock, WriteMask);
 }
 
 bool testAndRead(Addr addr, DataBlock datablk, Packet *pkt);
+bool testAndReadMask(Addr addr, DataBlock datablk, WriteMask mask, Packet *pkt);
 bool testAndWrite(Addr addr, DataBlock datablk, Packet *pkt);
 
 // AccessPermission
index 95fa1db170b2b066d9a4f0511d7a55e28de8386e..a6c57e1b05abc14bdee3e5ca998adab71184a316 100644 (file)
@@ -126,6 +126,8 @@ structure(RubyRequest, desc="...", interface="Message", external="yes") {
   int Size,                  desc="size in bytes of access";
   PrefetchBit Prefetch,      desc="Is this a prefetch request";
   int contextId,             desc="this goes away but must be replace with Nilay";
+  WriteMask writeMask,       desc="Writethrough mask";
+  DataBlock WTData,          desc="Writethrough data block";
   int wfid,                  desc="Writethrough wavefront";
   HSAScope scope,            desc="HSA scope";
   HSASegment segment,        desc="HSA segment";
index 3645706db421eadafed957a32bf4f94c92335a9a..16e932432cf1b10f3bed5fe61cd4c65541044084 100644 (file)
@@ -121,6 +121,7 @@ MakeInclude('common/IntVec.hh')
 MakeInclude('common/MachineID.hh')
 MakeInclude('common/NetDest.hh')
 MakeInclude('common/Set.hh')
+MakeInclude('common/WriteMask.hh')
 MakeInclude('filters/AbstractBloomFilter.hh')
 MakeInclude('network/MessageBuffer.hh')
 MakeInclude('structures/Prefetcher.hh')
index fb2e956e01bb2dfac62b1e0a23efbc8d1b48da2d..a4d7f4916ba1adfbafd95bf8f75bfb4865b480d4 100644 (file)
@@ -27,6 +27,8 @@
  */
 
 #include "mem/ruby/common/DataBlock.hh"
+
+#include "mem/ruby/common/WriteMask.hh"
 #include "mem/ruby/system/RubySystem.hh"
 
 DataBlock::DataBlock(const DataBlock &cp)
@@ -56,6 +58,25 @@ DataBlock::equal(const DataBlock& obj) const
     return !memcmp(m_data, obj.m_data, RubySystem::getBlockSizeBytes());
 }
 
+void
+DataBlock::copyPartial(const DataBlock &dblk, const WriteMask &mask)
+{
+    for (int i = 0; i < RubySystem::getBlockSizeBytes(); i++) {
+        if (mask.getMask(i, 1)) {
+            m_data[i] = dblk.m_data[i];
+        }
+    }
+}
+
+void
+DataBlock::atomicPartial(const DataBlock &dblk, const WriteMask &mask)
+{
+    for (int i = 0; i < RubySystem::getBlockSizeBytes(); i++) {
+        m_data[i] = dblk.m_data[i];
+    }
+    mask.performAtomic(m_data);
+}
+
 void
 DataBlock::print(std::ostream& out) const
 {
@@ -77,6 +98,12 @@ DataBlock::getData(int offset, int len) const
     return &m_data[offset];
 }
 
+uint8_t*
+DataBlock::getDataMod(int offset)
+{
+    return &m_data[offset];
+}
+
 void
 DataBlock::setData(const uint8_t *data, int offset, int len)
 {
index 49ce3624adfd858f2c3afc458914c7fd569a43bf..d52b6fa7257458684a241e438c90e5fa57c74698 100644 (file)
@@ -35,6 +35,8 @@
 #include <iomanip>
 #include <iostream>
 
+class WriteMask;
+
 class DataBlock
 {
   public:
@@ -58,8 +60,12 @@ class DataBlock
     void clear();
     uint8_t getByte(int whichByte) const;
     const uint8_t *getData(int offset, int len) const;
+    uint8_t *getDataMod(int offset);
     void setByte(int whichByte, uint8_t data);
     void setData(const uint8_t *data, int offset, int len);
+    void copyPartial(const DataBlock &dblk, int offset, int len);
+    void copyPartial(const DataBlock &dblk, const WriteMask &mask);
+    void atomicPartial(const DataBlock & dblk, const WriteMask & mask);
     bool equal(const DataBlock& obj) const;
     void print(std::ostream& out) const;
 
index 9e809f0d09ec2924bdc976dadc8a01448bc269e9..a19268cbad22d06daaa65837a2facadd11b6280e 100644 (file)
@@ -41,3 +41,4 @@ Source('Histogram.cc')
 Source('IntVec.cc')
 Source('NetDest.cc')
 Source('SubBlock.cc')
+Source('WriteMask.cc')
diff --git a/src/mem/ruby/common/WriteMask.cc b/src/mem/ruby/common/WriteMask.cc
new file mode 100644 (file)
index 0000000..4585077
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012-2015 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.
+ */
+
+#include "mem/ruby/common/WriteMask.hh"
+
+#include <string>
+
+#include "mem/ruby/system/RubySystem.hh"
+
+void
+WriteMask::print(std::ostream& out) const
+{
+    std::string str(mSize,'0');
+    for (int i = 0; i < mSize; i++) {
+        str[i] = mMask[i] ? ('1') : ('0');
+    }
+    out << "dirty mask="
+        << str
+        << std::flush;
+}
+
diff --git a/src/mem/ruby/common/WriteMask.hh b/src/mem/ruby/common/WriteMask.hh
new file mode 100644 (file)
index 0000000..2de02ef
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2012-15 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.
+ */
+
+#ifndef __MEM_RUBY_COMMON_WRITEMASK_HH__
+#define __MEM_RUBY_COMMON_WRITEMASK_HH__
+
+#include <cassert>
+#include <iomanip>
+#include <iostream>
+#include <vector>
+
+#include "mem/ruby/common/TypeDefines.hh"
+#include "mem/ruby/system/RubySystem.hh"
+
+class WriteMask
+{
+  public:
+    WriteMask()
+      : mSize(RubySystem::getBlockSizeBytes()), mMask(mSize, false),
+        mAtomic(false)
+    {}
+
+    WriteMask(int size)
+      : mSize(size), mMask(size, false), mAtomic(false)
+    {}
+
+    WriteMask(int size, std::vector<bool> & mask)
+      : mSize(size), mMask(mask), mAtomic(false)
+    {}
+
+    WriteMask(int size, std::vector<bool> &mask,
+              std::vector<std::pair<int, AtomicOpFunctor*> > atomicOp)
+      : mSize(size), mMask(mask), mAtomic(true), mAtomicOp(atomicOp)
+    {}
+
+    ~WriteMask()
+    {}
+
+    void
+    clear()
+    {
+        mMask = std::vector<bool>(mSize, false);
+    }
+
+    bool
+    test(int offset)
+    {
+        assert(offset < mSize);
+        return mMask[offset] == true;
+    }
+
+    void
+    setMask(int offset, int len)
+    {
+        assert(mSize >= (offset + len));
+        for (int i = 0; i < len; i++) {
+            mMask[offset + i] = true;
+        }
+    }
+    void
+    fillMask()
+    {
+        for (int i = 0; i < mSize; i++) {
+            mMask[i] = true;
+        }
+    }
+
+    bool
+    getMask(int offset, int len) const
+    {
+        bool tmp = true;
+        assert(mSize >= (offset + len));
+        for (int i = 0; i < len; i++) {
+            tmp = tmp & mMask.at(offset + i);
+        }
+        return tmp;
+    }
+
+    bool
+    isOverlap(const WriteMask &readMask) const
+    {
+        bool tmp = false;
+        assert(mSize == readMask.mSize);
+        for (int i = 0; i < mSize; i++) {
+            if (readMask.mMask.at(i)) {
+                tmp = tmp | mMask.at(i);
+            }
+        }
+        return tmp;
+    }
+
+    bool
+    cmpMask(const WriteMask &readMask) const
+    {
+        bool tmp = true;
+        assert(mSize == readMask.mSize);
+        for (int i = 0; i < mSize; i++) {
+            if (readMask.mMask.at(i)) {
+                tmp = tmp & mMask.at(i);
+            }
+        }
+        return tmp;
+    }
+
+    bool isEmpty() const
+    {
+        for (int i = 0; i < mSize; i++) {
+            if (mMask.at(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    bool
+    isFull() const
+    {
+        for (int i = 0; i < mSize; i++) {
+            if (!mMask.at(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    void
+    orMask(const WriteMask & writeMask)
+    {
+        assert(mSize == writeMask.mSize);
+        for (int i = 0; i < mSize; i++) {
+            mMask[i] = (mMask.at(i)) | (writeMask.mMask.at(i));
+        }
+
+        if (writeMask.mAtomic) {
+            mAtomic = true;
+            mAtomicOp = writeMask.mAtomicOp;
+        }
+    }
+
+    void print(std::ostream& out) const;
+
+    void
+    performAtomic(uint8_t * p) const
+    {
+        for (int i = 0; i < mAtomicOp.size(); i++) {
+            int offset = mAtomicOp[i].first;
+            AtomicOpFunctor *fnctr = mAtomicOp[i].second;
+            (*fnctr)(&p[offset]);
+        }
+    }
+
+    void
+    performAtomic(DataBlock & blk) const
+    {
+        for (int i = 0; i < mAtomicOp.size(); i++) {
+            int offset = mAtomicOp[i].first;
+            uint8_t *p = blk.getDataMod(offset);
+            AtomicOpFunctor *fnctr = mAtomicOp[i].second;
+            (*fnctr)(p);
+        }
+    }
+  private:
+    int mSize;
+    std::vector<bool> mMask;
+    bool mAtomic;
+    std::vector<std::pair<int, AtomicOpFunctor*> > mAtomicOp;
+};
+
+inline std::ostream&
+operator<<(std::ostream& out, const WriteMask& obj)
+{
+    obj.print(out);
+    out << std::flush;
+    return out;
+}
+
+#endif // __MEM_RUBY_COMMON_WRITEMASK_HH__
index 73f214a205d490d87e378aa8c0ce9fc3ee3308bd..689d559ce2a8b0736e710898b33ef50042feddab 100644 (file)
@@ -40,6 +40,7 @@
 #include "mem/protocol/RubyRequestType.hh"
 #include "mem/ruby/common/Address.hh"
 #include "mem/ruby/common/DataBlock.hh"
+#include "mem/ruby/common/WriteMask.hh"
 
 class RubyRequest : public Message
 {
@@ -54,6 +55,8 @@ class RubyRequest : public Message
     uint8_t* data;
     PacketPtr pkt;
     ContextID m_contextId;
+    WriteMask m_writeMask;
+    DataBlock m_WTData;
     int m_wfid;
     HSAScope m_scope;
     HSASegment m_segment;
@@ -99,6 +102,8 @@ class RubyRequest : public Message
           data(_data),
           pkt(_pkt),
           m_contextId(_core_id),
+          m_writeMask(_wm_size,_wm_mask),
+          m_WTData(_Data),
           m_wfid(_proc_id),
           m_scope(_scope),
           m_segment(_segment)
@@ -125,6 +130,8 @@ class RubyRequest : public Message
           data(_data),
           pkt(_pkt),
           m_contextId(_core_id),
+          m_writeMask(_wm_size,_wm_mask,_atomicOps),
+          m_WTData(_Data),
           m_wfid(_proc_id),
           m_scope(_scope),
           m_segment(_segment)
index 55f229d206445239574a72e37680d62c956a3c02..4fe065a3f1dc7086c9154caf9b4eb963255e081d 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
+ * Copyright (c) 2013 Advanced Micro Devices, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -41,6 +42,7 @@
 #include "mem/ruby/common/BoolVec.hh"
 #include "mem/ruby/common/DataBlock.hh"
 #include "mem/ruby/common/TypeDefines.hh"
+#include "mem/ruby/common/WriteMask.hh"
 
 inline Cycles zero_time() { return Cycles(0); }
 
@@ -88,6 +90,12 @@ inline int max_tokens()
  * range for the data block contains the address which the packet needs to
  * read, then the data from the data block is written to the packet. True is
  * returned if the data block was read, otherwise false is returned.
+ *
+ * This is used during a functional access "search the world" operation. The
+ * functional access looks in every place that might hold a valid data block
+ * and, if it finds one, checks to see if it is holding the address the access
+ * is searching for. During the access check, the WriteMask could be in any
+ * state, including empty.
  */
 inline bool
 testAndRead(Addr addr, DataBlock& blk, Packet *pkt)
@@ -108,6 +116,36 @@ testAndRead(Addr addr, DataBlock& blk, Packet *pkt)
     return false;
 }
 
+/**
+ * This function accepts an address, a data block, a write mask and a packet.
+ * If the valid address range for the data block contains the address which
+ * the packet needs to read, then the data from the data block is written to
+ * the packet. True is returned if any part of the data block was read,
+ * otherwise false is returned.
+ */
+inline bool
+testAndReadMask(Addr addr, DataBlock& blk, WriteMask& mask, Packet *pkt)
+{
+    Addr pktLineAddr = makeLineAddress(pkt->getAddr());
+    Addr lineAddr = makeLineAddress(addr);
+
+    if (pktLineAddr == lineAddr) {
+        uint8_t *data = pkt->getPtr<uint8_t>();
+        unsigned int size_in_bytes = pkt->getSize();
+        unsigned startByte = pkt->getAddr() - lineAddr;
+        bool was_read = false;
+
+        for (unsigned i = 0; i < size_in_bytes; ++i) {
+            if (mask.test(i + startByte)) {
+                was_read = true;
+                data[i] = blk.getByte(i + startByte);
+            }
+        }
+        return was_read;
+    }
+    return false;
+}
+
 /**
  * This function accepts an address, a data block and a packet. If the address
  * range for the data block contains the address which the packet needs to