| /* |
| * 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. |
| */ |
| |
| #include <mem/ruby/structures/MN_TBETable.hh> |
| |
| namespace gem5 |
| { |
| |
| namespace ruby |
| { |
| |
| // Based on the current set of TBEs, choose a new "distributor" |
| // Can return null -> no distributor |
| MiscNode_TBE* |
| MN_TBETable::chooseNewDistributor() |
| { |
| // Run over the current TBEs, gather information |
| std::vector<MiscNode_TBE*> ready_sync_tbes; |
| std::vector<MiscNode_TBE*> ready_nonsync_tbes; |
| std::vector<MiscNode_TBE*> potential_sync_dependency_tbes; |
| bool has_waiting_sync = false; |
| int waiting_count = 0; |
| for (auto& keyValuePair : m_map) { |
| MiscNode_TBE& tbe = keyValuePair.second; |
| |
| switch (tbe.getstate()) { |
| case MiscNode_State_DvmSync_Distributing: |
| case MiscNode_State_DvmNonSync_Distributing: |
| // If something is still distributing, just return it |
| return &tbe; |
| case MiscNode_State_DvmSync_ReadyToDist: |
| ready_sync_tbes.push_back(&tbe); |
| break; |
| case MiscNode_State_DvmNonSync_ReadyToDist: |
| ready_nonsync_tbes.push_back(&tbe); |
| // Sync ops can potentially depend on not-executed NonSync ops |
| potential_sync_dependency_tbes.push_back(&tbe); |
| break; |
| case MiscNode_State_DvmSync_Waiting: |
| has_waiting_sync = true; |
| waiting_count++; |
| break; |
| case MiscNode_State_DvmNonSync_Waiting: |
| waiting_count++; |
| // Sync ops can potentially depend on not-finished NonSync ops |
| potential_sync_dependency_tbes.push_back(&tbe); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| // At most ~4 pending snoops at the RN-F |
| // => for safety we only allow 4 ops waiting + distributing at a time |
| // => if 4 are waiting currently, don't start distributing another one |
| assert(waiting_count <= 4); |
| if (waiting_count == 4) { |
| return nullptr; |
| } |
| |
| // If there's a waiting Sync op, don't allow other Sync ops to start. |
| if (has_waiting_sync) { |
| ready_sync_tbes.clear(); |
| } |
| |
| // We need to handle NonSync -> Sync dependencies |
| // If we send CompDBIDResp for a Non-Sync that hasn't started, |
| // the RN-F can send a dependent Sync immediately afterwards. |
| // The Non-Sync must receive all responses before the Sync starts. |
| // => ignore Syncs which arrive after unfinished NonSyncs |
| auto hasNonSyncDependency = [&](const MiscNode_TBE* sync_tbe) { |
| for (const auto* potential_dep : potential_sync_dependency_tbes) { |
| if (sync_tbe->gettimestamp() > potential_dep->gettimestamp() && |
| sync_tbe->getrequestor() == potential_dep->getrequestor()) { |
| // A NonSync from the same machine arrived before us |
| // => we have a dependency |
| return true; |
| } |
| } |
| return false; |
| }; |
| // Erase-remove idiom to remove elements at arbitrary indices |
| // https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom |
| // This calls an O(n) function n times = O(n^2) worst case. |
| // TODO this should be improved if n grows > 16 |
| ready_sync_tbes.erase( |
| std::remove_if(ready_sync_tbes.begin(), ready_sync_tbes.end(), |
| hasNonSyncDependency), |
| ready_sync_tbes.end() |
| ); |
| |
| // TODO shouldn't use age? |
| |
| // Extend ready_nonsync_tbes with the contents of ready_sync_tbes |
| ready_nonsync_tbes.insert(ready_nonsync_tbes.end(), |
| ready_sync_tbes.begin(), ready_sync_tbes.end()); |
| |
| // Check if no candidates |
| if (ready_nonsync_tbes.empty()) |
| return nullptr; |
| |
| // Otherwise select the minimum timestamp = oldest element |
| auto it = std::min_element( |
| ready_nonsync_tbes.begin(), ready_nonsync_tbes.end(), |
| [](const MiscNode_TBE* a, const MiscNode_TBE* b) { |
| return a->gettimestamp() - b->gettimestamp(); |
| } |
| ); |
| assert(it != ready_nonsync_tbes.end()); |
| return *it; |
| } |
| |
| } // namespace ruby |
| |
| } // namespace gem5 |