aboutsummaryrefslogtreecommitdiff
path: root/riscv/processor.cc
diff options
context:
space:
mode:
Diffstat (limited to 'riscv/processor.cc')
-rw-r--r--riscv/processor.cc119
1 files changed, 82 insertions, 37 deletions
diff --git a/riscv/processor.cc b/riscv/processor.cc
index 7f2603a..6fe64ab 100644
--- a/riscv/processor.cc
+++ b/riscv/processor.cc
@@ -34,7 +34,8 @@ processor_t::processor_t(const char* isa_str, const char* priv_str,
const cfg_t *cfg,
simif_t* sim, uint32_t id, bool halt_on_reset,
FILE* log_file, std::ostream& sout_)
-: debug(false), halt_request(HR_NONE), isa(isa_str, priv_str), cfg(cfg), sim(sim), id(id), xlen(0),
+: debug(false), halt_request(HR_NONE), isa(isa_str, priv_str), cfg(cfg),
+ sim(sim), id(id), xlen(isa.get_max_xlen()),
histogram_enabled(false), log_commits_enabled(false),
log_file(log_file), sout_(sout_.rdbuf()), halt_on_reset(halt_on_reset),
in_wfi(false), check_triggers_icount(false),
@@ -62,7 +63,7 @@ processor_t::processor_t(const char* isa_str, const char* priv_str,
VU.vstart_alu = 0;
register_base_instructions();
- mmu = new mmu_t(sim, cfg->endianness, this);
+ mmu = new mmu_t(sim, cfg->endianness, this, cfg->cache_blocksz);
disassembler = new disassembler_t(&isa);
for (auto e : isa.get_extensions())
@@ -241,10 +242,79 @@ void processor_t::set_mmu_capability(int cap)
}
}
+reg_t processor_t::select_an_interrupt_with_default_priority(reg_t enabled_interrupts) const
+{
+ // nonstandard interrupts have highest priority
+ if (enabled_interrupts >> (IRQ_LCOF + 1))
+ enabled_interrupts = enabled_interrupts >> (IRQ_LCOF + 1) << (IRQ_LCOF + 1);
+ // standard interrupt priority is MEI, MSI, MTI, SEI, SSI, STI
+ else if (enabled_interrupts & MIP_MEIP)
+ enabled_interrupts = MIP_MEIP;
+ else if (enabled_interrupts & MIP_MSIP)
+ enabled_interrupts = MIP_MSIP;
+ else if (enabled_interrupts & MIP_MTIP)
+ enabled_interrupts = MIP_MTIP;
+ else if (enabled_interrupts & MIP_SEIP)
+ enabled_interrupts = MIP_SEIP;
+ else if (enabled_interrupts & MIP_SSIP)
+ enabled_interrupts = MIP_SSIP;
+ else if (enabled_interrupts & MIP_STIP)
+ enabled_interrupts = MIP_STIP;
+ else if (enabled_interrupts & MIP_LCOFIP)
+ enabled_interrupts = MIP_LCOFIP;
+ else if (enabled_interrupts & MIP_VSEIP)
+ enabled_interrupts = MIP_VSEIP;
+ else if (enabled_interrupts & MIP_VSSIP)
+ enabled_interrupts = MIP_VSSIP;
+ else if (enabled_interrupts & MIP_VSTIP)
+ enabled_interrupts = MIP_VSTIP;
+
+ return enabled_interrupts;
+}
+
+bool processor_t::is_handled_in_vs()
+{
+ reg_t pending_interrupts = state.mip->read() & state.mie->read();
+
+ const reg_t s_pending_interrupts = state.nonvirtual_sip->read() & state.nonvirtual_sie->read();
+ const reg_t vstopi = state.vstopi->read();
+ const reg_t vs_pending_interrupt = vstopi ? (reg_t(1) << get_field(vstopi, MTOPI_IID)) : 0; // SSIP -> VSSIP, etc
+
+ // M-ints have higher priority over HS-ints and VS-ints
+ const reg_t mie = get_field(state.mstatus->read(), MSTATUS_MIE);
+ const reg_t m_enabled = state.prv < PRV_M || (state.prv == PRV_M && mie);
+ reg_t enabled_interrupts = pending_interrupts & ~state.mideleg->read() & -m_enabled;
+ if (enabled_interrupts == 0) {
+ // HS-ints have higher priority over VS-ints
+ const reg_t deleg_to_hs = state.mideleg->read() & ~state.hideleg->read();
+ const reg_t sie = get_field(state.sstatus->read(), MSTATUS_SIE);
+ const reg_t hs_enabled = state.v || state.prv < PRV_S || (state.prv == PRV_S && sie);
+ enabled_interrupts = ((pending_interrupts & deleg_to_hs) | (s_pending_interrupts & ~state.hideleg->read())) & -hs_enabled;
+ if (state.v && enabled_interrupts == 0) {
+ // VS-ints have least priority and can only be taken with virt enabled
+ const reg_t vs_enabled = state.prv < PRV_S || (state.prv == PRV_S && sie);
+ enabled_interrupts = vs_pending_interrupt & -vs_enabled;
+ if (enabled_interrupts)
+ return true;
+ }
+ }
+ return false;
+}
+
void processor_t::take_interrupt(reg_t pending_interrupts)
{
+ reg_t s_pending_interrupts = 0;
+ reg_t vstopi = 0;
+ reg_t vs_pending_interrupt = 0;
+
+ if (extension_enable_table[EXT_SSAIA]) {
+ s_pending_interrupts = state.nonvirtual_sip->read() & state.nonvirtual_sie->read();
+ vstopi = state.vstopi->read();
+ vs_pending_interrupt = vstopi ? (reg_t(1) << get_field(vstopi, MTOPI_IID)) : 0;
+ }
+
// Do nothing if no pending interrupts
- if (!pending_interrupts) {
+ if (!pending_interrupts && !s_pending_interrupts && !vs_pending_interrupt) {
return;
}
@@ -260,46 +330,20 @@ void processor_t::take_interrupt(reg_t pending_interrupts)
const reg_t deleg_to_hs = state.mideleg->read() & ~state.hideleg->read();
const reg_t sie = get_field(state.sstatus->read(), MSTATUS_SIE);
const reg_t hs_enabled = state.v || state.prv < PRV_S || (state.prv == PRV_S && sie);
- enabled_interrupts = pending_interrupts & deleg_to_hs & -hs_enabled;
+ enabled_interrupts = ((pending_interrupts & deleg_to_hs) | (s_pending_interrupts & ~state.hideleg->read())) & -hs_enabled;
if (state.v && enabled_interrupts == 0) {
// VS-ints have least priority and can only be taken with virt enabled
- const reg_t deleg_to_vs = state.hideleg->read();
const reg_t vs_enabled = state.prv < PRV_S || (state.prv == PRV_S && sie);
- enabled_interrupts = pending_interrupts & deleg_to_vs & -vs_enabled;
+ enabled_interrupts = vs_pending_interrupt & -vs_enabled;
}
}
const bool nmie = !(state.mnstatus && !get_field(state.mnstatus->read(), MNSTATUS_NMIE));
if (!state.debug_mode && nmie && enabled_interrupts) {
- // nonstandard interrupts have highest priority
- if (enabled_interrupts >> (IRQ_LCOF + 1))
- enabled_interrupts = enabled_interrupts >> (IRQ_LCOF + 1) << (IRQ_LCOF + 1);
- // standard interrupt priority is MEI, MSI, MTI, SEI, SSI, STI
- else if (enabled_interrupts & MIP_MEIP)
- enabled_interrupts = MIP_MEIP;
- else if (enabled_interrupts & MIP_MSIP)
- enabled_interrupts = MIP_MSIP;
- else if (enabled_interrupts & MIP_MTIP)
- enabled_interrupts = MIP_MTIP;
- else if (enabled_interrupts & MIP_SEIP)
- enabled_interrupts = MIP_SEIP;
- else if (enabled_interrupts & MIP_SSIP)
- enabled_interrupts = MIP_SSIP;
- else if (enabled_interrupts & MIP_STIP)
- enabled_interrupts = MIP_STIP;
- else if (enabled_interrupts & MIP_LCOFIP)
- enabled_interrupts = MIP_LCOFIP;
- else if (enabled_interrupts & MIP_VSEIP)
- enabled_interrupts = MIP_VSEIP;
- else if (enabled_interrupts & MIP_VSSIP)
- enabled_interrupts = MIP_VSSIP;
- else if (enabled_interrupts & MIP_VSTIP)
- enabled_interrupts = MIP_VSTIP;
- else
- abort();
+ reg_t selected_interrupt = select_an_interrupt_with_default_priority(enabled_interrupts);
if (check_triggers_icount) TM.detect_icount_match();
- throw trap_t(((reg_t)1 << (isa.get_max_xlen() - 1)) | ctz(enabled_interrupts));
+ throw trap_t(((reg_t)1 << (isa.get_max_xlen() - 1)) | ctz(selected_interrupt));
}
}
@@ -327,7 +371,7 @@ void processor_t::set_privilege(reg_t prv, bool virt)
state.v_changed = state.v != state.prev_v;
}
-const char* processor_t::get_privilege_string()
+const char* processor_t::get_privilege_string() const
{
if (state.debug_mode)
return "D";
@@ -403,7 +447,8 @@ void processor_t::take_trap(trap_t& t, reg_t epc)
bool supv_double_trap = false;
if (interrupt) {
vsdeleg = (curr_virt && state.prv <= PRV_S) ? state.hideleg->read() : 0;
- hsdeleg = (state.prv <= PRV_S) ? state.mideleg->read() : 0;
+ vsdeleg >>= 1;
+ hsdeleg = (state.prv <= PRV_S) ? (state.mideleg->read() | state.nonvirtual_sip->read()) : 0;
bit &= ~((reg_t)1 << (max_xlen - 1));
} else {
vsdeleg = (curr_virt && state.prv <= PRV_S) ? (state.medeleg->read() & state.hedeleg->read()) : 0;
@@ -420,9 +465,9 @@ void processor_t::take_trap(trap_t& t, reg_t epc)
if (supv_double_trap)
vsdeleg = hsdeleg = 0;
}
- if (state.prv <= PRV_S && bit < max_xlen && ((vsdeleg >> bit) & 1)) {
+ if ((state.prv <= PRV_S && bit < max_xlen && ((vsdeleg >> bit) & 1)) || (state.v && interrupt && is_handled_in_vs())) {
// Handle the trap in VS-mode
- const reg_t adjusted_cause = interrupt ? bit - 1 : bit; // VSSIP -> SSIP, etc
+ const reg_t adjusted_cause = bit;
reg_t vector = (state.vstvec->read() & 1) && interrupt ? 4 * adjusted_cause : 0;
state.pc = (state.vstvec->read() & ~(reg_t)1) + vector;
state.vscause->write(adjusted_cause | (interrupt ? interrupt_bit : 0));