blob: 7c86cf55c8f7e03569432d558c1cced5d2f151d7 [file]
// 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