Generate instruction decoder dynamically
authorAndrew Waterman <waterman@cs.berkeley.edu>
Fri, 26 Jul 2013 10:34:51 +0000 (03:34 -0700)
committerAndrew Waterman <waterman@cs.berkeley.edu>
Fri, 26 Jul 2013 10:34:51 +0000 (03:34 -0700)
This will make it easier for accelerators to add instructions.

12 files changed:
riscv/decode.h
riscv/disasm.cc
riscv/dispatch [deleted file]
riscv/insn_header.h [deleted file]
riscv/insn_template.cc [new file with mode: 0644]
riscv/interactive.cc
riscv/mmu.cc
riscv/mmu.h
riscv/processor.cc
riscv/processor.h
riscv/riscv.mk.in
riscv/sim.h

index 3ef8864d496a0d852a6862535e6ffa7adc6099f5..be7d64d3db312c274ef2d382f6cd43ab2e314e04 100644 (file)
@@ -123,7 +123,7 @@ union insn_t
   btype_t btype;
   ltype_t ltype;
   ftype_t ftype;
-  uint32_t bits;
+  uint_fast32_t bits;
 };
 
 template <class T>
index ca3b3dd7b610335c0ad55f006a176130bc670dd1..57f43d7f52f37c9e8ddaeb32e918ec7b6092368d 100644 (file)
@@ -406,8 +406,8 @@ disassembler::disassembler()
   uint32_t match_rs1_ra = dummy.bits;
 
   #define DECLARE_INSN(code, match, mask) \
-   const uint32_t __attribute__((unused)) match_##code = match; \
-   const uint32_t __attribute__((unused)) mask_##code = mask;
+   const uint32_t match_##code = match; \
+   const uint32_t mask_##code = mask;
   #include "opcodes.h"
   #undef DECLARE_INSN
 
diff --git a/riscv/dispatch b/riscv/dispatch
deleted file mode 100755 (executable)
index 5afc3be..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/env python
-import sys
-
-if len(sys.argv) == 3:
-  numfiles = int(sys.argv[1])
-  tablesz = int(sys.argv[2])
-  filenum = numfiles+1
-else:
-  filenum = int(sys.argv[1])
-  numfiles = int(sys.argv[2])
-  tablesz = int(sys.argv[3])
-
-match = {}
-mask = {}
-seen = {}
-for line in sys.stdin:
-  (name, mtch, msk) = line.split('(')[1].split(')')[0].split(',')
-  match[name] = int(mtch,16)
-  mask[name] = int(msk,16)
-
-redundant = {}
-for name in match.iterkeys():
-  if (mask[name] & (tablesz-1)) == mask[name]:
-    for i in range(match[name]+1, tablesz):
-      if (i & mask[name]) == match[name]:
-        redundant[i] = match[name]
-
-illegal = -1
-for i in range(0, tablesz):
-  used = 0
-  for name in match.iterkeys():
-    if match[name] % tablesz == (i & mask[name]):
-      used = 1
-  if not used and illegal == -1:
-    illegal = i
-  elif not used:
-    redundant[i] = illegal
-
-if filenum == numfiles:
-  print '#include "processor.h"'
-  print 'const insn_func_t processor_t::dispatch_table[%d] = {' % (2*tablesz)
-  for xl in [32, 64]:
-    for i in range(0, tablesz):
-      func = i
-      if i in redundant:
-        func = redundant[i]
-      print '  &processor_t::insn_func_%d_%d,' % (xl, func)
-  print '};'
-
-if filenum == numfiles+1:
-  print '#define get_insn_func(insn, sr) \\'
-  print '  processor_t::dispatch_table[((((sr) & SR_S) ? (sr & SR_S64) : (sr & SR_U64)) ? %d : 0) + ((insn).bits %% %d)]' % (tablesz, tablesz)
-  
-  print 'static const insn_func_t dispatch_table[%d];' % (2*tablesz)
-  for i in range(0, tablesz):
-    if i not in redundant:
-      print 'reg_t insn_func_32_%d(insn_t insn, reg_t reg);' % i
-      print 'reg_t insn_func_64_%d(insn_t insn, reg_t reg);' % i
-  sys.exit(0)
-
-print '#include "insn_header.h"'
-
-for i in range(0, tablesz):
-  for xl in [32, 64]:
-    if i % numfiles != filenum or i in redundant:
-      continue
-
-    print 'reg_t processor_t::insn_func_%d_%d(insn_t insn, reg_t pc)' % (xl, i)
-    print '{'
-    for name in match.iterkeys():
-      if match[name] % tablesz == (i & mask[name]):
-        print '  if((insn.bits & 0x%x) == 0x%x)' % (mask[name] & ~(tablesz-1), \
-                                                    match[name] & ~(tablesz-1))
-        print '  {'
-        print '    int xprlen = %d;' % xl
-        print '    reg_t npc = sext_xprlen(pc + insn_length(0x%x));' % match[name]
-        print '    #include "insns/%s.h"' % name
-        print '    return npc;'
-        print '  }'
-        print '  else',
-
-    print '  throw trap_illegal_instruction;'
-    print '}\n'
diff --git a/riscv/insn_header.h b/riscv/insn_header.h
deleted file mode 100644 (file)
index c5f1080..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-// See LICENSE for license details.
-
-#include "processor.h"
-#include "config.h"
-#include "sim.h"
-#include "softfloat.h"
-#include "platform.h" // softfloat isNaNF32UI, etc.
-#include "internals.h" // ditto
-#include <assert.h>
diff --git a/riscv/insn_template.cc b/riscv/insn_template.cc
new file mode 100644 (file)
index 0000000..3aca669
--- /dev/null
@@ -0,0 +1,25 @@
+// See LICENSE for license details.
+
+#include "processor.h"
+#include "config.h"
+#include "sim.h"
+#include "softfloat.h"
+#include "platform.h" // softfloat isNaNF32UI, etc.
+#include "internals.h" // ditto
+#include <assert.h>
+
+reg_t processor_t::rv32_NAME(insn_t insn, reg_t pc)
+{
+  int xprlen = 32;
+  reg_t npc = sext_xprlen(pc + insn_length(OPCODE));
+  #include "insns/NAME.h"
+  return npc;
+}
+
+reg_t processor_t::rv64_NAME(insn_t insn, reg_t pc)
+{
+  int xprlen = 64;
+  reg_t npc = sext_xprlen(pc + insn_length(OPCODE));
+  #include "insns/NAME.h"
+  return npc;
+}
index 66ea48760c63b99d770dfa1176659bb33d22779a..fe70760a10dc3c1a6d23e35e0e8655b5d9fa401f 100644 (file)
@@ -185,7 +185,6 @@ reg_t sim_t::get_mem(const std::vector<std::string>& args)
     int p = atoi(args[0].c_str());
     if(p >= (int)num_cores())
       throw trap_illegal_instruction;
