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