blob: 576a331d5f03f1d02ad54d9ac02c09b050a42c10 [file]
#include "riscv/riscv_zicond_instructions.h"
#include <cstdint>
#include <string>
#include <tuple>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/log/check.h"
#include "absl/strings/string_view.h"
#include "googlemock/include/gmock/gmock.h"
#include "mpact/sim/generic/arch_state.h"
#include "mpact/sim/generic/data_buffer.h"
#include "mpact/sim/generic/instruction.h"
#include "riscv/riscv_register.h"
namespace {
using ::mpact::sim::generic::ArchState;
using ::mpact::sim::generic::Instruction;
using ::mpact::sim::riscv::RV32Register;
using ::mpact::sim::riscv::RV64Register;
constexpr uint32_t kInstAddress = 0x2468;
constexpr char kX1[] = "x1";
constexpr char kX2[] = "x2";
constexpr char kX3[] = "x3";
constexpr uint32_t kVal1 = 0x12345678;
constexpr uint32_t kVal2 = 0x87654321;
constexpr uint32_t kVal3 = 0xdeadbeef;
class TestState : public ArchState {
public:
TestState() : ArchState("test") {}
};
class RiscVZicondInstructionTest : public testing::Test {
public:
RiscVZicondInstructionTest() {
instruction_ = new Instruction(kInstAddress, &state_);
instruction_->set_size(4);
for (auto reg_name : {kX1, kX2, kX3}) {
rv32_regs_.insert({reg_name, new RV32Register(&state_, reg_name)});
rv64_regs_.insert({reg_name, new RV64Register(&state_, reg_name)});
}
}
~RiscVZicondInstructionTest() override {
delete instruction_;
for (auto reg : rv32_regs_) delete reg.second;
for (auto reg : rv64_regs_) delete reg.second;
}
// Initializes the semantic function of the instruction object.
void SetSemanticFunction(Instruction::SemanticFunction fcn) {
instruction_->set_semantic_function(fcn);
}
// Returns the value of the named register.
template <typename RegisterType>
typename RegisterType::ValueType GetRegisterValue(
absl::string_view reg_name) {
RegisterType *reg;
if constexpr (std::is_same_v<RegisterType, RV32Register>) {
reg = rv32_regs_[reg_name];
} else {
reg = rv64_regs_[reg_name];
}
CHECK_NE(reg, nullptr);
return reg->data_buffer()->template Get<typename RegisterType::ValueType>(
0);
}
template <typename RegisterType>
void AppendRegisterOperands(const std::vector<std::string> &sources,
const std::vector<std::string> &destinations) {
absl::flat_hash_map<std::string, RegisterType *> *regs;
if constexpr (std::is_same_v<RegisterType, RV32Register>) {
regs = &rv32_regs_;
} else {
regs = &rv64_regs_;
}
for (auto src : sources) {
auto *reg = (*regs)[src];
CHECK_NE(reg, nullptr);
instruction_->AppendSource(reg->CreateSourceOperand());
}
for (auto dest : destinations) {
auto *reg = (*regs)[dest];
CHECK_NE(reg, nullptr);
instruction_->AppendDestination(reg->CreateDestinationOperand(0));
}
}
template <typename RegisterType>
void SetRegisterValues(
const std::vector<
std::tuple<std::string, typename RegisterType::ValueType>>
values) {
absl::flat_hash_map<std::string, RegisterType *> *regs;
if constexpr (std::is_same_v<RegisterType, RV32Register>) {
regs = &rv32_regs_;
} else {
regs = &rv64_regs_;
}
for (auto &[reg_name, value] : values) {
auto *reg = (*regs)[reg_name];
CHECK_NE(reg, nullptr);
auto *db =
state_.db_factory()->Allocate<typename RegisterType::ValueType>(1);
db->template Set<typename RegisterType::ValueType>(0, value);
reg->SetDataBuffer(db);
db->DecRef();
}
}
Instruction *instruction() { return instruction_; }
private:
TestState state_;
Instruction *instruction_;
absl::flat_hash_map<std::string, RV32Register *> rv32_regs_;
absl::flat_hash_map<std::string, RV64Register *> rv64_regs_;
};
TEST_F(RiscVZicondInstructionTest, RV32CzeroEqz) {
using Reg = RV32Register;
AppendRegisterOperands<Reg>({kX1, kX2}, {kX3});
SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVCzeroEqz);
SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, kVal2}, {kX3, kVal3}});
instruction()->Execute(nullptr);
EXPECT_EQ(GetRegisterValue<Reg>(kX3), kVal1);
SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, 0}, {kX3, kVal3}});
instruction()->Execute(nullptr);
EXPECT_EQ(GetRegisterValue<Reg>(kX3), 0);
}
TEST_F(RiscVZicondInstructionTest, RV32CzeroNez) {
using Reg = RV32Register;
AppendRegisterOperands<Reg>({kX1, kX2}, {kX3});
SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVCzeroNez);
SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, 0}, {kX3, kVal3}});
instruction()->Execute(nullptr);
EXPECT_EQ(GetRegisterValue<Reg>(kX3), kVal1);
SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, kVal2}, {kX3, kVal3}});
instruction()->Execute(nullptr);
EXPECT_EQ(GetRegisterValue<Reg>(kX3), 0);
}
TEST_F(RiscVZicondInstructionTest, RV64CzeroEqz) {
using Reg = RV64Register;
AppendRegisterOperands<Reg>({kX1, kX2}, {kX3});
SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVCzeroEqz);
SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, kVal2}, {kX3, kVal3}});
instruction()->Execute(nullptr);
EXPECT_EQ(GetRegisterValue<Reg>(kX3), kVal1);
SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, 0}, {kX3, kVal3}});
instruction()->Execute(nullptr);
EXPECT_EQ(GetRegisterValue<Reg>(kX3), 0);
}
TEST_F(RiscVZicondInstructionTest, RV64CzeroNez) {
using Reg = RV64Register;
AppendRegisterOperands<Reg>({kX1, kX2}, {kX3});
SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVCzeroNez);
SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, 0}, {kX3, kVal3}});
instruction()->Execute(nullptr);
EXPECT_EQ(GetRegisterValue<Reg>(kX3), kVal1);
SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, kVal2}, {kX3, kVal3}});
instruction()->Execute(nullptr);
EXPECT_EQ(GetRegisterValue<Reg>(kX3), 0);
}
} // namespace