Adding mcycle/mcycleh CSRs, and refactoring how CRSs with counters work.

PiperOrigin-RevId: 670687615
Change-Id: I018468528f28cd48eaf7a08f6dbdd9f1aac471eb
diff --git a/cheriot/BUILD b/cheriot/BUILD
index 3e2156a..4085f38 100644
--- a/cheriot/BUILD
+++ b/cheriot/BUILD
@@ -175,14 +175,12 @@
         "cheriot_register.cc",
         "cheriot_state.cc",
         "cheriot_vector_true_operand.cc",
-        "riscv_cheriot_minstret.cc",
     ],
     hdrs = [
         "cheriot_register.h",
         "cheriot_state.h",
         "cheriot_vector_true_operand.h",
         "riscv_cheriot_csr_enum.h",
-        "riscv_cheriot_minstret.h",
         "riscv_cheriot_register_aliases.h",
     ],
     tags = ["not_run:arm"],
@@ -587,6 +585,7 @@
         "@com_google_absl//absl/time",
         "@com_google_mpact-riscv//riscv:riscv_arm_semihost",
         "@com_google_mpact-riscv//riscv:riscv_clint",
+        "@com_google_mpact-riscv//riscv:riscv_state",
         "@com_google_mpact-riscv//riscv:stoull_wrapper",
         "@com_google_mpact-sim//mpact/sim/generic:core",
         "@com_google_mpact-sim//mpact/sim/generic:core_debug_interface",
diff --git a/cheriot/cheriot_renode.cc b/cheriot/cheriot_renode.cc
index 88d2ea1..30ea54a 100644
--- a/cheriot/cheriot_renode.cc
+++ b/cheriot/cheriot_renode.cc
@@ -23,6 +23,7 @@
 #include <ios>
 #include <iostream>
 #include <memory>
+#include <new>
 #include <string>
 #include <string_view>
 
@@ -44,7 +45,6 @@
 #include "cheriot/cheriot_state.h"
 #include "cheriot/cheriot_top.h"
 #include "cheriot/debug_command_shell.h"
-#include "cheriot/riscv_cheriot_minstret.h"
 #include "mpact/sim/generic/core_debug_interface.h"
 #include "mpact/sim/generic/type_helpers.h"
 #include "mpact/sim/proto/component_data.pb.h"
@@ -57,6 +57,7 @@
 #include "mpact/sim/util/renode/renode_debug_interface.h"
 #include "riscv//riscv_arm_semihost.h"
 #include "riscv//riscv_clint.h"
+#include "riscv//riscv_counter_csr.h"
 #include "riscv//riscv_state.h"
 #include "riscv//stoull_wrapper.h"
 #include "src/google/protobuf/text_format.h"
