blob: f2a0a4fd1018ee3a7ae7f58bc60f42bc5c785227 [file]
#ifndef THIRD_PARTY_MPACT_RISCV_RISCV_COUNTER_CSR_H_
#define THIRD_PARTY_MPACT_RISCV_RISCV_COUNTER_CSR_H_
#include <cstdint>
#include <limits>
#include <string>
#include "mpact/sim/generic/counters.h"
#include "riscv/riscv_csr.h"
// This file defines a base class from which to derive CSR classes for
// minstret/minstreth and CounterCsr/mycycleh.Once instantiated, the instances
// of these classes need to be tied to a cycle counter of the top level of the
// simulator. That binding is done when the simulator is instantiated. Until
// that is done, the CSRs just work like scratch CSRs.
// Since these CSRs are both readable and writable, but the counter value cannot
// be changed, every time the registers are 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::riscv {
using ::mpact::sim::generic::SimpleCounter;
using ::mpact::sim::riscv::RiscVSimpleCsr;
template <typename S>
class RiscVCounterCsrHigh;
// This class implements the 64 bit CSR, or the low 32 bit CSR depending on the
// RiscV xlen width.
template <typename T, typename S>
class RiscVCounterCsr : public RiscVSimpleCsr<T> {
friend class RiscVCounterCsrHigh<S>;
public:
const T kMax = std::numeric_limits<T>::max();
RiscVCounterCsr(std::string name, RiscVCsrEnum csr_enum, S* state)
: RiscVSimpleCsr<T>(name, csr_enum, state) {}
RiscVCounterCsr(const RiscVCounterCsr&) = delete;
RiscVCounterCsr& operator=(const RiscVCounterCsr&) = delete;
~RiscVCounterCsr() override = default;
// RiscVSimpleCsr method overrides.
uint32_t GetUint32() override {
auto value = (GetCounterValue() + offset_) & kMax;
return value;
}
uint64_t GetUint64() override {
return (GetCounterValue() + offset_) & kMax;
};
// Any value written to the CSR is used to create an offset from the current
// value of the counter.
void Set(uint32_t value) override { offset_ = value - GetCounterValue(); }
void Set(uint64_t value) override {
offset_ = (value & kMax) - GetCounterValue();
}
// This is called to tie a cycle counter to the CSR.
void set_counter(SimpleCounter<uint64_t>* counter) { counter_ = counter; }
protected:
T offset_ = 0;
private:
inline T GetCounterValue() const {
if (counter_ == nullptr) return 0;
return static_cast<T>(counter_->GetValue() & kMax);
};
SimpleCounter<uint64_t>* counter_ = nullptr;
};
// This class implements the performance counter CSRs, which have slightly
// different behavior on write due to how they interact with the pipeline.
template <typename T, typename S>
class RiscVPerformanceCounterCsr : public RiscVCounterCsr<T, S> {
public:
RiscVPerformanceCounterCsr(std::string name, RiscVCsrEnum csr_enum, S* state)
: RiscVCounterCsr<T, S>(name, csr_enum, state) {}
RiscVPerformanceCounterCsr(const RiscVPerformanceCounterCsr&) = delete;
RiscVPerformanceCounterCsr& operator=(const RiscVPerformanceCounterCsr&) =
delete;
~RiscVPerformanceCounterCsr() override = default;
void Set(uint32_t value) override {
RiscVCounterCsr<T, S>::Set(value);
--this->offset_;
}
void Set(uint64_t value) override {
RiscVCounterCsr<T, S>::Set(value);
--this->offset_;
}
};
// This class implements the "high" version of the CSR on 32-bit RiscV.
template <typename S>
class RiscVCounterCsrHigh : public RiscVSimpleCsr<uint32_t> {
public:
RiscVCounterCsrHigh(std::string name, RiscVCsrEnum csr_enum, S* state,
RiscVCounterCsr<uint32_t, S>* low_csr)
: RiscVSimpleCsr<uint32_t>(name, csr_enum, state), low_csr_(low_csr) {}
RiscVCounterCsrHigh(const RiscVCounterCsrHigh&) = delete;
RiscVCounterCsrHigh& operator=(const RiscVCounterCsrHigh&) = delete;
~RiscVCounterCsrHigh() override = default;
// RiscVSimpleCsr method overrides.
uint32_t GetUint32() override {
uint64_t offset = (offset_ << 32) | low_csr_->offset_;
uint64_t value64 = GetCounterValue() + offset;
uint32_t value = static_cast<uint32_t>(value64 >> 32);
return value;
};
uint64_t GetUint64() override { return static_cast<uint64_t>(GetUint32()); };
// Any value written to the CSR is used to create an offset from the current
// value of the counter.
void Set(uint32_t value) override {
uint64_t counter_val = GetCounterValue();
uint32_t counter_low = static_cast<uint32_t>(counter_val);
uint32_t low_offset = low_csr_->offset_;
// Check for carry when reconstructing logical low value.
bool carry =
(static_cast<uint64_t>(counter_low) + low_offset) >= 0x100000000ULL;
offset_ = value - (counter_val >> 32) - (carry ? 1 : 0);
};
void Set(uint64_t value) override { Set(static_cast<uint32_t>(value)); };
// This is called to tie a cycle counter to the CSR.
void set_counter(SimpleCounter<uint64_t>* counter) { counter_ = counter; }
protected:
inline uint64_t GetCounterValue() const {
if (counter_ == nullptr) return 0;
return counter_->GetValue();
};
uint32_t get_low_offset() const { return low_csr_->offset_; }
void decr_low_offset() { low_csr_->offset_--; }
uint64_t offset_ = 0;
private:
RiscVCounterCsr<uint32_t, S>* low_csr_;
SimpleCounter<uint64_t>* counter_ = nullptr;
};
// This class implements the "high" version of the performance counter CSRs,
// which have slightly different behavior on write due to how they interact
// with the pipeline.
template <typename S>
class RiscVPerformanceCounterCsrHigh : public RiscVCounterCsrHigh<S> {
public:
RiscVPerformanceCounterCsrHigh(std::string name, RiscVCsrEnum csr_enum,
S* state,
RiscVCounterCsr<uint32_t, S>* low_csr)
: RiscVCounterCsrHigh<S>(name, csr_enum, state, low_csr) {}
RiscVPerformanceCounterCsrHigh(const RiscVPerformanceCounterCsrHigh&) =
delete;
RiscVPerformanceCounterCsrHigh& operator=(
const RiscVPerformanceCounterCsrHigh&) = delete;
~RiscVPerformanceCounterCsrHigh() override = default;
void Set(uint32_t value) override {
uint64_t counter_val = this->GetCounterValue();
uint32_t counter_low = static_cast<uint32_t>(counter_val);
uint32_t low_offset = this->get_low_offset();
// Check for carry when reconstructing logical low value.
bool carry =
(static_cast<uint64_t>(counter_low) + low_offset) >= 0x100000000ULL;
this->offset_ = value - (counter_val >> 32) - (carry ? 1 : 0);
if (this->get_low_offset() == 0) {
this->offset_--;
}
this->decr_low_offset();
}
};
} // namespace mpact::sim::riscv
#endif // THIRD_PARTY_MPACT_RISCV_RISCV_COUNTER_CSR_H_