Add minstret / minstreth to Kelvin sim

PiperOrigin-RevId: 600500043
diff --git a/sim/kelvin_state.cc b/sim/kelvin_state.cc
index 0c82a23..d6aeaa2 100644
--- a/sim/kelvin_state.cc
+++ b/sim/kelvin_state.cc
@@ -49,7 +49,9 @@
     : mpact::sim::riscv::RiscVState(id, xlen, memory, atomic_memory),
       kisa_("kisa", static_cast<RiscVCsrEnum>(KelvinCsrEnum::kKIsa), this),
       mcycle_("mcycle", RiscVCsrEnum::kMCycle, this),
-      mcycleh_("mcycleh", RiscVCsrEnum::kMCycleH, this) {
+      mcycleh_("mcycleh", RiscVCsrEnum::kMCycleH, this),
+      minstret_("minstret", RiscVCsrEnum::kMInstret, this),
+      minstreth_("minstreth", RiscVCsrEnum::kMInstretH, this) {
   set_vector_register_width(kVectorRegisterWidth);
   for (int i = 0; i < acc_register_.size(); ++i) {
     acc_register_[i].fill(0);
@@ -71,6 +73,12 @@
   if (!csr_set()->AddCsr(&mcycleh_).ok()) {
     LOG(FATAL) << "Failed to register mcycleh";
   }
+  if (!csr_set()->AddCsr(&minstret_).ok()) {
+    LOG(FATAL) << "Failed to register minstret";
+  }
+  if (!csr_set()->AddCsr(&minstreth_).ok()) {
+    LOG(FATAL) << "Failed to register minstreth";
+  }
 }
 
 KelvinState::KelvinState(absl::string_view id,
@@ -145,4 +153,11 @@
   mcycleh_.Set(new_cycle >> 32);
 }
 
+void KelvinState::IncrementMInstret(uint64_t value) {
+  uint64_t new_instret =
+      (minstret_.GetUint64() | (minstreth_.GetUint64() << 32)) + value;
+  minstret_.Set(new_instret & 0xFFFFFFFF);
+  minstreth_.Set(new_instret >> 32);
+}
+
 }  // namespace kelvin::sim
diff --git a/sim/kelvin_state.h b/sim/kelvin_state.h
index ffcfd84..7788fb1 100644
--- a/sim/kelvin_state.h
+++ b/sim/kelvin_state.h
@@ -98,6 +98,7 @@
   }
 
   void IncrementMCycle(uint64_t value);
+  void IncrementMInstret(uint64_t value);
 
  private:
   uint32_t vector_length_{kVectorLengthInBits};
@@ -120,6 +121,8 @@
   // mcycle/mcycleh CSR.
   mpact::sim::riscv::RiscV32SimpleCsr mcycle_;
   mpact::sim::riscv::RiscV32SimpleCsr mcycleh_;
+  mpact::sim::riscv::RiscV32SimpleCsr minstret_;
+  mpact::sim::riscv::RiscV32SimpleCsr minstreth_;
 };
 
 }  // namespace kelvin::sim
diff --git a/sim/kelvin_top.cc b/sim/kelvin_top.cc
index 3b49bd2..dcd2b79 100644
--- a/sim/kelvin_top.cc
+++ b/sim/kelvin_top.cc
@@ -257,7 +257,7 @@
   } while (!executed);
   // Increment counter.
   counter_opcode_[real_inst->opcode()].Increment(1);
-  counter_num_instructions_.Increment(1);
+  IncrementInstructionCount(1);
   real_inst->DecRef();
   // Re-enable the breakpoint.
   if (status.ok()) {
@@ -315,7 +315,7 @@
     count++;
     // Update counters.
     counter_opcode_[inst->opcode()].Increment(1);
-    counter_num_instructions_.Increment(1);
+    IncrementInstructionCount(1);
     // Get the next pc value.
     next_pc = pc_operand->AsUint64(0);
   }
@@ -411,7 +411,7 @@
       } while (!executed);
       // Update counters.
       counter_opcode_[inst->opcode()].Increment(1);
-      counter_num_instructions_.Increment(1);
+      IncrementInstructionCount(1);
       // Get the next pc value.
       next_pc = pc_operand->AsUint64(0);
     }
@@ -730,4 +730,9 @@
   state_->IncrementMCycle(value);
 }
 
+void KelvinTop::IncrementInstructionCount(uint64_t value) {
+  counter_num_instructions_.Increment(value);
+  state_->IncrementMInstret(value);
+}
+
 }  // namespace kelvin::sim
diff --git a/sim/kelvin_top.h b/sim/kelvin_top.h
index 5bab3d1..9b48889 100644
--- a/sim/kelvin_top.h
+++ b/sim/kelvin_top.h
@@ -123,6 +123,7 @@
   void SetPc(uint64_t value);
   // Increment the cycle count.
   void IncrementCycleCount(uint64_t value);
+  void IncrementInstructionCount(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/testfiles/kelvin_perf_counters.elf b/sim/test/testfiles/kelvin_perf_counters.elf
index 6037860..3a9c505 100755
--- a/sim/test/testfiles/kelvin_perf_counters.elf
+++ b/sim/test/testfiles/kelvin_perf_counters.elf
Binary files differ