From 28e353e0403ea379d244a418e8dc8ee0b48187cf Mon Sep 17 00:00:00 2001 From: Tony Gutierrez Date: Tue, 19 Jan 2016 14:05:03 -0500 Subject: [PATCH] mem: write combining for ruby protocols This patch adds support for write-combining in ruby. --- src/mem/protocol/RubySlicc_Exports.sm | 13 ++ src/mem/protocol/RubySlicc_Types.sm | 2 + src/mem/ruby/SConscript | 1 + src/mem/ruby/common/DataBlock.cc | 27 +++ src/mem/ruby/common/DataBlock.hh | 6 + src/mem/ruby/common/SConscript | 1 + src/mem/ruby/common/WriteMask.cc | 46 ++++ src/mem/ruby/common/WriteMask.hh | 202 ++++++++++++++++++ src/mem/ruby/slicc_interface/RubyRequest.hh | 7 + .../ruby/slicc_interface/RubySlicc_Util.hh | 38 ++++ 10 files changed, 343 insertions(+) create mode 100644 src/mem/ruby/common/WriteMask.cc create mode 100644 src/mem/ruby/common/WriteMask.hh diff --git a/src/mem/protocol/RubySlicc_Exports.sm b/src/mem/protocol/RubySlicc_Exports.sm index 882102923..5ee26d65c 100644 --- a/src/mem/protocol/RubySlicc_Exports.sm +++ b/src/mem/protocol/RubySlicc_Exports.sm @@ -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 diff --git a/src/mem/protocol/RubySlicc_Types.sm b/src/mem/protocol/RubySlicc_Types.sm index 95fa1db17..a6c57e1b0 100644 --- a/src/mem/protocol/RubySlicc_Types.sm +++ b/src/mem/protocol/RubySlicc_Types.sm @@ -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"; diff --git a/src/mem/ruby/SConscript b/src/mem/ruby/SConscript index 3645706db..16e932432 100644 --- a/src/mem/ruby/SConscript +++ b/src/mem/ruby/SConscript @@ -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') diff --git a/src/mem/ruby/common/DataBlock.cc b/src/mem/ruby/common/DataBlock.cc index fb2e956e0..a4d7f4916 100644 --- a/src/mem/ruby/common/DataBlock.cc +++ b/src/mem/ruby/common/DataBlock.cc @@ -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) { diff --git a/src/mem/ruby/common/DataBlock.hh b/src/mem/ruby/common/DataBlock.hh index 49ce3624a..d52b6fa72 100644 --- a/src/mem/ruby/common/DataBlock.hh +++ b/src/mem/ruby/common/DataBlock.hh @@ -35,6 +35,8 @@ #include #include +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; diff --git a/src/mem/ruby/common/SConscript b/src/mem/ruby/common/SConscript index 9e809f0d0..a19268cba 100644 --- a/src/mem/ruby/common/SConscript +++ b/src/mem/ruby/common/SConscript @@ -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 index 000000000..4585077a6 --- /dev/null +++ b/src/mem/ruby/common/WriteMask.cc @@ -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 + +#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 index 000000000..2de02ef74 --- /dev/null +++ b/src/mem/ruby/common/WriteMask.hh @@ -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 +#include +#include +#include + +#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 & mask) + : mSize(size), mMask(mask), mAtomic(false) + {} + + WriteMask(int size, std::vector &mask, + std::vector > atomicOp) + : mSize(size), mMask(mask), mAtomic(true), mAtomicOp(atomicOp) + {} + + ~WriteMask() + {} + + void + clear() + { + mMask = std::vector(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 mMask; + bool mAtomic; + std::vector > 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__ diff --git a/src/mem/ruby/slicc_interface/RubyRequest.hh b/src/mem/ruby/slicc_interface/RubyRequest.hh index 73f214a20..689d559ce 100644 --- a/src/mem/ruby/slicc_interface/RubyRequest.hh +++ b/src/mem/ruby/slicc_interface/RubyRequest.hh @@ -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) diff --git a/src/mem/ruby/slicc_interface/RubySlicc_Util.hh b/src/mem/ruby/slicc_interface/RubySlicc_Util.hh index 55f229d20..4fe065a3f 100644 --- a/src/mem/ruby/slicc_interface/RubySlicc_Util.hh +++ b/src/mem/ruby/slicc_interface/RubySlicc_Util.hh @@ -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(); + 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