| /* | |
| * 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 <cassert> | |
| #include <unordered_map> | |
| #include <vector> | |
| #include <base/statistics.hh> | |
| #include "mem/ruby/common/MachineID.hh" | |
| #include "mem/ruby/structures/TBEStorage.hh" | |
| namespace gem5 | |
| { | |
| namespace ruby | |
| { | |
| // 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 RetryEntry> | |
| class MN_TBEStorage | |
| { | |
| public: | |
| MN_TBEStorage(Stats::Group *parent, | |
| std::initializer_list<TBEStorage *> _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<TBEStorage *> partitions; | |
| std::list<RetryEntry> m_retryEntries; | |
| typename std::list<RetryEntry>::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 ruby | |
| } // namespace gem5 | |
| #endif |