From 0de1489e8ab4a527fbcb1440a8fd5b2d4c8c9260 Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Fri, 26 Jul 2013 03:34:51 -0700 Subject: [PATCH] Generate instruction decoder dynamically This will make it easier for accelerators to add instructions. --- riscv/decode.h | 2 +- riscv/disasm.cc | 4 +- riscv/dispatch | 83 ------------------------------------------ riscv/insn_header.h | 9 ----- riscv/insn_template.cc | 25 +++++++++++++ riscv/interactive.cc | 1 - riscv/mmu.cc | 14 ++----- riscv/mmu.h | 40 +++++++++----------- riscv/processor.cc | 51 ++++++++++++++++++++++++-- riscv/processor.h | 33 +++++++++++++---- riscv/riscv.mk.in | 22 +++++------ riscv/sim.h | 2 +- 12 files changed, 134 insertions(+), 152 deletions(-) delete mode 100755 riscv/dispatch delete mode 100644 riscv/insn_header.h create mode 100644 riscv/insn_template.cc diff --git a/riscv/decode.h b/riscv/decode.h index 3ef8864..be7d64d 100644 --- a/riscv/decode.h +++ b/riscv/decode.h @@ -123,7 +123,7 @@ union insn_t btype_t btype; ltype_t ltype; ftype_t ftype; - uint32_t bits; + uint_fast32_t bits; }; template diff --git a/riscv/disasm.cc b/riscv/disasm.cc index ca3b3dd..57f43d7 100644 --- a/riscv/disasm.cc +++ b/riscv/disasm.cc @@ -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 index 5afc3be..0000000 --- a/riscv/dispatch +++ /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 index c5f1080..0000000 --- a/riscv/insn_header.h +++ /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 diff --git a/riscv/insn_template.cc b/riscv/insn_template.cc new file mode 100644 index 0000000..3aca669 --- /dev/null +++ b/riscv/insn_template.cc @@ -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 + +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; +} diff --git a/riscv/interactive.cc b/riscv/interactive.cc index 66ea487..fe70760 100644 --- a/riscv/interactive.cc +++ b/riscv/interactive.cc @@ -185,7 +185,6 @@ reg_t sim_t::get_mem(const std::vector& 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]; } diff --git a/riscv/mmu.cc b/riscv/mmu.cc index 3cdb21d..8b63a43 100644 --- a/riscv/mmu.cc +++ b/riscv/mmu.cc @@ -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); diff --git a/riscv/mmu.h b/riscv/mmu.h index a5d150f..9e1218c 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -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 diff --git a/riscv/processor.cc b/riscv/processor.cc index 843279b..5d82937 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -13,9 +13,15 @@ #include 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; ifirst == 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<second)); + opcode_map = new_map; + } + + opcode_map.insert(std::make_pair(match & ((1L< #include "trap.h" #include "config.h" +#include #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 XPR; regfile_t 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 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 diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index ce131d4..b9030db 100644 --- a/riscv/riscv.mk.in +++ b/riscv/riscv.mk.in @@ -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) \ diff --git a/riscv/sim.h b/riscv/sim.h index 560bb70..a53f3f0 100644 --- a/riscv/sim.h +++ b/riscv/sim.h @@ -42,7 +42,7 @@ private: std::vector 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; -- 2.30.2