--- /dev/null
+#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
--- /dev/null
+#include "extension.h"
+#include "trap.h"
+#include "dummy-rocc.h"
+
+std::map<std::string, std::function<extension_t*()>>& extensions()
+{
+ static std::map<std::string, std::function<extension_t*()>> 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);
+}
--- /dev/null
+#ifndef _RISCV_COPROCESSOR_H
+#define _RISCV_COPROCESSOR_H
+
+#include "processor.h"
+#include <map>
+#include <string>
+#include <vector>
+#include <functional>
+
+class extension_t
+{
+ public:
+ virtual std::vector<insn_desc_t> 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<std::string, std::function<extension_t*()>>& extensions();
+
+#define REGISTER_EXTENSION(name, constructor) \
+ class register_##name { \
+ public: register_##name() { extensions()[#name] = constructor; } \
+ }; static register_##name dummy_##name;
+
+#endif
#define PCR_TOHOST 30
#define PCR_FROMHOST 31
+#define IRQ_COP 2
#define IRQ_IPI 5
#define IRQ_HOST 6
#define IRQ_TIMER 7
// See LICENSE for license details.
#include "processor.h"
+#include "extension.h"
#include "common.h"
#include "config.h"
#include "sim.h"
#include <limits.h>
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
}
size_t i = 0;
reg_t npc = state.pc;
- mmu_t& _mmu = mmu;
+ mmu_t* _mmu = mmu;
try
{
// 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)
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;
state.sr &= ~SR_EV;
#endif
state.sr &= ~SR_ZERO;
- mmu.flush_tlb();
+ mmu->flush_tlb();
break;
case PCR_EPC:
state.epc = 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);
case PCR_ASID:
return 0;
case PCR_FATC:
- mmu.flush_tlb();
+ mmu->flush_tlb();
return 0;
case PCR_HARTID:
return id;
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();
}
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;
opcode_map = new_map;
}
- opcode_map.insert(std::make_pair(match & ((1L<<opcode_bits)-1),
- (opcode_map_entry_t){match, mask, rv32, rv64}));
+ opcode_map.insert(std::make_pair(desc.match & ((1L<<opcode_bits)-1), desc));
+}
+
+void processor_t::register_extension(extension_t* x)
+{
+ for (auto insn : x->get_instructions())
+ register_insn(insn);
+ if (ext != NULL)
+ throw std::logic_error("only one extension may be registered");
+ ext = x;
+ x->set_processor(this);
}
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
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<uint32_t, opcode_map_entry_t> opcode_map;
+ std::multimap<uint32_t, insn_desc_t> 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
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
opcodes.h \
cachesim.h \
memtracer.h \
+ extension.h \
+ rocc.h \
+ dummy-rocc.h \
riscv_srcs = \
htif.cc \
cachesim.cc \
mmu.cc \
disasm.cc \
+ extension.cc \
+ rocc.cc \
$(riscv_gen_srcs) \
riscv_test_srcs =
--- /dev/null
+#include "rocc.h"
+#include "trap.h"
+#include <cstdlib>
+
+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<rocc_t*>(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<insn_desc_t> rocc_t::get_instructions()
+{
+ std::vector<insn_desc_t> 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;
+}
--- /dev/null
+#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<insn_desc_t> get_instructions();
+};
+
+#endif
{
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;
}
#include "sim.h"
#include "htif.h"
#include "cachesim.h"
+#include "extension.h"
#include <fesvr/option_parser.h>
#include <stdio.h>
#include <stdlib.h>
std::unique_ptr<icache_sim_t> ic;
std::unique_ptr<dcache_sim_t> dc;
std::unique_ptr<cache_sim_t> l2;
+ std::function<extension_t*()> extension;
option_parser_t parser;
parser.help(&help);
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)
{
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);