From bbb0f2179c858c77918ef37dbfcd7bb5f3fd0417 Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Tue, 13 Aug 2013 00:51:07 -0700 Subject: [PATCH] Implement RoCC and add a dummy RoCC Enable it with --extension=dummy --- riscv/dummy-rocc.h | 46 +++++++++++++++++++++++++++++++++++++ riscv/extension.cc | 29 ++++++++++++++++++++++++ riscv/extension.h | 33 +++++++++++++++++++++++++++ riscv/pcr.h | 1 + riscv/processor.cc | 56 ++++++++++++++++++++++++---------------------- riscv/processor.h | 37 ++++++++++++++++++++---------- riscv/riscv.mk.in | 5 +++++ riscv/rocc.cc | 44 ++++++++++++++++++++++++++++++++++++ riscv/rocc.h | 28 +++++++++++++++++++++++ riscv/sim.cc | 2 +- riscv/spike.cc | 8 +++++++ 11 files changed, 249 insertions(+), 40 deletions(-) create mode 100644 riscv/dummy-rocc.h create mode 100644 riscv/extension.cc create mode 100644 riscv/extension.h create mode 100644 riscv/rocc.cc create mode 100644 riscv/rocc.h diff --git a/riscv/dummy-rocc.h b/riscv/dummy-rocc.h new file mode 100644 index 0000000..55cd48f --- /dev/null +++ b/riscv/dummy-rocc.h @@ -0,0 +1,46 @@ +#ifndef _RISCV_DUMMY_ROCC_H +#define _RISCV_DUMMY_ROCC_H + +#include "rocc.h" +#include "mmu.h" + +class dummy_rocc_t : public rocc_t +{ + public: + const char* name() { return "dummy"; } + + reg_t custom0(rocc_insn_t insn, reg_t xs1, reg_t xs2) + { + reg_t prev_acc = acc[insn.rs2]; + + if (insn.rs2 > num_acc) + illegal_instruction(); + + switch (insn.funct) + { + case 0: // acc <- xs1 + acc[insn.rs2] = xs1; + break; + case 1: // xd <- acc (the only real work is the return statement below) + break; + case 2: // acc[rs2] <- Mem[xs1] + acc[insn.rs2] = p->get_mmu()->load_uint64(xs1); + break; + case 3: // acc[rs2] <- accX + xs1 + acc[insn.rs2] += xs1; + break; + default: + illegal_instruction(); + } + + return prev_acc; // in all cases, xd <- previous value of acc[rs2] + } + + private: + static const int num_acc = 4; + reg_t acc[num_acc]; +}; + +REGISTER_EXTENSION(dummy, []() { return new dummy_rocc_t; }) + +#endif diff --git a/riscv/extension.cc b/riscv/extension.cc new file mode 100644 index 0000000..718ef6d --- /dev/null +++ b/riscv/extension.cc @@ -0,0 +1,29 @@ +#include "extension.h" +#include "trap.h" +#include "dummy-rocc.h" + +std::map>& extensions() +{ + static std::map> v; + return v; +} + +extension_t::~extension_t() +{ +} + +void extension_t::illegal_instruction() +{ + throw trap_illegal_instruction(); +} + +void extension_t::raise_interrupt() +{ + p->set_interrupt(IRQ_COP, true); + p->take_interrupt(); +} + +void extension_t::clear_interrupt() +{ + p->set_interrupt(IRQ_COP, false); +} diff --git a/riscv/extension.h b/riscv/extension.h new file mode 100644 index 0000000..218deb4 --- /dev/null +++ b/riscv/extension.h @@ -0,0 +1,33 @@ +#ifndef _RISCV_COPROCESSOR_H +#define _RISCV_COPROCESSOR_H + +#include "processor.h" +#include +#include +#include +#include + +class extension_t +{ + public: + virtual std::vector get_instructions() = 0; + virtual const char* name() = 0; + virtual ~extension_t(); + + void set_processor(processor_t* _p) { p = _p; } + protected: + processor_t* p; + + void illegal_instruction(); + void raise_interrupt(); + void clear_interrupt(); +}; + +std::map>& extensions(); + +#define REGISTER_EXTENSION(name, constructor) \ + class register_##name { \ + public: register_##name() { extensions()[#name] = constructor; } \ + }; static register_##name dummy_##name; + +#endif diff --git a/riscv/pcr.h b/riscv/pcr.h index 77cee01..62c90dd 100644 --- a/riscv/pcr.h +++ b/riscv/pcr.h @@ -40,6 +40,7 @@ #define PCR_TOHOST 30 #define PCR_FROMHOST 31 +#define IRQ_COP 2 #define IRQ_IPI 5 #define IRQ_HOST 6 #define IRQ_TIMER 7 diff --git a/riscv/processor.cc b/riscv/processor.cc index 0510a3d..a4a1430 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -1,6 +1,7 @@ // See LICENSE for license details. #include "processor.h" +#include "extension.h" #include "common.h" #include "config.h" #include "sim.h" @@ -13,15 +14,12 @@ #include processor_t::processor_t(sim_t* _sim, mmu_t* _mmu, uint32_t _id) - : sim(*_sim), mmu(*_mmu), id(_id), opcode_bits(0) + : sim(_sim), mmu(_mmu), ext(NULL), id(_id), opcode_bits(0) { reset(true); - mmu.set_processor(this); + mmu->set_processor(this); - #define DECLARE_INSN(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); \ - register_insn(match, mask, rv32_##name, rv64_##name); + #define DECLARE_INSN(name, match, mask) REGISTER_INSN(this, name, match, mask) #include "opcodes.h" #undef DECLARE_INSN } @@ -94,7 +92,7 @@ void processor_t::step(size_t n, bool noisy) size_t i = 0; reg_t npc = state.pc; - mmu_t& _mmu = mmu; + mmu_t* _mmu = mmu; try { @@ -103,7 +101,7 @@ void processor_t::step(size_t n, bool noisy) // execute_insn fetches and executes one instruction #define execute_insn(noisy) \ do { \ - mmu_t::insn_fetch_t fetch = _mmu.load_insn(npc); \ + mmu_t::insn_fetch_t fetch = _mmu->load_insn(npc); \ if(noisy) disasm(fetch.insn, npc); \ npc = fetch.func(this, fetch.insn, npc); \ } while(0) @@ -142,20 +140,15 @@ void processor_t::step(size_t n, bool noisy) void processor_t::take_trap(reg_t pc, trap_t& t, bool noisy) { - if(noisy) - { - if ((sreg_t)t.cause() < 0) - fprintf(stderr, "core %3d: interrupt %d, epc 0x%016" PRIx64 "\n", - id, uint8_t(t.cause()), pc); - else - fprintf(stderr, "core %3d: trap %s, epc 0x%016" PRIx64 "\n", - id, t.name(), pc); - } + if (noisy) + fprintf(stderr, "core %3d: exception %s, epc 0x%016" PRIx64 "\n", + id, t.name(), pc); // switch to supervisor, set previous supervisor bit, disable interrupts set_pcr(PCR_SR, (((state.sr & ~SR_EI) | SR_S) & ~SR_PS & ~SR_PEI) | ((state.sr & SR_S) ? SR_PS : 0) | ((state.sr & SR_EI) ? SR_PEI : 0)); + yield_load_reservation(); state.cause = t.cause(); state.epc = pc; @@ -196,7 +189,7 @@ reg_t processor_t::set_pcr(int which, reg_t val) state.sr &= ~SR_EV; #endif state.sr &= ~SR_ZERO; - mmu.flush_tlb(); + mmu->flush_tlb(); break; case PCR_EPC: state.epc = val; @@ -215,7 +208,7 @@ reg_t processor_t::set_pcr(int which, reg_t val) state.ptbr = val & ~(PGSIZE-1); break; case PCR_SEND_IPI: - sim.send_ipi(val); + sim->send_ipi(val); break; case PCR_CLR_IPI: set_interrupt(IRQ_IPI, val & 1); @@ -262,7 +255,7 @@ reg_t processor_t::get_pcr(int which) case PCR_ASID: return 0; case PCR_FATC: - mmu.flush_tlb(); + mmu->flush_tlb(); return 0; case PCR_HARTID: return id; @@ -289,7 +282,7 @@ void processor_t::set_interrupt(int which, bool on) state.sr &= ~mask; } -static reg_t illegal_instruction(processor_t* p, insn_t insn, reg_t pc) +reg_t illegal_instruction(processor_t* p, insn_t insn, reg_t pc) { throw trap_illegal_instruction(); } @@ -306,13 +299,13 @@ insn_func_t processor_t::decode_insn(insn_t insn) return &illegal_instruction; } -void processor_t::register_insn(uint32_t match, uint32_t mask, insn_func_t rv32, insn_func_t rv64) +void processor_t::register_insn(insn_desc_t desc) { - assert(mask & 1); - if (opcode_bits == 0 || (mask & ((1L << opcode_bits)-1)) != ((1L << opcode_bits)-1)) + assert(desc.mask & 1); + if (opcode_bits == 0 || (desc.mask & ((1L << opcode_bits)-1)) != ((1L << opcode_bits)-1)) { unsigned x = 0; - while ((mask & ((1L << (x+1))-1)) == ((1L << (x+1))-1) && + while ((desc.mask & ((1L << (x+1))-1)) == ((1L << (x+1))-1) && (opcode_bits == 0 || x <= opcode_bits)) x++; opcode_bits = x; @@ -323,6 +316,15 @@ void processor_t::register_insn(uint32_t match, uint32_t mask, insn_func_t rv32, opcode_map = new_map; } - opcode_map.insert(std::make_pair(match & ((1L<get_instructions()) + register_insn(insn); + if (ext != NULL) + throw std::logic_error("only one extension may be registered"); + ext = x; + x->set_processor(this); } diff --git a/riscv/processor.h b/riscv/processor.h index 7b7f314..6b007f2 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -13,6 +13,15 @@ class mmu_t; typedef reg_t (*insn_func_t)(processor_t*, insn_t, reg_t); class sim_t; class trap_t; +class extension_t; + +struct insn_desc_t +{ + uint32_t match; + uint32_t mask; + insn_func_t rv32; + insn_func_t rv64; +}; // architectural state of a RISC-V hart struct state_t @@ -59,28 +68,24 @@ public: void set_interrupt(int which, bool on); reg_t get_pcr(int which); uint32_t get_fsr() { return state.fsr; } - mmu_t* get_mmu() { return &mmu; } + mmu_t* get_mmu() { return mmu; } state_t* get_state() { return &state; } + extension_t* get_extension() { return ext; } void yield_load_reservation() { state.load_reservation = (reg_t)-1; } - void register_insn(uint32_t match, uint32_t mask, insn_func_t rv32, insn_func_t rv64); + void register_insn(insn_desc_t); + void register_extension(extension_t*); private: - sim_t& sim; - mmu_t& mmu; // main memory is always accessed via the mmu + sim_t* sim; + mmu_t* mmu; // main memory is always accessed via the mmu + extension_t* ext; state_t state; uint32_t id; bool run; // !reset - struct opcode_map_entry_t - { - uint32_t match; - uint32_t mask; - insn_func_t rv32; - insn_func_t rv64; - }; unsigned opcode_bits; - std::multimap opcode_map; + std::multimap opcode_map; void take_interrupt(); // take a trap if any interrupts are pending void take_trap(reg_t pc, trap_t& t, bool noisy); // take an exception @@ -88,9 +93,17 @@ private: friend class sim_t; friend class mmu_t; + friend class extension_t; friend class htif_isasim_t; insn_func_t decode_insn(insn_t insn); }; +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 diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index b9030db..781a1a3 100644 --- a/riscv/riscv.mk.in +++ b/riscv/riscv.mk.in @@ -21,6 +21,9 @@ riscv_hdrs = \ opcodes.h \ cachesim.h \ memtracer.h \ + extension.h \ + rocc.h \ + dummy-rocc.h \ riscv_srcs = \ htif.cc \ @@ -31,6 +34,8 @@ riscv_srcs = \ cachesim.cc \ mmu.cc \ disasm.cc \ + extension.cc \ + rocc.cc \ $(riscv_gen_srcs) \ riscv_test_srcs = diff --git a/riscv/rocc.cc b/riscv/rocc.cc new file mode 100644 index 0000000..3e8596f --- /dev/null +++ b/riscv/rocc.cc @@ -0,0 +1,44 @@ +#include "rocc.h" +#include "trap.h" +#include + +union rocc_insn_union_t +{ + rocc_insn_t r; + insn_t i; +}; + +#define customX(n) \ + static reg_t c##n(processor_t* p, insn_t insn, reg_t pc) \ + { \ + rocc_t* rocc = static_cast(p->get_extension()); \ + rocc_insn_union_t u; \ + u.i = insn; \ + reg_t xs1 = u.r.xs1 ? RS1 : -1; \ + reg_t xs2 = u.r.xs1 ? RS2 : -1; \ + reg_t xd = rocc->custom##n(u.r, xs1, xs2); \ + if (u.r.xd) \ + RD = xd; \ + return pc+4; \ + } \ + \ + reg_t rocc_t::custom##n(rocc_insn_t insn, reg_t xs1, reg_t xs2) \ + { \ + illegal_instruction(); \ + return 0; \ + } + +customX(0) +customX(1) +customX(2) +customX(3) + +std::vector rocc_t::get_instructions() +{ + std::vector insns; + insns.push_back((insn_desc_t){0x0b, 0x7f, &::illegal_instruction, c0}); + insns.push_back((insn_desc_t){0x0f, 0x7f, &::illegal_instruction, c1}); + insns.push_back((insn_desc_t){0x5b, 0x7f, &::illegal_instruction, c2}); + insns.push_back((insn_desc_t){0x7b, 0x7f, &::illegal_instruction, c3}); + return insns; +} diff --git a/riscv/rocc.h b/riscv/rocc.h new file mode 100644 index 0000000..6deccf8 --- /dev/null +++ b/riscv/rocc.h @@ -0,0 +1,28 @@ +#ifndef _RISCV_ROCC_H +#define _RISCV_ROCC_H + +#include "extension.h" + +struct rocc_insn_t +{ + unsigned opcode : 7; + unsigned xs2 : 1; + unsigned xs1 : 1; + unsigned xd : 1; + unsigned funct : 7; + unsigned rs2 : 5; + unsigned rs1 : 5; + unsigned rd : 5; +}; + +class rocc_t : public extension_t +{ + public: + virtual reg_t custom0(rocc_insn_t insn, reg_t xs1, reg_t xs2); + virtual reg_t custom1(rocc_insn_t insn, reg_t xs1, reg_t xs2); + virtual reg_t custom2(rocc_insn_t insn, reg_t xs1, reg_t xs2); + virtual reg_t custom3(rocc_insn_t insn, reg_t xs1, reg_t xs2); + std::vector get_instructions(); +}; + +#endif diff --git a/riscv/sim.cc b/riscv/sim.cc index 2ac3266..9ab61b6 100644 --- a/riscv/sim.cc +++ b/riscv/sim.cc @@ -48,7 +48,7 @@ sim_t::~sim_t() { for (size_t i = 0; i < procs.size(); i++) { - mmu_t* pmmu = &procs[i]->mmu; + mmu_t* pmmu = procs[i]->get_mmu(); delete procs[i]; delete pmmu; } diff --git a/riscv/spike.cc b/riscv/spike.cc index 00997bd..0ab1616 100644 --- a/riscv/spike.cc +++ b/riscv/spike.cc @@ -3,6 +3,7 @@ #include "sim.h" #include "htif.h" #include "cachesim.h" +#include "extension.h" #include #include #include @@ -33,6 +34,7 @@ int main(int argc, char** argv) std::unique_ptr ic; std::unique_ptr dc; std::unique_ptr l2; + std::function extension; option_parser_t parser; parser.help(&help); @@ -43,6 +45,11 @@ int main(int argc, char** argv) parser.option(0, "ic", 1, [&](const char* s){ic.reset(new icache_sim_t(s));}); parser.option(0, "dc", 1, [&](const char* s){dc.reset(new dcache_sim_t(s));}); parser.option(0, "l2", 1, [&](const char* s){l2.reset(cache_sim_t::construct(s, "L2$"));}); + parser.option(0, "extension", 1, [&](const char* s){ + if (!extensions().count(s)) + fprintf(stderr, "unknown extension %s!\n", s), exit(-1); + extension = extensions()[s]; + }); auto argv1 = parser.parse(argv); if (!*argv1) @@ -56,6 +63,7 @@ int main(int argc, char** argv) { if (ic) s.get_core(i)->get_mmu()->register_memtracer(&*ic); if (dc) s.get_core(i)->get_mmu()->register_memtracer(&*dc); + if (extension) s.get_core(i)->register_extension(extension()); } s.set_debug(debug); -- 2.30.2