Add mcycle / mcycleh to Kelvin sim

PiperOrigin-RevId: 597650959
diff --git a/WORKSPACE b/WORKSPACE
index 3e2833e..54b618f 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -6,9 +6,9 @@
 # MPACT-RiscV repo
 http_archive(
     name = "com_google_mpact-riscv",
-    sha256 = "2f51e0d90f6adf639fe6b23bee6dd86283326d86f8952e60cb26abeccc1e939b",
-    strip_prefix = "mpact-riscv-641f0748c6b17aa8789d455e82fb8f1296f79d26",
-    url = "https://github.com/google/mpact-riscv/archive/641f0748c6b17aa8789d455e82fb8f1296f79d26.tar.gz",
+    sha256 = "68c65bae71654ce59f3bd047af9083840b4b463aee7a17ba7d75c84322534347",
+    strip_prefix = "mpact-riscv-a4c534a97b107f3734e04c8e32d182e49d400ac3",
+    url = "https://github.com/google/mpact-riscv/archive/a4c534a97b107f3734e04c8e32d182e49d400ac3.tar.gz",
 )
 
 # MPACT-Sim repo
diff --git a/sim/kelvin_state.cc b/sim/kelvin_state.cc
index 62b92f2..0c82a23 100644
--- a/sim/kelvin_state.cc
+++ b/sim/kelvin_state.cc
@@ -47,7 +47,9 @@
     mpact::sim::util::MemoryInterface *memory,
     mpact::sim::util::AtomicMemoryOpInterface *atomic_memory)
     : mpact::sim::riscv::RiscVState(id, xlen, memory, atomic_memory),
-      kisa_("kisa", static_cast<RiscVCsrEnum>(KelvinCsrEnum::kKIsa), this) {
+      kisa_("kisa", static_cast<RiscVCsrEnum>(KelvinCsrEnum::kKIsa), this),
+      mcycle_("mcycle", RiscVCsrEnum::kMCycle, this),
+      mcycleh_("mcycleh", RiscVCsrEnum::kMCycleH, this) {
   set_vector_register_width(kVectorRegisterWidth);
   for (int i = 0; i < acc_register_.size(); ++i) {
     acc_register_[i].fill(0);
@@ -62,6 +64,13 @@
   }
   auto *misa = *result;
   misa->Set(kKelvinMisaVal);
+
+  if (!csr_set()->AddCsr(&mcycle_).ok()) {
+    LOG(FATAL) << "Failed to register mcycle";
+  }
+  if (!csr_set()->AddCsr(&mcycleh_).ok()) {
+    LOG(FATAL) << "Failed to register mcycleh";
+  }
 }
 
 KelvinState::KelvinState(absl::string_view id,
@@ -129,4 +138,11 @@
   log_args_.clear();
 }
 
+void KelvinState::IncrementMCycle(uint64_t value) {
+  uint64_t new_cycle =
+      (mcycle_.GetUint64() | (mcycleh_.GetUint64() << 32)) + value;
+  mcycle_.Set(new_cycle & 0xFFFFFFFF);
+  mcycleh_.Set(new_cycle >> 32);
+}
+
 }  // namespace kelvin::sim
diff --git a/sim/kelvin_state.h b/sim/kelvin_state.h
index 5c66649..ffcfd84 100644
--- a/sim/kelvin_state.h
+++ b/sim/kelvin_state.h
@@ -97,6 +97,8 @@
     on_mpause_.emplace_back(std::move(handler));
   }
 
+  void IncrementMCycle(uint64_t value);
+
  private:
   uint32_t vector_length_{kVectorLengthInBits};
 
@@ -114,6 +116,10 @@
 
   // Kelvin-specific CSR, contains information about the Kelvin ISA version.
   mpact::sim::riscv::RiscV32SimpleCsr kisa_;
+
+  // mcycle/mcycleh CSR.
+  mpact::sim::riscv::RiscV32SimpleCsr mcycle_;
+  mpact::sim::riscv::RiscV32SimpleCsr mcycleh_;
 };
 
 }  // namespace kelvin::sim
