blob: fa3f8429018163f2770e398f92aea23c8fc223be [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 <sys/types.h>
#include <array>
#include <cstdint>
#include <string>
#include "sim/kelvin_instructions.h"
#include "sim/test/kelvin_vector_instructions_test_base.h"
#include "googletest/include/gtest/gtest.h"
#include "absl/functional/bind_front.h"
#include "mpact/sim/generic/instruction.h"
// Test Kelvin logging instruction functionality
namespace {
// Semantic function
using kelvin::sim::KelvinLogInstruction;
constexpr uint32_t kMemAddress = 0x1000;
class KelvinLogInstructionsTest
: public kelvin::sim::test::KelvinVectorInstructionsTestBase {};
TEST_F(KelvinLogInstructionsTest, SimplePrint) {
constexpr char kHelloString[] = "Hello World!\n";
// Initialize memory.
auto *db = state_->db_factory()->Allocate<char>(sizeof(kHelloString));
for (int i = 0; i < sizeof(kHelloString); ++i) {
db->Set<char>(i, kHelloString[i]);
}
db->DecRef();
auto instruction = CreateInstruction();
state_->StoreMemory(instruction.get(), kMemAddress, db);
AppendRegisterOperands(instruction.get(), {kelvin::sim::test::kRs1Name}, {});
SetRegisterValues<uint32_t>({{kelvin::sim::test::kRs1Name, kMemAddress}});
instruction->set_semantic_function(
absl::bind_front(&KelvinLogInstruction, /*mode=*/0));
// Execute the instruction and check the stdout.
testing::internal::CaptureStdout();
instruction->Execute(nullptr);
const std::string stdout_str = testing::internal::GetCapturedStdout();
EXPECT_EQ(kHelloString, stdout_str);
}
TEST_F(KelvinLogInstructionsTest, PrintUnsignedNumber) {
constexpr char kFormatString[] = "Hello %u\n";
constexpr uint32_t kPrintNum = 2200000000; // a number > INT32_MAX
// Initialize memory.
auto *db = state_->db_factory()->Allocate<char>(sizeof(kFormatString));
for (int i = 0; i < sizeof(kFormatString); ++i) {
db->Set<char>(i, kFormatString[i]);
}
db->DecRef();
std::array<InstructionPtr, 2> instructions = {CreateInstruction(),
CreateInstruction()};
AppendRegisterOperands(instructions[0].get(), {kelvin::sim::test::kRs1Name},
{});
instructions[0]->set_semantic_function(
absl::bind_front(&KelvinLogInstruction, /*mode=*/1)); // scalar log
// Set the second instruction for the actual print out.
state_->StoreMemory(instructions[1].get(), kMemAddress, db);
AppendRegisterOperands(instructions[1].get(), {kelvin::sim::test::kRs2Name},
{});
instructions[1]->set_semantic_function(
absl::bind_front(&KelvinLogInstruction, /*mode=*/0));
SetRegisterValues<uint32_t>({{kelvin::sim::test::kRs1Name, kPrintNum},
{kelvin::sim::test::kRs2Name, kMemAddress}});
testing::internal::CaptureStdout();
for (int i = 0; i < instructions.size(); ++i) {
instructions[i]->Execute(nullptr);
}
const std::string stdout_str = testing::internal::GetCapturedStdout();
EXPECT_EQ("Hello 2200000000\n", stdout_str);
}
TEST_F(KelvinLogInstructionsTest, PrintSignedNumber) {
constexpr char kFormatString[] = "Hello %d\n";
constexpr int32_t kPrintNum = -1337;
// Initialize memory.
auto *db = state_->db_factory()->Allocate<char>(sizeof(kFormatString));
for (int i = 0; i < sizeof(kFormatString); ++i) {
db->Set<char>(i, kFormatString[i]);
}
db->DecRef();
std::array<InstructionPtr, 2> instructions = {CreateInstruction(),
CreateInstruction()};
AppendRegisterOperands(instructions[0].get(), {kelvin::sim::test::kRs1Name},
{});
instructions[0]->set_semantic_function(
absl::bind_front(&KelvinLogInstruction, /*mode=*/1)); // scalar log
// Set the second instruction for the actual print out.
state_->StoreMemory(instructions[1].get(), kMemAddress, db);
AppendRegisterOperands(instructions[1].get(), {kelvin::sim::test::kRs2Name},
{});
instructions[1]->set_semantic_function(
absl::bind_front(&KelvinLogInstruction, /*mode=*/0));
SetRegisterValues<int32_t>({{kelvin::sim::test::kRs1Name, kPrintNum}});
SetRegisterValues<uint32_t>({{kelvin::sim::test::kRs2Name, kMemAddress}});
testing::internal::CaptureStdout();
for (int i = 0; i < instructions.size(); ++i) {
instructions[i]->Execute(nullptr);
}
const std::string stdout_str = testing::internal::GetCapturedStdout();
EXPECT_EQ("Hello -1337\n", stdout_str);
}
TEST_F(KelvinLogInstructionsTest, PrintCharacterStream) {
constexpr char kFormatString[] = "%s World\n";
constexpr uint32_t kCharStream[] = {0x6c6c6548, 0x0000006f}; // "Hello"
// Initialize memory.
auto *db = state_->db_factory()->Allocate<char>(sizeof(kFormatString));
for (int i = 0; i < sizeof(kFormatString); ++i) {
db->Set<char>(i, kFormatString[i]);
}
db->DecRef();
std::array<InstructionPtr, 3> instructions = {
CreateInstruction(), CreateInstruction(), CreateInstruction()};
AppendRegisterOperands(instructions[0].get(), {kelvin::sim::test::kRs1Name},
{});
AppendRegisterOperands(instructions[1].get(), {kelvin::sim::test::kRs2Name},
{});
for (int i = 0; i < 2; ++i) {
instructions[i]->set_semantic_function(
absl::bind_front(&KelvinLogInstruction, /*mode=*/2)); // character log
}
constexpr char kRs3Name[] = "x3";
state_->StoreMemory(instructions[2].get(), kMemAddress, db);
AppendRegisterOperands(instructions[2].get(), {kRs3Name}, {});
instructions[2]->set_semantic_function(
absl::bind_front(&KelvinLogInstruction, /*mode=*/0));
SetRegisterValues<uint32_t>({{kelvin::sim::test::kRs1Name, kCharStream[0]},
{kelvin::sim::test::kRs2Name, kCharStream[1]},
{kRs3Name, kMemAddress}});
testing::internal::CaptureStdout();
for (int i = 0; i < instructions.size(); ++i) {
instructions[i]->Execute(nullptr);
}
const std::string stdout_str = testing::internal::GetCapturedStdout();
EXPECT_EQ("Hello World\n", stdout_str);
}
TEST_F(KelvinLogInstructionsTest, PrintTwoArguments) {
constexpr char kFormatString[] = "%s World %x\n";
constexpr uint32_t kCharStream = 0x00006948; // "Hi"
constexpr uint32_t kPrintNum = 0xbaddecaf;
// Initialize memory.
auto *db = state_->db_factory()->Allocate<char>(sizeof(kFormatString));
for (int i = 0; i < sizeof(kFormatString); ++i) {
db->Set<char>(i, kFormatString[i]);
}
db->DecRef();
std::array<InstructionPtr, 3> instructions = {
CreateInstruction(), CreateInstruction(), CreateInstruction()};
// Also store the kCharStream elsewhere in the memory.
auto *str_db = state_->db_factory()->Allocate<uint32_t>(sizeof(1));
str_db->Set<uint32_t>(0, kCharStream);
str_db->DecRef();
constexpr uint32_t kStrMemAddress = kMemAddress + 20;
state_->StoreMemory(instructions[0].get(), kStrMemAddress, str_db);
AppendRegisterOperands(instructions[0].get(), {kelvin::sim::test::kRs1Name},
{});
instructions[0]->set_semantic_function(
absl::bind_front(&KelvinLogInstruction, /*mode=*/3));
AppendRegisterOperands(instructions[1].get(), {kelvin::sim::test::kRs2Name},
{});
instructions[1]->set_semantic_function(
absl::bind_front(&KelvinLogInstruction, /*mode=*/1));
constexpr char kRs3Name[] = "x3";
state_->StoreMemory(instructions[2].get(), kMemAddress, db);
AppendRegisterOperands(instructions[2].get(), {kRs3Name}, {});
instructions[2]->set_semantic_function(
absl::bind_front(&KelvinLogInstruction, /*mode=*/0));
SetRegisterValues<uint32_t>({{kelvin::sim::test::kRs1Name, kStrMemAddress},
{kelvin::sim::test::kRs2Name, kPrintNum},
{kRs3Name, kMemAddress}});
// Execute the instructions.
testing::internal::CaptureStdout();
for (int i = 0; i < instructions.size(); ++i) {
instructions[i]->Execute(nullptr);
}
const std::string stdout_str = testing::internal::GetCapturedStdout();
EXPECT_EQ("Hi World baddecaf\n", stdout_str);
}
} // namespace