blob: 8dcaac21ebdf03b5e0f17c0d0d9e3d47cdefbea4 [file]
// Copyright 2023 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 <any>
#include <cstdint>
#include "absl/strings/string_view.h"
#include "absl/types/any.h"
#include "googlemock/include/gmock/gmock.h"
#include "googletest/include/gtest/gtest.h"
#include "mpact/sim/generic/arch_state.h"
#include "mpact/sim/generic/data_buffer.h"
#include "mpact/sim/generic/delay_line.h"
#include "mpact/sim/generic/operand_interface.h"
#include "mpact/sim/generic/register.h"
#include "mpact/sim/generic/simple_resource.h"
namespace mpact {
namespace sim {
namespace generic {
namespace {
using ScalarRegister = Register<uint32_t>;
using Vector8Register = VectorRegister<uint32_t, 8>;
using ScalarReservedRegister = ReservedRegister<uint32_t>;
static constexpr char kTestPoolName[] = "TestPool";
static constexpr int kTestPoolSize = 35;
// Define a class that derives from ArchState since constructors are
// protected.
class MockArchState : public ArchState {
public:
MockArchState(absl::string_view id, SourceOperandInterface *pc_op)
: ArchState(id, pc_op) {}
explicit MockArchState(absl::string_view id) : MockArchState(id, nullptr) {}
};
// Test fixture to keep a single copy of registers, delay line and
// DataBufferFactory.
class RegisterOperandTest : public testing::Test {
protected:
RegisterOperandTest() {
arch_state_ = new MockArchState("MockArchState");
sreg_ = new ScalarRegister(arch_state_, "S0");
vreg_ = new Vector8Register(arch_state_, "V0");
pool_ = new SimpleResourcePool(kTestPoolName, kTestPoolSize);
auto result = pool_->AddResource("R0");
if (!result.ok()) return;
rreg_ =
new ScalarReservedRegister(arch_state_, "R0", pool_->GetResource("R0"));
}
~RegisterOperandTest() override {
delete vreg_;
delete sreg_;
delete rreg_;
delete pool_;
delete arch_state_;
}
MockArchState *arch_state_;
ScalarRegister *sreg_;
Vector8Register *vreg_;
ScalarReservedRegister *rreg_ = nullptr;
SimpleResourcePool *pool_;
};
// Tests that the register source operands are initialized correctly.
TEST_F(RegisterOperandTest, SourceOperandInitialization) {
auto s_src_op = sreg_->CreateSourceOperand();
EXPECT_EQ(std::any_cast<RegisterBase *>(s_src_op->GetObject()),
static_cast<RegisterBase *>(sreg_));
EXPECT_EQ(s_src_op->shape(), sreg_->shape());
delete s_src_op;
auto v_src_op = vreg_->CreateSourceOperand();
EXPECT_EQ(std::any_cast<RegisterBase *>(v_src_op->GetObject()),
static_cast<RegisterBase *>(vreg_));
EXPECT_EQ(v_src_op->shape(), vreg_->shape());
delete v_src_op;
auto r_src_op = rreg_->CreateSourceOperand();
EXPECT_EQ(std::any_cast<RegisterBase *>(r_src_op->GetObject()),
static_cast<RegisterBase *>(rreg_));
EXPECT_EQ(r_src_op->shape(), rreg_->shape());
delete r_src_op;
}
// Tests that the register destination operands are initialized correctly.
TEST_F(RegisterOperandTest, DestinationOperandInitialization) {
auto s_dst_op = sreg_->CreateDestinationOperand(1);
EXPECT_EQ(s_dst_op->latency(), 1);
EXPECT_EQ(s_dst_op->shape(), sreg_->shape());
EXPECT_EQ(std::any_cast<RegisterBase *>(s_dst_op->GetObject()),
static_cast<RegisterBase *>(sreg_));
delete s_dst_op;
auto v_dst_op = vreg_->CreateDestinationOperand(4);
EXPECT_EQ(v_dst_op->latency(), 4);
EXPECT_EQ(v_dst_op->shape(), vreg_->shape());
EXPECT_EQ(std::any_cast<RegisterBase *>(v_dst_op->GetObject()),
static_cast<RegisterBase *>(vreg_));
delete v_dst_op;
auto r_dst_op = rreg_->CreateDestinationOperand(3);
EXPECT_EQ(r_dst_op->latency(), 3);
EXPECT_EQ(r_dst_op->shape(), rreg_->shape());
EXPECT_EQ(std::any_cast<RegisterBase *>(r_dst_op->GetObject()),
static_cast<RegisterBase *>(rreg_));
delete r_dst_op;
}
// Tests that a destination register operand can update a register so that
// it is visible in a source register operand.
TEST_F(RegisterOperandTest, ScalarRegisterValueWriteAndRead) {
auto dst_op = sreg_->CreateDestinationOperand(1);
auto src_op = sreg_->CreateSourceOperand();
// Get DataBuffer from destination operand and initialize the value.
DataBuffer *db = dst_op->AllocateDataBuffer();
db->Set<uint32_t>(0, 0xDEADBEEF);
// Submit data buffer and advance the delay line by the 1 cycle latency.
db->Submit();
arch_state_->AdvanceDelayLines();
// Verify that the source operand can read the new value.
EXPECT_EQ(src_op->AsUint32(0), 0xDEADBEEF);
// Get a new data buffer with copy of the current value and verify value is
// correct.
DataBuffer *db2 = dst_op->CopyDataBuffer();
EXPECT_EQ(db2->Get<uint32_t>(0), 0xDEADBEEF);
// Change value, submit the data buffer and advance the delay line 1 cycle.
db2->Set<uint32_t>(0, 0);
db2->Submit();
arch_state_->AdvanceDelayLines();
// Verify the updated value.
EXPECT_EQ(src_op->AsUint32(0), 0);
delete src_op;
delete dst_op;
}
// Tests that a destination vector register operand can update a register so
// that it is visible in a source register operand.
TEST_F(RegisterOperandTest, VectorRegisterValueWriteAndRead) {
auto dst_op = vreg_->CreateDestinationOperand(2);
auto src_op = vreg_->CreateSourceOperand();
// Get DataBuffer from destination operand and initialize the value.
DataBuffer *db = dst_op->AllocateDataBuffer();
for (int index = 0; index < vreg_->shape()[0]; index++) {
db->Set<uint32_t>(index, 0xDEAD0000 | index);
}
// Submit the data buffer and advance the delay line by the 2 cycle latency.
db->Submit();
arch_state_->AdvanceDelayLines();
arch_state_->AdvanceDelayLines();
// Verify that the value has been written correctly to the register.
for (int index = 0; index < vreg_->shape()[0]; index++) {
EXPECT_EQ(src_op->AsUint32(index), 0xDEAD0000 | index);
}
// Get DataBuffer from destination operand with copy of the current value.
DataBuffer *db2 = dst_op->CopyDataBuffer();
// Verify that the value is the same as before.
for (int index = 0; index < vreg_->shape()[0]; index++) {
EXPECT_EQ(db2->Get<uint32_t>(index), 0xDEAD0000 | index);
db2->Set<uint32_t>(index, 0);
}
// Submit the data buffer and advance the delay line by the 2 cycle latency.
db2->Submit();
arch_state_->AdvanceDelayLines();
arch_state_->AdvanceDelayLines();
// Verify that the value has been updated.
for (int index = 0; index < vreg_->shape()[0]; index++) {
EXPECT_EQ(src_op->AsUint32(index), 0);
}
delete src_op;
delete dst_op;
}
// Tests that a destination reserved register operand can update a register so
// that it is visible in a source register operand.
TEST_F(RegisterOperandTest, ReservedRegisterValueWriteAndRead) {
auto dst_op = rreg_->CreateDestinationOperand(2);
auto src_op = rreg_->CreateSourceOperand();
EXPECT_TRUE(rreg_->resource()->IsFree());
rreg_->resource()->Acquire();
// Get DataBuffer from destination operand and initialize the value.
DataBuffer *db = dst_op->AllocateDataBuffer();
db->Set<uint32_t>(0, 0xDEAD0000);
// Submit the data buffer and advance the delay line by the 2 cycle latency.
db->Submit();
EXPECT_FALSE(rreg_->resource()->IsFree());
arch_state_->AdvanceDelayLines();
EXPECT_FALSE(rreg_->resource()->IsFree());
arch_state_->AdvanceDelayLines();
EXPECT_TRUE(rreg_->resource()->IsFree());
EXPECT_EQ(src_op->AsUint32(0), 0xDEAD0000);
// Get DataBuffer from destination operand with copy of the current value.
DataBuffer *db2 = dst_op->CopyDataBuffer();
EXPECT_TRUE(rreg_->resource()->IsFree());
rreg_->resource()->Acquire();
// Verify that the value is the same as before.
EXPECT_EQ(db2->Get<uint32_t>(0), 0xDEAD0000);
db2->Set<uint32_t>(0, 0);
// Submit the data buffer and advance the delay line by the 2 cycle latency.
db2->Submit();
EXPECT_FALSE(rreg_->resource()->IsFree());
arch_state_->AdvanceDelayLines();
EXPECT_FALSE(rreg_->resource()->IsFree());
arch_state_->AdvanceDelayLines();
EXPECT_TRUE(rreg_->resource()->IsFree());
// Verify that the value has been updated.
EXPECT_EQ(src_op->AsUint32(0), 0);
delete src_op;
delete dst_op;
}
} // namespace
} // namespace generic
} // namespace sim
} // namespace mpact