-    mmu->set_sr(procs[p]->sr);
     mmu->set_ptbr(procs[p]->mmu.get_ptbr());
     addr_str = args[1];
   }
index 3cdb21dc2b91aa84c17af1ae2d0b89c277c1ee8b..8b63a43180621e45693c7fc06e7e25b6fa53ef4a 100644 (file)
@@ -6,9 +6,9 @@
 
 mmu_t::mmu_t(char* _mem, size_t _memsz)
  : mem(_mem), memsz(_memsz), badvaddr(0),
-   ptbr(0)
+   ptbr(0), proc(NULL)
 {
-  set_sr(SR_S);
+  flush_tlb();
 }
 
 mmu_t::~mmu_t()
@@ -27,12 +27,6 @@ void mmu_t::flush_tlb()
   memset(tlb_store_tag, -1, sizeof(tlb_store_tag));
 
   flush_icache();
-}
-
-void mmu_t::set_sr(uint32_t _sr)
-{
-  sr = _sr;
-  flush_tlb();
   yield_load_reservation();
 }
 
@@ -44,7 +38,7 @@ reg_t mmu_t::refill_tlb(reg_t addr, reg_t bytes, bool store, bool fetch)
   reg_t pte = walk(addr);
 
   reg_t pte_perm = pte & PTE_PERM;
-  if (sr & SR_S) // shift supervisor permission bits into user perm bits
+  if (proc == NULL || (proc->sr & SR_S))
     pte_perm = (pte_perm/(PTE_SX/PTE_UX)) & PTE_PERM;
   pte_perm |= pte & PTE_E;
 
@@ -83,7 +77,7 @@ pte_t mmu_t::walk(reg_t addr)
   int shift = 8*sizeof(reg_t) - VA_BITS;
   if (((sreg_t)addr << shift >> shift) != (sreg_t)addr)
     ;
