mem: write combining for ruby protocols

This patch adds support for write-combining in ruby.
This commit is contained in:
Tony Gutierrez 2016-01-19 14:05:03 -05:00
parent d658b6e1cc
commit 28e353e040
10 changed files with 343 additions and 0 deletions

View file

@ -39,12 +39,25 @@ external_type(Addr, primitive="yes");
external_type(Cycles, primitive="yes", default="Cycles(0)"); external_type(Cycles, primitive="yes", default="Cycles(0)");
external_type(Tick, primitive="yes", default="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="..."){ structure(DataBlock, external = "yes", desc="..."){
void clear(); void clear();
void copyPartial(DataBlock, int, int);
void copyPartial(DataBlock, WriteMask);
void atomicPartial(DataBlock, WriteMask); void atomicPartial(DataBlock, WriteMask);
} }
bool testAndRead(Addr addr, DataBlock datablk, Packet *pkt); 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); bool testAndWrite(Addr addr, DataBlock datablk, Packet *pkt);
// AccessPermission // AccessPermission

View file

@ -126,6 +126,8 @@ structure(RubyRequest, desc="...", interface="Message", external="yes") {
int Size, desc="size in bytes of access"; int Size, desc="size in bytes of access";
PrefetchBit Prefetch, desc="Is this a prefetch request"; PrefetchBit Prefetch, desc="Is this a prefetch request";
int contextId, desc="this goes away but must be replace with Nilay"; 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"; int wfid, desc="Writethrough wavefront";
HSAScope scope, desc="HSA scope"; HSAScope scope, desc="HSA scope";
HSASegment segment, desc="HSA segment"; HSASegment segment, desc="HSA segment";

View file

@ -121,6 +121,7 @@ MakeInclude('common/IntVec.hh')
MakeInclude('common/MachineID.hh') MakeInclude('common/MachineID.hh')
MakeInclude('common/NetDest.hh') MakeInclude('common/NetDest.hh')
MakeInclude('common/Set.hh') MakeInclude('common/Set.hh')
MakeInclude('common/WriteMask.hh')
MakeInclude('filters/AbstractBloomFilter.hh') MakeInclude('filters/AbstractBloomFilter.hh')
MakeInclude('network/MessageBuffer.hh') MakeInclude('network/MessageBuffer.hh')
MakeInclude('structures/Prefetcher.hh') MakeInclude('structures/Prefetcher.hh')

View file

@ -27,6 +27,8 @@
*/ */
#include "mem/ruby/common/DataBlock.hh" #include "mem/ruby/common/DataBlock.hh"
#include "mem/ruby/common/WriteMask.hh"
#include "mem/ruby/system/RubySystem.hh" #include "mem/ruby/system/RubySystem.hh"
DataBlock::DataBlock(const DataBlock &cp) DataBlock::DataBlock(const DataBlock &cp)
@ -56,6 +58,25 @@ DataBlock::equal(const DataBlock& obj) const
return !memcmp(m_data, obj.m_data, RubySystem::getBlockSizeBytes()); 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 void
DataBlock::print(std::ostream& out) const DataBlock::print(std::ostream& out) const
{ {
@ -77,6 +98,12 @@ DataBlock::getData(int offset, int len) const
return &m_data[offset]; return &m_data[offset];
} }
uint8_t*
DataBlock::getDataMod(int offset)
{
return &m_data[offset];
}
void void
DataBlock::setData(const uint8_t *data, int offset, int len) DataBlock::setData(const uint8_t *data, int offset, int len)
{ {

View file

@ -35,6 +35,8 @@
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
class WriteMask;
class DataBlock class DataBlock
{ {
public: public:
@ -58,8 +60,12 @@ class DataBlock
void clear(); void clear();
uint8_t getByte(int whichByte) const; uint8_t getByte(int whichByte) const;
const uint8_t *getData(int offset, int len) const; const uint8_t *getData(int offset, int len) const;
uint8_t *getDataMod(int offset);
void setByte(int whichByte, uint8_t data); void setByte(int whichByte, uint8_t data);
void setData(const uint8_t *data, int offset, int len); 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; bool equal(const DataBlock& obj) const;
void print(std::ostream& out) const; void print(std::ostream& out) const;

View file

@ -41,3 +41,4 @@ Source('Histogram.cc')
Source('IntVec.cc') Source('IntVec.cc')
Source('NetDest.cc') Source('NetDest.cc')
Source('SubBlock.cc') Source('SubBlock.cc')
Source('WriteMask.cc')

View file

@ -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;
}

View file

@ -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__

View file

@ -40,6 +40,7 @@
#include "mem/protocol/RubyRequestType.hh" #include "mem/protocol/RubyRequestType.hh"
#include "mem/ruby/common/Address.hh" #include "mem/ruby/common/Address.hh"
#include "mem/ruby/common/DataBlock.hh" #include "mem/ruby/common/DataBlock.hh"
#include "mem/ruby/common/WriteMask.hh"
class RubyRequest : public Message class RubyRequest : public Message
{ {
@ -54,6 +55,8 @@ class RubyRequest : public Message
uint8_t* data; uint8_t* data;
PacketPtr pkt; PacketPtr pkt;
ContextID m_contextId; ContextID m_contextId;
WriteMask m_writeMask;
DataBlock m_WTData;
int m_wfid; int m_wfid;
HSAScope m_scope; HSAScope m_scope;
HSASegment m_segment; HSASegment m_segment;
@ -99,6 +102,8 @@ class RubyRequest : public Message
data(_data), data(_data),
pkt(_pkt), pkt(_pkt),
m_contextId(_core_id), m_contextId(_core_id),
m_writeMask(_wm_size,_wm_mask),
m_WTData(_Data),
m_wfid(_proc_id), m_wfid(_proc_id),
m_scope(_scope), m_scope(_scope),
m_segment(_segment) m_segment(_segment)
@ -125,6 +130,8 @@ class RubyRequest : public Message
data(_data), data(_data),
pkt(_pkt), pkt(_pkt),
m_contextId(_core_id), m_contextId(_core_id),
m_writeMask(_wm_size,_wm_mask,_atomicOps),
m_WTData(_Data),
m_wfid(_proc_id), m_wfid(_proc_id),
m_scope(_scope), m_scope(_scope),
m_segment(_segment) m_segment(_segment)

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 1999-2008 Mark D. Hill and David A. Wood * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
* Copyright (c) 2013 Advanced Micro Devices, Inc.
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * 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/BoolVec.hh"
#include "mem/ruby/common/DataBlock.hh" #include "mem/ruby/common/DataBlock.hh"
#include "mem/ruby/common/TypeDefines.hh" #include "mem/ruby/common/TypeDefines.hh"
#include "mem/ruby/common/WriteMask.hh"
inline Cycles zero_time() { return Cycles(0); } 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 * 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 * 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. * 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 inline bool
testAndRead(Addr addr, DataBlock& blk, Packet *pkt) testAndRead(Addr addr, DataBlock& blk, Packet *pkt)
@ -108,6 +116,36 @@ testAndRead(Addr addr, DataBlock& blk, Packet *pkt)
return false; 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 * 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 * range for the data block contains the address which the packet needs to