This will make it easier for accelerators to add instructions.
btype_t btype;
ltype_t ltype;
ftype_t ftype;
- uint32_t bits;
+ uint_fast32_t bits;
};
template <class T>
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
+++ /dev/null
-#!/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'
+++ /dev/null
-// 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>
--- /dev/null
+// 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;
+}
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];
}
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()
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();
}
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;
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);
struct insn_fetch_t
{
- insn_t insn;
insn_func_t func;
+ insn_t insn;
};
// load instruction from memory at aligned address.
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))
{
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*);
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
#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++)
{
// 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());
}
sr &= ~SR_EV;
#endif
sr &= ~SR_ZERO;
- mmu.set_sr(sr);
+ mmu.flush_tlb();
break;
case PCR_EPC:
epc = val;
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}));
+}
#include <cstring>
#include "trap.h"
#include "config.h"
+#include <map>
#define MAX_UTS 2048
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
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
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
+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 \
sim.h \
trap.h \
opcodes.h \
- insn_header.h \
cachesim.h \
memtracer.h \
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) \
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;