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.