blob: 2f94ec1d8057510ba5dd397999762ff8ac837757 [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 <memory>
#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/fifo.h"
#include "mpact/sim/generic/operand_interface.h"
namespace mpact {
namespace sim {
namespace generic {
namespace {
using ScalarFifo = Fifo<uint32_t>;
using Vector8Fifo = VectorFifo<uint32_t, 8>;
constexpr int kFifoCapacity = 3;
// 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 FifoOperandTest : public testing::Test {
protected:
FifoOperandTest() {
arch_state_ = new MockArchState("MockArchState");
sfifo_ = new ScalarFifo(arch_state_, "S0", kFifoCapacity);
vfifo_ = new Vector8Fifo(arch_state_, "V0", kFifoCapacity);
}
~FifoOperandTest() override {
delete vfifo_;
delete sfifo_;
delete arch_state_;
}
MockArchState *arch_state_;
ScalarFifo *sfifo_;
Vector8Fifo *vfifo_;
};
// Tests that the fifo source operands are initialized correctly.
TEST_F(FifoOperandTest, SourceOperandInitialization) {
auto s_src_op = std::make_unique<FifoSourceOperand<uint32_t>>(sfifo_);
EXPECT_EQ(std::any_cast<FifoBase *>(s_src_op->GetObject()),
static_cast<FifoBase *>(sfifo_));
EXPECT_EQ(s_src_op->shape(), sfifo_->shape());
auto v_src_op = std::make_unique<FifoSourceOperand<uint32_t>>(vfifo_);
EXPECT_EQ(std::any_cast<FifoBase *>(v_src_op->GetObject()),
static_cast<FifoBase *>(vfifo_));
EXPECT_EQ(v_src_op->shape(), vfifo_->shape());
}
// Tests that the fifo destination operands are initialized correctly.
TEST_F(FifoOperandTest, DestinationOperandInitialization) {
auto s_dst_op = std::make_unique<FifoDestinationOperand<uint32_t>>(sfifo_, 1);
EXPECT_EQ(s_dst_op->latency(), 1);
EXPECT_EQ(s_dst_op->shape(), sfifo_->shape());
auto v_dst_op = std::make_unique<FifoDestinationOperand<uint32_t>>(vfifo_, 4);
EXPECT_EQ(v_dst_op->latency(), 4);
EXPECT_EQ(v_dst_op->shape(), vfifo_->shape());
}
// Tests that a destination fifo operand can update a fifo so that
// it is visible in a source fifo operand.
TEST_F(FifoOperandTest, ScalarFifoValueWriteAndRead) {
auto dst_op = sfifo_->CreateDestinationOperand(1);
auto src_op = sfifo_->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 and initialize it to zero.
DataBuffer *db2 = dst_op->AllocateDataBuffer();
db2->Set<uint32_t>(0, 0);
// Submit the data buffer and advance the delay line 1 cycle.
db2->Submit();
arch_state_->AdvanceDelayLines();
// Verify the fifo still has the old value (hasn't been pop'ed).
EXPECT_EQ(src_op->AsUint32(0), 0xDEADBEEF);
// Pop the fifo and verify the new value is there.
std::any_cast<FifoBase *>(src_op->GetObject())->Pop();
EXPECT_EQ(src_op->AsUint32(0), 0);
delete dst_op;
delete src_op;
}
// Tests that a destination vector register operand can update a register so
// that it is visible in a source register operand.
TEST_F(FifoOperandTest, VectorFifoValueWriteAndRead) {
auto dst_op = vfifo_->CreateDestinationOperand(2);
auto src_op = vfifo_->CreateSourceOperand();
// Get DataBuffer from destination operand and initialize the value.
DataBuffer *db = dst_op->AllocateDataBuffer();
for (int index = 0; index < vfifo_->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 < vfifo_->shape()[0]; index++) {
EXPECT_EQ(src_op->AsUint32(index), 0xDEAD0000 | index);
}
// Get another DataBuffer from destination operand and initialize to zeros.
DataBuffer *db2 = dst_op->AllocateDataBuffer();
for (int index = 0; index < vfifo_->shape()[0]; 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 is the same as before (fifo hasn't been pop'ed).
for (int index = 0; index < vfifo_->shape()[0]; index++) {
EXPECT_EQ(src_op->AsUint32(index), 0xDEAD0000 | index);
}
// Pop the fifo and verify that the value has been updated.
std::any_cast<FifoBase *>(src_op->GetObject())->Pop();
for (int index = 0; index < vfifo_->shape()[0]; index++) {
EXPECT_EQ(src_op->AsUint32(index), 0);
}
delete dst_op;
delete src_op;
}
} // namespace
} // namespace generic
} // namespace sim
} // namespace mpact