@@ -77,10 +78,10 @@
 namespace sim {
 namespace cheriot {
 
-using ::mpact::sim::cheriot::RiscVCheriotMInstret;
-using ::mpact::sim::cheriot::RiscVCheriotMInstreth;
 using ::mpact::sim::proto::ComponentData;
 using ::mpact::sim::riscv::RiscVClint;
+using ::mpact::sim::riscv::RiscVCounterCsr;
+using ::mpact::sim::riscv::RiscVCounterCsrHigh;
 using ::mpact::sim::util::AtomicMemoryOpInterface;
 using ::mpact::sim::util::TaggedMemoryWatcher;
 using ::mpact::sim::util::TaggedToUntaggedMemoryTransactor;
@@ -500,11 +501,26 @@
     return absl::InternalError(
         absl::StrCat(name_, ": Error while initializing minstret/minstreth\n"));
   }
-  auto *minstret = static_cast<RiscVCheriotMInstret *>(minstret_res.value());
-  auto *minstreth = static_cast<RiscVCheriotMInstreth *>(minstreth_res.value());
+  auto *minstret = static_cast<RiscVCounterCsr<uint32_t, CheriotState> *>(
+      minstret_res.value());
+  auto *minstreth =
+      static_cast<RiscVCounterCsrHigh<CheriotState> *>(minstreth_res.value());
   minstret->set_counter(cheriot_top_->counter_num_instructions());
   minstreth->set_counter(cheriot_top_->counter_num_instructions());
-
+  // Initialize mcycle/mcycleh. Bind the instruction counter to those
+  // registers.
+  auto mcycle_res = cheriot_top_->state()->csr_set()->GetCsr("mcycle");
+  auto mcycleh_res = cheriot_top_->state()->csr_set()->GetCsr("mcycleh");
+  if (!mcycle_res.ok() || !mcycleh_res.ok()) {
+    return absl::InternalError(
+        absl::StrCat(name_, ": Error while initializing mcycle/mcycleh\n"));
+  }
+  auto *mcycle = static_cast<RiscVCounterCsr<uint32_t, CheriotState> *>(
+      mcycle_res.value());
+  auto *mcycleh =
+      static_cast<RiscVCounterCsrHigh<CheriotState> *>(mcycleh_res.value());
+  mcycle->set_counter(cheriot_top_->counter_num_cycles());
+  mcycleh->set_counter(cheriot_top_->counter_num_cycles());
   // Set up the memory router with the system bus. Other devices are added once
   // config info has been received. Add a tagged default memory transactor, so
   // that any tagged loads/stores are forward to the sysbus without tags.
diff --git a/cheriot/cheriot_state.cc b/cheriot/cheriot_state.cc
index 685e6d0..39ac0bf 100644
--- a/cheriot/cheriot_state.cc
+++ b/cheriot/cheriot_state.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 #include <cstdint>
 #include <limits>
+#include <new>
 #include <string>
 #include <string_view>
 #include <utility>
@@ -28,12 +29,12 @@
 #include "absl/strings/str_cat.h"
 #include "cheriot/cheriot_register.h"
 #include "cheriot/riscv_cheriot_csr_enum.h"
-#include "cheriot/riscv_cheriot_minstret.h"
 #include "mpact/sim/generic/arch_state.h"
 #include "mpact/sim/generic/type_helpers.h"
 #include "mpact/sim/util/memory/memory_interface.h"
 #include "mpact/sim/util/memory/tagged_flat_demand_memory.h"
 #include "mpact/sim/util/memory/tagged_memory_interface.h"
+#include "riscv//riscv_counter_csr.h"
 #include "riscv//riscv_csr.h"
 #include "riscv//riscv_misa.h"
 #include "riscv//riscv_pmp.h"
@@ -51,6 +52,8 @@
 using EC = ::mpact::sim::riscv::ExceptionCode;
 using ::mpact::sim::generic::operator*;  // NOLINT: used below (clang error).
 using ::mpact::sim::riscv::IsaExtension;
+using ::mpact::sim::riscv::RiscVCounterCsr;
+using ::mpact::sim::riscv::RiscVCounterCsrHigh;
 using ::mpact::sim::riscv::RiscVCsrEnum;
 using ::mpact::sim::riscv::RiscVCsrInterface;
 using ::mpact::sim::riscv::RiscVPmp;
@@ -191,10 +194,23 @@
   CHECK_NE(mtval, nullptr);
 
   // minstret/minstreth
-  CHECK_NE(CreateCsr<RiscVCheriotMInstret>(state, csr_vec, "minstret", state),
-           nullptr);
-  CHECK_NE(CreateCsr<RiscVCheriotMInstreth>(state, csr_vec, "minstreth", state),
-           nullptr);
+  auto *minstret = CreateCsr<RiscVCounterCsr<T, CheriotState>>(
+      state, csr_vec, "minstret", RiscVCsrEnum ::kMInstret, state);
+  CHECK_NE(minstret, nullptr);
+  if (sizeof(T) == sizeof(uint32_t)) {
+    CHECK_NE(CreateCsr<RiscVCounterCsrHigh<CheriotState>>(
+                 state, csr_vec, "minstreth", RiscVCsrEnum::kMInstretH, state),
+             nullptr);
+  }
+  // mcycle/mcycleh
+  auto *mcycle = CreateCsr<RiscVCounterCsr<T, CheriotState>>(
+      state, csr_vec, "mcycle", RiscVCsrEnum::kMCycle, state);
+  CHECK_NE(mcycle, nullptr);
+  if (sizeof(T) == sizeof(uint32_t)) {
+    CHECK_NE(CreateCsr<RiscVCounterCsrHigh<CheriotState>>(
+                 state, csr_vec, "mcycleh", RiscVCsrEnum::kMCycleH, state),
+             nullptr);
+  }
 
   // Stack high water mark CSRs. Mshwm gets updated automatically during
   // execution. mshwm
diff --git a/cheriot/cheriot_test_rig.cc b/cheriot/cheriot_test_rig.cc
index c794a9c..91e17ec 100644
--- a/cheriot/cheriot_test_rig.cc
+++ b/cheriot/cheriot_test_rig.cc
@@ -16,6 +16,7 @@
 
 #include <cstdint>
 #include <cstring>
+#include <new>
 #include <string>
 
 #include "absl/functional/bind_front.h"
@@ -26,12 +27,12 @@
 #include "cheriot/cheriot_register.h"
 #include "cheriot/cheriot_state.h"
 #include "cheriot/cheriot_test_rig_decoder.h"
-#include "cheriot/riscv_cheriot_minstret.h"
 #include "cheriot/riscv_cheriot_register_aliases.h"
 #include "cheriot/test_rig_packets.h"
 #include "mpact/sim/generic/component.h"
 #include "mpact/sim/util/memory/tagged_flat_demand_memory.h"
 #include "mpact/sim/util/memory/tagged_memory_watcher.h"
+#include "riscv//riscv_counter_csr.h"
 #include "riscv//riscv_register.h"
 #include "riscv//riscv_state.h"
 
@@ -40,6 +41,8 @@
 using EC = ::mpact::sim::riscv::ExceptionCode;
 using PB = ::mpact::sim::cheriot::CheriotRegister::PermissionBits;
 using CheriotEC = ::mpact::sim::cheriot::ExceptionCode;
+using ::mpact::sim::riscv::RiscVCounterCsr;
+using ::mpact::sim::riscv::RiscVCounterCsrHigh;
 using ::mpact::sim::util::TaggedFlatDemandMemory;
 using ::mpact::sim::util::TaggedMemoryWatcher;
 
@@ -105,13 +108,27 @@
   // registers.
   auto minstret_res = state_->csr_set()->GetCsr("minstret");
   auto minstreth_res = state_->csr_set()->GetCsr("minstreth");
-  if (!minstret_res.ok() || !minstreth_res.ok()) {
-    LOG(ERROR) << "Error while initializing minstret/minstreth";
-  }
-  auto *minstret = static_cast<RiscVCheriotMInstret *>(minstret_res.value());
-  auto *minstreth = static_cast<RiscVCheriotMInstreth *>(minstreth_res.value());
+  CHECK_OK(minstret_res.status());
+  CHECK_OK(minstreth_res.status());
+  auto *minstret = static_cast<RiscVCounterCsr<uint32_t, CheriotState> *>(
+      minstret_res.value());
+  auto *minstreth =
+      static_cast<RiscVCounterCsrHigh<CheriotState> *>(minstreth_res.value());
   minstret->set_counter(&counter_num_instructions_);
   minstreth->set_counter(&counter_num_instructions_);
+
+  // Initialize mcycle/mcycleh. Bind the instruction counter to those
+  // registers.
+  auto mcycle_res = state_->csr_set()->GetCsr("mcycle");
+  auto mcycleh_res = state_->csr_set()->GetCsr("mcycleh");
+  CHECK_OK(mcycle_res.status());
+  CHECK_OK(mcycleh_res.status());
+  auto *mcycle = static_cast<RiscVCounterCsr<uint32_t, CheriotState> *>(
+      mcycle_res.value());
+  auto *mcycleh =
+      static_cast<RiscVCounterCsrHigh<CheriotState> *>(mcycleh_res.value());
+  mcycle->set_counter(&counter_num_instructions_);
+  mcycleh->set_counter(&counter_num_instructions_);
   // Set memory limits according to the memory space for TestRIG.
   state_->set_max_physical_address(0x8000'0000ULL + 64 * 1024);
   state_->set_min_physical_address(0x8000'0000ULL);
diff --git a/cheriot/mpact_cheriot.cc b/cheriot/mpact_cheriot.cc
index a24abce..c8a5b0e 100644
--- a/cheriot/mpact_cheriot.cc
+++ b/cheriot/mpact_cheriot.cc
@@ -20,6 +20,7 @@
 #include <ios>
 #include <iostream>
 #include <memory>
+#include <new>
 #include <optional>
 #include <ostream>
 #include <string>
@@ -43,9 +44,9 @@
 #include "cheriot/cheriot_instrumentation_control.h"
 #include "cheriot/cheriot_rvv_decoder.h"
 #include "cheriot/cheriot_rvv_fp_decoder.h"
+#include "cheriot/cheriot_state.h"
 #include "cheriot/cheriot_top.h"
 #include "cheriot/debug_command_shell.h"
-#include "cheriot/riscv_cheriot_minstret.h"
 #include "mpact/sim/generic/core_debug_interface.h"
 #include "mpact/sim/generic/counters.h"
 #include "mpact/sim/generic/decoder_interface.h"
@@ -65,6 +66,7 @@
 #include "re2/re2.h"
 #include "riscv//riscv_arm_semihost.h"
 #include "riscv//riscv_clint.h"
+#include "riscv//riscv_counter_csr.h"
 #include "src/google/protobuf/text_format.h"
 
 using AddressRange = mpact::sim::util::MemoryWatcher::AddressRange;
@@ -75,6 +77,8 @@
 using ::mpact::sim::cheriot::CheriotState;
 using ::mpact::sim::generic::DecoderInterface;
 using ::mpact::sim::proto::ComponentData;
+using ::mpact::sim::riscv::RiscVCounterCsr;
+using ::mpact::sim::riscv::RiscVCounterCsrHigh;
 using ::mpact::sim::util::InstructionProfiler;
 using ::mpact::sim::util::TaggedMemoryUseProfiler;
 
@@ -156,8 +160,6 @@
 
 using HaltReason = ::mpact::sim::generic::CoreDebugInterface::HaltReason;
 using ::mpact::sim::cheriot::CheriotTop;
-using ::mpact::sim::cheriot::RiscVCheriotMInstret;
-using ::mpact::sim::cheriot::RiscVCheriotMInstreth;
 using ::mpact::sim::generic::Instruction;
 using ::mpact::sim::riscv::RiscVArmSemihost;
 using ::mpact::sim::riscv::RiscVClint;
@@ -334,11 +336,28 @@
     std::cerr << "Error while initializing minstret/minstreth";
     return -1;
   }
-  auto *minstret = static_cast<RiscVCheriotMInstret *>(minstret_res.value());
-  auto *minstreth = static_cast<RiscVCheriotMInstreth *>(minstreth_res.value());
+  auto *minstret = static_cast<RiscVCounterCsr<uint32_t, CheriotState> *>(
+      minstret_res.value());
+  auto *minstreth =
+      static_cast<RiscVCounterCsrHigh<CheriotState> *>(minstreth_res.value());
   minstret->set_counter(cheriot_top.counter_num_instructions());
   minstreth->set_counter(cheriot_top.counter_num_instructions());
 
+  // Initialize mcycle/mcycleh. Bind the instruction counter to those
+  // registers.
+  auto mcycle_res = cheriot_top.state()->csr_set()->GetCsr("mcycle");
+  auto mcycleh_res = cheriot_top.state()->csr_set()->GetCsr("mcycleh");
+  if (!mcycle_res.ok() || !mcycleh_res.ok()) {
+    std::cerr << "Error while initializing mcycle/mcycleh";
+    return -1;
+  }
+  auto *mcycle = static_cast<RiscVCounterCsr<uint32_t, CheriotState> *>(
+      mcycle_res.value());
+  auto *mcycleh =
+      static_cast<RiscVCounterCsrHigh<CheriotState> *>(mcycleh_res.value());
+  mcycle->set_counter(cheriot_top.counter_num_cycles());
+  mcycleh->set_counter(cheriot_top.counter_num_cycles());
+
   // Set up the memory router with the appropriate targets.
   ::mpact::sim::util::AtomicMemory *atomic_memory = nullptr;
   atomic_memory = new mpact::sim::util::AtomicMemory(tagged_memory);
diff --git a/cheriot/riscv_cheriot_minstret.cc b/cheriot/riscv_cheriot_minstret.cc
deleted file mode 100644
index 6eae2dd..0000000
--- a/cheriot/riscv_cheriot_minstret.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2024 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "cheriot/riscv_cheriot_minstret.h"
-
-#include <cstdint>
-#include <string>
-
-#include "cheriot/cheriot_state.h"
-#include "riscv//riscv_csr.h"
-
-namespace mpact::sim::cheriot {
-
-using ::mpact::sim::riscv::RiscVCsrEnum;
-
-RiscVCheriotMInstret::RiscVCheriotMInstret(std::string name,
-                                           CheriotState *state)
-    : RiscVSimpleCsr<uint32_t>(name, RiscVCsrEnum::kMInstret, state) {}
-
-// Read the current value of the counter and apply the offset.
-uint32_t RiscVCheriotMInstret::GetUint32() {
-  if (counter_ == nullptr) return offset_;
-  uint32_t value = GetCounterValue() + offset_;
-  return value;
-}
-
-uint64_t RiscVCheriotMInstret::GetUint64() {
-  return static_cast<uint64_t>(GetUint32());
-}
-
-void RiscVCheriotMInstret::Set(uint32_t value) {
-  offset_ = value - GetCounterValue();
-}
-
-void RiscVCheriotMInstret::Set(uint64_t value) {
-  Set(static_cast<uint32_t>(value));
-}
-
-RiscVCheriotMInstreth::RiscVCheriotMInstreth(std::string name,
-                                             CheriotState *state)
-    : RiscVSimpleCsr<uint32_t>(name, RiscVCsrEnum::kMInstretH, state) {}
-
-// Read the current value of the counter and apply the offset.
-uint32_t RiscVCheriotMInstreth::GetUint32() {
-  if (counter_ == nullptr) return offset_;
-  uint32_t value = GetCounterValue() + offset_;
-  return value;
-}
-
-uint64_t RiscVCheriotMInstreth::GetUint64() {
-  return static_cast<uint64_t>(GetUint32());
-}
-
-void RiscVCheriotMInstreth::Set(uint32_t value) {
-  offset_ = value - GetCounterValue();
-}
-
-void RiscVCheriotMInstreth::Set(uint64_t value) {
-  Set(static_cast<uint32_t>(value));
-}
-
-}  // namespace mpact::sim::cheriot
diff --git a/cheriot/riscv_cheriot_minstret.h b/cheriot/riscv_cheriot_minstret.h
deleted file mode 100644
index 1df90b0..0000000
--- a/cheriot/riscv_cheriot_minstret.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2024 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MPACT_CHERIOT__RISCV_CHERIOT_MINSTRET_H_
-#define MPACT_CHERIOT__RISCV_CHERIOT_MINSTRET_H_
-
-#include <cstdint>
-#include <string>
-
-#include "cheriot/riscv_cheriot_csr_enum.h"
-#include "mpact/sim/generic/counters.h"
-#include "riscv//riscv_csr.h"
-
-// This file provides the declarations for the CherIoT minstret/minstreth
-// CSRs. They are tied to the instruction counter of the top level of the
-// simulator. That binding is done when the simulator is instantiated. Until
-// that is done, the CSR just works like a scratch CSR.
-
-// Since this CSR is both readable and writable, but the counter value cannot
-// be changed, every time the register is written, a relative offset is computed
-// from the counter, so that the values read are relative to the most recent
-// write of the CSR.
-
-namespace mpact::sim::cheriot {
-
-using ::mpact::sim::generic::SimpleCounter;
-using ::mpact::sim::riscv::RiscVSimpleCsr;
-
-class CheriotState;
-
-class RiscVCheriotMInstret : public RiscVSimpleCsr<uint32_t> {
- public:
-  RiscVCheriotMInstret(std::string name, CheriotState* state);
-  RiscVCheriotMInstret(const RiscVCheriotMInstret&) = delete;
-  RiscVCheriotMInstret& operator=(const RiscVCheriotMInstret&) = delete;
-  ~RiscVCheriotMInstret() override = default;
-
-  // RiscVSimpleCsr method overrides.
-  uint32_t GetUint32() override;
-  uint64_t GetUint64() override;
-
-  void Set(uint32_t) override;
-  void Set(uint64_t) override;
-
-  void set_counter(SimpleCounter<uint64_t>* counter) { counter_ = counter; }
-
- private:
-  inline uint32_t GetCounterValue() const {
-    return static_cast<uint32_t>(counter_->GetValue() & 0xffff'ffffULL);
-  };
-
-  SimpleCounter<uint64_t>* counter_ = nullptr;
-  uint32_t offset_ = 0;
-};
-
-class RiscVCheriotMInstreth : public RiscVSimpleCsr<uint32_t> {
- public:
-  RiscVCheriotMInstreth(std::string name, CheriotState* state);
-  RiscVCheriotMInstreth(const RiscVCheriotMInstret&) = delete;
-  RiscVCheriotMInstreth& operator=(const RiscVCheriotMInstret&) = delete;
-  ~RiscVCheriotMInstreth() override = default;
-
-  // RiscVSimpleCsr method overrides.
-  uint32_t GetUint32() override;
-  uint64_t GetUint64() override;
-
-  void Set(uint32_t) override;
-  void Set(uint64_t) override;
-
-  void set_counter(SimpleCounter<uint64_t>* counter) { counter_ = counter; }
-
- private:
-  inline uint32_t GetCounterValue() const {
-    return static_cast<uint32_t>(counter_->GetValue() >> 32);
-  };
-
-  SimpleCounter<uint64_t>* counter_ = nullptr;
-  uint32_t offset_ = 0;
-};
-
-}  // namespace mpact::sim::cheriot
-
-#endif  // MPACT_CHERIOT__RISCV_CHERIOT_MINSTRET_H_