-  else if (!(sr & SR_VM))
+  else if (proc == NULL || !(proc->sr & SR_VM))
   {
     if(addr < memsz)
       pte = PTE_E | PTE_PERM | ((addr >> PGSHIFT) << PTE_PPN_SHIFT);
index a5d150f8e5a4ceed9cdf76d1527cdd9f0216505b..9e1218cc0debbb46eafb2dc296f7947682527d46 100644 (file)
@@ -97,8 +97,8 @@ public:
 
   struct insn_fetch_t
   {
-    insn_t insn;
     insn_func_t func;
+    insn_t insn;
   };
 
   // load instruction from memory at aligned address.
@@ -115,7 +115,7 @@ public:
       reg_t addr_lo = translate(addr, 2, false, true);
       insn_fetch_t fetch;
       fetch.insn.bits = *(uint16_t*)(mem + addr_lo);
-      fetch.func = get_insn_func(fetch.insn, sr);
+      fetch.func = proc->decode_insn(fetch.insn);
 
       if(!INSN_IS_RVC(fetch.insn.bits))
       {
@@ -127,43 +127,39 @@ public:
     else
     #endif
     {
-      reg_t idx = (addr/sizeof(insn_t)) % ICACHE_ENTRIES;
+      reg_t idx = (addr/sizeof(insn_t::itype)) % ICACHE_ENTRIES;
       insn_fetch_t fetch;
       if (unlikely(icache_tag[idx] != addr))
       {
-        reg_t paddr = translate(addr, sizeof(insn_t), false, true);
-        fetch.insn = *(insn_t*)(mem + paddr);
-        fetch.func = get_insn_func(fetch.insn, sr);
+        reg_t paddr = translate(addr, sizeof(insn_t::itype), false, true);
+        fetch.insn.itype = *(decltype(insn_t::itype)*)(mem + paddr);
+        fetch.func = proc->decode_insn(fetch.insn);
 
-        reg_t idx = (paddr/sizeof(insn_t)) % ICACHE_ENTRIES;
+        reg_t idx = (paddr/sizeof(insn_t::itype)) % ICACHE_ENTRIES;
         icache_tag[idx] = addr;
         icache_data[idx] = fetch.insn;
         icache_func[idx] = fetch.func;
 
-        if (tracer.interested_in_range(paddr, paddr + sizeof(insn_t), false, true))
+        if (tracer.interested_in_range(paddr, paddr + sizeof(insn_t::itype), false, true))
         {
           icache_tag[idx] = -1;
-          tracer.trace(paddr, sizeof(insn_t), false, true);
+          tracer.trace(paddr, sizeof(insn_t::itype), false, true);
         }
       }
-      fetch.insn = icache_data[idx];;
+      fetch.insn = icache_data[idx];
       fetch.func = icache_func[idx];
       return fetch;
     }
   }
 
-  // get the virtual address that caused a fault
   reg_t get_badvaddr() { return badvaddr; }
-
-  // get/set the page table base register
   reg_t get_ptbr() { return ptbr; }
   void set_ptbr(reg_t addr) { ptbr = addr & ~(PGSIZE-1); flush_tlb(); }
-  void yield_load_reservation() { load_reservation = -1; }
-  void set_sr(uint32_t sr); // keep the MMU in sync with the processor mode
+  void set_processor(processor_t* p) { proc = p; flush_tlb(); }
 
-  // flush the TLB and instruction cache
   void flush_tlb();
   void flush_icache();
+  void yield_load_reservation() { load_reservation = -1; }
 
   void register_memtracer(memtracer_t*);
 
@@ -173,20 +169,20 @@ private:
   reg_t load_reservation;
   reg_t badvaddr;
   reg_t ptbr;
-  uint32_t sr;
+  processor_t* proc;
   memtracer_list_t tracer;
 
+  // implement an instruction cache for simulator performance
+  static const reg_t ICACHE_ENTRIES = 256;
+  insn_t icache_data[ICACHE_ENTRIES];
+  insn_func_t icache_func[ICACHE_ENTRIES];
+
   // implement a TLB for simulator performance
   static const reg_t TLB_ENTRIES = 256;
   reg_t tlb_data[TLB_ENTRIES];
   reg_t tlb_insn_tag[TLB_ENTRIES];
   reg_t tlb_load_tag[TLB_ENTRIES];
   reg_t tlb_store_tag[TLB_ENTRIES];
-
-  // implement an instruction cache for simulator performance
-  static const reg_t ICACHE_ENTRIES = 256;
-  insn_t icache_data[ICACHE_ENTRIES];
-  insn_func_t icache_func[ICACHE_ENTRIES];
   reg_t icache_tag[ICACHE_ENTRIES];
 
   // finish translation on a TLB miss and upate the TLB
index 843279b4444cbb6c06c58541d4b850c79a29fbb3..5d82937421f6343db790c6ac4ad4c7c4a05ffc7a 100644 (file)
 #include <limits.h>
 
 processor_t::processor_t(sim_t* _sim, mmu_t* _mmu, uint32_t _id)
-  : sim(*_sim), mmu(*_mmu), id(_id), utidx(0)
+  : sim(*_sim), mmu(*_mmu), id(_id), opcode_bits(0), utidx(0)
 {
   reset(true);
+  mmu.set_processor(this);
+
+  #define DECLARE_INSN(name, match, mask) \
+    register_insn(match, mask, (insn_func_t)&processor_t::rv32_##name, (insn_func_t)&processor_t::rv64_##name);
+  #include "opcodes.h"
+  #undef DECLARE_INSN
 
   // create microthreads
   for (int i=0; i<MAX_UTS; i++)
@@ -202,7 +208,7 @@ void processor_t::disasm(insn_t insn, reg_t pc)
 {
   // the disassembler is stateless, so we share it
   static disassembler disasm;
-  fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIx32 ") %s\n",
+  fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIxFAST32 ") %s\n",
           id, pc, insn.bits, disasm.disassemble(insn).c_str());
 }
 
@@ -225,7 +231,7 @@ void processor_t::set_pcr(int which, reg_t val)
       sr &= ~SR_EV;
 #endif
       sr &= ~SR_ZERO;
-      mmu.set_sr(sr);
+      mmu.flush_tlb();
       break;
     case PCR_EPC:
       epc = val;
@@ -318,3 +324,42 @@ void processor_t::set_interrupt(int which, bool on)
   else
     sr &= ~mask;
 }
+
+insn_func_t processor_t::decode_insn(insn_t insn)
+{
+  bool rv64 = (sr & SR_S) ? (sr & SR_S64) : (sr & SR_U64);
+
+  auto key = insn.bits & ((1L << opcode_bits)-1);
+  auto it = opcode_map.find(key);
+  for (auto it = opcode_map.find(key); it != opcode_map.end() && it->first == key; ++it)
+    if ((insn.bits & it->second.mask) == it->second.match)
+      return rv64 ? it->second.rv64 : it->second.rv32;
+
+  return &processor_t::illegal_instruction;
+}
+
+reg_t processor_t::illegal_instruction(insn_t insn, reg_t pc)
+{
+  throw trap_illegal_instruction;
+}
+
+void processor_t::register_insn(uint32_t match, uint32_t mask, insn_func_t rv32, insn_func_t rv64)
+{
+  assert(mask & 1);
+  if (opcode_bits == 0 || (mask & ((1L << opcode_bits)-1)) != ((1L << opcode_bits)-1))
+  {
+    unsigned x = 0;
+    while ((mask & ((1L << (x+1))-1)) == ((1L << (x+1))-1) &&
+           (opcode_bits == 0 || x <= opcode_bits))
+      x++;
+    opcode_bits = x;
+
+    decltype(opcode_map) new_map;
+    for (auto it = opcode_map.begin(); it != opcode_map.end(); ++it)
+      new_map.insert(std::make_pair(it->second.match & ((1L<<x)-1), it->second));
+    opcode_map = new_map;
+  }
+
+  opcode_map.insert(std::make_pair(match & ((1L<<opcode_bits)-1),
+    (opcode_map_entry_t){match, mask, rv32, rv64}));
+}
index f3b2f62cd69443d1eaa0c3cfd1fbb351143ebb3c..c2d0ea5709a2939833abde117f69e50c16ddcd11 100644 (file)
@@ -7,6 +7,7 @@
 #include <cstring>
 #include "trap.h"
 #include "config.h"
+#include <map>
 
 #define MAX_UTS 2048
 
@@ -31,14 +32,16 @@ public:
   reg_t get_pcr(int which);
   mmu_t* get_mmu() { return &mmu; }
 
+  void register_insn(uint32_t match, uint32_t mask, insn_func_t rv32, insn_func_t rv64);
+
 private:
   sim_t& sim;
   mmu_t& mmu; // main memory is always accessed via the mmu
 
   // user-visible architected state
+  reg_t pc;
   regfile_t<reg_t, NXPR, true> XPR;
   regfile_t<freg_t, NFPR, false> FPR;
-  reg_t pc;
   reg_t cycle;
 
   // privileged control registers
@@ -58,7 +61,16 @@ private:
 
   bool run; // !reset
 
-  // functions
+  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;
+
   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
@@ -87,19 +99,26 @@ private:
   friend class mmu_t;
   friend class htif_isasim_t;
 
-  #include "dispatch.h"
+  #define DECLARE_INSN(name, match, mask) \
+    reg_t rv32_ ## name(insn_t insn, reg_t pc); \
+    reg_t rv64_ ## name(insn_t insn, reg_t pc);
+  #include "opcodes.h"
+  #undef DECLARE_INSN
+
+  insn_func_t decode_insn(insn_t insn);
+  reg_t illegal_instruction(insn_t insn, reg_t pc);
 };
 
 #ifndef RISCV_ENABLE_RVC
 # define set_pc(x) \
