| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "mpact/sim/util/memory/memory_router.h" |
| |
| #include <cstdint> |
| #include <string> |
| |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/str_cat.h" |
| #include "mpact/sim/util/memory/memory_interface.h" |
| #include "mpact/sim/util/memory/single_initiator_router.h" |
| #include "mpact/sim/util/memory/tagged_memory_interface.h" |
| |
| namespace mpact { |
| namespace sim { |
| namespace util { |
| |
| MemoryRouter::MemoryRouter() {} |
| |
| MemoryRouter::~MemoryRouter() { |
| for (auto& [unused, initiator] : initiators_) delete initiator; |
| |
| initiators_.clear(); |
| } |
| |
| // Templated helper method used in implementing the three methods to add named |
| // initiators to the router (one for each type of initiator interface). |
| template <typename Interface> |
| Interface* AddInitiator(MemoryRouter::InitiatorMap& initiators, |
| const std::string& name) { |
| // See if it already exists. |
| auto it = initiators.find(name); |
| if (it != initiators.end()) { |
| return static_cast<Interface*>(it->second); |
| } |
| auto initiator = new SingleInitiatorRouter(name); |
| initiators.emplace(std::string(name), initiator); |
| return static_cast<Interface*>(initiator); |
| } |
| |
| MemoryInterface* MemoryRouter::AddMemoryInitiator(const std::string& name) { |
| return AddInitiator<MemoryInterface>(initiators_, name); |
| } |
| |
| TaggedMemoryInterface* MemoryRouter::AddTaggedInitiator( |
| const std::string& name) { |
| return AddInitiator<TaggedMemoryInterface>(initiators_, name); |
| } |
| |
| AtomicMemoryOpInterface* MemoryRouter::AddAtomicInitiator( |
| const std::string& name) { |
| return AddInitiator<AtomicMemoryOpInterface>(initiators_, name); |
| } |
| |
| namespace internal { |
| |
| // Templated helper method used in implementing the next three methods that |
| // add named targets to the router (one for each type of target interface). |
| template <typename Interface> |
| absl::Status AddTargetPrivate( |
| MemoryRouter::TargetMap<Interface>& target_interface_map, |
| absl::flat_hash_set<std::string>& target_names, const std::string& name, |
| Interface* memory) { |
| // Only one instance of each target name can exist. |
| if (target_names.contains(name)) { |
| return absl::AlreadyExistsError( |
| absl::StrCat("Target: ", name, " already exists")); |
| } |
| target_names.insert(name); |
| target_interface_map.emplace(std::string(name), memory); |
| return absl::OkStatus(); |
| } |
| |
| } // namespace internal |
| |
| absl::Status MemoryRouter::AddTarget(const std::string& name, |
| MemoryInterface* memory) { |
| return internal::AddTargetPrivate<MemoryInterface>( |
| memory_targets_, target_names_, name, memory); |
| } |
| |
| absl::Status MemoryRouter::AddTarget(const std::string& name, |
| TaggedMemoryInterface* tagged_memory) { |
| return internal::AddTargetPrivate<TaggedMemoryInterface>( |
| tagged_targets_, target_names_, name, tagged_memory); |
| } |
| |
| absl::Status MemoryRouter::AddTarget(const std::string& name, |
| AtomicMemoryOpInterface* atomic_memory) { |
| return internal::AddTargetPrivate<AtomicMemoryOpInterface>( |
| atomic_targets_, target_names_, name, atomic_memory); |
| } |
| |
| // Add a mapping between 'initiator_name' and 'target_name' for the given |
| // address range (inclusive). |
| absl::Status MemoryRouter::AddMapping(const std::string& initiator_name, |
| const std::string& target_name, |
| uint64_t base, uint64_t top) { |
| // Return error if the initiator doesn't exist. |
| auto it = initiators_.find(initiator_name); |
| if (it == initiators_.end()) { |
| return absl::NotFoundError( |
| absl::StrCat("Initiator: ", initiator_name, " not found")); |
| } |
| auto* initiator = it->second; |
| |
| // Check each type of target and add the one found to the initiator with the |
| // given address range. |
| |
| auto memory_it = memory_targets_.find(target_name); |
| if (memory_it != memory_targets_.end()) { |
| return initiator->AddTarget(memory_it->second, base, top); |
| } |
| auto tagged_it = tagged_targets_.find(target_name); |
| if (tagged_it != tagged_targets_.end()) { |
| return initiator->AddTarget(tagged_it->second, base, top); |
| } |
| auto atomic_it = atomic_targets_.find(target_name); |
| if (atomic_it != atomic_targets_.end()) { |
| return initiator->AddTarget(atomic_it->second, base, top); |
| } |
| // Return error if there is no such target. |
| return absl::NotFoundError( |
| absl::StrCat("Target: ", target_name, " not found")); |
| } |
| |
| } // namespace util |
| } // namespace sim |
| } // namespace mpact |