diff --git a/sim/kelvin_top.cc b/sim/kelvin_top.cc
index 26e2391..3b49bd2 100644
--- a/sim/kelvin_top.cc
+++ b/sim/kelvin_top.cc
@@ -252,7 +252,7 @@
   bool executed = false;
   do {
     executed = ExecuteInstruction(real_inst);
-    counter_num_cycles_.Increment(1);
+    IncrementCycleCount(1);
     state_->AdvanceDelayLines();
   } while (!executed);
   // Increment counter.
@@ -309,7 +309,7 @@
     bool executed = false;
     do {
       executed = ExecuteInstruction(inst);
-      counter_num_cycles_.Increment(1);
+      IncrementCycleCount(1);
       state_->AdvanceDelayLines();
     } while (!executed);
     count++;
@@ -406,7 +406,7 @@
             trace_entry->set_disasm(inst->AsString());
           }
         }
-        counter_num_cycles_.Increment(1);
+        IncrementCycleCount(1);
         state_->AdvanceDelayLines();
       } while (!executed);
       // Update counters.
@@ -725,4 +725,9 @@
   }
 }
 
+void KelvinTop::IncrementCycleCount(uint64_t value) {
+  counter_num_cycles_.Increment(value);
+  state_->IncrementMCycle(value);
+}
+
 }  // namespace kelvin::sim
diff --git a/sim/kelvin_top.h b/sim/kelvin_top.h
index 1aa0637..5bab3d1 100644
--- a/sim/kelvin_top.h
+++ b/sim/kelvin_top.h
@@ -121,6 +121,8 @@
   absl::Status StepPastBreakpoint();
   // Set the pc value.
   void SetPc(uint64_t value);
+  // Increment the cycle count.
+  void IncrementCycleCount(uint64_t value);
 
   // The DB factory is used to manage data buffers for memory read/writes.
   mpact::sim::generic::DataBufferFactory db_factory_;
diff --git a/sim/test/BUILD b/sim/test/BUILD
index a965726..b634ef2 100644
--- a/sim/test/BUILD
+++ b/sim/test/BUILD
@@ -20,6 +20,7 @@
         "testfiles/hello_world_mpause.elf",
         "testfiles/hello_world_rv32imf.elf",
         "testfiles/kelvin_ebreak.elf",
+        "testfiles/kelvin_perf_counters.elf",
         "testfiles/kelvin_vldvst.elf",
         "testfiles/rv32i.elf",
         "testfiles/rv32m.elf",
@@ -80,6 +81,7 @@
         "testfiles/hello_world_mpause.elf",
         "testfiles/hello_world_rv32imf.elf",
         "testfiles/kelvin_ebreak.elf",
+        "testfiles/kelvin_perf_counters.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 b4883d7..3d9a956 100644
--- a/sim/test/kelvin_top_test.cc
+++ b/sim/test/kelvin_top_test.cc
@@ -51,6 +51,7 @@
 constexpr char kRV32SoftFloatElfFileName[] = "rv32soft_fp.elf";
 constexpr char kRV32fElfFileName[] = "rv32uf_fadd.elf";
 constexpr char kKelvinVldVstFileName[] = "kelvin_vldvst.elf";
+constexpr char kKelvinPerfCountersFileName[] = "kelvin_perf_counters.elf";
 
 // The depot path to the test directory.
 constexpr char kDepotPath[] = "sim/test/";
@@ -474,6 +475,17 @@
   EXPECT_THAT(stdout_str, testing::HasSubstr("vld_vst test passed!"));
 }
 
+TEST_F(KelvinTopTest, RunKelvinPerfCountersProgram) {
+  LoadFile(kKelvinPerfCountersFileName);
+  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));
+}
+
 constexpr int kMemoryBlockSize = 256 * 1024;  // 256KB
 // Default max memory address is 4MB - 1. Round up to find the number of memory
 // blocks.
diff --git a/sim/test/testfiles/kelvin_perf_counters.elf b/sim/test/testfiles/kelvin_perf_counters.elf
new file mode 100755
index 0000000..6037860
--- /dev/null
+++ b/sim/test/testfiles/kelvin_perf_counters.elf
Binary files differ