Implement RoCC and add a dummy RoCC
authorAndrew Waterman <waterman@cs.berkeley.edu>
Tue, 13 Aug 2013 07:51:07 +0000 (00:51 -0700)
committerAndrew Waterman <waterman@cs.berkeley.edu>
Tue, 13 Aug 2013 07:51:07 +0000 (00:51 -0700)
Enable it with --extension=dummy

riscv/dummy-rocc.h [new file with mode: 0644]
riscv/extension.cc [new file with mode: 0644]
riscv/extension.h [new file with mode: 0644]
riscv/pcr.h
riscv/processor.cc
riscv/processor.h
riscv/riscv.mk.in
riscv/rocc.cc [new file with mode: 0644]
riscv/rocc.h [new file with mode: 0644]
riscv/sim.cc
riscv/spike.cc

diff --git a/riscv/dummy-rocc.h b/riscv/dummy-rocc.h
new file mode 100644 (file)
index 0000000..55cd48f
--- /dev/null
@@ -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 (file)
index 0000000..718ef6d
--- /dev/null
@@ -0,0 +1,29 @@
+#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);
+}
diff --git a/riscv/extension.h b/riscv/extension.h
new file mode 100644 (file)
index 0000000..218deb4
--- /dev/null
@@ -0,0 +1,33 @@
+#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
index 77cee01d83f17932ca070409ef1f4fae1d89197f..62c90dd2ec649cd5b2ad81d47488a1880a127b3d 100644 (file)
@@ -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
index 0510a3dd6f6f5bd80c332acb64eae1b07c59daec..a4a143037fbe02e5c45b99e8ffc532c0293295ca 100644 (file)
@@ -1,6 +1,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
 }
@@ -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<<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);
 }
index 7b7f31407eb414cdfb66c581162dcab96529e3fa..6b007f24e263de5b6a4406d741bd4d3eaee64e43 100644 (file)
@@ -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<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
@@ -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
index b9030dbad5cf73c6eaa214fe2028379a31c417c8..781a1a33c0f7078d1868981e6634c67d3a1c009a 100644 (file)
@@ -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 (file)
index 0000000..3e8596f
--- /dev/null
@@ -0,0 +1,44 @@
+#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;
+}
diff --git a/riscv/rocc.h b/riscv/rocc.h
new file mode 100644 (file)
index 0000000..6deccf8
--- /dev/null
@@ -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<insn_desc_t> get_instructions();
+};
+
+#endif
index 2ac326698f5a1292ef845e71582b7425a20156d7..9ab61b6257015441e49d32bc8c94c7f2a0d47997 100644 (file)
@@ -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;
   }
index 00997bd1fe1c345d896b6b376915dc3ad9af1aa7..0ab1616a9e131fb2e84825bac6f4ef052ed31d01 100644 (file)
@@ -3,6 +3,7 @@
 #include "sim.h"
 #include "htif.h"
 #include "cachesim.h"
+#include "extension.h"
 #include <fesvr/option_parser.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -33,6 +34,7 @@ int main(int argc, char** argv)
   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);
@@ -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);