blob: dae78c70619e0ff210a88e98e4a049ff08ff87b7 [file] [log] [blame]
// 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
//
// http://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 <ios>
#include <string>
#include "sim/decoder.h"
#include "sim/kelvin_state.h"
#include "googletest/include/gtest/gtest.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/strings/str_cat.h"
#include "elfio/elf_types.hpp"
#include "elfio/elfio.hpp"
#include "elfio/elfio_section.hpp"
#include "elfio/elfio_symbols.hpp"
#include "riscv/riscv_state.h"
#include "mpact/sim/generic/instruction.h"
#include "mpact/sim/util/memory/flat_demand_memory.h"
#include "mpact/sim/util/program_loader/elf_program_loader.h"
namespace {
using ::mpact::sim::riscv::RiscVXlen;
constexpr char kFileName[] = "hello_world_rv32imf.elf";
// The depot path to the test directory.
constexpr char kDepotPath[] = "sim/test/";
using SymbolAccessor = ELFIO::symbol_section_accessor_template<ELFIO::section>;
class KelvinDecoderTest : public testing::Test {
protected:
KelvinDecoderTest()
: state_("kelvin_decoder_test", RiscVXlen::RV32),
memory_(0),
loader_(&memory_),
decoder_(&state_, &memory_) {
const std::string input_file =
absl::StrCat(kDepotPath, "testfiles/", kFileName);
auto result = loader_.LoadProgram(input_file);
CHECK_OK(result.status());
elf_reader_.load(input_file);
auto *symtab = elf_reader_.sections[".symtab"];
CHECK_NE(symtab, nullptr);
symbol_accessor_ = new SymbolAccessor(elf_reader_, symtab);
}
~KelvinDecoderTest() override { delete symbol_accessor_; }
ELFIO::elfio elf_reader_;
kelvin::sim::KelvinState state_;
mpact::sim::util::FlatDemandMemory memory_;
mpact::sim::util::ElfProgramLoader loader_;
kelvin::sim::KelvinDecoder decoder_;
SymbolAccessor *symbol_accessor_;
};
// This test is really pretty simple. It decodes the instructions in "main".
// The goal of this test is not so much to ensure that the decoder is accurate,
// but that the decoder returns a non-null instruction object for each address
// in main, and that executing this instruction does not generate an error.
TEST_F(KelvinDecoderTest, HelloWorldMain) {
ELFIO::Elf64_Addr value;
ELFIO::Elf_Xword size;
unsigned char bind;
unsigned char type;
ELFIO::Elf_Half section_index;
unsigned char other;
bool success = symbol_accessor_->get_symbol("main", value, size, bind, type,
section_index, other);
ASSERT_TRUE(success);
uint64_t address = value;
while (address < value + size) {
LOG(INFO) << "Address: " << std::hex << address;
EXPECT_FALSE(state_.program_error_controller()->HasError());
auto *inst = decoder_.DecodeInstruction(address);
ASSERT_NE(inst, nullptr);
inst->Execute(nullptr);
if (state_.program_error_controller()->HasError()) {
auto errvec = state_.program_error_controller()->GetUnmaskedErrorNames();
for (auto &err : errvec) {
LOG(INFO) << "Error: " << err;
auto msgvec = state_.program_error_controller()->GetErrorMessages(err);
for (auto &msg : msgvec) {
LOG(INFO) << " " << msg;
}
}
}
EXPECT_FALSE(state_.program_error_controller()->HasError());
state_.program_error_controller()->ClearAll();
address += inst->size();
inst->DecRef();
state_.AdvanceDelayLines();
}
}
// Even with a bad address, a valid instruction object should be returned.
TEST_F(KelvinDecoderTest, BadAddress) {
auto *inst = decoder_.DecodeInstruction(0x4321);
ASSERT_NE(inst, nullptr);
inst->Execute(nullptr);
inst->DecRef();
}
} // namespace