diff --git a/riscv/clint.cc b/riscv/clint.cc index 72d1bbeb..3f2d4d7d 100644 --- a/riscv/clint.cc +++ b/riscv/clint.cc @@ -82,6 +82,7 @@ void clint_t::increment(reg_t inc) mtime += inc; } for (size_t i = 0; i < procs.size(); i++) { + procs[i]->state.time->sync(mtime); procs[i]->state.mip->backdoor_write_with_mask(MIP_MTIP, 0); if (mtime >= mtimecmp[i]) procs[i]->state.mip->backdoor_write_with_mask(MIP_MTIP, MIP_MTIP); diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 7a63cbcd..3cb6af2a 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -525,6 +525,10 @@ bool rv32_low_csr_t::unlogged_write(const reg_t val) noexcept { return orig->unlogged_write((orig->written_value() >> 32 << 32) | (val & 0xffffffffU)); } +reg_t rv32_low_csr_t::written_value() const noexcept { + return orig->written_value() & 0xffffffffU; +} + // implement class rv32_high_csr_t rv32_high_csr_t::rv32_high_csr_t(processor_t* const proc, const reg_t addr, csr_t_p orig): csr_t(proc, addr), @@ -543,6 +547,10 @@ bool rv32_high_csr_t::unlogged_write(const reg_t val) noexcept { return orig->unlogged_write((orig->written_value() << 32 >> 32) | ((val & 0xffffffffU) << 32)); } +reg_t rv32_high_csr_t::written_value() const noexcept { + return (orig->written_value() >> 32) & 0xffffffffU; +} + // implement class sstatus_csr_t sstatus_csr_t::sstatus_csr_t(processor_t* const proc, sstatus_proxy_csr_t_p orig, vsstatus_csr_t_p virt): virtualized_csr_t(proc, orig, virt), @@ -934,10 +942,7 @@ void wide_counter_csr_t::bump(const reg_t howmuch) noexcept { } bool wide_counter_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; + 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. @@ -951,25 +956,23 @@ reg_t wide_counter_csr_t::written_value() const noexcept { return this->val + 1; } -void wide_counter_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); -} - -counter_top_csr_t::counter_top_csr_t(processor_t* const proc, const reg_t addr, wide_counter_csr_t_p parent): +// implement class time_counter_csr_t +time_counter_csr_t::time_counter_csr_t(processor_t* const proc, const reg_t addr): csr_t(proc, addr), - parent(parent) { + shadow_val(0) { } -reg_t counter_top_csr_t::read() const noexcept { - return parent->read() >> 32; +reg_t time_counter_csr_t::read() const noexcept { + // reading the time CSR in VS or VU mode returns the sum of the contents of + // htimedelta and the actual value of time. + if (state->v) + return shadow_val + state->htimedelta->read(); + else + return shadow_val; } -bool counter_top_csr_t::unlogged_write(const reg_t val) noexcept { - parent->write_upper_half(val); - return true; +void time_counter_csr_t::sync(const reg_t val) noexcept { + shadow_val = val; } proxy_csr_t::proxy_csr_t(processor_t* const proc, const reg_t addr, csr_t_p delegate): @@ -1008,11 +1011,13 @@ bool counter_proxy_csr_t::myenable(csr_t_p counteren) const noexcept { } 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 (write || !mctr_ok) + if (!mctr_ok) throw trap_illegal_instruction(insn.bits()); if (!hctr_ok) throw trap_virtual_instruction(insn.bits()); diff --git a/riscv/csrs.h b/riscv/csrs.h index 2ca1d992..c979942e 100644 --- a/riscv/csrs.h +++ b/riscv/csrs.h @@ -262,6 +262,7 @@ class rv32_low_csr_t: public csr_t { virtual void verify_permissions(insn_t insn, bool write) const override; protected: virtual bool unlogged_write(const reg_t val) noexcept override; + virtual reg_t written_value() const noexcept override; private: csr_t_p orig; }; @@ -273,6 +274,7 @@ class rv32_high_csr_t: public csr_t { virtual void verify_permissions(insn_t insn, bool write) const override; protected: virtual bool unlogged_write(const reg_t val) noexcept override; + virtual reg_t written_value() const noexcept override; private: csr_t_p orig; }; @@ -500,7 +502,6 @@ class wide_counter_csr_t: public csr_t { // 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; @@ -510,18 +511,20 @@ class wide_counter_csr_t: public csr_t { typedef std::shared_ptr wide_counter_csr_t_p; -// A simple proxy to read/write the upper half of minstret/mcycle -class counter_top_csr_t: public csr_t { +class time_counter_csr_t: public csr_t { public: - counter_top_csr_t(processor_t* const proc, const reg_t addr, wide_counter_csr_t_p parent); + time_counter_csr_t(processor_t* const proc, const reg_t addr); virtual reg_t read() const noexcept override; + + void sync(const reg_t val) noexcept; + protected: - virtual bool unlogged_write(const reg_t val) noexcept override; + virtual bool unlogged_write(const reg_t val) noexcept override { return false; }; private: - wide_counter_csr_t_p parent; + reg_t shadow_val; }; -typedef std::shared_ptr counter_top_csr_t_p; +typedef std::shared_ptr time_counter_csr_t_p; // For a CSR that is an alias of another class proxy_csr_t: public csr_t { diff --git a/riscv/processor.cc b/riscv/processor.cc index df82f9ec..6d0d3499 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -202,20 +202,29 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) csrmap[CSR_MSCRATCH] = std::make_shared(proc, CSR_MSCRATCH, 0); csrmap[CSR_MTVEC] = mtvec = std::make_shared(proc, CSR_MTVEC); csrmap[CSR_MCAUSE] = mcause = std::make_shared(proc, CSR_MCAUSE); - csrmap[CSR_MINSTRET] = minstret = std::make_shared(proc, CSR_MINSTRET); - csrmap[CSR_MCYCLE] = mcycle = std::make_shared(proc, CSR_MCYCLE); + minstret = std::make_shared(proc, CSR_MINSTRET); + mcycle = std::make_shared(proc, CSR_MCYCLE); + time = std::make_shared(proc, CSR_TIME); if (proc->extension_enabled_const(EXT_ZICNTR)) { csrmap[CSR_INSTRET] = std::make_shared(proc, CSR_INSTRET, minstret); csrmap[CSR_CYCLE] = std::make_shared(proc, CSR_CYCLE, mcycle); + csrmap[CSR_TIME] = std::make_shared(proc, CSR_TIME, time); } if (xlen == 32) { - counter_top_csr_t_p minstreth, mcycleh; - csrmap[CSR_MINSTRETH] = minstreth = std::make_shared(proc, CSR_MINSTRETH, minstret); - csrmap[CSR_MCYCLEH] = mcycleh = std::make_shared(proc, CSR_MCYCLEH, mcycle); + csr_t_p minstreth, mcycleh; + csrmap[CSR_MINSTRET] = std::make_shared(proc, CSR_MINSTRET, minstret); + csrmap[CSR_MINSTRETH] = minstreth = std::make_shared(proc, CSR_MINSTRETH, minstret); + csrmap[CSR_MCYCLE] = std::make_shared(proc, CSR_MCYCLE, mcycle); + csrmap[CSR_MCYCLEH] = mcycleh = std::make_shared(proc, CSR_MCYCLEH, mcycle); if (proc->extension_enabled_const(EXT_ZICNTR)) { + auto timeh = std::make_shared(proc, CSR_TIMEH, time); csrmap[CSR_INSTRETH] = std::make_shared(proc, CSR_INSTRETH, minstreth); csrmap[CSR_CYCLEH] = std::make_shared(proc, CSR_CYCLEH, mcycleh); + csrmap[CSR_TIMEH] = std::make_shared(proc, CSR_TIMEH, timeh); } + } else { + csrmap[CSR_MINSTRET] = minstret; + csrmap[CSR_MCYCLE] = mcycle; } for (reg_t i = 3; i <= 31; ++i) { const reg_t which_mevent = CSR_MHPMEVENT3 + i - 3; @@ -344,6 +353,13 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) (1 << CAUSE_STORE_PAGE_FAULT); csrmap[CSR_HEDELEG] = hedeleg = std::make_shared(proc, CSR_HEDELEG, hedeleg_mask, 0); csrmap[CSR_HCOUNTEREN] = hcounteren = std::make_shared(proc, CSR_HCOUNTEREN, counteren_mask, 0); + htimedelta = std::make_shared(proc, CSR_HTIMEDELTA, 0); + if (xlen == 32) { + csrmap[CSR_HTIMEDELTA] = std::make_shared(proc, CSR_HTIMEDELTA, htimedelta); + csrmap[CSR_HTIMEDELTAH] = std::make_shared(proc, CSR_HTIMEDELTAH, htimedelta); + } else { + csrmap[CSR_HTIMEDELTA] = htimedelta; + } csrmap[CSR_HTVAL] = htval = std::make_shared(proc, CSR_HTVAL, 0); csrmap[CSR_HTINST] = htinst = std::make_shared(proc, CSR_HTINST, 0); csrmap[CSR_HGATP] = hgatp = std::make_shared(proc, CSR_HGATP); diff --git a/riscv/processor.h b/riscv/processor.h index 347ae160..b415402d 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -191,6 +191,9 @@ struct state_t csr_t_p sstateen[4]; csr_t_p hstateen[4]; + csr_t_p htimedelta; + time_counter_csr_t_p time; + bool serialized; // whether timer CSRs are in a well-defined state // When true, execute a single instruction and then enter debug mode. This