X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;ds=sidebyside;f=riscv%2Fprocessor.cc;h=6eb5ddb9fc8c975e1efadca13f66f2e96a1d4d23;hb=980a0121e0b7244020bba7a8f422d97e7886fb08;hp=c11657556f6e2b3539c3319500dde2ad4aff4978;hpb=07d126d57ede2d3c7e28fddd08338b2171f884bb;p=riscv-isa-sim.git diff --git a/riscv/processor.cc b/riscv/processor.cc index c116575..6eb5ddb 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -1,320 +1,549 @@ +// See LICENSE for license details. + #include "processor.h" +#include "extension.h" #include "common.h" #include "config.h" #include "sim.h" +#include "mmu.h" +#include "htif.h" #include "disasm.h" -#include +#include #include #include #include #include +#include +#include +#include + +#undef STATE +#define STATE state -processor_t::processor_t(sim_t* _sim, mmu_t* _mmu, uint32_t _id) - : sim(*_sim), mmu(*_mmu), id(_id), utidx(0) +processor_t::processor_t(const char* isa, sim_t* sim, uint32_t id) + : sim(sim), ext(NULL), disassembler(new disassembler_t), + id(id), run(false), debug(false) { + parse_isa_string(isa); + + mmu = new mmu_t(sim, this); + reset(true); - // create microthreads - for (int i=0; iset_debug(value); +} - vlmax = std::min(vlmax, MAX_UTS); +void processor_t::set_histogram(bool value) +{ + histogram_enabled = value; +#ifndef RISCV_ENABLE_HISTOGRAM + if (value) { + fprintf(stderr, "PC Histogram support has not been properly enabled;"); + fprintf(stderr, " please re-build the riscv-isa-run project using \"configure --enable-histogram\".\n"); + } +#endif } -void processor_t::setvl(int vlapp) +void processor_t::reset(bool value) { - vl = std::min(vlmax, vlapp); + if (run == !value) + return; + run = !value; + + state.reset(); + set_csr(CSR_MSTATUS, state.mstatus); + + if (ext) + ext->reset(); // reset the extension } -void processor_t::take_interrupt() +void processor_t::raise_interrupt(reg_t which) { - uint32_t interrupts = interrupts_pending; - interrupts &= (sr & SR_IM) >> SR_IM_SHIFT; + throw trap_t(((reg_t)1 << (max_xlen-1)) | which); +} - if(interrupts && (sr & SR_ET)) - for(int i = 0; ; i++, interrupts >>= 1) - if(interrupts & 1) - throw interrupt_t(i); +static int ctz(reg_t val) +{ + int res = 0; + if (val) + while ((val & 1) == 0) + val >>= 1, res++; + return res; } -void processor_t::step(size_t n, bool noisy) +void processor_t::take_interrupt() { - if(!run) - return; + reg_t pending_interrupts = state.mip & state.mie; - size_t i = 0; - while(1) try - { - take_interrupt(); - - mmu_t& _mmu = mmu; - insn_t insn; - insn_func_t func; - reg_t npc = pc; - - // execute_insn fetches and executes one instruction - #define execute_insn(noisy) \ - do { \ - insn = _mmu.load_insn(npc, sr & SR_EC, &func); \ - if(noisy) disasm(insn,pc); \ - npc = func(this, insn, npc); \ - pc = npc; \ - } while(0) - - if(noisy) for( ; i < n; i++) // print out instructions as we go - execute_insn(true); - else - { - // unrolled for speed - for( ; n > 3 && i < n-3; i+=4) - { - execute_insn(false); - execute_insn(false); - execute_insn(false); - execute_insn(false); - } - for( ; i < n; i++) - execute_insn(false); - } + reg_t mie = get_field(state.mstatus, MSTATUS_MIE); + reg_t m_enabled = state.prv < PRV_M || (state.prv == PRV_M && mie); + reg_t enabled_interrupts = pending_interrupts & ~state.mideleg & -m_enabled; - break; - } - catch(trap_t t) - { - // an exception occurred in the target processor - i++; - take_trap(t,noisy); - } - catch(interrupt_t t) - { - i++; - take_trap((1ULL << (8*sizeof(reg_t)-1)) + t.i, noisy); - } - catch(vt_command_t cmd) - { - // this microthread has finished - i++; - assert(cmd == vt_command_stop); - break; - } + reg_t sie = get_field(state.mstatus, MSTATUS_SIE); + reg_t s_enabled = state.prv < PRV_S || (state.prv == PRV_S && sie); + enabled_interrupts |= pending_interrupts & state.mideleg & -s_enabled; + + if (enabled_interrupts) + raise_interrupt(ctz(enabled_interrupts)); +} - cycle += i; +static bool validate_priv(reg_t priv) +{ + return priv == PRV_U || priv == PRV_S || priv == PRV_M; +} - // update timer and possibly register a timer interrupt - uint32_t old_count = count; - count += i; - if(old_count < compare && uint64_t(old_count) + i >= compare) - interrupts_pending |= 1 << IRQ_TIMER; +void processor_t::set_privilege(reg_t prv) +{ + assert(validate_priv(prv)); + mmu->flush_tlb(); + state.prv = prv; } -void processor_t::take_trap(reg_t t, bool noisy) +void processor_t::take_trap(trap_t& t, reg_t epc) { - if(noisy) - { - if ((sreg_t)t < 0) - printf("core %3d: interrupt %lld, pc 0x%016llx\n", - id, (long long)(t << 1 >> 1), (unsigned long long)pc); - else - printf("core %3d: trap %s, pc 0x%016llx\n", - id, trap_name(trap_t(t)), (unsigned long long)pc); + if (debug) + fprintf(stderr, "core %3d: exception %s, epc 0x%016" PRIx64 "\n", + id, t.name(), epc); + + // by default, trap to M-mode, unless delegated to S-mode + reg_t bit = t.cause(); + reg_t deleg = state.medeleg; + if (bit & ((reg_t)1 << (max_xlen-1))) + deleg = state.mideleg, bit &= ~((reg_t)1 << (max_xlen-1)); + if (state.prv <= PRV_S && bit < max_xlen && ((deleg >> bit) & 1)) { + // handle the trap in S-mode + state.pc = state.stvec; + state.scause = t.cause(); + state.sepc = epc; + if (t.has_badaddr()) + state.sbadaddr = t.get_badaddr(); + + reg_t s = state.mstatus; + s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_UIE << state.prv)); + s = set_field(s, MSTATUS_SPP, state.prv); + s = set_field(s, MSTATUS_SIE, 0); + set_csr(CSR_MSTATUS, s); + set_privilege(PRV_S); + } else { + state.pc = state.mtvec; + state.mcause = t.cause(); + state.mepc = epc; + if (t.has_badaddr()) + state.mbadaddr = t.get_badaddr(); + + reg_t s = state.mstatus; + s = set_field(s, MSTATUS_MPIE, get_field(s, MSTATUS_UIE << state.prv)); + s = set_field(s, MSTATUS_MPP, state.prv); + s = set_field(s, MSTATUS_MIE, 0); + set_csr(CSR_MSTATUS, s); + set_privilege(PRV_M); } - // switch to supervisor, set previous supervisor bit, disable traps - set_pcr(PCR_SR, (((sr & ~SR_ET) | SR_S) & ~SR_PS) | ((sr & SR_S) ? SR_PS : 0)); - cause = t; - epc = pc; - pc = evec; - badvaddr = mmu.get_badvaddr(); + yield_load_reservation(); } -void processor_t::deliver_ipi() +void processor_t::disasm(insn_t insn) { - if (run) - set_pcr(PCR_CLR_IPI, 1); + uint64_t bits = insn.bits() & ((1ULL << (8 * insn_length(insn.bits()))) - 1); + fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIx64 ") %s\n", + id, state.pc, bits, disassembler->disassemble(insn).c_str()); } -void processor_t::disasm(insn_t insn, reg_t pc) +static bool validate_vm(int max_xlen, reg_t vm) { - // the disassembler is stateless, so we share it - static disassembler disasm; - printf("core %3d: 0x%016llx (0x%08x) %s\n", id, (unsigned long long)pc, - insn.bits, disasm.disassemble(insn).c_str()); + if (max_xlen == 64 && (vm == VM_SV39 || vm == VM_SV48)) + return true; + if (max_xlen == 32 && vm == VM_SV32) + return true; + return vm == VM_MBARE; } -void processor_t::set_pcr(int which, reg_t val) +void processor_t::set_csr(int which, reg_t val) { + val = zext_xlen(val); + reg_t delegable_ints = MIP_SSIP | MIP_STIP | (1 << IRQ_HOST) | (1 << IRQ_COP); + reg_t all_ints = delegable_ints | MIP_MSIP | MIP_MTIP; switch (which) { - case PCR_SR: - sr = val & ~SR_ZERO; // clear SR bits that read as zero -#ifndef RISCV_ENABLE_64BIT - sr &= ~(SR_S64 | SR_U64); -#endif -#ifndef RISCV_ENABLE_FPU - sr &= ~SR_EF; -#endif -#ifndef RISCV_ENABLE_RVC - sr &= ~SR_EC; -#endif -#ifndef RISCV_ENABLE_VEC - sr &= ~SR_EV; -#endif - // update MMU state and flush TLB - mmu.set_vm_enabled(sr & SR_VM); - mmu.set_supervisor(sr & SR_S); - mmu.flush_tlb(); - // set the fixed-point register length - xprlen = ((sr & SR_S) ? (sr & SR_S64) : (sr & SR_U64)) ? 64 : 32; - break; - case PCR_EPC: - epc = val; + case CSR_FFLAGS: + dirty_fp_state; + state.fflags = val & (FSR_AEXC >> FSR_AEXC_SHIFT); break; - case PCR_EVEC: - evec = val; + case CSR_FRM: + dirty_fp_state; + state.frm = val & (FSR_RD >> FSR_RD_SHIFT); break; - case PCR_COUNT: - count = val; + case CSR_FCSR: + dirty_fp_state; + state.fflags = (val & FSR_AEXC) >> FSR_AEXC_SHIFT; + state.frm = (val & FSR_RD) >> FSR_RD_SHIFT; break; - case PCR_COMPARE: - interrupts_pending &= ~(1 << IRQ_TIMER); - compare = val; - break; - case PCR_PTBR: - mmu.set_ptbr(val); + case CSR_MSTATUS: { + if ((val ^ state.mstatus) & + (MSTATUS_VM | MSTATUS_MPP | MSTATUS_MPRV | MSTATUS_PUM)) + mmu->flush_tlb(); + + reg_t mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE + | MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_PUM + | (ext ? MSTATUS_XS : 0); + + if (validate_vm(max_xlen, get_field(val, MSTATUS_VM))) + mask |= MSTATUS_VM; + if (validate_priv(get_field(val, MSTATUS_MPP))) + mask |= MSTATUS_MPP; + + state.mstatus = (state.mstatus & ~mask) | (val & mask); + + bool dirty = (state.mstatus & MSTATUS_FS) == MSTATUS_FS; + dirty |= (state.mstatus & MSTATUS_XS) == MSTATUS_XS; + if (max_xlen == 32) + state.mstatus = set_field(state.mstatus, MSTATUS32_SD, dirty); + else + state.mstatus = set_field(state.mstatus, MSTATUS64_SD, dirty); + + // spike supports the notion of xlen < max_xlen, but current priv spec + // doesn't provide a mechanism to run RV32 software on an RV64 machine + xlen = max_xlen; break; - case PCR_SEND_IPI: - sim.send_ipi(val); + } + case CSR_MIP: { + reg_t mask = MIP_SSIP | MIP_STIP | MIP_MSIP; + state.mip = (state.mip & ~mask) | (val & mask); break; - case PCR_CLR_IPI: - if (val & 1) - interrupts_pending |= (1 << IRQ_IPI); - else - interrupts_pending &= ~(1 << IRQ_IPI); + } + case CSR_MIPI: + state.mip = set_field(state.mip, MIP_MSIP, val & 1); break; - case PCR_K0: - pcr_k0 = val; + case CSR_MIE: + state.mie = (state.mie & ~all_ints) | (val & all_ints); break; - case PCR_K1: - pcr_k1 = val; + case CSR_MIDELEG: + state.mideleg = (state.mideleg & ~delegable_ints) | (val & delegable_ints); break; - case PCR_VECBANK: - vecbanks = val & 0xff; - vecbanks_count = __builtin_popcountll(vecbanks); + case CSR_MEDELEG: { + reg_t mask = 0; +#define DECLARE_CAUSE(name, value) mask |= 1ULL << (value); +#include "encoding.h" +#undef DECLARE_CAUSE + state.medeleg = (state.medeleg & ~mask) | (val & mask); break; - case PCR_TOHOST: - fromhost = 0; - tohost = val; + } + case CSR_MUCOUNTEREN: + state.mucounteren = val & 7; break; - case PCR_FROMHOST: - fromhost = val; - tohost = 0; + case CSR_MSCOUNTEREN: + state.mscounteren = val & 7; break; + case CSR_SSTATUS: { + reg_t mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_SPP | SSTATUS_FS + | SSTATUS_XS | SSTATUS_PUM; + return set_csr(CSR_MSTATUS, (state.mstatus & ~mask) | (val & mask)); + } + case CSR_SIP: + return set_csr(CSR_MIP, + (state.mip & ~state.mideleg) | (val & state.mideleg)); + case CSR_SIE: + return set_csr(CSR_MIE, + (state.mie & ~state.mideleg) | (val & state.mideleg)); + case CSR_SEPC: state.sepc = val; break; + case CSR_STVEC: state.stvec = val >> 2 << 2; break; + case CSR_SPTBR: state.sptbr = val; break; + case CSR_SSCRATCH: state.sscratch = val; break; + case CSR_SCAUSE: state.scause = val; break; + case CSR_SBADADDR: state.sbadaddr = val; break; + case CSR_MEPC: state.mepc = val; break; + case CSR_MTVEC: state.mtvec = val >> 2 << 2; break; + case CSR_MSCRATCH: state.mscratch = val; break; + case CSR_MCAUSE: state.mcause = val; break; + case CSR_MBADADDR: state.mbadaddr = val; break; } } -reg_t processor_t::get_pcr(int which) +reg_t processor_t::get_csr(int which) { switch (which) { - case PCR_SR: - return sr; - case PCR_EPC: - return epc; - case PCR_BADVADDR: - return badvaddr; - case PCR_EVEC: - return evec; - case PCR_COUNT: - return count; - case PCR_COMPARE: - return compare; - case PCR_CAUSE: - return cause; - case PCR_PTBR: - return mmu.get_ptbr(); - case PCR_COREID: - return id; - case PCR_IMPL: - return 1; - case PCR_K0: - return pcr_k0; - case PCR_K1: - return pcr_k1; - case PCR_VECBANK: - return vecbanks; - case PCR_TOHOST: - return tohost; - case PCR_FROMHOST: - return fromhost; + case CSR_FFLAGS: + require_fp; + if (!supports_extension('F')) + break; + return state.fflags; + case CSR_FRM: + require_fp; + if (!supports_extension('F')) + break; + return state.frm; + case CSR_FCSR: + require_fp; + if (!supports_extension('F')) + break; + return (state.fflags << FSR_AEXC_SHIFT) | (state.frm << FSR_RD_SHIFT); + case CSR_TIME: + case CSR_INSTRET: + case CSR_CYCLE: + if ((state.mucounteren >> (which & (xlen-1))) & 1) + return get_csr(which + (CSR_MCYCLE - CSR_CYCLE)); + break; + case CSR_STIME: + case CSR_SINSTRET: + case CSR_SCYCLE: + if ((state.mscounteren >> (which & (xlen-1))) & 1) + return get_csr(which + (CSR_MCYCLE - CSR_SCYCLE)); + break; + case CSR_MUCOUNTEREN: return state.mucounteren; + case CSR_MSCOUNTEREN: return state.mscounteren; + case CSR_MUCYCLE_DELTA: return 0; + case CSR_MUTIME_DELTA: return 0; + case CSR_MUINSTRET_DELTA: return 0; + case CSR_MSCYCLE_DELTA: return 0; + case CSR_MSTIME_DELTA: return 0; + case CSR_MSINSTRET_DELTA: return 0; + case CSR_MUCYCLE_DELTAH: if (xlen > 32) break; else return 0; + case CSR_MUTIME_DELTAH: if (xlen > 32) break; else return 0; + case CSR_MUINSTRET_DELTAH: if (xlen > 32) break; else return 0; + case CSR_MSCYCLE_DELTAH: if (xlen > 32) break; else return 0; + case CSR_MSTIME_DELTAH: if (xlen > 32) break; else return 0; + case CSR_MSINSTRET_DELTAH: if (xlen > 32) break; else return 0; + case CSR_MCYCLE: return state.minstret; + case CSR_MINSTRET: return state.minstret; + case CSR_MCYCLEH: if (xlen > 32) break; else return state.minstret >> 32; + case CSR_MINSTRETH: if (xlen > 32) break; else return state.minstret >> 32; + case CSR_SSTATUS: { + reg_t mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_SPP | SSTATUS_FS + | SSTATUS_XS | SSTATUS_PUM; + reg_t sstatus = state.mstatus & mask; + if ((sstatus & SSTATUS_FS) == SSTATUS_FS || + (sstatus & SSTATUS_XS) == SSTATUS_XS) + sstatus |= (xlen == 32 ? SSTATUS32_SD : SSTATUS64_SD); + return sstatus; + } + case CSR_SIP: return state.mip & state.mideleg; + case CSR_SIE: return state.mie & state.mideleg; + case CSR_SEPC: return state.sepc; + case CSR_SBADADDR: return state.sbadaddr; + case CSR_STVEC: return state.stvec; + case CSR_SCAUSE: + if (max_xlen > xlen) + return state.scause | ((state.scause >> (max_xlen-1)) << (xlen-1)); + return state.scause; + case CSR_SPTBR: return state.sptbr; + case CSR_SASID: return 0; + case CSR_SSCRATCH: return state.sscratch; + case CSR_MSTATUS: return state.mstatus; + case CSR_MIP: return state.mip; + case CSR_MIPI: return 0; + case CSR_MIE: return state.mie; + case CSR_MEPC: return state.mepc; + case CSR_MSCRATCH: return state.mscratch; + case CSR_MCAUSE: return state.mcause; + case CSR_MBADADDR: return state.mbadaddr; + case CSR_MISA: return isa; + case CSR_MARCHID: return 0; + case CSR_MIMPID: return 0; + case CSR_MVENDORID: return 0; + case CSR_MHARTID: return id; + case CSR_MTVEC: return state.mtvec; + case CSR_MEDELEG: return state.medeleg; + case CSR_MIDELEG: return state.mideleg; + } + throw trap_illegal_instruction(); +} + +reg_t illegal_instruction(processor_t* p, insn_t insn, reg_t pc) +{ + throw trap_illegal_instruction(); +} + +insn_func_t processor_t::decode_insn(insn_t insn) +{ + // look up opcode in hash table + size_t idx = insn.bits() % OPCODE_CACHE_SIZE; + insn_desc_t desc = opcode_cache[idx]; + + if (unlikely(insn.bits() != desc.match)) { + // fall back to linear search + insn_desc_t* p = &instructions[0]; + while ((insn.bits() & p->mask) != p->match) + p++; + desc = *p; + + if (p->mask != 0 && p > &instructions[0]) { + if (p->match != (p-1)->match && p->match != (p+1)->match) { + // move to front of opcode list to reduce miss penalty + while (--p >= &instructions[0]) + *(p+1) = *p; + instructions[0] = desc; + } + } + + opcode_cache[idx] = desc; + opcode_cache[idx].match = insn.bits(); + } + + return xlen == 64 ? desc.rv64 : desc.rv32; +} + +void processor_t::register_insn(insn_desc_t desc) +{ + instructions.push_back(desc); +} + +void processor_t::build_opcode_map() +{ + struct cmp { + bool operator()(const insn_desc_t& lhs, const insn_desc_t& rhs) { + if (lhs.match == rhs.match) + return lhs.mask > rhs.mask; + return lhs.match > rhs.match; + } + }; + std::sort(instructions.begin(), instructions.end(), cmp()); + + for (size_t i = 0; i < OPCODE_CACHE_SIZE; i++) + opcode_cache[i] = {1, 0, &illegal_instruction, &illegal_instruction}; +} + +void processor_t::register_extension(extension_t* x) +{ + for (auto insn : x->get_instructions()) + register_insn(insn); + build_opcode_map(); + for (auto disasm_insn : x->get_disasms()) + disassembler->add_insn(disasm_insn); + if (ext != NULL) + throw std::logic_error("only one extension may be registered"); + ext = x; + x->set_processor(this); +} + +void processor_t::register_base_instructions() +{ + #define DECLARE_INSN(name, match, mask) \ + insn_bits_t name##_match = (match), name##_mask = (mask); + #include "encoding.h" + #undef DECLARE_INSN + + #define DEFINE_INSN(name) \ + REGISTER_INSN(this, name, name##_match, name##_mask) + #include "insn_list.h" + #undef DEFINE_INSN + + register_insn({0, 0, &illegal_instruction, &illegal_instruction}); + build_opcode_map(); +} + +bool processor_t::load(reg_t addr, size_t len, uint8_t* bytes) +{ + try { + auto res = get_csr(addr / (max_xlen / 8)); + memcpy(bytes, &res, len); + return true; + } catch (trap_illegal_instruction& t) { + return false; + } +} + +bool processor_t::store(reg_t addr, size_t len, const uint8_t* bytes) +{ + try { + reg_t value = 0; + memcpy(&value, bytes, len); + set_csr(addr / (max_xlen / 8), value); + return true; + } catch (trap_illegal_instruction& t) { + return false; } - return -1; }