blob: b3c6c261dc63d2d4b30acdab4cba62ac4e910b7b [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 <memory>
#include "absl/status/status.h"
#include "googlemock/include/gmock/gmock.h"
#include "googletest/include/gtest/gtest.h"
#include "mpact/sim/generic/data_buffer.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"
#include "mpact/sim/util/memory/test/dummy_memory.h"
// This file contains unit tests for the MemoryRouter class.
namespace {
using ::mpact::sim::generic::DataBufferFactory;
using ::mpact::sim::util::AtomicMemoryOpInterface;
using ::mpact::sim::util::MemoryInterface;
using ::mpact::sim::util::MemoryRouter;
using ::mpact::sim::util::SingleInitiatorRouter;
using ::mpact::sim::util::TaggedMemoryInterface;
using ::mpact::sim::util::test::DummyMemory;
TEST(MemoryRouterTest, AddInitiator) {
auto memory_router = std::make_unique<MemoryRouter>();
// Add initiator.
auto* memory_initiator0 = memory_router->AddMemoryInitiator("initiator0");
// Try to add a second initiator with the same name.
auto* memory_initiator1 = memory_router->AddMemoryInitiator("initiator0");
// They should be the same object.
EXPECT_EQ(memory_initiator0, memory_initiator1);
// Add tagged initiator by the same name - it should also be the same object.
auto* tagged_initiator = memory_router->AddTaggedInitiator("initiator0");
// Since the types are different, make sure that the pointers point to the
// same area within the size of SingleInitiatorRouter.
uint64_t p0 = reinterpret_cast<uint64_t>(memory_initiator0);
uint64_t p1 = reinterpret_cast<uint64_t>(tagged_initiator);
EXPECT_TRUE(((p0 >= p1) && (p0 < p1 + sizeof(SingleInitiatorRouter))) ||
((p1 >= p0) && (p1 < p0 + sizeof(SingleInitiatorRouter))));
// Add atomic initiator by the same name - it should also be the same object.
auto* atomic_initiator = memory_router->AddAtomicInitiator("initiator0");
// Since the types are different, make sure that the pointers point to the
// same area within the size of SingleInitiatorRouter.
p0 = reinterpret_cast<uint64_t>(tagged_initiator);
p1 = reinterpret_cast<uint64_t>(atomic_initiator);
EXPECT_TRUE(((p0 >= p1) && (p0 < p1 + sizeof(SingleInitiatorRouter))) ||
((p1 >= p0) && (p1 < p0 + sizeof(SingleInitiatorRouter))));
}
TEST(MemoryRouterTest, AddTarget) {
auto memory_router = std::make_unique<MemoryRouter>();
auto memory = std::make_unique<DummyMemory>();
// Add memory target.
EXPECT_TRUE(memory_router
->AddTarget("memory_target",
static_cast<MemoryInterface*>(memory.get()))
.ok());
// Try adding it again, for each interface. It should fail.
EXPECT_FALSE(memory_router
->AddTarget("memory_target",
static_cast<MemoryInterface*>(memory.get()))
.ok());
EXPECT_FALSE(
memory_router
->AddTarget("memory_target",
static_cast<TaggedMemoryInterface*>(memory.get()))
.ok());
EXPECT_FALSE(
memory_router
->AddTarget("memory_target",
static_cast<AtomicMemoryOpInterface*>(memory.get()))
.ok());
// Add the memory target with different names. This should work.
EXPECT_TRUE(memory_router
->AddTarget("memory_target_2",
static_cast<MemoryInterface*>(memory.get()))
.ok());
EXPECT_TRUE(memory_router
->AddTarget("tagged_target",
static_cast<TaggedMemoryInterface*>(memory.get()))
.ok());
EXPECT_TRUE(
memory_router
->AddTarget("atomic_target",
static_cast<AtomicMemoryOpInterface*>(memory.get()))
.ok());
}
TEST(MemoryRouterTest, AddMapping) {
auto memory_router = std::make_unique<MemoryRouter>();
auto memory = std::make_unique<DummyMemory>();
// Add initiator (ignoring the return value) and target.
(void)memory_router->AddMemoryInitiator("initiator");
EXPECT_TRUE(
memory_router
->AddTarget("mem", static_cast<MemoryInterface*>(memory.get()))
.ok());
EXPECT_TRUE(
memory_router->AddMapping("initiator", "mem", 0x1000, 0x1fff).ok());
EXPECT_TRUE(
memory_router->AddMapping("initiator", "mem", 0x1000, 0x1fff).ok());
EXPECT_FALSE(
memory_router->AddMapping("initiator", "mem", 0x800, 0x1800).ok());
EXPECT_FALSE(memory_router->AddMapping("none", "mem", 0x2000, 0x2fff).ok());
EXPECT_FALSE(
memory_router->AddMapping("initiator", "none", 0x2000, 0x2fff).ok());
EXPECT_TRUE(
memory_router->AddMapping("initiator", "mem", 0x2000, 0x2fff).ok());
}
TEST(MemoryRouterTest, RoutingTest) {
// Create a router with 2 initiators and 2 memory targets. With different
// mappings for each initiator.
DataBufferFactory factory;
auto* db = factory.Allocate<uint32_t>(1);
auto memory_router = std::make_unique<MemoryRouter>();
auto memory0 = std::make_unique<DummyMemory>();
auto memory1 = std::make_unique<DummyMemory>();
auto* initiator0 = memory_router->AddMemoryInitiator("initiator0");
auto* initiator1 = memory_router->AddMemoryInitiator("initiator1");
EXPECT_TRUE(
memory_router
->AddTarget("mem0", static_cast<MemoryInterface*>(memory0.get()))
.ok());
EXPECT_TRUE(
memory_router
->AddTarget("mem1", static_cast<MemoryInterface*>(memory1.get()))
.ok());
EXPECT_TRUE(
memory_router->AddMapping("initiator0", "mem0", 0x1000, 0x1fff).ok());
EXPECT_TRUE(
memory_router->AddMapping("initiator0", "mem1", 0x2000, 0x2fff).ok());
EXPECT_TRUE(
memory_router
->AddMapping("initiator1", "mem0", 0x1'0000'0000, 0x1'0000'ffff)
.ok());
EXPECT_TRUE(
memory_router
->AddMapping("initiator1", "mem1", 0x2'0000'0000, 0x2'0000'ffff)
.ok());
// Load using initiator 0.
initiator0->Load(0x1000, db, nullptr, nullptr);
initiator0->Load(0x2000, db, nullptr, nullptr);
EXPECT_EQ(memory0->load_address(), 0x1000);
EXPECT_EQ(memory1->load_address(), 0x2000);
memory0->ClearValues();
memory1->ClearValues();
// Load using initiator 1.
initiator1->Load(0x1'0000'1000, db, nullptr, nullptr);
initiator1->Load(0x2'0000'2000, db, nullptr, nullptr);
EXPECT_EQ(memory0->load_address(), 0x1'0000'1000);
EXPECT_EQ(memory1->load_address(), 0x2'0000'2000);
EXPECT_EQ(memory0->load_address(), 0x1'0000'1000);
EXPECT_EQ(memory1->load_address(), 0x2'0000'2000);
db->DecRef();
}
} // namespace