/* * Copyright (c) 2021-2022 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_MN_TBESTORAGE_HH__ #define __MEM_RUBY_STRUCTURES_MN_TBESTORAGE_HH__ #include #include #include #include #include "mem/ruby/common/MachineID.hh" #include "mem/ruby/structures/TBEStorage.hh" namespace gem5 { namespace ruby { namespace CHI { // MN_TBEStorage is composed of multiple TBEStorage // partitions that could be used for specific types of TBEs. // Partition number 0 is the generic partition and will // store any kind of TBEs. // Space for specific TBEs will be looked first into the matching // partition, and when no space is available the generic one will // be used template class MN_TBEStorage { public: MN_TBEStorage(statistics::Group *parent, std::initializer_list _partitions) : m_stats(parent), partitions(_partitions) {} // Returns the current number of slots allocated int size() const { int total = 0; for (auto part : partitions) { total += part->size(); } return total; } // Returns the total capacity of this TBEStorage table int capacity() const { int total = 0; for (auto part : partitions) { total += part->capacity(); } return total; } // Returns number of slots currently reserved int reserved() const { int total = 0; for (auto part : partitions) { total += part->reserved(); } return total; } // Returns the number of slots available for objects of a certain type; int slotsAvailable(int partition) const { auto generic_slots = partitions[0]->slotsAvailable(); if (partition) { return partitions[partition]->slotsAvailable() + generic_slots; } else { return generic_slots; } } // Returns the TBEStorage utilization float utilization() const { return size() / (float)capacity(); } // Returns true if slotsAvailable(partition) >= 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, int partition, Tick current_time = 0) const { return slotsAvailable(partition) >= n; } // Increase/decrease the number of reserved slots. Having reserved slots // reduces the number of slots available for allocation void incrementReserved(int partition) { if (partition && partitions[partition]->areNSlotsAvailable(1)) { partitions[partition]->incrementReserved(); } else { partitions[0]->incrementReserved(); } m_stats.avg_reserved = reserved(); } void decrementReserved(int partition) { if (partition && (partitions[partition]->reserved() > 0)) { partitions[partition]->decrementReserved(); } else { partitions[0]->decrementReserved(); } m_stats.avg_reserved = reserved(); } // 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(int partition) { if (partition && partitions[partition]->areNSlotsAvailable(1)) { int part_slot = partitions[partition]->addEntryToNewSlot(); m_stats.avg_size = size(); m_stats.avg_util = utilization(); return part_slot; } else { int generic_slot = partitions[0]->addEntryToNewSlot(); m_stats.avg_size = size(); m_stats.avg_util = utilization(); return partitions[partition]->capacity() + generic_slot; } } // addEntryToSlot(int) is not supported. // 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, int partition) { auto part_capacity = partitions[partition]->capacity(); if (slot < part_capacity) { partitions[partition]->removeEntryFromSlot(slot); } else { partitions[0]->removeEntryFromSlot( slot - part_capacity); } m_stats.avg_size = size(); m_stats.avg_util = utilization(); } // Insert a "retry entry" into the queue void emplaceRetryEntry(RetryEntry entry) { m_retryEntries.push_back(entry); } // Check if a retry is possible bool hasPossibleRetry() { auto retry_iter = getNextRetryEntryIter(); return retry_iter != m_retryEntries.end(); } // Peek what the next thing to retry should be // Should only be called if hasPossibleRetry() returns true RetryEntry popNextRetryEntry() { auto retry_iter = getNextRetryEntryIter(); assert(retry_iter != m_retryEntries.end()); auto entry = *retry_iter; m_retryEntries.erase(retry_iter); return entry; } private: struct MN_TBEStorageStats : public statistics::Group { MN_TBEStorageStats(statistics::Group *parent) : statistics::Group(parent), ADD_STAT(avg_size, "Avg. number of slots allocated"), ADD_STAT(avg_util, "Avg. utilization"), ADD_STAT(avg_reserved, "Avg. number of slots reserved") {} // Statistical variables statistics::Average avg_size; statistics::Average avg_util; statistics::Average avg_reserved; } m_stats; std::vector partitions; std::list m_retryEntries; typename std::list::iterator getNextRetryEntryIter() { auto begin_it = m_retryEntries.begin(); auto end_it = m_retryEntries.end(); for (auto it = begin_it; it != end_it; it++) { if (areNSlotsAvailable(1, it->getisNonSync())) return it; } return end_it; } }; } // namespace CHI } // namespace ruby } // namespace gem5 #endif