ruby: Remove RubyMemoryControl and associated files
This patch removes the deprecated RubyMemoryControl. The DRAMCtrl module should be used instead.
This commit is contained in:
parent
ebd9018a13
commit
c642d6fc37
10 changed files with 1 additions and 1573 deletions
|
@ -42,7 +42,6 @@ DebugFlag('RubyCache')
|
||||||
DebugFlag('RubyCacheTrace')
|
DebugFlag('RubyCacheTrace')
|
||||||
DebugFlag('RubyDma')
|
DebugFlag('RubyDma')
|
||||||
DebugFlag('RubyGenerated')
|
DebugFlag('RubyGenerated')
|
||||||
DebugFlag('RubyMemory')
|
|
||||||
DebugFlag('RubyNetwork')
|
DebugFlag('RubyNetwork')
|
||||||
DebugFlag('RubyPort')
|
DebugFlag('RubyPort')
|
||||||
DebugFlag('RubyPrefetcher')
|
DebugFlag('RubyPrefetcher')
|
||||||
|
@ -56,7 +55,7 @@ DebugFlag('RubyResourceStalls')
|
||||||
|
|
||||||
CompoundFlag('Ruby', [ 'RubyQueue', 'RubyNetwork', 'RubyTester',
|
CompoundFlag('Ruby', [ 'RubyQueue', 'RubyNetwork', 'RubyTester',
|
||||||
'RubyGenerated', 'RubySlicc', 'RubySystem', 'RubyCache',
|
'RubyGenerated', 'RubySlicc', 'RubySystem', 'RubyCache',
|
||||||
'RubyMemory', 'RubyDma', 'RubyPort', 'RubySequencer', 'RubyCacheTrace',
|
'RubyDma', 'RubyPort', 'RubySequencer', 'RubyCacheTrace',
|
||||||
'RubyPrefetcher'])
|
'RubyPrefetcher'])
|
||||||
|
|
||||||
if env['PROTOCOL'] == 'None':
|
if env['PROTOCOL'] == 'None':
|
||||||
|
|
|
@ -1,266 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
|
|
||||||
* 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/profiler/MemCntrlProfiler.hh"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
MemCntrlProfiler::MemCntrlProfiler(const string& description,
|
|
||||||
int banks_per_rank, int ranks_per_dimm, int dimms_per_channel)
|
|
||||||
{
|
|
||||||
m_description = description;
|
|
||||||
m_banks_per_rank = banks_per_rank;
|
|
||||||
m_ranks_per_dimm = ranks_per_dimm;
|
|
||||||
m_dimms_per_channel = dimms_per_channel;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemCntrlProfiler::~MemCntrlProfiler()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemReq(int bank)
|
|
||||||
{
|
|
||||||
m_memReq++;
|
|
||||||
m_memBankCount[bank]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemBankBusy()
|
|
||||||
{
|
|
||||||
m_memBankBusy++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemBusBusy()
|
|
||||||
{
|
|
||||||
m_memBusBusy++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemReadWriteBusy()
|
|
||||||
{
|
|
||||||
m_memReadWriteBusy++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemDataBusBusy()
|
|
||||||
{
|
|
||||||
m_memDataBusBusy++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemTfawBusy()
|
|
||||||
{
|
|
||||||
m_memTfawBusy++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemRefresh()
|
|
||||||
{
|
|
||||||
m_memRefresh++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemRead()
|
|
||||||
{
|
|
||||||
m_memRead++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemWrite()
|
|
||||||
{
|
|
||||||
m_memWrite++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemWaitCycles(int cycles)
|
|
||||||
{
|
|
||||||
m_memWaitCycles += cycles;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemInputQ(int cycles)
|
|
||||||
{
|
|
||||||
m_memInputQ += cycles;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemBankQ(int cycles)
|
|
||||||
{
|
|
||||||
m_memBankQ += cycles;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemArbWait(int cycles)
|
|
||||||
{
|
|
||||||
m_memArbWait += cycles;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemRandBusy()
|
|
||||||
{
|
|
||||||
m_memRandBusy++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::profileMemNotOld()
|
|
||||||
{
|
|
||||||
m_memNotOld++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MemCntrlProfiler::regStats()
|
|
||||||
{
|
|
||||||
m_memReq
|
|
||||||
.name(m_description + ".memReq")
|
|
||||||
.desc("Total number of memory requests")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_memRead
|
|
||||||
.name(m_description + ".memRead")
|
|
||||||
.desc("Number of memory reads")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_memWrite
|
|
||||||
.name(m_description + ".memWrite")
|
|
||||||
.desc("Number of memory writes")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_memRefresh
|
|
||||||
.name(m_description + ".memRefresh")
|
|
||||||
.desc("Number of memory refreshes")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_memInputQ
|
|
||||||
.name(m_description + ".memInputQ")
|
|
||||||
.desc("Delay in the input queue")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_memBankQ
|
|
||||||
.name(m_description + ".memBankQ")
|
|
||||||
.desc("Delay behind the head of the bank queue")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_memWaitCycles
|
|
||||||
.name(m_description + ".memWaitCycles")
|
|
||||||
.desc("Delay stalled at the head of the bank queue")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_totalStalls
|
|
||||||
.name(m_description + ".totalStalls")
|
|
||||||
.desc("Total number of stall cycles")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_totalStalls = m_memInputQ + m_memBankQ + m_memWaitCycles;
|
|
||||||
|
|
||||||
m_stallsPerReq
|
|
||||||
.name(m_description + ".stallsPerReq")
|
|
||||||
.desc("Expected number of stall cycles per request")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_stallsPerReq = m_totalStalls / m_memReq;
|
|
||||||
|
|
||||||
// Note: The following "memory stalls" entries are a breakdown of
|
|
||||||
// the cycles which already showed up in m_memWaitCycles. The
|
|
||||||
// order is significant; it is the priority of attributing the
|
|
||||||
// cycles. For example, bank_busy is before arbitration because
|
|
||||||
// if the bank was busy, we didn't even check arbitration.
|
|
||||||
// Note: "not old enough" means that since we grouped waiting
|
|
||||||
// heads-of-queues into batches to avoid starvation, a request in
|
|
||||||
// a newer batch didn't try to arbitrate yet because there are
|
|
||||||
// older requests waiting.
|
|
||||||
m_memBankBusy
|
|
||||||
.name(m_description + ".memBankBusy")
|
|
||||||
.desc("memory stalls due to busy bank")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_memRandBusy
|
|
||||||
.name(m_description + ".memRandBusy")
|
|
||||||
.desc("memory stalls due to busy random")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_memNotOld
|
|
||||||
.name(m_description + ".memNotOld")
|
|
||||||
.desc("memory stalls due to anti starvation")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_memArbWait
|
|
||||||
.name(m_description + ".memArbWait")
|
|
||||||
.desc("memory stalls due to arbitration")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_memBusBusy
|
|
||||||
.name(m_description + ".memBusBusy")
|
|
||||||
.desc("memory stalls due to busy bus")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_memTfawBusy
|
|
||||||
.name(m_description + ".memTfawBusy")
|
|
||||||
.desc("memory stalls for Tfaw")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_memReadWriteBusy
|
|
||||||
.name(m_description + ".memReadWriteBusy")
|
|
||||||
.desc("memory stalls due to read write turnaround")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
m_memDataBusBusy
|
|
||||||
.name(m_description + ".memDataBusBusy")
|
|
||||||
.desc("memory stalls due to read read turnaround")
|
|
||||||
.flags(Stats::nozero)
|
|
||||||
;
|
|
||||||
|
|
||||||
int totalBanks = m_banks_per_rank * m_ranks_per_dimm * m_dimms_per_channel;
|
|
||||||
m_memBankCount
|
|
||||||
.init(totalBanks)
|
|
||||||
.name(m_description + ".memBankCount")
|
|
||||||
.desc("Number of accesses per bank")
|
|
||||||
.flags(Stats::pdf | Stats::total|Stats::oneline)
|
|
||||||
;
|
|
||||||
for (int i = 0; i < totalBanks; i++) {
|
|
||||||
m_memBankCount.subname(i, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
|
|
||||||
* 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_PROFILER_MEMCNTRLPROFILER_HH__
|
|
||||||
#define __MEM_RUBY_PROFILER_MEMCNTRLPROFILER_HH__
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "base/statistics.hh"
|
|
||||||
#include "mem/ruby/common/TypeDefines.hh"
|
|
||||||
|
|
||||||
class MemCntrlProfiler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MemCntrlProfiler(const std::string& description, int banks_per_rank,
|
|
||||||
int ranks_per_dimm, int dimms_per_channel);
|
|
||||||
~MemCntrlProfiler();
|
|
||||||
|
|
||||||
void regStats();
|
|
||||||
|
|
||||||
void profileMemReq(int bank);
|
|
||||||
void profileMemBankBusy();
|
|
||||||
void profileMemBusBusy();
|
|
||||||
void profileMemTfawBusy();
|
|
||||||
void profileMemReadWriteBusy();
|
|
||||||
void profileMemDataBusBusy();
|
|
||||||
void profileMemRefresh();
|
|
||||||
void profileMemRead();
|
|
||||||
void profileMemWrite();
|
|
||||||
void profileMemWaitCycles(int cycles);
|
|
||||||
void profileMemInputQ(int cycles);
|
|
||||||
void profileMemBankQ(int cycles);
|
|
||||||
void profileMemArbWait(int cycles);
|
|
||||||
void profileMemRandBusy();
|
|
||||||
void profileMemNotOld();
|
|
||||||
|
|
||||||
void print(std::ostream& out) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Private copy constructor and assignment operator
|
|
||||||
MemCntrlProfiler(const MemCntrlProfiler& obj);
|
|
||||||
MemCntrlProfiler& operator=(const MemCntrlProfiler& obj);
|
|
||||||
|
|
||||||
std::string m_description;
|
|
||||||
Stats::Scalar m_memReq;
|
|
||||||
Stats::Scalar m_memRead;
|
|
||||||
Stats::Scalar m_memWrite;
|
|
||||||
Stats::Scalar m_memRefresh;
|
|
||||||
|
|
||||||
Stats::Scalar m_memWaitCycles;
|
|
||||||
Stats::Scalar m_memInputQ;
|
|
||||||
Stats::Scalar m_memBankQ;
|
|
||||||
Stats::Formula m_totalStalls;
|
|
||||||
Stats::Formula m_stallsPerReq;
|
|
||||||
|
|
||||||
Stats::Scalar m_memBankBusy;
|
|
||||||
Stats::Scalar m_memBusBusy;
|
|
||||||
Stats::Scalar m_memTfawBusy;
|
|
||||||
Stats::Scalar m_memReadWriteBusy;
|
|
||||||
Stats::Scalar m_memDataBusBusy;
|
|
||||||
Stats::Scalar m_memArbWait;
|
|
||||||
Stats::Scalar m_memRandBusy;
|
|
||||||
Stats::Scalar m_memNotOld;
|
|
||||||
Stats::Vector m_memBankCount;
|
|
||||||
|
|
||||||
int m_banks_per_rank;
|
|
||||||
int m_ranks_per_dimm;
|
|
||||||
int m_dimms_per_channel;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::ostream&
|
|
||||||
operator<<(std::ostream& out, const MemCntrlProfiler& obj)
|
|
||||||
{
|
|
||||||
obj.print(out);
|
|
||||||
out << std::flush;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __MEM_RUBY_PROFILER_MEMCNTRLPROFILER_HH__
|
|
|
@ -35,6 +35,5 @@ if env['PROTOCOL'] == 'None':
|
||||||
|
|
||||||
Source('AccessTraceForAddress.cc')
|
Source('AccessTraceForAddress.cc')
|
||||||
Source('AddressProfiler.cc')
|
Source('AddressProfiler.cc')
|
||||||
Source('MemCntrlProfiler.cc')
|
|
||||||
Source('Profiler.cc')
|
Source('Profiler.cc')
|
||||||
Source('StoreTrace.cc')
|
Source('StoreTrace.cc')
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 1999 Mark D. Hill and David A. Wood
|
|
||||||
* 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/structures/MemoryNode.hh"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
void
|
|
||||||
MemoryNode::print(ostream& out) const
|
|
||||||
{
|
|
||||||
out << "[";
|
|
||||||
out << m_time << ", ";
|
|
||||||
out << m_msg_counter << ", ";
|
|
||||||
out << pkt << "; ";
|
|
||||||
out << "]";
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008 Mark D. Hill and David A. Wood
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Description:
|
|
||||||
* This structure records everything known about a single
|
|
||||||
* memory request that is queued in the memory controller.
|
|
||||||
* It is created when the memory request first arrives
|
|
||||||
* at a memory controller and is deleted when the underlying
|
|
||||||
* message is enqueued to be sent back to the directory.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __MEM_RUBY_STRUCTURES_MEMORYNODE_HH__
|
|
||||||
#define __MEM_RUBY_STRUCTURES_MEMORYNODE_HH__
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "mem/ruby/common/TypeDefines.hh"
|
|
||||||
#include "mem/ruby/slicc_interface/Message.hh"
|
|
||||||
|
|
||||||
class MemoryNode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// old constructor
|
|
||||||
MemoryNode(const Cycles& time, int counter, const PacketPtr p,
|
|
||||||
Addr addr, const bool is_mem_read)
|
|
||||||
: m_time(time), pkt(p)
|
|
||||||
{
|
|
||||||
m_msg_counter = counter;
|
|
||||||
m_addr = addr;
|
|
||||||
m_is_mem_read = is_mem_read;
|
|
||||||
m_is_dirty_wb = !is_mem_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
// new constructor
|
|
||||||
MemoryNode(const Cycles& time, const PacketPtr p,
|
|
||||||
Addr addr, const bool is_mem_read,
|
|
||||||
const bool is_dirty_wb)
|
|
||||||
: m_time(time), pkt(p)
|
|
||||||
{
|
|
||||||
m_msg_counter = 0;
|
|
||||||
m_addr = addr;
|
|
||||||
m_is_mem_read = is_mem_read;
|
|
||||||
m_is_dirty_wb = is_dirty_wb;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print(std::ostream& out) const;
|
|
||||||
|
|
||||||
Cycles m_time;
|
|
||||||
int m_msg_counter;
|
|
||||||
PacketPtr pkt;
|
|
||||||
Addr m_addr;
|
|
||||||
bool m_is_mem_read;
|
|
||||||
bool m_is_dirty_wb;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::ostream&
|
|
||||||
operator<<(std::ostream& out, const MemoryNode& obj)
|
|
||||||
{
|
|
||||||
obj.print(out);
|
|
||||||
out << std::flush;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __MEM_RUBY_STRUCTURES_MEMORYNODE_HH__
|
|
|
@ -1,802 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
|
|
||||||
* Copyright (c) 2012 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Description: This module simulates a basic DDR-style memory controller
|
|
||||||
* (and can easily be extended to do FB-DIMM as well).
|
|
||||||
*
|
|
||||||
* This module models a single channel, connected to any number of
|
|
||||||
* DIMMs with any number of ranks of DRAMs each. If you want multiple
|
|
||||||
* address/data channels, you need to instantiate multiple copies of
|
|
||||||
* this module.
|
|
||||||
*
|
|
||||||
* Each memory request is placed in a queue associated with a specific
|
|
||||||
* memory bank. This queue is of finite size; if the queue is full
|
|
||||||
* the request will back up in an (infinite) common queue and will
|
|
||||||
* effectively throttle the whole system. This sort of behavior is
|
|
||||||
* intended to be closer to real system behavior than if we had an
|
|
||||||
* infinite queue on each bank. If you want the latter, just make
|
|
||||||
* the bank queues unreasonably large.
|
|
||||||
*
|
|
||||||
* The head item on a bank queue is issued when all of the
|
|
||||||
* following are true:
|
|
||||||
* the bank is available
|
|
||||||
* the address path to the DIMM is available
|
|
||||||
* the data path to or from the DIMM is available
|
|
||||||
*
|
|
||||||
* Note that we are not concerned about fixed offsets in time. The bank
|
|
||||||
* will not be used at the same moment as the address path, but since
|
|
||||||
* there is no queue in the DIMM or the DRAM it will be used at a constant
|
|
||||||
* number of cycles later, so it is treated as if it is used at the same
|
|
||||||
* time.
|
|
||||||
*
|
|
||||||
* We are assuming closed bank policy; that is, we automatically close
|
|
||||||
* each bank after a single read or write. Adding an option for open
|
|
||||||
* bank policy is for future work.
|
|
||||||
*
|
|
||||||
* We are assuming "posted CAS"; that is, we send the READ or WRITE
|
|
||||||
* immediately after the ACTIVATE. This makes scheduling the address
|
|
||||||
* bus trivial; we always schedule a fixed set of cycles. For DDR-400,
|
|
||||||
* this is a set of two cycles; for some configurations such as
|
|
||||||
* DDR-800 the parameter tRRD forces this to be set to three cycles.
|
|
||||||
*
|
|
||||||
* We assume a four-bit-time transfer on the data wires. This is
|
|
||||||
* the minimum burst length for DDR-2. This would correspond
|
|
||||||
* to (for example) a memory where each DIMM is 72 bits wide
|
|
||||||
* and DIMMs are ganged in pairs to deliver 64 bytes at a shot.
|
|
||||||
* This gives us the same occupancy on the data wires as on the
|
|
||||||
* address wires (for the two-address-cycle case).
|
|
||||||
*
|
|
||||||
* The only non-trivial scheduling problem is the data wires.
|
|
||||||
* A write will use the wires earlier in the operation than a read
|
|
||||||
* will; typically one cycle earlier as seen at the DRAM, but earlier
|
|
||||||
* by a worst-case round-trip wire delay when seen at the memory controller.
|
|
||||||
* So, while reads from one rank can be scheduled back-to-back
|
|
||||||
* every two cycles, and writes (to any rank) scheduled every two cycles,
|
|
||||||
* when a read is followed by a write we need to insert a bubble.
|
|
||||||
* Furthermore, consecutive reads from two different ranks may need
|
|
||||||
* to insert a bubble due to skew between when one DRAM stops driving the
|
|
||||||
* wires and when the other one starts. (These bubbles are parameters.)
|
|
||||||
*
|
|
||||||
* This means that when some number of reads and writes are at the
|
|
||||||
* heads of their queues, reads could starve writes, and/or reads
|
|
||||||
* to the same rank could starve out other requests, since the others
|
|
||||||
* would never see the data bus ready.
|
|
||||||
* For this reason, we have implemented an anti-starvation feature.
|
|
||||||
* A group of requests is marked "old", and a counter is incremented
|
|
||||||
* each cycle as long as any request from that batch has not issued.
|
|
||||||
* if the counter reaches twice the bank busy time, we hold off any
|
|
||||||
* newer requests until all of the "old" requests have issued.
|
|
||||||
*
|
|
||||||
* We also model tFAW. This is an obscure DRAM parameter that says
|
|
||||||
* that no more than four activate requests can happen within a window
|
|
||||||
* of a certain size. For most configurations this does not come into play,
|
|
||||||
* or has very little effect, but it could be used to throttle the power
|
|
||||||
* consumption of the DRAM. In this implementation (unlike in a DRAM
|
|
||||||
* data sheet) TFAW is measured in memory bus cycles; i.e. if TFAW = 16
|
|
||||||
* then no more than four activates may happen within any 16 cycle window.
|
|
||||||
* Refreshes are included in the activates.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "base/cast.hh"
|
|
||||||
#include "base/cprintf.hh"
|
|
||||||
#include "base/random.hh"
|
|
||||||
#include "debug/RubyMemory.hh"
|
|
||||||
#include "mem/ruby/common/Address.hh"
|
|
||||||
#include "mem/ruby/profiler/Profiler.hh"
|
|
||||||
#include "mem/ruby/slicc_interface/Message.hh"
|
|
||||||
#include "mem/ruby/slicc_interface/RubySlicc_ComponentMapping.hh"
|
|
||||||
#include "mem/ruby/structures/RubyMemoryControl.hh"
|
|
||||||
#include "mem/ruby/system/RubySystem.hh"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// Value to reset watchdog timer to.
|
|
||||||
// If we're idle for this many memory control cycles,
|
|
||||||
// shut down our clock (our rescheduling of ourselves).
|
|
||||||
// Refresh shuts down as well.
|
|
||||||
// When we restart, we'll be in a different phase
|
|
||||||
// with respect to ruby cycles, so this introduces
|
|
||||||
// a slight inaccuracy. But it is necessary or the
|
|
||||||
// ruby tester never terminates because the event
|
|
||||||
// queue is never empty.
|
|
||||||
#define IDLECOUNT_MAX_VALUE 1000
|
|
||||||
|
|
||||||
// Output operator definition
|
|
||||||
|
|
||||||
ostream&
|
|
||||||
operator<<(ostream& out, const RubyMemoryControl& obj)
|
|
||||||
{
|
|
||||||
obj.print(out);
|
|
||||||
out << flush;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ****************************************************************
|
|
||||||
|
|
||||||
// CONSTRUCTOR
|
|
||||||
RubyMemoryControl::RubyMemoryControl(const Params *p)
|
|
||||||
: AbstractMemory(p), Consumer(this), port(name() + ".port", *this),
|
|
||||||
m_event(this)
|
|
||||||
{
|
|
||||||
m_banks_per_rank = p->banks_per_rank;
|
|
||||||
m_ranks_per_dimm = p->ranks_per_dimm;
|
|
||||||
m_dimms_per_channel = p->dimms_per_channel;
|
|
||||||
m_bank_bit_0 = p->bank_bit_0;
|
|
||||||
m_rank_bit_0 = p->rank_bit_0;
|
|
||||||
m_dimm_bit_0 = p->dimm_bit_0;
|
|
||||||
m_bank_queue_size = p->bank_queue_size;
|
|
||||||
m_bank_busy_time = p->bank_busy_time;
|
|
||||||
m_rank_rank_delay = p->rank_rank_delay;
|
|
||||||
m_read_write_delay = p->read_write_delay;
|
|
||||||
m_basic_bus_busy_time = p->basic_bus_busy_time;
|
|
||||||
m_mem_ctl_latency = p->mem_ctl_latency;
|
|
||||||
m_refresh_period = p->refresh_period;
|
|
||||||
m_tFaw = p->tFaw;
|
|
||||||
m_mem_random_arbitrate = p->mem_random_arbitrate;
|
|
||||||
m_mem_fixed_delay = p->mem_fixed_delay;
|
|
||||||
|
|
||||||
m_profiler_ptr = new MemCntrlProfiler(name(),
|
|
||||||
m_banks_per_rank,
|
|
||||||
m_ranks_per_dimm,
|
|
||||||
m_dimms_per_channel);
|
|
||||||
|
|
||||||
warn("RubyMemoryControl is deprecated, use a DRAMCtrl subclass instead\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RubyMemoryControl::init()
|
|
||||||
{
|
|
||||||
m_msg_counter = 0;
|
|
||||||
assert(m_tFaw <= 62); // must fit in a uint64_t shift register
|
|
||||||
|
|
||||||
m_total_banks = m_banks_per_rank * m_ranks_per_dimm * m_dimms_per_channel;
|
|
||||||
m_total_ranks = m_ranks_per_dimm * m_dimms_per_channel;
|
|
||||||
m_refresh_period_system = m_refresh_period / m_total_banks;
|
|
||||||
|
|
||||||
m_bankQueues = new list<MemoryNode *> [m_total_banks];
|
|
||||||
assert(m_bankQueues);
|
|
||||||
|
|
||||||
m_bankBusyCounter = new int [m_total_banks];
|
|
||||||
assert(m_bankBusyCounter);
|
|
||||||
|
|
||||||
m_oldRequest = new int [m_total_banks];
|
|
||||||
assert(m_oldRequest);
|
|
||||||
|
|
||||||
for (int i = 0; i < m_total_banks; i++) {
|
|
||||||
m_bankBusyCounter[i] = 0;
|
|
||||||
m_oldRequest[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_busBusyCounter_Basic = 0;
|
|
||||||
m_busBusyCounter_Write = 0;
|
|
||||||
m_busBusyCounter_ReadNewRank = 0;
|
|
||||||
m_busBusy_WhichRank = 0;
|
|
||||||
|
|
||||||
m_roundRobin = 0;
|
|
||||||
m_refresh_count = 1;
|
|
||||||
m_need_refresh = 0;
|
|
||||||
m_refresh_bank = 0;
|
|
||||||
m_idleCount = 0;
|
|
||||||
m_ageCounter = 0;
|
|
||||||
|
|
||||||
// Each tfaw shift register keeps a moving bit pattern
|
|
||||||
// which shows when recent activates have occurred.
|
|
||||||
// m_tfaw_count keeps track of how many 1 bits are set
|
|
||||||
// in each shift register. When m_tfaw_count is >= 4,
|
|
||||||
// new activates are not allowed.
|
|
||||||
m_tfaw_shift = new uint64_t[m_total_ranks];
|
|
||||||
m_tfaw_count = new int[m_total_ranks];
|
|
||||||
for (int i = 0; i < m_total_ranks; i++) {
|
|
||||||
m_tfaw_shift[i] = 0;
|
|
||||||
m_tfaw_count[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseSlavePort&
|
|
||||||
RubyMemoryControl::getSlavePort(const string &if_name, PortID idx)
|
|
||||||
{
|
|
||||||
if (if_name != "port") {
|
|
||||||
return MemObject::getSlavePort(if_name, idx);
|
|
||||||
} else {
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RubyMemoryControl::reset()
|
|
||||||
{
|
|
||||||
m_msg_counter = 0;
|
|
||||||
|
|
||||||
assert(m_tFaw <= 62); // must fit in a uint64_t shift register
|
|
||||||
|
|
||||||
m_total_banks = m_banks_per_rank * m_ranks_per_dimm * m_dimms_per_channel;
|
|
||||||
m_total_ranks = m_ranks_per_dimm * m_dimms_per_channel;
|
|
||||||
m_refresh_period_system = m_refresh_period / m_total_banks;
|
|
||||||
|
|
||||||
assert(m_bankQueues);
|
|
||||||
|
|
||||||
assert(m_bankBusyCounter);
|
|
||||||
|
|
||||||
assert(m_oldRequest);
|
|
||||||
|
|
||||||
for (int i = 0; i < m_total_banks; i++) {
|
|
||||||
m_bankBusyCounter[i] = 0;
|
|
||||||
m_oldRequest[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_busBusyCounter_Basic = 0;
|
|
||||||
m_busBusyCounter_Write = 0;
|
|
||||||
m_busBusyCounter_ReadNewRank = 0;
|
|
||||||
m_busBusy_WhichRank = 0;
|
|
||||||
|
|
||||||
m_roundRobin = 0;
|
|
||||||
m_refresh_count = 1;
|
|
||||||
m_need_refresh = 0;
|
|
||||||
m_refresh_bank = 0;
|
|
||||||
m_idleCount = 0;
|
|
||||||
m_ageCounter = 0;
|
|
||||||
|
|
||||||
// Each tfaw shift register keeps a moving bit pattern
|
|
||||||
// which shows when recent activates have occurred.
|
|
||||||
// m_tfaw_count keeps track of how many 1 bits are set
|
|
||||||
// in each shift register. When m_tfaw_count is >= 4,
|
|
||||||
// new activates are not allowed.
|
|
||||||
for (int i = 0; i < m_total_ranks; i++) {
|
|
||||||
m_tfaw_shift[i] = 0;
|
|
||||||
m_tfaw_count[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RubyMemoryControl::~RubyMemoryControl()
|
|
||||||
{
|
|
||||||
delete [] m_bankQueues;
|
|
||||||
delete [] m_bankBusyCounter;
|
|
||||||
delete [] m_oldRequest;
|
|
||||||
delete m_profiler_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// enqueue new request from directory
|
|
||||||
bool
|
|
||||||
RubyMemoryControl::recvTimingReq(PacketPtr pkt)
|
|
||||||
{
|
|
||||||
Cycles arrival_time = curCycle();
|
|
||||||
Addr addr = pkt->getAddr();
|
|
||||||
bool is_mem_read = pkt->isRead();
|
|
||||||
|
|
||||||
access(pkt);
|
|
||||||
MemoryNode *thisReq = new MemoryNode(arrival_time, pkt, addr,
|
|
||||||
is_mem_read, !is_mem_read);
|
|
||||||
enqueueMemRef(thisReq);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alternate entry point used when we already have a MemoryNode
|
|
||||||
// structure built.
|
|
||||||
void
|
|
||||||
RubyMemoryControl::enqueueMemRef(MemoryNode *memRef)
|
|
||||||
{
|
|
||||||
m_msg_counter++;
|
|
||||||
memRef->m_msg_counter = m_msg_counter;
|
|
||||||
Addr addr = memRef->m_addr;
|
|
||||||
int bank = getBank(addr);
|
|
||||||
|
|
||||||
m_profiler_ptr->profileMemReq(bank);
|
|
||||||
m_input_queue.push_back(memRef);
|
|
||||||
|
|
||||||
if (!m_event.scheduled()) {
|
|
||||||
schedule(m_event, clockEdge());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RubyMemoryControl::print(ostream& out) const
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Queue up a completed request to send back to directory
|
|
||||||
void
|
|
||||||
RubyMemoryControl::enqueueToDirectory(MemoryNode *req, Cycles latency)
|
|
||||||
{
|
|
||||||
Tick arrival_time = clockEdge(latency);
|
|
||||||
PacketPtr pkt = req->pkt;
|
|
||||||
|
|
||||||
// access already turned the packet into a response
|
|
||||||
assert(pkt->isResponse());
|
|
||||||
|
|
||||||
// queue the packet in the response queue to be sent out after
|
|
||||||
// the static latency has passed
|
|
||||||
port.schedTimingResp(pkt, arrival_time);
|
|
||||||
|
|
||||||
DPRINTF(RubyMemory, "Enqueueing msg %#08x %c back to directory at %15d\n",
|
|
||||||
req->m_addr, req->m_is_mem_read ? 'R':'W', arrival_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
// getBank returns an integer that is unique for each
|
|
||||||
// bank across this memory controller.
|
|
||||||
int
|
|
||||||
RubyMemoryControl::getBank(const Addr addr) const
|
|
||||||
{
|
|
||||||
int dimm = (addr >> m_dimm_bit_0) & (m_dimms_per_channel - 1);
|
|
||||||
int rank = (addr >> m_rank_bit_0) & (m_ranks_per_dimm - 1);
|
|
||||||
int bank = (addr >> m_bank_bit_0) & (m_banks_per_rank - 1);
|
|
||||||
return (dimm * m_ranks_per_dimm * m_banks_per_rank)
|
|
||||||
+ (rank * m_banks_per_rank)
|
|
||||||
+ bank;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
RubyMemoryControl::getRank(const Addr addr) const
|
|
||||||
{
|
|
||||||
int bank = getBank(addr);
|
|
||||||
int rank = (bank / m_banks_per_rank);
|
|
||||||
assert (rank < (m_ranks_per_dimm * m_dimms_per_channel));
|
|
||||||
return rank;
|
|
||||||
}
|
|
||||||
|
|
||||||
// getRank returns an integer that is unique for each rank
|
|
||||||
// and independent of individual bank.
|
|
||||||
int
|
|
||||||
RubyMemoryControl::getRank(int bank) const
|
|
||||||
{
|
|
||||||
int rank = (bank / m_banks_per_rank);
|
|
||||||
assert (rank < (m_ranks_per_dimm * m_dimms_per_channel));
|
|
||||||
return rank;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not used!
|
|
||||||
int
|
|
||||||
RubyMemoryControl::getChannel(const Addr addr) const
|
|
||||||
{
|
|
||||||
assert(false);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not used!
|
|
||||||
int
|
|
||||||
RubyMemoryControl::getRow(const Addr addr) const
|
|
||||||
{
|
|
||||||
assert(false);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// queueReady determines if the head item in a bank queue
|
|
||||||
// can be issued this cycle
|
|
||||||
bool
|
|
||||||
RubyMemoryControl::queueReady(int bank)
|
|
||||||
{
|
|
||||||
if ((m_bankBusyCounter[bank] > 0) && !m_mem_fixed_delay) {
|
|
||||||
m_profiler_ptr->profileMemBankBusy();
|
|
||||||
|
|
||||||
DPRINTF(RubyMemory, "bank %x busy %d\n", bank, m_bankBusyCounter[bank]);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_mem_random_arbitrate >= 2) {
|
|
||||||
if (random_mt.random(0, 100) < m_mem_random_arbitrate) {
|
|
||||||
m_profiler_ptr->profileMemRandBusy();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_mem_fixed_delay)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if ((m_ageCounter > (2 * m_bank_busy_time)) && !m_oldRequest[bank]) {
|
|
||||||
m_profiler_ptr->profileMemNotOld();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_busBusyCounter_Basic == m_basic_bus_busy_time) {
|
|
||||||
// Another bank must have issued this same cycle. For
|
|
||||||
// profiling, we count this as an arb wait rather than a bus
|
|
||||||
// wait. This is a little inaccurate since it MIGHT have also
|
|
||||||
// been blocked waiting for a read-write or a read-read
|
|
||||||
// instead, but it's pretty close.
|
|
||||||
m_profiler_ptr->profileMemArbWait(1);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_busBusyCounter_Basic > 0) {
|
|
||||||
m_profiler_ptr->profileMemBusBusy();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rank = getRank(bank);
|
|
||||||
if (m_tfaw_count[rank] >= ACTIVATE_PER_TFAW) {
|
|
||||||
m_profiler_ptr->profileMemTfawBusy();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool write = !m_bankQueues[bank].front()->m_is_mem_read;
|
|
||||||
if (write && (m_busBusyCounter_Write > 0)) {
|
|
||||||
m_profiler_ptr->profileMemReadWriteBusy();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!write && (rank != m_busBusy_WhichRank)
|
|
||||||
&& (m_busBusyCounter_ReadNewRank > 0)) {
|
|
||||||
m_profiler_ptr->profileMemDataBusBusy();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// issueRefresh checks to see if this bank has a refresh scheduled
|
|
||||||
// and, if so, does the refresh and returns true
|
|
||||||
bool
|
|
||||||
RubyMemoryControl::issueRefresh(int bank)
|
|
||||||
{
|
|
||||||
if (!m_need_refresh || (m_refresh_bank != bank))
|
|
||||||
return false;
|
|
||||||
if (m_bankBusyCounter[bank] > 0)
|
|
||||||
return false;
|
|
||||||
// Note that m_busBusyCounter will prevent multiple issues during
|
|
||||||
// the same cycle, as well as on different but close cycles:
|
|
||||||
if (m_busBusyCounter_Basic > 0)
|
|
||||||
return false;
|
|
||||||
int rank = getRank(bank);
|
|
||||||
if (m_tfaw_count[rank] >= ACTIVATE_PER_TFAW)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Issue it:
|
|
||||||
DPRINTF(RubyMemory, "Refresh bank %3x\n", bank);
|
|
||||||
|
|
||||||
m_profiler_ptr->profileMemRefresh();
|
|
||||||
m_need_refresh--;
|
|
||||||
m_refresh_bank++;
|
|
||||||
if (m_refresh_bank >= m_total_banks)
|
|
||||||
m_refresh_bank = 0;
|
|
||||||
m_bankBusyCounter[bank] = m_bank_busy_time;
|
|
||||||
m_busBusyCounter_Basic = m_basic_bus_busy_time;
|
|
||||||
m_busBusyCounter_Write = m_basic_bus_busy_time;
|
|
||||||
m_busBusyCounter_ReadNewRank = m_basic_bus_busy_time;
|
|
||||||
markTfaw(rank);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark the activate in the tFaw shift register
|
|
||||||
void
|
|
||||||
RubyMemoryControl::markTfaw(int rank)
|
|
||||||
{
|
|
||||||
if (m_tFaw) {
|
|
||||||
m_tfaw_shift[rank] |= (1 << (m_tFaw-1));
|
|
||||||
m_tfaw_count[rank]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue a memory request: Activate the bank, reserve the address and
|
|
||||||
// data buses, and queue the request for return to the requesting
|
|
||||||
// processor after a fixed latency.
|
|
||||||
void
|
|
||||||
RubyMemoryControl::issueRequest(int bank)
|
|
||||||
{
|
|
||||||
int rank = getRank(bank);
|
|
||||||
MemoryNode *req = m_bankQueues[bank].front();
|
|
||||||
m_bankQueues[bank].pop_front();
|
|
||||||
|
|
||||||
DPRINTF(RubyMemory, "Mem issue request%7d: %#08x %c "
|
|
||||||
"bank=%3x sched %c\n", req->m_msg_counter, req->m_addr,
|
|
||||||
req->m_is_mem_read? 'R':'W',
|
|
||||||
bank, m_event.scheduled() ? 'Y':'N');
|
|
||||||
|
|
||||||
enqueueToDirectory(req, Cycles(m_mem_ctl_latency + m_mem_fixed_delay));
|
|
||||||
|
|
||||||
m_oldRequest[bank] = 0;
|
|
||||||
markTfaw(rank);
|
|
||||||
m_bankBusyCounter[bank] = m_bank_busy_time;
|
|
||||||
m_busBusy_WhichRank = rank;
|
|
||||||
if (req->m_is_mem_read) {
|
|
||||||
m_profiler_ptr->profileMemRead();
|
|
||||||
m_busBusyCounter_Basic = m_basic_bus_busy_time;
|
|
||||||
m_busBusyCounter_Write = m_basic_bus_busy_time + m_read_write_delay;
|
|
||||||
m_busBusyCounter_ReadNewRank =
|
|
||||||
m_basic_bus_busy_time + m_rank_rank_delay;
|
|
||||||
} else {
|
|
||||||
m_profiler_ptr->profileMemWrite();
|
|
||||||
m_busBusyCounter_Basic = m_basic_bus_busy_time;
|
|
||||||
m_busBusyCounter_Write = m_basic_bus_busy_time;
|
|
||||||
m_busBusyCounter_ReadNewRank = m_basic_bus_busy_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete req;
|
|
||||||
}
|
|
||||||
|
|
||||||
// executeCycle: This function is called once per memory clock cycle
|
|
||||||
// to simulate all the periodic hardware.
|
|
||||||
void
|
|
||||||
RubyMemoryControl::executeCycle()
|
|
||||||
{
|
|
||||||
// Keep track of time by counting down the busy counters:
|
|
||||||
for (int bank=0; bank < m_total_banks; bank++) {
|
|
||||||
if (m_bankBusyCounter[bank] > 0) m_bankBusyCounter[bank]--;
|
|
||||||
}
|
|
||||||
if (m_busBusyCounter_Write > 0)
|
|
||||||
m_busBusyCounter_Write--;
|
|
||||||
if (m_busBusyCounter_ReadNewRank > 0)
|
|
||||||
m_busBusyCounter_ReadNewRank--;
|
|
||||||
if (m_busBusyCounter_Basic > 0)
|
|
||||||
m_busBusyCounter_Basic--;
|
|
||||||
|
|
||||||
// Count down the tFAW shift registers:
|
|
||||||
for (int rank=0; rank < m_total_ranks; rank++) {
|
|
||||||
if (m_tfaw_shift[rank] & 1) m_tfaw_count[rank]--;
|
|
||||||
m_tfaw_shift[rank] >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// After time period expires, latch an indication that we need a refresh.
|
|
||||||
// Disable refresh if in mem_fixed_delay mode.
|
|
||||||
if (!m_mem_fixed_delay) m_refresh_count--;
|
|
||||||
if (m_refresh_count == 0) {
|
|
||||||
m_refresh_count = m_refresh_period_system;
|
|
||||||
|
|
||||||
// Are we overrunning our ability to refresh?
|
|
||||||
assert(m_need_refresh < 10);
|
|
||||||
m_need_refresh++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this batch of requests is all done, make a new batch:
|
|
||||||
m_ageCounter++;
|
|
||||||
int anyOld = 0;
|
|
||||||
for (int bank=0; bank < m_total_banks; bank++) {
|
|
||||||
anyOld |= m_oldRequest[bank];
|
|
||||||
}
|
|
||||||
if (!anyOld) {
|
|
||||||
for (int bank=0; bank < m_total_banks; bank++) {
|
|
||||||
if (!m_bankQueues[bank].empty()) m_oldRequest[bank] = 1;
|
|
||||||
}
|
|
||||||
m_ageCounter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If randomness desired, re-randomize round-robin position each cycle
|
|
||||||
if (m_mem_random_arbitrate) {
|
|
||||||
m_roundRobin = random_mt.random(0, m_total_banks - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each channel, scan round-robin, and pick an old, ready
|
|
||||||
// request and issue it. Treat a refresh request as if it were at
|
|
||||||
// the head of its bank queue. After we issue something, keep
|
|
||||||
// scanning the queues just to gather statistics about how many
|
|
||||||
// are waiting. If in mem_fixed_delay mode, we can issue more
|
|
||||||
// than one request per cycle.
|
|
||||||
int queueHeads = 0;
|
|
||||||
int banksIssued = 0;
|
|
||||||
for (int i = 0; i < m_total_banks; i++) {
|
|
||||||
m_roundRobin++;
|
|
||||||
if (m_roundRobin >= m_total_banks) m_roundRobin = 0;
|
|
||||||
issueRefresh(m_roundRobin);
|
|
||||||
int qs = m_bankQueues[m_roundRobin].size();
|
|
||||||
if (qs > 1) {
|
|
||||||
m_profiler_ptr->profileMemBankQ(qs-1);
|
|
||||||
}
|
|
||||||
if (qs > 0) {
|
|
||||||
// we're not idle if anything is queued
|
|
||||||
m_idleCount = IDLECOUNT_MAX_VALUE;
|
|
||||||
queueHeads++;
|
|
||||||
if (queueReady(m_roundRobin)) {
|
|
||||||
issueRequest(m_roundRobin);
|
|
||||||
banksIssued++;
|
|
||||||
if (m_mem_fixed_delay) {
|
|
||||||
m_profiler_ptr->profileMemWaitCycles(m_mem_fixed_delay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// memWaitCycles is a redundant catch-all for the specific
|
|
||||||
// counters in queueReady
|
|
||||||
m_profiler_ptr->profileMemWaitCycles(queueHeads - banksIssued);
|
|
||||||
|
|
||||||
// Check input queue and move anything to bank queues if not full.
|
|
||||||
// Since this is done here at the end of the cycle, there will
|
|
||||||
// always be at least one cycle of latency in the bank queue. We
|
|
||||||
// deliberately move at most one request per cycle (to simulate
|
|
||||||
// typical hardware). Note that if one bank queue fills up, other
|
|
||||||
// requests can get stuck behind it here.
|
|
||||||
if (!m_input_queue.empty()) {
|
|
||||||
// we're not idle if anything is pending
|
|
||||||
m_idleCount = IDLECOUNT_MAX_VALUE;
|
|
||||||
MemoryNode *req = m_input_queue.front();
|
|
||||||
int bank = getBank(req->m_addr);
|
|
||||||
if (m_bankQueues[bank].size() < m_bank_queue_size) {
|
|
||||||
m_input_queue.pop_front();
|
|
||||||
m_bankQueues[bank].push_back(req);
|
|
||||||
}
|
|
||||||
m_profiler_ptr->profileMemInputQ(m_input_queue.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DrainState
|
|
||||||
RubyMemoryControl::drain()
|
|
||||||
{
|
|
||||||
DPRINTF(RubyMemory, "MemoryController drain\n");
|
|
||||||
if (m_event.scheduled()) {
|
|
||||||
deschedule(m_event);
|
|
||||||
}
|
|
||||||
return DrainState::Drained;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wakeup: This function is called once per memory controller clock cycle.
|
|
||||||
void
|
|
||||||
RubyMemoryControl::wakeup()
|
|
||||||
{
|
|
||||||
DPRINTF(RubyMemory, "MemoryController wakeup\n");
|
|
||||||
// execute everything
|
|
||||||
executeCycle();
|
|
||||||
|
|
||||||
m_idleCount--;
|
|
||||||
if (m_idleCount > 0) {
|
|
||||||
assert(!m_event.scheduled());
|
|
||||||
schedule(m_event, clockEdge(Cycles(1)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function reads the different buffers that exist in the Ruby Memory
|
|
||||||
* Controller, and figures out if any of the buffers hold a message that
|
|
||||||
* contains the data for the address provided in the packet. True is returned
|
|
||||||
* if any of the messages was read, otherwise false is returned.
|
|
||||||
*
|
|
||||||
* I think we should move these buffers to being message buffers, instead of
|
|
||||||
* being lists.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
RubyMemoryControl::functionalRead(Packet *pkt)
|
|
||||||
{
|
|
||||||
for (std::list<MemoryNode *>::iterator it = m_input_queue.begin();
|
|
||||||
it != m_input_queue.end(); ++it) {
|
|
||||||
PacketPtr msg = (*it)->pkt;
|
|
||||||
if (pkt->checkFunctional(msg)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::list<MemoryNode *>::iterator it = m_response_queue.begin();
|
|
||||||
it != m_response_queue.end(); ++it) {
|
|
||||||
PacketPtr msg = (*it)->pkt;
|
|
||||||
if (pkt->checkFunctional(msg)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t bank = 0; bank < m_total_banks; ++bank) {
|
|
||||||
for (std::list<MemoryNode *>::iterator it = m_bankQueues[bank].begin();
|
|
||||||
it != m_bankQueues[bank].end(); ++it) {
|
|
||||||
PacketPtr msg = (*it)->pkt;
|
|
||||||
if (pkt->checkFunctional(msg)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
functionalAccess(pkt);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function reads the different buffers that exist in the Ruby Memory
|
|
||||||
* Controller, and figures out if any of the buffers hold a message that
|
|
||||||
* needs to functionally written with the data in the packet.
|
|
||||||
*
|
|
||||||
* The number of messages written is returned at the end. This is required
|
|
||||||
* for debugging purposes.
|
|
||||||
*/
|
|
||||||
uint32_t
|
|
||||||
RubyMemoryControl::functionalWrite(Packet *pkt)
|
|
||||||
{
|
|
||||||
uint32_t num_functional_writes = 0;
|
|
||||||
|
|
||||||
for (std::list<MemoryNode *>::iterator it = m_input_queue.begin();
|
|
||||||
it != m_input_queue.end(); ++it) {
|
|
||||||
PacketPtr msg = (*it)->pkt;
|
|
||||||
if (pkt->checkFunctional(msg)) {
|
|
||||||
num_functional_writes++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::list<MemoryNode *>::iterator it = m_response_queue.begin();
|
|
||||||
it != m_response_queue.end(); ++it) {
|
|
||||||
PacketPtr msg = (*it)->pkt;
|
|
||||||
if (pkt->checkFunctional(msg)) {
|
|
||||||
num_functional_writes++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t bank = 0; bank < m_total_banks; ++bank) {
|
|
||||||
for (std::list<MemoryNode *>::iterator it = m_bankQueues[bank].begin();
|
|
||||||
it != m_bankQueues[bank].end(); ++it) {
|
|
||||||
PacketPtr msg = (*it)->pkt;
|
|
||||||
if (pkt->checkFunctional(msg)) {
|
|
||||||
num_functional_writes++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
functionalAccess(pkt);
|
|
||||||
num_functional_writes++;
|
|
||||||
return num_functional_writes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RubyMemoryControl::regStats()
|
|
||||||
{
|
|
||||||
m_profiler_ptr->regStats();
|
|
||||||
AbstractMemory::regStats();
|
|
||||||
}
|
|
||||||
|
|
||||||
RubyMemoryControl *
|
|
||||||
RubyMemoryControlParams::create()
|
|
||||||
{
|
|
||||||
return new RubyMemoryControl(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
RubyMemoryControl::MemoryPort::MemoryPort(const std::string& name,
|
|
||||||
RubyMemoryControl& _memory)
|
|
||||||
: QueuedSlavePort(name, &_memory, queue), queue(_memory, *this),
|
|
||||||
memory(_memory)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
AddrRangeList
|
|
||||||
RubyMemoryControl::MemoryPort::getAddrRanges() const
|
|
||||||
{
|
|
||||||
AddrRangeList ranges;
|
|
||||||
ranges.push_back(memory.getAddrRange());
|
|
||||||
return ranges;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RubyMemoryControl::MemoryPort::recvFunctional(PacketPtr pkt)
|
|
||||||
{
|
|
||||||
pkt->pushLabel(memory.name());
|
|
||||||
|
|
||||||
if (!queue.checkFunctional(pkt)) {
|
|
||||||
// Default implementation of SimpleTimingPort::recvFunctional()
|
|
||||||
// calls recvAtomic() and throws away the latency; we can save a
|
|
||||||
// little here by just not calculating the latency.
|
|
||||||
memory.functionalWrite(pkt);
|
|
||||||
}
|
|
||||||
|
|
||||||
pkt->popLabel();
|
|
||||||
}
|
|
||||||
|
|
||||||
Tick
|
|
||||||
RubyMemoryControl::MemoryPort::recvAtomic(PacketPtr pkt)
|
|
||||||
{
|
|
||||||
panic("This controller does not support recv atomic!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
RubyMemoryControl::MemoryPort::recvTimingReq(PacketPtr pkt)
|
|
||||||
{
|
|
||||||
// pass it to the memory controller
|
|
||||||
return memory.recvTimingReq(pkt);
|
|
||||||
}
|
|
|
@ -1,205 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
|
|
||||||
* Copyright (c) 2012 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_STRUCTURES_MEMORY_CONTROL_HH__
|
|
||||||
#define __MEM_RUBY_STRUCTURES_MEMORY_CONTROL_HH__
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <list>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "mem/abstract_mem.hh"
|
|
||||||
#include "mem/protocol/MemoryMsg.hh"
|
|
||||||
#include "mem/ruby/common/Address.hh"
|
|
||||||
#include "mem/ruby/profiler/MemCntrlProfiler.hh"
|
|
||||||
#include "mem/ruby/structures/MemoryNode.hh"
|
|
||||||
#include "mem/ruby/system/RubySystem.hh"
|
|
||||||
#include "params/RubyMemoryControl.hh"
|
|
||||||
|
|
||||||
// This constant is part of the definition of tFAW; see
|
|
||||||
// the comments in header to RubyMemoryControl.cc
|
|
||||||
#define ACTIVATE_PER_TFAW 4
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
class RubyMemoryControl : public AbstractMemory, public Consumer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef RubyMemoryControlParams Params;
|
|
||||||
RubyMemoryControl(const Params *p);
|
|
||||||
void init() override;
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
~RubyMemoryControl();
|
|
||||||
|
|
||||||
virtual BaseSlavePort& getSlavePort(const std::string& if_name,
|
|
||||||
PortID idx = InvalidPortID) override;
|
|
||||||
DrainState drain() override;
|
|
||||||
void wakeup() override;
|
|
||||||
|
|
||||||
void setDescription(const std::string& name) { m_description = name; };
|
|
||||||
std::string getDescription() { return m_description; };
|
|
||||||
|
|
||||||
// Called from the directory:
|
|
||||||
bool recvTimingReq(PacketPtr pkt);
|
|
||||||
void recvFunctional(PacketPtr pkt);
|
|
||||||
void enqueueMemRef(MemoryNode *memRef);
|
|
||||||
bool areNSlotsAvailable(int n) { return true; }; // infinite queue length
|
|
||||||
|
|
||||||
void print(std::ostream& out) const override;
|
|
||||||
void regStats() override;
|
|
||||||
|
|
||||||
int getBank(const Addr addr) const;
|
|
||||||
int getRank(const Addr addr) const;
|
|
||||||
|
|
||||||
// not used in Ruby memory controller
|
|
||||||
int getChannel(const Addr addr) const;
|
|
||||||
int getRow(const Addr addr) const;
|
|
||||||
|
|
||||||
//added by SS
|
|
||||||
int getBanksPerRank() { return m_banks_per_rank; };
|
|
||||||
int getRanksPerDimm() { return m_ranks_per_dimm; };
|
|
||||||
int getDimmsPerChannel() { return m_dimms_per_channel; }
|
|
||||||
|
|
||||||
bool functionalRead(Packet *pkt);
|
|
||||||
uint32_t functionalWrite(Packet *pkt);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void enqueueToDirectory(MemoryNode *req, Cycles latency);
|
|
||||||
int getRank(int bank) const;
|
|
||||||
bool queueReady(int bank);
|
|
||||||
void issueRequest(int bank);
|
|
||||||
bool issueRefresh(int bank);
|
|
||||||
void markTfaw(int rank);
|
|
||||||
void executeCycle();
|
|
||||||
|
|
||||||
// Private copy constructor and assignment operator
|
|
||||||
RubyMemoryControl (const RubyMemoryControl& obj);
|
|
||||||
RubyMemoryControl& operator=(const RubyMemoryControl& obj);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// For now, make use of a queued slave port to avoid dealing with
|
|
||||||
// flow control for the responses being sent back
|
|
||||||
class MemoryPort : public QueuedSlavePort
|
|
||||||
{
|
|
||||||
RespPacketQueue queue;
|
|
||||||
RubyMemoryControl& memory;
|
|
||||||
|
|
||||||
public:
|
|
||||||
MemoryPort(const std::string& name, RubyMemoryControl& _memory);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Tick recvAtomic(PacketPtr pkt);
|
|
||||||
|
|
||||||
void recvFunctional(PacketPtr pkt);
|
|
||||||
|
|
||||||
bool recvTimingReq(PacketPtr);
|
|
||||||
|
|
||||||
virtual AddrRangeList getAddrRanges() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Our incoming port, for a multi-ported controller add a crossbar
|
|
||||||
* in front of it
|
|
||||||
*/
|
|
||||||
MemoryPort port;
|
|
||||||
|
|
||||||
// data members
|
|
||||||
std::string m_description;
|
|
||||||
int m_msg_counter;
|
|
||||||
|
|
||||||
int m_banks_per_rank;
|
|
||||||
int m_ranks_per_dimm;
|
|
||||||
int m_dimms_per_channel;
|
|
||||||
int m_bank_bit_0;
|
|
||||||
int m_rank_bit_0;
|
|
||||||
int m_dimm_bit_0;
|
|
||||||
unsigned int m_bank_queue_size;
|
|
||||||
int m_bank_busy_time;
|
|
||||||
int m_rank_rank_delay;
|
|
||||||
int m_read_write_delay;
|
|
||||||
int m_basic_bus_busy_time;
|
|
||||||
Cycles m_mem_ctl_latency;
|
|
||||||
int m_refresh_period;
|
|
||||||
int m_mem_random_arbitrate;
|
|
||||||
int m_tFaw;
|
|
||||||
Cycles m_mem_fixed_delay;
|
|
||||||
|
|
||||||
int m_total_banks;
|
|
||||||
int m_total_ranks;
|
|
||||||
int m_refresh_period_system;
|
|
||||||
|
|
||||||
// queues where memory requests live
|
|
||||||
std::list<MemoryNode *> m_response_queue;
|
|
||||||
std::list<MemoryNode *> m_input_queue;
|
|
||||||
std::list<MemoryNode *>* m_bankQueues;
|
|
||||||
|
|
||||||
// Each entry indicates number of address-bus cycles until bank
|
|
||||||
// is reschedulable:
|
|
||||||
int *m_bankBusyCounter;
|
|
||||||
int *m_oldRequest;
|
|
||||||
|
|
||||||
uint64_t *m_tfaw_shift;
|
|
||||||
int *m_tfaw_count;
|
|
||||||
|
|
||||||
// Each of these indicates number of address-bus cycles until
|
|
||||||
// we can issue a new request of the corresponding type:
|
|
||||||
int m_busBusyCounter_Write;
|
|
||||||
int m_busBusyCounter_ReadNewRank;
|
|
||||||
int m_busBusyCounter_Basic;
|
|
||||||
|
|
||||||
int m_busBusy_WhichRank; // which rank last granted
|
|
||||||
int m_roundRobin; // which bank queue was last granted
|
|
||||||
int m_refresh_count; // cycles until next refresh
|
|
||||||
int m_need_refresh; // set whenever m_refresh_count goes to zero
|
|
||||||
int m_refresh_bank; // which bank to refresh next
|
|
||||||
int m_ageCounter; // age of old requests; to detect starvation
|
|
||||||
int m_idleCount; // watchdog timer for shutting down
|
|
||||||
|
|
||||||
MemCntrlProfiler *m_profiler_ptr;
|
|
||||||
|
|
||||||
class MemCntrlEvent : public Event
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MemCntrlEvent(RubyMemoryControl *_mem_cntrl)
|
|
||||||
{
|
|
||||||
mem_cntrl = _mem_cntrl;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
void process() { mem_cntrl->wakeup(); }
|
|
||||||
|
|
||||||
RubyMemoryControl* mem_cntrl;
|
|
||||||
};
|
|
||||||
|
|
||||||
MemCntrlEvent m_event;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, const RubyMemoryControl& obj);
|
|
||||||
|
|
||||||
#endif // __MEM_RUBY_STRUCTURES_MEMORY_CONTROL_HH__
|
|
|
@ -1,57 +0,0 @@
|
||||||
# Copyright (c) 2009 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.
|
|
||||||
#
|
|
||||||
# Authors: Steve Reinhardt
|
|
||||||
# Brad Beckmann
|
|
||||||
|
|
||||||
from m5.params import *
|
|
||||||
from AbstractMemory import AbstractMemory
|
|
||||||
|
|
||||||
class RubyMemoryControl(AbstractMemory):
|
|
||||||
type = 'RubyMemoryControl'
|
|
||||||
cxx_class = 'RubyMemoryControl'
|
|
||||||
cxx_header = "mem/ruby/structures/RubyMemoryControl.hh"
|
|
||||||
|
|
||||||
banks_per_rank = Param.Int(8, "");
|
|
||||||
ranks_per_dimm = Param.Int(2, "");
|
|
||||||
dimms_per_channel = Param.Int(2, "");
|
|
||||||
bank_bit_0 = Param.Int(8, "");
|
|
||||||
rank_bit_0 = Param.Int(11, "");
|
|
||||||
dimm_bit_0 = Param.Int(12, "");
|
|
||||||
bank_queue_size = Param.Int(12, "");
|
|
||||||
bank_busy_time = Param.Int(11, "");
|
|
||||||
rank_rank_delay = Param.Int(1, "");
|
|
||||||
read_write_delay = Param.Int(2, "");
|
|
||||||
basic_bus_busy_time = Param.Int(2, "");
|
|
||||||
mem_ctl_latency = Param.Cycles(12, "");
|
|
||||||
refresh_period = Param.Cycles(1560, "");
|
|
||||||
tFaw = Param.Int(0, "");
|
|
||||||
mem_random_arbitrate = Param.Int(0, "");
|
|
||||||
mem_fixed_delay = Param.Cycles(0, "");
|
|
||||||
|
|
||||||
# single-ported on the system interface side, instantiate with a
|
|
||||||
# crossbar in front of the controller for multiple ports
|
|
||||||
port = SlavePort("Slave port")
|
|
|
@ -38,7 +38,6 @@ SimObject('DirectoryMemory.py')
|
||||||
SimObject('LRUReplacementPolicy.py')
|
SimObject('LRUReplacementPolicy.py')
|
||||||
SimObject('PseudoLRUReplacementPolicy.py')
|
SimObject('PseudoLRUReplacementPolicy.py')
|
||||||
SimObject('ReplacementPolicy.py')
|
SimObject('ReplacementPolicy.py')
|
||||||
SimObject('RubyMemoryControl.py')
|
|
||||||
SimObject('RubyPrefetcher.py')
|
SimObject('RubyPrefetcher.py')
|
||||||
SimObject('WireBuffer.py')
|
SimObject('WireBuffer.py')
|
||||||
|
|
||||||
|
@ -48,8 +47,6 @@ Source('CacheMemory.cc')
|
||||||
Source('LRUPolicy.cc')
|
Source('LRUPolicy.cc')
|
||||||
Source('PseudoLRUPolicy.cc')
|
Source('PseudoLRUPolicy.cc')
|
||||||
Source('WireBuffer.cc')
|
Source('WireBuffer.cc')
|
||||||
Source('RubyMemoryControl.cc')
|
|
||||||
Source('MemoryNode.cc')
|
|
||||||
Source('PersistentTable.cc')
|
Source('PersistentTable.cc')
|
||||||
Source('Prefetcher.cc')
|
Source('Prefetcher.cc')
|
||||||
Source('TimerTable.cc')
|
Source('TimerTable.cc')
|
||||||
|
|
Loading…
Reference in a new issue