RVA23: Add Zihpm CSRs. The hardware performance counters now exist but don't do anything since they are hardware / implementation specific. PiperOrigin-RevId: 828994924 Change-Id: I17534f8dd59ed2da4aa326a0cd257e34cf65450e
diff --git a/riscv/riscv_csr.h b/riscv/riscv_csr.h index 53e40b8..eacf424 100644 --- a/riscv/riscv_csr.h +++ b/riscv/riscv_csr.h
@@ -68,10 +68,68 @@ kCycle = 0xc00, kTime = 0xc01, kInstret = 0xc02, + kHpmcounter3 = 0xc03, + kHpmcounter4 = 0xc04, + kHpmcounter5 = 0xc05, + kHpmcounter6 = 0xc06, + kHpmcounter7 = 0xc07, + kHpmcounter8 = 0xc08, + kHpmcounter9 = 0xc09, + kHpmcounter10 = 0xc0a, + kHpmcounter11 = 0xc0b, + kHpmcounter12 = 0xc0c, + kHpmcounter13 = 0xc0d, + kHpmcounter14 = 0xc0e, + kHpmcounter15 = 0xc0f, + kHpmcounter16 = 0xc10, + kHpmcounter17 = 0xc11, + kHpmcounter18 = 0xc12, + kHpmcounter19 = 0xc13, + kHpmcounter20 = 0xc14, + kHpmcounter21 = 0xc15, + kHpmcounter22 = 0xc16, + kHpmcounter23 = 0xc17, + kHpmcounter24 = 0xc18, + kHpmcounter25 = 0xc19, + kHpmcounter26 = 0xc1a, + kHpmcounter27 = 0xc1b, + kHpmcounter28 = 0xc1c, + kHpmcounter29 = 0xc1d, + kHpmcounter30 = 0xc1e, + kHpmcounter31 = 0xc1f, kCycleH = 0xc80, kTimeH = 0xc81, kInstretH = 0x82, + kHpmcounter3H = 0xc83, + kHpmcounter4H = 0xc84, + kHpmcounter5H = 0xc85, + kHpmcounter6H = 0xc86, + kHpmcounter7H = 0xc87, + kHpmcounter8H = 0xc88, + kHpmcounter9H = 0xc89, + kHpmcounter10H = 0xc8a, + kHpmcounter11H = 0xc8b, + kHpmcounter12H = 0xc8c, + kHpmcounter13H = 0xc8d, + kHpmcounter14H = 0xc8e, + kHpmcounter15H = 0xc8f, + kHpmcounter16H = 0xc90, + kHpmcounter17H = 0xc91, + kHpmcounter18H = 0xc92, + kHpmcounter19H = 0xc93, + kHpmcounter20H = 0xc94, + kHpmcounter21H = 0xc95, + kHpmcounter22H = 0xc96, + kHpmcounter23H = 0xc97, + kHpmcounter24H = 0xc98, + kHpmcounter25H = 0xc99, + kHpmcounter26H = 0xc9a, + kHpmcounter27H = 0xc9b, + kHpmcounter28H = 0xc9c, + kHpmcounter29H = 0xc9d, + kHpmcounter30H = 0xc9e, + kHpmcounter31H = 0xc9f, // Ignoring perf monitoring counters for now. @@ -145,6 +203,35 @@ kMCycle = 0xb00, // Machine cycle counter. kMInstret = 0xb02, // Machine instructions-retired counter. + kMhpmcounter3 = 0xb03, + kMhpmcounter4 = 0xb04, + kMhpmcounter5 = 0xb05, + kMhpmcounter6 = 0xb06, + kMhpmcounter7 = 0xb07, + kMhpmcounter8 = 0xb08, + kMhpmcounter9 = 0xb09, + kMhpmcounter10 = 0xb0a, + kMhpmcounter11 = 0xb0b, + kMhpmcounter12 = 0xb0c, + kMhpmcounter13 = 0xb0d, + kMhpmcounter14 = 0xb0e, + kMhpmcounter15 = 0xb0f, + kMhpmcounter16 = 0xb10, + kMhpmcounter17 = 0xb11, + kMhpmcounter18 = 0xb12, + kMhpmcounter19 = 0xb13, + kMhpmcounter20 = 0xb14, + kMhpmcounter21 = 0xb15, + kMhpmcounter22 = 0xb16, + kMhpmcounter23 = 0xb17, + kMhpmcounter24 = 0xb18, + kMhpmcounter25 = 0xb19, + kMhpmcounter26 = 0xb1a, + kMhpmcounter27 = 0xb1b, + kMhpmcounter28 = 0xb1c, + kMhpmcounter29 = 0xb1d, + kMhpmcounter30 = 0xb1e, + kMhpmcounter31 = 0xb1f, // Ignoring machine performance counters for now. @@ -154,6 +241,35 @@ kMCycleH = 0xb80, // Upper 32 bits of mcycle. kMInstretH = 0xb82, // Upper 32 bits of MInstret + kMhpmcounter3H = 0xb83, + kMhpmcounter4H = 0xb84, + kMhpmcounter5H = 0xb85, + kMhpmcounter6H = 0xb86, + kMhpmcounter7H = 0xb87, + kMhpmcounter8H = 0xb88, + kMhpmcounter9H = 0xb89, + kMhpmcounter10H = 0xb8a, + kMhpmcounter11H = 0xb8b, + kMhpmcounter12H = 0xb8c, + kMhpmcounter13H = 0xb8d, + kMhpmcounter14H = 0xb8e, + kMhpmcounter15H = 0xb8f, + kMhpmcounter16H = 0xb90, + kMhpmcounter17H = 0xb91, + kMhpmcounter18H = 0xb92, + kMhpmcounter19H = 0xb93, + kMhpmcounter20H = 0xb94, + kMhpmcounter21H = 0xb95, + kMhpmcounter22H = 0xb96, + kMhpmcounter23H = 0xb97, + kMhpmcounter24H = 0xb98, + kMhpmcounter25H = 0xb99, + kMhpmcounter26H = 0xb9a, + kMhpmcounter27H = 0xb9b, + kMhpmcounter28H = 0xb9c, + kMhpmcounter29H = 0xb9d, + kMhpmcounter30H = 0xb9e, + kMhpmcounter31H = 0xb9f, // Ignoring machine counter setup for now.
diff --git a/riscv/riscv_state.cc b/riscv/riscv_state.cc index f7f6321..135bbf9 100644 --- a/riscv/riscv_state.cc +++ b/riscv/riscv_state.cc
@@ -289,6 +289,59 @@ nullptr); } + // hpmcounterN + hpmcounterNh (N=3..31) + uint32_t hpmcounter_base = static_cast<uint32_t>(RiscVCsrEnum::kCycle); + uint32_t hpmcounter_base_high = static_cast<uint32_t>(RiscVCsrEnum::kCycleH); + for (int i = 0; i < kNumHardwarePerfCounters; i++) { + RiscVCounterCsr<T, RiscVState>* hpmcounter = + CreateCsr<RiscVCounterCsr<T, RiscVState>>( + state, csr_vec, + absl::StrCat("hpmcounter", i + kMinimumHardwarePerfIndex), + static_cast<RiscVCsrEnum>(hpmcounter_base + i + + kMinimumHardwarePerfIndex), + state); + CHECK_NE(hpmcounter, nullptr); + if (std::is_same_v<T, uint32_t>) { + CHECK_NE( + CreateCsr<RiscVCounterCsrHigh<RiscVState>>( + state, csr_vec, + absl::StrCat("hpmcounter", i + kMinimumHardwarePerfIndex, "h"), + static_cast<RiscVCsrEnum>(hpmcounter_base_high + i + + kMinimumHardwarePerfIndex), + state, + reinterpret_cast<RiscVCounterCsr<uint32_t, RiscVState>*>( + hpmcounter)), + nullptr); + } + } + + // mhpmcounterN + mhpmcounterNh (N=3..31) + uint32_t mhpmcounter_base = static_cast<uint32_t>(RiscVCsrEnum::kMCycle); + uint32_t mhpmcounter_base_high = + static_cast<uint32_t>(RiscVCsrEnum::kMCycleH); + for (int i = 0; i < kNumHardwarePerfCounters; i++) { + RiscVCounterCsr<T, RiscVState>* mhpmcounter = + CreateCsr<RiscVCounterCsr<T, RiscVState>>( + state, csr_vec, + absl::StrCat("mhpmcounter", i + kMinimumHardwarePerfIndex), + static_cast<RiscVCsrEnum>(mhpmcounter_base + i + + kMinimumHardwarePerfIndex), + state); + CHECK_NE(mhpmcounter, nullptr); + if (std::is_same_v<T, uint32_t>) { + CHECK_NE( + CreateCsr<RiscVCounterCsrHigh<RiscVState>>( + state, csr_vec, + absl::StrCat("mhpmcounter", i + kMinimumHardwarePerfIndex, "h"), + static_cast<RiscVCsrEnum>(mhpmcounter_base_high + i + + kMinimumHardwarePerfIndex), + state, + reinterpret_cast<RiscVCounterCsr<uint32_t, RiscVState>*>( + mhpmcounter + kMinimumHardwarePerfIndex)), + nullptr); + } + } + // Hypervisor level CSRs // henvcfg
diff --git a/riscv/riscv_state.h b/riscv/riscv_state.h index f04d56d..94196d4 100644 --- a/riscv/riscv_state.h +++ b/riscv/riscv_state.h
@@ -43,6 +43,9 @@ namespace sim { namespace riscv { +static constexpr int kNumHardwarePerfCounters = 29; +static constexpr int kMinimumHardwarePerfIndex = 3; + using ArchState = ::mpact::sim::generic::ArchState; using DataBuffer = ::mpact::sim::generic::DataBuffer; using Instruction = ::mpact::sim::generic::Instruction;
diff --git a/riscv/riscv_top.cc b/riscv/riscv_top.cc index b0ae232..d83ef4e 100644 --- a/riscv/riscv_top.cc +++ b/riscv/riscv_top.cc
@@ -129,6 +129,13 @@ memory_watcher_ = new util::MemoryWatcher(state_->memory()); state_->set_memory(memory_watcher_); + counter_hardware_perf_.resize(kNumHardwarePerfCounters); + for (int i = 0; i < kNumHardwarePerfCounters; i++) { + counter_hardware_perf_[i].Initialize(absl::StrCat("hardware_perf_", i), 0); + CHECK_OK(AddCounter(&counter_hardware_perf_[i])) + << "Failed to register hardware_perf counter"; + } + // Register instruction and cycle counters. CHECK_OK(AddCounter(&counter_num_instructions_)) << "Failed to register instruction counter"; @@ -154,6 +161,20 @@ CHECK_OK(SetCsrCounter("cycle", counter_num_cycles_)); CHECK_OK(SetCsrCounter("time", counter_num_cycles_)); + // Add Zihpm counters - Unprivileged. + for (int i = 0; i < kNumHardwarePerfCounters; i++) { + std::string name = + absl::StrCat("hpmcounter", i + kMinimumHardwarePerfIndex); + CHECK_OK(SetCsrCounter(name, counter_hardware_perf_[i])); + } + + // Add Zihpm counters - Machine Privileged. + for (int i = 0; i < kNumHardwarePerfCounters; i++) { + std::string name = + absl::StrCat("mhpmcounter", i + kMinimumHardwarePerfIndex); + CHECK_OK(SetCsrCounter(name, counter_hardware_perf_[i])); + } + // Set up break and action points. rv_action_point_memory_interface_ = new RiscVActionPointMemoryInterface( state_->memory(),
diff --git a/riscv/riscv_top.h b/riscv/riscv_top.h index 22ca7f9..028f9a7 100644 --- a/riscv/riscv_top.h +++ b/riscv/riscv_top.h
@@ -145,6 +145,9 @@ return &counter_num_cycles_; } generic::SimpleCounter<uint64_t>* counter_pc() { return &counter_pc_; } + std::vector<generic::SimpleCounter<uint64_t>>* counter_hardware_perf() { + return &counter_hardware_perf_; + } // Memory watchers used for data watch points. util::MemoryWatcher* memory_watcher() { return memory_watcher_; } @@ -218,6 +221,8 @@ std::vector<generic::SimpleCounter<uint64_t>> counter_opcode_; generic::SimpleCounter<uint64_t> counter_num_instructions_; generic::SimpleCounter<uint64_t> counter_num_cycles_; + // Placeholders for the counters required for the Zihpm extension. + std::vector<generic::SimpleCounter<uint64_t>> counter_hardware_perf_; // Counter used for profiling by connecting it to a profiler. This allows // the pc to be written to the counter, and the profiling can be enabled/ // disabled with the other counters.
diff --git a/riscv/test/BUILD b/riscv/test/BUILD index 3d0b397..fc60503 100644 --- a/riscv/test/BUILD +++ b/riscv/test/BUILD
@@ -107,6 +107,7 @@ deps = [ "//riscv:riscv_state", "@com_google_absl//absl/log:check", + "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", "@com_google_mpact-sim//mpact/sim/util/memory", ],
diff --git a/riscv/test/riscv_state_test.cc b/riscv/test/riscv_state_test.cc index 26fca24..23e2dcc 100644 --- a/riscv/test/riscv_state_test.cc +++ b/riscv/test/riscv_state_test.cc
@@ -16,16 +16,21 @@ #include <cstdint> #include <iostream> +#include <memory> #include <ostream> #include <string> #include "absl/log/check.h" +#include "absl/strings/str_cat.h" #include "googlemock/include/gmock/gmock.h" #include "mpact/sim/util/memory/flat_demand_memory.h" +#include "riscv/riscv_csr.h" #include "riscv/riscv_register.h" namespace { +using ::mpact::sim::riscv::RiscVCsrEnum; +using ::mpact::sim::riscv::RiscVCsrInterface; using ::mpact::sim::riscv::RiscVState; using ::mpact::sim::riscv::RiscVXlen; using ::mpact::sim::riscv::RV32Register; @@ -40,7 +45,7 @@ TEST(RiscVStateTest, Basic) { FlatDemandMemory memory; - auto* state = new RiscVState("test", RiscVXlen::RV32, &memory); + auto state = std::make_unique<RiscVState>("test", RiscVXlen::RV32, &memory); // Make sure pc has been created. auto iter = state->registers()->find("pc"); auto* ptr = (iter != state->registers()->end()) ? iter->second : nullptr; @@ -50,12 +55,11 @@ pc->data_buffer()->Set<uint32_t>(0, kPcValue); auto* pc_op = state->pc_operand(); EXPECT_EQ(pc_op->AsUint32(0), kPcValue); - delete state; } TEST(RiscVStateTest, Memory) { FlatDemandMemory memory; - auto* state = new RiscVState("test", RiscVXlen::RV32, &memory); + auto state = std::make_unique<RiscVState>("test", RiscVXlen::RV32, &memory); auto* db = state->db_factory()->Allocate<uint32_t>(1); state->LoadMemory(nullptr, kMemAddr, db, nullptr, nullptr); EXPECT_EQ(db->Get<uint32_t>(0), 0); @@ -65,12 +69,11 @@ state->LoadMemory(nullptr, kMemAddr, db, nullptr, nullptr); EXPECT_EQ(db->Get<uint32_t>(0), kMemValue); db->DecRef(); - delete state; } TEST(RiscVStateTest, OutOfBoundLoad) { FlatDemandMemory memory; - auto* state = new RiscVState("test", RiscVXlen::RV32, &memory); + auto state = std::make_unique<RiscVState>("test", RiscVXlen::RV32, &memory); state->set_max_physical_address(kMemAddr - 4); state->set_on_trap([](bool is_interrupt, uint64_t trap_value, uint64_t exception_code, uint64_t epc, @@ -93,7 +96,67 @@ EXPECT_THAT(stderr, testing::HasSubstr("Load Access Fault")); db->DecRef(); dummy_inst->DecRef(); - delete state; +} + +TEST(RiscVStateTest, PerfCounterCsrNameAndIndexMatch_hpm) { + FlatDemandMemory memory; + auto state = std::make_unique<RiscVState>("test", RiscVXlen::RV32, &memory); + + uint32_t hpmcounter_base = static_cast<uint32_t>(RiscVCsrEnum::kCycle); + for (int i = 3; i < 32; i++) { + ASSERT_OK_AND_ASSIGN( + RiscVCsrInterface * csr_by_name, + state->csr_set()->GetCsr(absl::StrCat("hpmcounter", i))); + ASSERT_OK_AND_ASSIGN(RiscVCsrInterface * csr_by_index, + state->csr_set()->GetCsr(hpmcounter_base + i)); + EXPECT_EQ(csr_by_name, csr_by_index); + } +} + +TEST(RiscVStateTest, PerfCounterCsrNameAndIndexMatch_hpm_high) { + FlatDemandMemory memory; + auto state = std::make_unique<RiscVState>("test", RiscVXlen::RV32, &memory); + + uint32_t hpmcounter_base_high = static_cast<uint32_t>(RiscVCsrEnum::kCycleH); + for (int i = 3; i < 32; i++) { + ASSERT_OK_AND_ASSIGN( + RiscVCsrInterface * csr_by_name, + state->csr_set()->GetCsr(absl::StrCat("hpmcounter", i, "h"))); + ASSERT_OK_AND_ASSIGN(RiscVCsrInterface * csr_by_index, + state->csr_set()->GetCsr(hpmcounter_base_high + i)); + EXPECT_EQ(csr_by_name, csr_by_index); + } +} + +TEST(RiscVStateTest, PerfCounterCsrNameAndIndexMatch_mhpm) { + FlatDemandMemory memory; + auto state = std::make_unique<RiscVState>("test", RiscVXlen::RV32, &memory); + + uint32_t mhpmcounter_base = static_cast<uint32_t>(RiscVCsrEnum::kMCycle); + for (int i = 3; i < 32; i++) { + ASSERT_OK_AND_ASSIGN( + RiscVCsrInterface * csr_by_name, + state->csr_set()->GetCsr(absl::StrCat("mhpmcounter", i))); + ASSERT_OK_AND_ASSIGN(RiscVCsrInterface * csr_by_index, + state->csr_set()->GetCsr(mhpmcounter_base + i)); + EXPECT_EQ(csr_by_name, csr_by_index); + } +} + +TEST(RiscVStateTest, PerfCounterCsrNameAndIndexMatch_mhpm_high) { + FlatDemandMemory memory; + auto state = std::make_unique<RiscVState>("test", RiscVXlen::RV32, &memory); + + uint32_t mhpmcounter_base_high = + static_cast<uint32_t>(RiscVCsrEnum::kMCycleH); + for (int i = 3; i < 32; i++) { + ASSERT_OK_AND_ASSIGN( + RiscVCsrInterface * csr_by_name, + state->csr_set()->GetCsr(absl::StrCat("mhpmcounter", i, "h"))); + ASSERT_OK_AND_ASSIGN(RiscVCsrInterface * csr_by_index, + state->csr_set()->GetCsr(mhpmcounter_base_high + i)); + EXPECT_EQ(csr_by_name, csr_by_index); + } } } // namespace
diff --git a/riscv/test/riscv_top_test.cc b/riscv/test/riscv_top_test.cc index a5d3709..058b6d2 100644 --- a/riscv/test/riscv_top_test.cc +++ b/riscv/test/riscv_top_test.cc
@@ -16,10 +16,12 @@ #include <cstdint> #include <string> +#include <vector> #include "absl/log/check.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" #include "googlemock/include/gmock/gmock.h" #include "mpact/sim/generic/core_debug_interface.h" #include "mpact/sim/generic/decoder_interface.h" @@ -590,6 +592,39 @@ EXPECT_NE(riscv_top_->ReadRegister("cycle").value(), 0); EXPECT_NE(riscv_top_->ReadRegister("time").value(), 0); EXPECT_NE(riscv_top_->ReadRegister("instret").value(), 0); + + // Verify that the hardware perf counters are initialized. These checks are + // based on coverage gaps found in mutation testing. + std::vector<int> uninitialized_perf_counter_indices; + for (int i = 0; i < riscv_top_->counter_hardware_perf()->size(); ++i) { + if (!riscv_top_->counter_hardware_perf()->at(i).IsInitialized()) { + uninitialized_perf_counter_indices.push_back(i); + } + } + EXPECT_EQ(uninitialized_perf_counter_indices.size(), 0) + << "Uninitialized perf counters: " + << absl::StrJoin(uninitialized_perf_counter_indices, ","); + + // Verify that the hardware perf counters are connected to the CSRs. + // This is based on a mutation testing finding. + // Get the CSR value, increment the underlying counter, and verify that the + // CSR value changes. + std::vector<std::string> failed_perf_counter_csr_names; + for (int i = 0; i < riscv_top_->counter_hardware_perf()->size(); ++i) { + std::string csr_name = absl::StrCat("hpmcounter", i + 3); + std::string machine_csr_name = absl::StrCat("mhpmcounter", i + 3); + auto csr_value = riscv_top_->ReadRegister(csr_name).value(); + riscv_top_->counter_hardware_perf()->at(i).Increment(1); + if (riscv_top_->ReadRegister(csr_name).value() == csr_value) { + failed_perf_counter_csr_names.push_back(csr_name); + } + if (riscv_top_->ReadRegister(machine_csr_name).value() == csr_value) { + failed_perf_counter_csr_names.push_back(machine_csr_name); + } + } + EXPECT_EQ(failed_perf_counter_csr_names.size(), 0) + << "Failed CSR names: " + << absl::StrJoin(failed_perf_counter_csr_names, ","); } } // namespace