diff options
-rw-r--r-- | riscv/csrs.cc | 122 | ||||
-rw-r--r-- | riscv/csrs.h | 74 | ||||
-rw-r--r-- | riscv/execute.cc | 2 | ||||
-rw-r--r-- | riscv/processor.cc | 128 | ||||
-rw-r--r-- | riscv/processor.h | 2 |
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; |