aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--riscv/csrs.cc122
-rw-r--r--riscv/csrs.h74
-rw-r--r--riscv/execute.cc2
-rw-r--r--riscv/processor.cc128
-rw-r--r--riscv/processor.h2
5 files changed, 228 insertions, 100 deletions
diff --git a/riscv/csrs.cc b/riscv/csrs.cc
index 92ec114..482ba49 100644
--- a/riscv/csrs.cc
+++ b/riscv/csrs.cc
@@ -48,11 +48,19 @@ void csr_t::write(const reg_t val) noexcept {
}
void csr_t::log_write() const noexcept {
+ log_special_write(address, written_value());
+}
+
+void csr_t::log_special_write(const reg_t address, const reg_t val) const noexcept {
#if defined(RISCV_ENABLE_COMMITLOG)
- proc->get_state()->log_reg_write[((address) << 4) | 4] = {read(), 0};
+ proc->get_state()->log_reg_write[((address) << 4) | 4] = {val, 0};
#endif
}
+reg_t csr_t::written_value() const noexcept {
+ return read();
+}
+
// implement class basic_csr_t
basic_csr_t::basic_csr_t(processor_t* const proc, const reg_t addr, const reg_t init):
csr_t(proc, addr),
@@ -800,3 +808,115 @@ bool virtualized_satp_csr_t::unlogged_write(const reg_t val) noexcept {
const reg_t newval = orig_satp->satp_valid(val) ? val : read();
return virtualized_csr_t::unlogged_write(newval);
}
+
+
+// implement class minstret_csr_t
+minstret_csr_t::minstret_csr_t(processor_t* const proc, const reg_t addr):
+ csr_t(proc, addr),
+ val(0) {
+}
+
+reg_t minstret_csr_t::read() const noexcept {
+ return val;
+}
+
+void minstret_csr_t::bump(const reg_t howmuch) noexcept {
+ val += howmuch; // to keep log reasonable size, don't log every bump
+}
+
+bool minstret_csr_t::unlogged_write(const reg_t val) noexcept {
+ if (proc->get_xlen() == 32)
+ this->val = (this->val >> 32 << 32) | (val & 0xffffffffU);
+ else
+ this->val = val;
+ // The ISA mandates that if an instruction writes instret, the write
+ // takes precedence over the increment to instret. However, Spike
+ // unconditionally increments instret after executing an instruction.
+ // Correct for this artifact by decrementing instret here.
+ this->val--;
+ return true;
+}
+
+reg_t minstret_csr_t::written_value() const noexcept {
+ // Re-adjust for upcoming bump()
+ return this->val + 1;
+}
+
+void minstret_csr_t::write_upper_half(const reg_t val) noexcept {
+ this->val = (val << 32) | (this->val << 32 >> 32);
+ this->val--; // See comment above.
+ // Log upper half only.
+ log_special_write(address + (CSR_MINSTRETH - CSR_MINSTRET), written_value() >> 32);
+}
+
+
+minstreth_csr_t::minstreth_csr_t(processor_t* const proc, const reg_t addr, minstret_csr_t_p minstret):
+ csr_t(proc, addr),
+ minstret(minstret) {
+}
+
+reg_t minstreth_csr_t::read() const noexcept {
+ return minstret->read() >> 32;
+}
+
+bool minstreth_csr_t::unlogged_write(const reg_t val) noexcept {
+ minstret->write_upper_half(val);
+ return true;
+}
+
+
+proxy_csr_t::proxy_csr_t(processor_t* const proc, const reg_t addr, csr_t_p delegate):
+ csr_t(proc, addr),
+ delegate(delegate) {
+}
+
+reg_t proxy_csr_t::read() const noexcept {
+ return delegate->read();
+}
+
+bool proxy_csr_t::unlogged_write(const reg_t val) noexcept {
+ delegate->write(val); // log only under the original (delegate's) name
+ return false;
+}
+
+
+const_csr_t::const_csr_t(processor_t* const proc, const reg_t addr, reg_t val):
+ csr_t(proc, addr),
+ val(val) {
+}
+
+reg_t const_csr_t::read() const noexcept {
+ return val;
+}
+
+bool const_csr_t::unlogged_write(const reg_t val) noexcept {
+ return false;
+}
+
+
+counter_proxy_csr_t::counter_proxy_csr_t(processor_t* const proc, const reg_t addr, csr_t_p delegate):
+ proxy_csr_t(proc, addr, delegate) {
+}
+
+bool counter_proxy_csr_t::myenable(csr_t_p counteren) const noexcept {
+ return 1 & (counteren->read() >> (address & 31));
+}
+
+void counter_proxy_csr_t::verify_permissions(insn_t insn, bool write) const {
+ proxy_csr_t::verify_permissions(insn, write);
+
+ const bool mctr_ok = (state->prv < PRV_M) ? myenable(state->mcounteren) : true;
+ const bool hctr_ok = state->v ? myenable(state->hcounteren) : true;
+ const bool sctr_ok = (proc->extension_enabled('S') && state->prv < PRV_S) ? myenable(state->scounteren) : true;
+
+ if (!mctr_ok)
+ throw trap_illegal_instruction(insn.bits());
+ if (!hctr_ok)
+ throw trap_virtual_instruction(insn.bits());
+ if (!sctr_ok) {
+ if (state->v)
+ throw trap_virtual_instruction(insn.bits());
+ else
+ throw trap_illegal_instruction(insn.bits());
+ }
+}
diff --git a/riscv/csrs.h b/riscv/csrs.h
index e143c66..8400b11 100644
--- a/riscv/csrs.h
+++ b/riscv/csrs.h
@@ -38,6 +38,13 @@ class csr_t {
// Record this CSR update (which has already happened) in the commit log
void log_write() const noexcept;
+ // Record a write to an alternate CSR (e.g. minstreth instead of minstret)
+ void log_special_write(const reg_t address, const reg_t val) const noexcept;
+
+ // What value was written to this reg? Default implementation simply
+ // calls read(), but a few CSRs are special.
+ virtual reg_t written_value() const noexcept;
+
processor_t* const proc;
state_t* const state;
public:
@@ -414,4 +421,71 @@ class virtualized_satp_csr_t: public virtualized_csr_t {
};
+// For minstret, which is always 64 bits, but in RV32 is split into
+// high and low halves. The first class always holds the full 64-bit
+// value.
+class minstret_csr_t: public csr_t {
+ public:
+ minstret_csr_t(processor_t* const proc, const reg_t addr);
+ // Always returns full 64-bit value
+ virtual reg_t read() const noexcept override;
+ void bump(const reg_t howmuch) noexcept;
+ void write_upper_half(const reg_t val) noexcept;
+ protected:
+ virtual bool unlogged_write(const reg_t val) noexcept override;
+ virtual reg_t written_value() const noexcept override;
+ private:
+ reg_t val;
+};
+
+typedef std::shared_ptr<minstret_csr_t> minstret_csr_t_p;
+
+
+// A simple proxy to read/write the upper half of minstret
+class minstreth_csr_t: public csr_t {
+ public:
+ minstreth_csr_t(processor_t* const proc, const reg_t addr, minstret_csr_t_p minstret);
+ virtual reg_t read() const noexcept override;
+ protected:
+ virtual bool unlogged_write(const reg_t val) noexcept override;
+ private:
+ minstret_csr_t_p minstret;
+};
+
+typedef std::shared_ptr<minstreth_csr_t> minstreth_csr_t_p;
+
+
+// For a CSR that is an alias of another
+class proxy_csr_t: public csr_t {
+ public:
+ proxy_csr_t(processor_t* const proc, const reg_t addr, csr_t_p delegate);
+ virtual reg_t read() const noexcept override;
+ protected:
+ bool unlogged_write(const reg_t val) noexcept override;
+ private:
+ csr_t_p delegate;
+};
+
+
+// For a CSR with a fixed, unchanging value
+class const_csr_t: public csr_t {
+ public:
+ const_csr_t(processor_t* const proc, const reg_t addr, reg_t val);
+ virtual reg_t read() const noexcept override;
+ protected:
+ bool unlogged_write(const reg_t val) noexcept override;
+ private:
+ const reg_t val;
+};
+
+
+// For a CSR that is an unprivileged accessor of a privileged counter
+class counter_proxy_csr_t: public proxy_csr_t {
+ public:
+ counter_proxy_csr_t(processor_t* const proc, const reg_t addr, csr_t_p delegate);
+ virtual void verify_permissions(insn_t insn, bool write) const override;
+ private:
+ bool myenable(csr_t_p counteren) const noexcept;
+};
+
#endif
diff --git a/riscv/execute.cc b/riscv/execute.cc
index 53f956c..ed64277 100644
--- a/riscv/execute.cc
+++ b/riscv/execute.cc
@@ -348,7 +348,7 @@ void processor_t::step(size_t n)
n = ++instret;
}
- state.minstret += instret;
+ state.minstret->bump(instret);
n -= instret;
}
}
diff --git a/riscv/processor.cc b/riscv/processor.cc
index 876c32d..6320c53 100644
--- a/riscv/processor.cc
+++ b/riscv/processor.cc
@@ -351,7 +351,37 @@ void state_t::reset(processor_t* const proc, reg_t max_isa)
csrmap[CSR_MSCRATCH] = std::make_shared<basic_csr_t>(proc, CSR_MSCRATCH, 0);
csrmap[CSR_MTVEC] = mtvec = std::make_shared<tvec_csr_t>(proc, CSR_MTVEC);
csrmap[CSR_MCAUSE] = mcause = std::make_shared<cause_csr_t>(proc, CSR_MCAUSE);
- minstret = 0;
+ csrmap[CSR_MINSTRET] = minstret = std::make_shared<minstret_csr_t>(proc, CSR_MINSTRET);
+ csrmap[CSR_MCYCLE] = std::make_shared<proxy_csr_t>(proc, CSR_MCYCLE, minstret);
+ csrmap[CSR_INSTRET] = std::make_shared<counter_proxy_csr_t>(proc, CSR_INSTRET, minstret);
+ csrmap[CSR_CYCLE] = std::make_shared<counter_proxy_csr_t>(proc, CSR_CYCLE, minstret);
+ if (xlen == 32) {
+ minstreth_csr_t_p minstreth;
+ csrmap[CSR_MINSTRETH] = minstreth = std::make_shared<minstreth_csr_t>(proc, CSR_MINSTRETH, minstret);
+ csrmap[CSR_MCYCLEH] = std::make_shared<proxy_csr_t>(proc, CSR_MCYCLEH, minstreth);
+ csrmap[CSR_INSTRETH] = std::make_shared<counter_proxy_csr_t>(proc, CSR_INSTRETH, minstreth);
+ csrmap[CSR_CYCLEH] = std::make_shared<counter_proxy_csr_t>(proc, CSR_CYCLEH, minstreth);
+ }
+ for (reg_t i=3; i<=31; ++i) {
+ const reg_t which_mevent = CSR_MHPMEVENT3 + i - 3;
+ const reg_t which_mcounter = CSR_MHPMCOUNTER3 + i - 3;
+ const reg_t which_mcounterh = CSR_MHPMCOUNTER3H + i - 3;
+ const reg_t which_counter = CSR_HPMCOUNTER3 + i - 3;
+ const reg_t which_counterh = CSR_HPMCOUNTER3H + i - 3;
+ auto mevent = std::make_shared<const_csr_t>(proc, which_mevent, 0);
+ auto mcounter = std::make_shared<const_csr_t>(proc, which_mcounter, 0);
+ auto counter = std::make_shared<counter_proxy_csr_t>(proc, which_counter, mcounter);
+ csrmap[which_mevent] = mevent;
+ csrmap[which_mcounter] = mcounter;
+ csrmap[which_counter] = counter;
+ if (xlen == 32) {
+ auto mcounterh = std::make_shared<const_csr_t>(proc, which_mcounterh, 0);
+ auto counterh = std::make_shared<counter_proxy_csr_t>(proc, which_counterh, mcounterh);
+ csrmap[which_mcounterh] = mcounterh;
+ csrmap[which_counterh] = counterh;
+ }
+ }
+ csrmap[CSR_MCOUNTINHIBIT] = std::make_shared<const_csr_t>(proc, CSR_MCOUNTINHIBIT, 0);
csrmap[CSR_MIE] = mie = std::make_shared<mie_csr_t>(proc, CSR_MIE);
csrmap[CSR_MIP] = mip = std::make_shared<mip_csr_t>(proc, CSR_MIP);
auto sip_sie_accr = std::make_shared<generic_int_accessor_t>(this,
@@ -949,23 +979,6 @@ void processor_t::set_csr(int which, reg_t val)
VU.vxsat = (val & VCSR_VXSAT) >> VCSR_VXSAT_SHIFT;
VU.vxrm = (val & VCSR_VXRM) >> VCSR_VXRM_SHIFT;
break;
- case CSR_MINSTRET:
- case CSR_MCYCLE:
- if (xlen == 32)
- state.minstret = (state.minstret >> 32 << 32) | (val & 0xffffffffU);
- else
- state.minstret = val;
- // The ISA mandates that if an instruction writes instret, the write
- // takes precedence over the increment to instret. However, Spike
- // unconditionally increments instret after executing an instruction.
- // Correct for this artifact by decrementing instret here.
- state.minstret--;
- break;
- case CSR_MINSTRETH:
- case CSR_MCYCLEH:
- state.minstret = (val << 32) | (state.minstret << 32 >> 32);
- state.minstret--; // See comment above.
- break;
case CSR_MTVAL2: state.mtval2 = val; break;
case CSR_MTINST: state.mtinst = val; break;
case CSR_HEDELEG: {
@@ -1118,10 +1131,6 @@ void processor_t::set_csr(int which, reg_t val)
LOG_CSR(CSR_VXRM);
break;
- case CSR_MINSTRET:
- case CSR_MCYCLE:
- case CSR_MINSTRETH:
- case CSR_MCYCLEH:
case CSR_TSELECT:
case CSR_TDATA1:
case CSR_TDATA2:
@@ -1141,28 +1150,6 @@ void processor_t::set_csr(int which, reg_t val)
// side effects on reads.
reg_t processor_t::get_csr(int which, insn_t insn, bool write, bool peek)
{
-#define mcounteren_ok(__which) \
-({ \
- bool __ctr_ok = true; \
- if (state.prv < PRV_M) \
- __ctr_ok = (state.mcounteren->read() >> (__which & 31)) & 1; \
- __ctr_ok; \
-})
-#define hcounteren_ok(__which) \
-({ \
- bool __ctr_ok = true; \
- if (state.v) \
- __ctr_ok = (state.hcounteren->read() >> (__which & 31)) & 1; \
- __ctr_ok; \
-})
-#define scounteren_ok(__which) \
-({ \
- bool __ctr_ok = true; \
- if (extension_enabled('S') && state.prv < PRV_S) \
- __ctr_ok = (state.scounteren->read() >> (__which & 31)) & 1; \
- __ctr_ok; \
-})
-
reg_t res = 0;
#define ret(n) do { \
res = (n); \
@@ -1205,59 +1192,6 @@ reg_t processor_t::get_csr(int which, insn_t insn, bool write, bool peek)
if (!extension_enabled('V'))
break;
ret((VU.vxsat << VCSR_VXSAT_SHIFT) | (VU.vxrm << VCSR_VXRM_SHIFT));
- case CSR_INSTRET:
- case CSR_CYCLE:
- case CSR_HPMCOUNTER3 ... CSR_HPMCOUNTER31:
- if (!mcounteren_ok(which))
- goto throw_illegal;
- if (!hcounteren_ok(which))
- goto throw_virtual;
- if (!scounteren_ok(which)) {
- if (state.v)
- goto throw_virtual;
- else
- goto throw_illegal;
- }
- if (which == CSR_INSTRET || which == CSR_CYCLE)
- ret(state.minstret);
- else
- ret(0);
- case CSR_MINSTRET:
- case CSR_MCYCLE:
- case CSR_MHPMCOUNTER3 ... CSR_MHPMCOUNTER31:
- case CSR_MHPMEVENT3 ... CSR_MHPMEVENT31:
- if (which == CSR_MINSTRET || which == CSR_MCYCLE)
- ret(state.minstret);
- else
- ret(0);
- case CSR_INSTRETH:
- case CSR_CYCLEH:
- case CSR_HPMCOUNTER3H ... CSR_HPMCOUNTER31H:
- if (!mcounteren_ok(which) || xlen != 32)
- goto throw_illegal;
- if (!hcounteren_ok(which))
- goto throw_virtual;
- if (!scounteren_ok(which)) {
- if (state.v)
- goto throw_virtual;
- else
- goto throw_illegal;
- }
- if (which == CSR_INSTRETH || which == CSR_CYCLEH)
- ret(state.minstret >> 32);
- else
- ret(0);
- case CSR_MINSTRETH:
- case CSR_MCYCLEH:
- case CSR_MHPMCOUNTER3H ... CSR_MHPMCOUNTER31H:
- if (xlen == 32) {
- if (which == CSR_MINSTRETH || which == CSR_MCYCLEH)
- ret(state.minstret >> 32);
- else
- ret(0);
- }
- break;
- case CSR_MCOUNTINHIBIT: ret(0);
case CSR_MSTATUSH:
if (xlen == 32)
ret((state.mstatus->read() >> 32) & (MSTATUSH_SBE | MSTATUSH_MBE));
diff --git a/riscv/processor.h b/riscv/processor.h
index 3a240f8..8eef244 100644
--- a/riscv/processor.h
+++ b/riscv/processor.h
@@ -171,7 +171,7 @@ struct state_t
csr_t_p mtval;
csr_t_p mtvec;
csr_t_p mcause;
- reg_t minstret;
+ minstret_csr_t_p minstret;
mie_csr_t_p mie;
mip_csr_t_p mip;
csr_t_p medeleg;