Add custom `HaltAbort` reason for `ebreak` termination. PiperOrigin-RevId: 563617193
diff --git a/sim/kelvin_top.cc b/sim/kelvin_top.cc index a18cc08..5ec7fa9 100644 --- a/sim/kelvin_top.cc +++ b/sim/kelvin_top.cc
@@ -150,7 +150,7 @@ RequestHalt(HaltReason::kSoftwareBreakpoint, inst); } else { // The default Kelvin simulation mode. std::cout << "Program exits with fault" << std::endl; - RequestHalt(HaltReason::kUserRequest, inst); + RequestHalt(kHaltAbort, inst); } return true; }
diff --git a/sim/kelvin_top.h b/sim/kelvin_top.h index 9c28e30..3f8aace 100644 --- a/sim/kelvin_top.h +++ b/sim/kelvin_top.h
@@ -30,6 +30,12 @@ namespace kelvin::sim { using ::mpact::sim::generic::DataBuffer; +using HaltReason = mpact::sim::generic::CoreDebugInterface::HaltReason; +using HaltReasonValueType = + mpact::sim::generic::CoreDebugInterface::HaltReasonValueType; + +// Custom HaltReason for `ebreak` +const HaltReasonValueType kHaltAbort = *HaltReason::kUserSpecifiedMin + 1; // Top level class for the Kelvin simulator. This is the main interface for // interacting and controlling execution of programs running on the simulator. @@ -38,9 +44,6 @@ public mpact::sim::generic::CoreDebugInterface { public: using RunStatus = mpact::sim::generic::CoreDebugInterface::RunStatus; - using HaltReason = mpact::sim::generic::CoreDebugInterface::HaltReason; - using HaltReasonValueType = - mpact::sim::generic::CoreDebugInterface::HaltReasonValueType; explicit KelvinTop(std::string name); ~KelvinTop() override;
diff --git a/sim/renode/BUILD b/sim/renode/BUILD index c53af89..a215a01 100644 --- a/sim/renode/BUILD +++ b/sim/renode/BUILD
@@ -14,6 +14,7 @@ ], deps = [ ":renode_debug_interface", + "//sim:kelvin_top", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/log", "@com_google_absl//absl/strings",
diff --git a/sim/renode/renode_mpact.cc b/sim/renode/renode_mpact.cc index f8a796d..3c25082 100644 --- a/sim/renode/renode_mpact.cc +++ b/sim/renode/renode_mpact.cc
@@ -4,6 +4,7 @@ #include <limits> #include <string> +#include "sim/kelvin_top.h" #include "sim/renode/renode_debug_interface.h" #include "absl/log/log.h" #include "absl/strings/str_cat.h" @@ -242,7 +243,6 @@ } // If the previous halt reason was a semihost halt request, then we shouldn't // step any further. Just return with "waiting for interrupt" code. - using HaltReason = RenodeDebugInterface::HaltReason; using mpact::sim::generic::operator*; // NOLINT: used below. if (halt_res.value() == *HaltReason::kSemihostHaltRequest) { if (status != nullptr) { @@ -288,6 +288,12 @@ } return total_executed; break; + case kHaltAbort: // `ebreak` custom halt reason + if (status != nullptr) { + *status = static_cast<int32_t>(ExecutionResult::kAborted); + } + return total_executed; + break; default: break; } @@ -333,7 +339,6 @@ return -1; } // Map the halt status appropriately. - using HaltReason = RenodeDebugInterface::HaltReason; using mpact::sim::generic::operator*; // NOLINT: used below. if (status != nullptr) { switch (halt_res.value()) { @@ -349,6 +354,9 @@ case *HaltReason::kNone: *status = static_cast<int32_t>(ExecutionResult::kOk); break; + case kelvin::sim::kHaltAbort: + *status = static_cast<int32_t>(ExecutionResult::kAborted); + break; default: break; }
diff --git a/sim/renode/test/BUILD b/sim/renode/test/BUILD index 4b1d191..b1d3ac2 100644 --- a/sim/renode/test/BUILD +++ b/sim/renode/test/BUILD
@@ -8,6 +8,7 @@ data = [ "//sim/test:testfiles/hello_world_mpause.bin", "//sim/test:testfiles/hello_world_mpause.elf", + "//sim/test:testfiles/kelvin_ebreak.elf", ], deps = [ "//sim/renode:kelvin_renode", @@ -26,6 +27,7 @@ data = [ "//sim/test:testfiles/hello_world_mpause.bin", "//sim/test:testfiles/hello_world_mpause.elf", + "//sim/test:testfiles/kelvin_ebreak.elf", ], deps = [ "//sim:kelvin_top",
diff --git a/sim/renode/test/kelvin_renode_test.cc b/sim/renode/test/kelvin_renode_test.cc index 397dcf6..a64c689 100644 --- a/sim/renode/test/kelvin_renode_test.cc +++ b/sim/renode/test/kelvin_renode_test.cc
@@ -19,6 +19,7 @@ constexpr char kFileName[] = "hello_world_mpause.elf"; constexpr char kBinFileName[] = "hello_world_mpause.bin"; +constexpr char kEbreakFileName[] = "kelvin_ebreak.elf"; // The depot path to the test directory. constexpr char kDepotPath[] = "sim/test/"; constexpr char kTopName[] = "test"; @@ -76,6 +77,30 @@ delete loader; } +TEST_F(KelvinRenodeTest, RunEbreakElfProgram) { + std::string file_name = + absl::StrCat(kDepotPath, "testfiles/", kEbreakFileName); + // Load the program. + auto *loader = new mpact::sim::util::ElfProgramLoader(top_); + auto result = loader->LoadProgram(file_name); + CHECK_OK(result); + auto entry_point = result.value(); + // Run the program. + testing::internal::CaptureStdout(); + EXPECT_TRUE(top_->WriteRegister("pc", entry_point).ok()); + EXPECT_TRUE(top_->Run().ok()); + EXPECT_TRUE(top_->Wait().ok()); + // Check the results. + auto halt_result = top_->GetLastHaltReason(); + CHECK_OK(halt_result); + EXPECT_EQ(static_cast<int>(halt_result.value()), + static_cast<int>(kelvin::sim::kHaltAbort)); + const std::string stdout_str = testing::internal::GetCapturedStdout(); + EXPECT_EQ("Program exits with fault\n", stdout_str); + // Clean up. + delete loader; +} + TEST_F(KelvinRenodeTest, RunBinProgram) { std::string file_name = absl::StrCat(kDepotPath, "testfiles/", kBinFileName); constexpr uint64_t kBinFileAddress = 0x0;
diff --git a/sim/renode/test/renode_mpact_test.cc b/sim/renode/test/renode_mpact_test.cc index d639225..5eda348 100644 --- a/sim/renode/test/renode_mpact_test.cc +++ b/sim/renode/test/renode_mpact_test.cc
@@ -18,6 +18,7 @@ using kelvin::sim::renode::ExecutionResult; using mpact::sim::riscv::DebugRegisterEnum; +constexpr char kEbreakExecutableFileName[] = "kelvin_ebreak.elf"; constexpr char kExecutableFileName[] = "hello_world_mpause.elf"; constexpr char kBinFileName[] = "hello_world_mpause.bin"; constexpr uint64_t kBinFileAddress = 0x0; @@ -196,6 +197,27 @@ EXPECT_EQ("Program exits properly\n", stdout_str); } +TEST_F(RenodeMpactTest, StepEbreakExecutableProgram) { + testing::internal::CaptureStdout(); + const std::string input_file_name = + absl::StrCat(kDepotPath, "testfiles/", kEbreakExecutableFileName); + int32_t status; + (void)load_executable(sim_id_, input_file_name.c_str(), &status); + CHECK_EQ(status, 0); + constexpr uint64_t kStepCount = 500; + uint64_t count; + while (true) { + count = step(sim_id_, kStepCount, &status); + if (count != kStepCount) break; + EXPECT_EQ(status, static_cast<int32_t>(ExecutionResult::kOk)); + } + // Execution should now have completed and the program has printed the fault + // exit message. + EXPECT_EQ(status, static_cast<int32_t>(ExecutionResult::kAborted)); + const std::string stdout_str = testing::internal::GetCapturedStdout(); + EXPECT_EQ("Program exits with fault\n", stdout_str); +} + TEST_F(RenodeMpactTest, StepImageProgram) { const std::string input_file_name = absl::StrCat(kDepotPath, "testfiles/", kBinFileName);
diff --git a/sim/test/BUILD b/sim/test/BUILD index 58b2fa5..7db1c7b 100644 --- a/sim/test/BUILD +++ b/sim/test/BUILD
@@ -5,6 +5,7 @@ "testfiles/hello_world_mpause.bin", "testfiles/hello_world_mpause.elf", "testfiles/hello_world_rv32imf.elf", + "testfiles/kelvin_ebreak.elf", "testfiles/kelvin_vldvst.elf", "testfiles/rv32i.elf", "testfiles/rv32m.elf", @@ -62,6 +63,7 @@ "testfiles/hello_world_mpause.bin", "testfiles/hello_world_mpause.elf", "testfiles/hello_world_rv32imf.elf", + "testfiles/kelvin_ebreak.elf", "testfiles/kelvin_vldvst.elf", "testfiles/rv32i.elf", "testfiles/rv32m.elf",
diff --git a/sim/test/kelvin_top_test.cc b/sim/test/kelvin_top_test.cc index e5ab21c..85d2491 100644 --- a/sim/test/kelvin_top_test.cc +++ b/sim/test/kelvin_top_test.cc
@@ -26,6 +26,7 @@ using ::mpact::sim::util::FlatDemandMemory; using HaltReason = ::mpact::sim::generic::CoreDebugInterface::HaltReason; +constexpr char kEbreakElfFileName[] = "kelvin_ebreak.elf"; constexpr char kMpauseBinaryFileName[] = "hello_world_mpause.bin"; constexpr char kMpauseElfFileName[] = "hello_world_mpause.elf"; constexpr char kRV32imfElfFileName[] = "hello_world_rv32imf.elf"; @@ -95,16 +96,14 @@ // Runs the program from has ebreak (from syscall). TEST_F(KelvinTopTest, RunEbreakProgram) { - kelvin_top_->state()->set_max_physical_address(kRiscv32MaxAddress); - LoadFile(kRV32imfElfFileName); + LoadFile(kEbreakElfFileName); testing::internal::CaptureStdout(); EXPECT_OK(kelvin_top_->WriteRegister("pc", entry_point_)); EXPECT_OK(kelvin_top_->Run()); EXPECT_OK(kelvin_top_->Wait()); auto halt_result = kelvin_top_->GetLastHaltReason(); CHECK_OK(halt_result); - EXPECT_EQ(static_cast<int>(halt_result.value()), - static_cast<int>(HaltReason::kUserRequest)); + EXPECT_EQ(halt_result.value(), kelvin::sim::kHaltAbort); const std::string stdout_str = testing::internal::GetCapturedStdout(); EXPECT_EQ("Program exits with fault\n", stdout_str); } @@ -289,8 +288,7 @@ // Steps through the program from beginning to end. TEST_F(KelvinTopTest, StepProgram) { - LoadFile(kRV32imfElfFileName); - kelvin_top_->state()->set_max_physical_address(kRiscv32MaxAddress); + LoadFile(kEbreakElfFileName); testing::internal::CaptureStdout(); EXPECT_OK(kelvin_top_->WriteRegister("pc", entry_point_)); @@ -298,8 +296,7 @@ EXPECT_OK(res.status()); auto halt_result = kelvin_top_->GetLastHaltReason(); CHECK_OK(halt_result); - EXPECT_EQ(static_cast<int>(halt_result.value()), - static_cast<int>(HaltReason::kUserRequest)); + EXPECT_EQ(halt_result.value(), kelvin::sim::kHaltAbort); EXPECT_EQ("Program exits with fault\n", testing::internal::GetCapturedStdout());
diff --git a/sim/test/testfiles/kelvin_ebreak.elf b/sim/test/testfiles/kelvin_ebreak.elf new file mode 100644 index 0000000..5931229 --- /dev/null +++ b/sim/test/testfiles/kelvin_ebreak.elf Binary files differ