-  do { if((x) & (sizeof(insn_t)-1)) \
-       { badvaddr = (x); throw trap_instruction_address_misaligned; } \
+  do { if ((x) & 3) \
+         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; } \
+  do { if ((x) & ((sr & SR_EC) ? 1 : 3)) \
+         throw trap_instruction_address_misaligned; \
        npc = (x); \
      } while(0)
 #endif
index ce131d4e02057a347b350da3b84598d4ce8b696b..b9030dbad5cf73c6eaa214fe2028379a31c417c8 100644 (file)
@@ -1,3 +1,6 @@
+get_insn_list = $(shell cat $(1) | sed 's/DECLARE_INSN(\(.*\),.*,.*)/\1/')
+get_opcode = $(shell grep \\\<$(2)\\\> $(1) | sed 's/DECLARE_INSN(.*,\(.*\),.*)/\1/')
+
 riscv_subproject_deps = \
        softfloat_riscv \
        softfloat \
@@ -16,7 +19,6 @@ riscv_hdrs = \
        sim.h \
        trap.h \
        opcodes.h \
-       insn_header.h \
        cachesim.h \
        memtracer.h \
 
@@ -29,23 +31,17 @@ riscv_srcs = \
        cachesim.cc \
        mmu.cc \
        disasm.cc \
-       $(DISPATCH_SRCS) \
+       $(riscv_gen_srcs) \
 
 riscv_test_srcs =
 
 riscv_gen_hdrs = \
-       dispatch.h \
-
-NDISPATCH = 9
-DISPATCH_SRCS = $(addsuffix .cc,$(addprefix dispatch,$(shell seq 0 $(NDISPATCH))))
 
-$(DISPATCH_SRCS): %.cc: dispatch $(wildcard insns/*.h) opcodes.h
-       $< $(subst dispatch,,$(subst .cc,,$@)) $(NDISPATCH) 1024 < $(src_dir)/riscv/opcodes.h > $@
+riscv_gen_srcs = \
+       $(addsuffix .cc, $(call get_insn_list,$(src_dir)/riscv/opcodes.h))
 
-dispatch.h: %.h: dispatch opcodes.h
-       echo $(riscv_srcs)
-       $< $(NDISPATCH) 1024 < $(src_dir)/riscv/opcodes.h > $@
+$(riscv_gen_srcs): %.cc: insns/%.h insn_template.cc
+       sed 's/NAME/$(subst .cc,,$@)/' $(src_dir)/riscv/insn_template.cc | sed 's/OPCODE/$(call get_opcode,$(src_dir)/riscv/opcodes.h,$(subst .cc,,$@))/' > $@
 
 riscv_junk = \
-  dispatch.h \
-  $(DISPATCH_SRCS) \
+       $(riscv_gen_srcs) \
index 560bb705d10c5301ec71b1baff95bd89d81c7f09..a53f3f0f65102cd7602e4ad7ec54f945b7325314 100644 (file)
@@ -42,7 +42,7 @@ private:
   std::vector<processor_t*> procs;
 
   void step(size_t n, bool noisy); // step through simulation
-  static const size_t INTERLEAVE = 1000;
+  static const size_t INTERLEAVE = 5000;
   size_t current_step;
   size_t current_proc;
   bool debug;