Set maximum physical memory of Kelvin simulation
kelvin HW only has 4MB memory. Set the memory size in the simulator so it can catch the illegal memory access.
PiperOrigin-RevId: 550580376
diff --git a/WORKSPACE b/WORKSPACE
index d40c02d..50975dd 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -6,9 +6,9 @@
# MPACT-RiscV repo
http_archive(
name = "com_google_mpact-riscv",
- sha256 = "b2b3ce1354d2da48ee195b72b682ecffeb0844b1fbd4f8e83a429d8b21194e61",
- strip_prefix = "mpact-riscv-a21d14041b9f034519e2f9fa359f2902b433cf23",
- url = "https://github.com/google/mpact-riscv/archive/a21d14041b9f034519e2f9fa359f2902b433cf23.tar.gz",
+ sha256 = "de8672c59d454182a406fe859de4c87f86116dac320ae366aca60a3258370c1b",
+ strip_prefix = "mpact-riscv-eadd26c36777070935ae85ff7f556185085dc188",
+ url = "https://github.com/google/mpact-riscv/archive/eadd26c36777070935ae85ff7f556185085dc188.tar.gz",
)
# MPACT-Sim repo
diff --git a/sim/kelvin_top.cc b/sim/kelvin_top.cc
index ffcd3c3..5cd361c 100644
--- a/sim/kelvin_top.cc
+++ b/sim/kelvin_top.cc
@@ -78,6 +78,7 @@
// Create the simulation state
state_ = new sim::KelvinState(kKelvinName, mpact::sim::riscv::RiscVXlen::RV32,
memory_);
+ state_->set_max_physical_address(kKelvinMaxMemoryAddress);
fp_state_ = new mpact::sim::riscv::RiscVFPState(state_);
state_->set_rv_fp(fp_state_);
pc_ = state_->registers()->at(sim::KelvinState::kPcName);
@@ -143,18 +144,35 @@
return false;
});
- // Set illegal instruction callback.
+ // Set trap callbacks.
state_->set_on_trap([this](bool is_interrupt, uint64_t trap_value,
uint64_t exception_code, uint64_t epc,
const Instruction *inst) -> bool {
- if (exception_code ==
- static_cast<uint64_t>(
- mpact::sim::riscv::ExceptionCode::kIllegalInstruction)) {
- std::cerr << "Illegal instruction at 0x" << std::hex << epc << std::endl;
- RequestHalt(HaltReason::kUserRequest, nullptr);
- return true;
+ auto code = static_cast<mpact::sim::riscv::ExceptionCode>(exception_code);
+ bool result = false;
+ switch (code) {
+ case mpact::sim::riscv::ExceptionCode::kIllegalInstruction: {
+ std::cerr << "Illegal instruction at 0x" << std::hex << epc
+ << std::endl;
+ RequestHalt(HaltReason::kUserRequest, nullptr);
+ result = true;
+ } break;
+ case mpact::sim::riscv::ExceptionCode::kLoadAccessFault: {
+ std::cerr << "Memory load access fault at 0x" << std::hex << epc
+ << " as: " << inst->AsString() << std::endl;
+ RequestHalt(HaltReason::kUserRequest, nullptr);
+ result = true;
+ } break;
+ case mpact::sim::riscv::ExceptionCode::kStoreAccessFault: {
+ std::cerr << "Memory store access fault at 0x" << std::hex << epc
+ << " as: " << inst->AsString() << std::endl;
+ RequestHalt(HaltReason::kUserRequest, nullptr);
+ result = true;
+ } break;
+ default:
+ break;
}
- return false;
+ return result;
});
semihost_->set_exit_callback(
diff --git a/sim/kelvin_top.h b/sim/kelvin_top.h
index e562e3b..206623b 100644
--- a/sim/kelvin_top.h
+++ b/sim/kelvin_top.h
@@ -23,6 +23,8 @@
namespace kelvin::sim {
+constexpr uint64_t kKelvinMaxMemoryAddress = 0x3f'ffffULL; // 4MB
+
// Top level class for the Kelvin simulator. This is the main interface for
// interacting and controlling execution of programs running on the simulator.
// This class brings together the decoder, the architecture state, and control.
diff --git a/sim/test/kelvin_top_test.cc b/sim/test/kelvin_top_test.cc
index 28e4b8b..84edeb5 100644
--- a/sim/test/kelvin_top_test.cc
+++ b/sim/test/kelvin_top_test.cc
@@ -34,6 +34,9 @@
// The depot path to the test directory.
constexpr char kDepotPath[] = "sim/test/";
+// Maximum memory size used by riscv programs build for userspace.
+constexpr uint64_t kRiscv32MaxAddress = 0x3'ffff'ffffULL;
+
class KelvinTopTest : public testing::Test {
protected:
KelvinTopTest() {
@@ -63,8 +66,30 @@
FlatDemandMemory *memory_ = nullptr;
};
-// Runs the program from beginning to end.
-TEST_F(KelvinTopTest, RunHelloProgram) {
+// Check the max memory size
+TEST_F(KelvinTopTest, CheckDefaultMaxMemorySize) {
+ EXPECT_EQ(kelvin_top_->state()->max_physical_address(),
+ kelvin::sim::kKelvinMaxMemoryAddress);
+}
+
+// Run a program exceeds the default memory setting
+TEST_F(KelvinTopTest, RunProgramExceedDefaultMemory) {
+ LoadFile(kRV32imfElfFileName);
+ testing::internal::CaptureStderr();
+ 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));
+ const std::string stderr_str = testing::internal::GetCapturedStderr();
+ EXPECT_THAT(stderr_str, testing::HasSubstr("Memory store access fault"));
+}
+
+// Runs the program from has ebreak (from syscall).
+TEST_F(KelvinTopTest, RunEbreakProgram) {
+ kelvin_top_->state()->set_max_physical_address(kRiscv32MaxAddress);
LoadFile(kRV32imfElfFileName);
testing::internal::CaptureStdout();
EXPECT_OK(kelvin_top_->WriteRegister("pc", entry_point_));
@@ -81,6 +106,7 @@
// Runs the program from beginning to end. Enable arm semihosting.
TEST_F(KelvinTopTest, RunHelloProgramSemihost) {
absl::SetFlag(&FLAGS_use_semihost, true);
+ kelvin_top_->state()->set_max_physical_address(kRiscv32MaxAddress);
LoadFile(kRV32imfElfFileName);
testing::internal::CaptureStdout();
EXPECT_OK(kelvin_top_->WriteRegister("pc", entry_point_));
@@ -113,6 +139,7 @@
// Runs the rv32i program with arm semihosting.
TEST_F(KelvinTopTest, RunRV32IProgram) {
absl::SetFlag(&FLAGS_use_semihost, true);
+ kelvin_top_->state()->set_max_physical_address(kRiscv32MaxAddress);
LoadFile(kRV32iElfFileName);
testing::internal::CaptureStdout();
EXPECT_OK(kelvin_top_->WriteRegister("pc", entry_point_));
@@ -130,6 +157,7 @@
// Runs the rv32m program with arm semihosting.
TEST_F(KelvinTopTest, RunRV32MProgram) {
absl::SetFlag(&FLAGS_use_semihost, true);
+ kelvin_top_->state()->set_max_physical_address(kRiscv32MaxAddress);
LoadFile(kRV32mElfFileName);
testing::internal::CaptureStdout();
EXPECT_OK(kelvin_top_->WriteRegister("pc", entry_point_));
@@ -147,6 +175,7 @@
// Runs the rv32 soft float program with arm semihosting.
TEST_F(KelvinTopTest, RunRV32SoftFProgram) {
absl::SetFlag(&FLAGS_use_semihost, true);
+ kelvin_top_->state()->set_max_physical_address(kRiscv32MaxAddress);
LoadFile(kRV32SoftFloatElfFileName);
testing::internal::CaptureStdout();
EXPECT_OK(kelvin_top_->WriteRegister("pc", entry_point_));
@@ -180,6 +209,7 @@
// Steps through the program from beginning to end.
TEST_F(KelvinTopTest, StepProgram) {
LoadFile(kRV32imfElfFileName);
+ kelvin_top_->state()->set_max_physical_address(kRiscv32MaxAddress);
testing::internal::CaptureStdout();
EXPECT_OK(kelvin_top_->WriteRegister("pc", entry_point_));
@@ -197,6 +227,7 @@
// Sets/Clears breakpoints without executing the program.
TEST_F(KelvinTopTest, SetAndClearBreakpoint) {
LoadFile(kRV32imfElfFileName);
+ kelvin_top_->state()->set_max_physical_address(kRiscv32MaxAddress);
auto result = loader_->GetSymbol("printf");
EXPECT_OK(result);
auto address = result.value().first;
@@ -217,6 +248,7 @@
// Runs program with breakpoint at printf with arm semihosting.
TEST_F(KelvinTopTest, RunWithBreakpoint) {
absl::SetFlag(&FLAGS_use_semihost, true);
+ kelvin_top_->state()->set_max_physical_address(kRiscv32MaxAddress);
LoadFile(kRV32imfElfFileName);
// Set breakpoint at printf.