blob: 7248d8f0470e029852e12a3d025cc99a3414196d [file] [log] [blame]
/*
* Copyright (c) 2021 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* 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_TBESTORAGE_HH__
#define __MEM_RUBY_STRUCTURES_TBESTORAGE_HH__
#include <cassert>
#include <stack>
#include <unordered_map>
#include <base/statistics.hh>
namespace gem5
{
namespace ruby
{
// The TBEStorage is used to track the resources consumed by the TBETable,
// i.e. the number of available TBE slots.
//
// TBEStorage resource tracking has two main differences from TBETable:
//
// 1) Allows slot reservation. This is useful to implement protocols that
// employ retry/credit messages instead of stall when the controller runs
// out of TBEs to accept new request.
//
// 2) Can also assign multiple entries to the same slot. This is useful to
// more easily model cases where multiple transactions share the same TBE
// resource (i.e. the slot).
// E.g: a request that triggers a replacement in a system without
// dedicated WB/Eviction buffer; both transactions can can have separate
// logical TBEs associated to the same slot.
//
// The motivation for having a separate structures for tracking TBEs
// availability are twofold:
//
// - Keeps TBETable simple and without the additional overhead for
// protocols that do not need these additional features.
//
// - Having two separate transactions sharing the same TBE resource using
// the current TBETable would be cumbersome since the TBETable is indexed
// by the transaction address.
class TBEStorage
{
public:
TBEStorage(statistics::Group *parent, int number_of_TBEs);
// Returns the current number of slots allocated
int size() const { return m_slots_used.size(); }
// Returns the total capacity of this TBEStorage table
int capacity() const { return m_slots_used.size() + m_slots_avail.size(); }
// Returns number of slots currently reserved
int reserved() const { return m_reserved; }
// Returns the number of slots available
int slotsAvailable() const { return m_slots_avail.size() - m_reserved; }
// Returns the TBEStorage utilization
float utilization() const { return size() / (float)capacity(); }
// Returns true if slotsAvailable() >= n; current_time is always ignored
// This allows this class to be used with check_allocate in SLICC to
// trigger resource stalls when there are no slots available
bool areNSlotsAvailable(int n, Tick current_time = 0) const;
// Increase/decrease the number of reserved slots. Having reserved slots
// reduces the number of slots available for allocation
void incrementReserved();
void decrementReserved();
// Assign a TBETable entry to a free slot and returns the slot number.
// Notice we don't need any info from TBETable and just track the number
// of entries assigned to each slot.
// This funcion requires slotsAvailable() > 0
int addEntryToNewSlot();
// Assign an entry to an existing non-empty slot
void addEntryToSlot(int slot);
// Remove an entry from an existing non-empty slot. The slot becomes
// available again when the number of assigned entries == 0
void removeEntryFromSlot(int slot);
private:
int m_reserved;
std::stack<int> m_slots_avail;
std::unordered_map<int, int> m_slots_used;
struct TBEStorageStats : public statistics::Group
{
TBEStorageStats(statistics::Group *parent);
// Statistical variables
statistics::Average avg_size;
statistics::Average avg_util;
statistics::Average avg_reserved;
} m_stats;
};
inline bool
TBEStorage::areNSlotsAvailable(int n, Tick current_time) const
{
return slotsAvailable() >= n;
}
inline void
TBEStorage::incrementReserved()
{
++m_reserved;
m_stats.avg_reserved = m_reserved;
}
inline void
TBEStorage::decrementReserved()
{
assert(m_reserved > 0);
--m_reserved;
m_stats.avg_reserved = m_reserved;
}
inline int
TBEStorage::addEntryToNewSlot()
{
assert(slotsAvailable() > 0);
assert(m_slots_avail.size() > 0);
int slot = m_slots_avail.top();
m_slots_used[slot] = 1;
m_slots_avail.pop();
m_stats.avg_size = size();
m_stats.avg_util = utilization();
return slot;
}
inline void
TBEStorage::addEntryToSlot(int slot)
{
auto iter = m_slots_used.find(slot);
assert(iter != m_slots_used.end());
iter->second += 1;
}
inline void
TBEStorage::removeEntryFromSlot(int slot)
{
auto iter = m_slots_used.find(slot);
assert(iter != m_slots_used.end());
assert(iter->second > 0);
iter->second -= 1;
if (iter->second == 0) {
m_slots_used.erase(iter);
m_slots_avail.push(slot);
}
m_stats.avg_size = size();
m_stats.avg_util = utilization();
}
} // namespace ruby
} // namespace gem5
#endif