X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=riscv%2Fprocessor.h;h=1b94b1f710e0c69d22e276318331a8e004d6e3cf;hb=12714e371e9b8ce2efcf0e77347ed1b33c8de27b;hp=927f06c74c85aaeaa55105a6ac1dda0142992276;hpb=07d126d57ede2d3c7e28fddd08338b2171f884bb;p=riscv-isa-sim.git diff --git a/riscv/processor.h b/riscv/processor.h index 927f06c..1b94b1f 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -1,108 +1,339 @@ +// See LICENSE for license details. #ifndef _RISCV_PROCESSOR_H #define _RISCV_PROCESSOR_H #include "decode.h" -#include -#include "trap.h" #include "config.h" - -#define MAX_UTS 2048 +#include "devices.h" +#include +#include +#include +#include "debug_rom/debug_rom_defines.h" class processor_t; class mmu_t; typedef reg_t (*insn_func_t)(processor_t*, insn_t, reg_t); class sim_t; +class trap_t; +class extension_t; +class disassembler_t; + +struct insn_desc_t +{ + insn_bits_t match; + insn_bits_t mask; + insn_func_t rv32; + insn_func_t rv64; +}; + +struct commit_log_reg_t +{ + reg_t addr; + freg_t data; +}; + +typedef struct +{ + uint8_t prv; + bool step; + bool ebreakm; + bool ebreakh; + bool ebreaks; + bool ebreaku; + bool halt; + uint8_t cause; +} dcsr_t; + +typedef enum +{ + ACTION_DEBUG_EXCEPTION = MCONTROL_ACTION_DEBUG_EXCEPTION, + ACTION_DEBUG_MODE = MCONTROL_ACTION_DEBUG_MODE, + ACTION_TRACE_START = MCONTROL_ACTION_TRACE_START, + ACTION_TRACE_STOP = MCONTROL_ACTION_TRACE_STOP, + ACTION_TRACE_EMIT = MCONTROL_ACTION_TRACE_EMIT +} mcontrol_action_t; + +typedef enum +{ + MATCH_EQUAL = MCONTROL_MATCH_EQUAL, + MATCH_NAPOT = MCONTROL_MATCH_NAPOT, + MATCH_GE = MCONTROL_MATCH_GE, + MATCH_LT = MCONTROL_MATCH_LT, + MATCH_MASK_LOW = MCONTROL_MATCH_MASK_LOW, + MATCH_MASK_HIGH = MCONTROL_MATCH_MASK_HIGH +} mcontrol_match_t; + +typedef struct +{ + uint8_t type; + bool dmode; + uint8_t maskmax; + bool select; + bool timing; + mcontrol_action_t action; + bool chain; + mcontrol_match_t match; + bool m; + bool h; + bool s; + bool u; + bool execute; + bool store; + bool load; +} mcontrol_t; + +// architectural state of a RISC-V hart +struct state_t +{ + void reset(); + + static const int num_triggers = 4; + + reg_t pc; + regfile_t XPR; + regfile_t FPR; + + // control and status registers + reg_t prv; // TODO: Can this be an enum instead? + reg_t mstatus; + reg_t mepc; + reg_t mtval; + reg_t mscratch; + reg_t mtvec; + reg_t mcause; + reg_t minstret; + reg_t mie; + reg_t mip; + reg_t medeleg; + reg_t mideleg; + uint32_t mcounteren; + uint32_t scounteren; + reg_t sepc; + reg_t stval; + reg_t sscratch; + reg_t stvec; + reg_t satp; + reg_t scause; + reg_t dpc; + reg_t dscratch; + dcsr_t dcsr; + reg_t tselect; + mcontrol_t mcontrol[num_triggers]; + reg_t tdata2[num_triggers]; + + uint32_t fflags; + uint32_t frm; + bool serialized; // whether timer CSRs are in a well-defined state + + // When true, execute a single instruction and then enter debug mode. This + // can only be set by executing dret. + enum { + STEP_NONE, + STEP_STEPPING, + STEP_STEPPED + } single_step; + + reg_t load_reservation; + +#ifdef RISCV_ENABLE_COMMITLOG + commit_log_reg_t log_reg_write; + reg_t last_inst_priv; + int last_inst_xlen; + int last_inst_flen; +#endif +}; + +typedef enum { + OPERATION_EXECUTE, + OPERATION_STORE, + OPERATION_LOAD, +} trigger_operation_t; + +// Count number of contiguous 1 bits starting from the LSB. +static int cto(reg_t val) +{ + int res = 0; + while ((val & 1) == 1) + val >>= 1, res++; + return res; +} // this class represents one processor in a RISC-V machine. -class processor_t +class processor_t : public abstract_device_t { public: - processor_t(sim_t* _sim, mmu_t* _mmu, uint32_t _id); + processor_t(const char* isa, sim_t* sim, uint32_t id, bool halt_on_reset=false); ~processor_t(); - void reset(bool value); - void step(size_t n, bool noisy); // run for n cycles - void deliver_ipi(); // register an interprocessor interrupt - bool running() { return run; } - void set_pcr(int which, reg_t val); - reg_t get_pcr(int which); + void set_debug(bool value); + void set_histogram(bool value); + void reset(); + void step(size_t n); // run for n cycles + void set_csr(int which, reg_t val); + reg_t get_csr(int which); + mmu_t* get_mmu() { return mmu; } + state_t* get_state() { return &state; } + unsigned get_xlen() { return xlen; } + unsigned get_flen() { + return supports_extension('Q') ? 128 : + supports_extension('D') ? 64 : + supports_extension('F') ? 32 : 0; + } + extension_t* get_extension() { return ext; } + bool supports_extension(unsigned char ext) { + if (ext >= 'a' && ext <= 'z') ext += 'A' - 'a'; + return ext >= 'A' && ext <= 'Z' && ((isa >> (ext - 'A')) & 1); + } + reg_t legalize_privilege(reg_t); + void set_privilege(reg_t); + void yield_load_reservation() { state.load_reservation = (reg_t)-1; } + void update_histogram(reg_t pc); + const disassembler_t* get_disassembler() { return disassembler; } -private: - sim_t& sim; - mmu_t& mmu; // main memory is always accessed via the mmu + void register_insn(insn_desc_t); + void register_extension(extension_t*); - // user-visible architected state - regfile_t XPR; - regfile_t FPR; - reg_t pc; + // MMIO slave interface + bool load(reg_t addr, size_t len, uint8_t* bytes); + bool store(reg_t addr, size_t len, const uint8_t* bytes); - // counters - reg_t cycle; - - // privileged control registers - reg_t epc; - reg_t badvaddr; - reg_t evec; - reg_t pcr_k0; - reg_t pcr_k1; - reg_t cause; - reg_t tohost; - reg_t fromhost; - uint32_t interrupts_pending; - uint32_t id; - uint32_t sr; // only modify the status register using set_pcr() - uint32_t fsr; - uint32_t count; - uint32_t compare; + // When true, display disassembly of each instruction that's executed. + bool debug; + // When true, take the slow simulation path. + bool slow_path(); + bool halted() { return state.dcsr.cause ? true : false; } + bool halt_request; - // # of bits in an XPR (32 or 64). (redundant with sr) - int xprlen; + // Return the index of a trigger that matched, or -1. + inline int trigger_match(trigger_operation_t operation, reg_t address, reg_t data) + { + if (state.dcsr.cause) + return -1; - bool run; // !reset + bool chain_ok = true; - // functions - void take_interrupt(); // take a trap if any interrupts are pending - void set_fsr(uint32_t val); // set the floating-point status register - void take_trap(reg_t t, bool noisy); // take an exception - void disasm(insn_t insn, reg_t pc); // disassemble and print an instruction + for (unsigned int i = 0; i < state.num_triggers; i++) { + if (!chain_ok) { + chain_ok |= !state.mcontrol[i].chain; + continue; + } - // vector stuff - void vcfg(); - void setvl(int vlapp); + if ((operation == OPERATION_EXECUTE && !state.mcontrol[i].execute) || + (operation == OPERATION_STORE && !state.mcontrol[i].store) || + (operation == OPERATION_LOAD && !state.mcontrol[i].load) || + (state.prv == PRV_M && !state.mcontrol[i].m) || + (state.prv == PRV_S && !state.mcontrol[i].s) || + (state.prv == PRV_U && !state.mcontrol[i].u)) { + continue; + } - reg_t vecbanks; - uint32_t vecbanks_count; + reg_t value; + if (state.mcontrol[i].select) { + value = data; + } else { + value = address; + } - bool utmode; - uint32_t utidx; - int vlmax; - int vl; - int nxfpr_bank; - int nxpr_use; - int nfpr_use; - processor_t* uts[MAX_UTS]; + // We need this because in 32-bit mode sometimes the PC bits get sign + // extended. + if (xlen == 32) { + value &= 0xffffffff; + } - // this constructor is used for each of the uts - processor_t(sim_t* _sim, mmu_t* _mmu, uint32_t _id, uint32_t _utidx); + switch (state.mcontrol[i].match) { + case MATCH_EQUAL: + if (value != state.tdata2[i]) + continue; + break; + case MATCH_NAPOT: + { + reg_t mask = ~((1 << cto(state.tdata2[i])) - 1); + if ((value & mask) != (state.tdata2[i] & mask)) + continue; + } + break; + case MATCH_GE: + if (value < state.tdata2[i]) + continue; + break; + case MATCH_LT: + if (value >= state.tdata2[i]) + continue; + break; + case MATCH_MASK_LOW: + { + reg_t mask = state.tdata2[i] >> (xlen/2); + if ((value & mask) != (state.tdata2[i] & mask)) + continue; + } + break; + case MATCH_MASK_HIGH: + { + reg_t mask = state.tdata2[i] >> (xlen/2); + if (((value >> (xlen/2)) & mask) != (state.tdata2[i] & mask)) + continue; + } + break; + } + + if (!state.mcontrol[i].chain) { + return i; + } + chain_ok = true; + } + return -1; + } + + void trigger_updated(); + +private: + sim_t* sim; + mmu_t* mmu; // main memory is always accessed via the mmu + extension_t* ext; + disassembler_t* disassembler; + state_t state; + uint32_t id; + unsigned max_xlen; + unsigned xlen; + reg_t isa; + reg_t max_isa; + std::string isa_string; + bool histogram_enabled; + bool halt_on_reset; + + std::vector instructions; + std::map pc_histogram; + + static const size_t OPCODE_CACHE_SIZE = 8191; + insn_desc_t opcode_cache[OPCODE_CACHE_SIZE]; + + void take_pending_interrupt() { take_interrupt(state.mip & state.mie); } + void take_interrupt(reg_t mask); // take first enabled interrupt in mask + void take_trap(trap_t& t, reg_t epc); // take an exception + void disasm(insn_t insn); // disassemble and print an instruction + int paddr_bits(); + + void enter_debug_mode(uint8_t cause); friend class sim_t; friend class mmu_t; + friend class clint_t; + friend class extension_t; - #include "dispatch.h" + void parse_isa_string(const char* isa); + void build_opcode_map(); + void register_base_instructions(); + insn_func_t decode_insn(insn_t insn); + + // Track repeated executions for processor_t::disasm() + uint64_t last_pc, last_bits, executions; }; -#ifndef RISCV_ENABLE_RVC -# define set_pc(x) \ - do { if((x) & (sizeof(insn_t)-1)) \ - { badvaddr = (x); throw trap_instruction_address_misaligned; } \ - npc = (x); \ - } while(0) -#else -# define set_pc(x) \ - do { if((x) & ((sr & SR_EC) ? 1 : 3)) \ - { badvaddr = (x); throw trap_instruction_address_misaligned; } \ - npc = (x); \ - } while(0) -#endif +reg_t illegal_instruction(processor_t* p, insn_t insn, reg_t pc); + +#define REGISTER_INSN(proc, name, match, mask) \ + extern reg_t rv32_##name(processor_t*, insn_t, reg_t); \ + extern reg_t rv64_##name(processor_t*, insn_t, reg_t); \ + proc->register_insn((insn_desc_t){match, mask, rv32_##name, rv64_##name}); #endif