Browse Source

Support 2/4/6/8-byte instructions

Most of the complexity is in instruction address translation, since
instructions may span page boundaries.
pull/11/head
Andrew Waterman 11 years ago
parent
commit
d643e43dca
  1. 4
      hwacha/insns/vf.h
  2. 40
      riscv/decode.h
  3. 45
      riscv/mmu.h
  4. 18
      riscv/processor.cc
  5. 6
      spike/riscv-dis.cc

4
hwacha/insns/vf.h

@ -10,7 +10,7 @@ vf_loop:
if (VF_PC & 3)
h->take_exception(HWACHA_CAUSE_VF_MISALIGNED_FETCH, VF_PC);
insn_t ut_insn = p->get_mmu()->load_insn(VF_PC).insn.insn;
insn_t ut_insn = p->get_mmu()->load_insn(VF_PC).insn;
bool matched = false;
@ -30,7 +30,7 @@ vf_loop:
if (h->vf_active())
goto vf_loop;
} else {
fprintf(stderr, "vf block: 0x%016" PRIx64 " (0x%08" PRIx32 ") %s\n",
fprintf(stderr, "vf block: 0x%016" PRIx64 " (0x%08" PRIx64 ") %s\n",
VF_PC, ut_insn.bits(), h->get_ut_disassembler()->disassemble(ut_insn).c_str());
if (h->vf_active())
npc = pc;

40
riscv/decode.h

@ -44,26 +44,29 @@ const int NFPR = 32;
#define FSR_NXA (FPEXC_NX << FSR_AEXC_SHIFT)
#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA)
typedef uint64_t insn_bits_t;
class insn_t
{
public:
uint32_t bits() { return b; }
int32_t i_imm() { return int32_t(b) >> 20; }
int32_t s_imm() { return x(7, 5) + (xs(25, 7) << 5); }
int32_t sb_imm() { return (x(8, 4) << 1) + (x(25,6) << 5) + (x(7,1) << 11) + (imm_sign() << 12); }
int32_t u_imm() { return int32_t(b) >> 12 << 12; }
int32_t uj_imm() { return (x(21, 10) << 1) + (x(20, 1) << 11) + (x(12, 8) << 12) + (imm_sign() << 20); }
uint32_t rd() { return x(7, 5); }
uint32_t rs1() { return x(15, 5); }
uint32_t rs2() { return x(20, 5); }
uint32_t rs3() { return x(27, 5); }
uint32_t rm() { return x(12, 3); }
uint32_t csr() { return x(20, 12); }
insn_t() = default;
insn_t(insn_bits_t bits) : b(bits) {}
insn_bits_t bits() { return b; }
int64_t i_imm() { return int64_t(b) >> 20; }
int64_t s_imm() { return x(7, 5) + (xs(25, 7) << 5); }
int64_t sb_imm() { return (x(8, 4) << 1) + (x(25,6) << 5) + (x(7,1) << 11) + (imm_sign() << 12); }
int64_t u_imm() { return int64_t(b) >> 12 << 12; }
int64_t uj_imm() { return (x(21, 10) << 1) + (x(20, 1) << 11) + (x(12, 8) << 12) + (imm_sign() << 20); }
uint64_t rd() { return x(7, 5); }
uint64_t rs1() { return x(15, 5); }
uint64_t rs2() { return x(20, 5); }
uint64_t rs3() { return x(27, 5); }
uint64_t rm() { return x(12, 3); }
uint64_t csr() { return x(20, 12); }
private:
uint32_t b;
uint32_t x(int lo, int len) { return b << (32-lo-len) >> (32-len); }
uint32_t xs(int lo, int len) { return int32_t(b) << (32-lo-len) >> (32-len); }
uint32_t imm_sign() { return xs(31, 1); }
insn_bits_t b;
uint64_t x(int lo, int len) { return (b >> lo) & ((insn_bits_t(1) << len)-1); }
uint64_t xs(int lo, int len) { return int64_t(b) << (64-lo-len) >> (64-len); }
uint64_t imm_sign() { return xs(63, 1); }
};
template <class T, size_t N, bool zero_reg>
@ -76,9 +79,8 @@ public:
}
void write(size_t i, T value)
{
data[i] = value;
if (zero_reg)
data[0] = 0;
if (!zero_reg || i != 0)
data[i] = value;
}
const T& operator [] (size_t i) const
{

45
riscv/mmu.h

@ -25,10 +25,7 @@ const reg_t VA_BITS = VPN_BITS + PGSHIFT;
struct insn_fetch_t
{
insn_func_t func;
union {
insn_t insn;
uint_fast32_t pad;
} insn;
insn_t insn;
};
struct icache_entry_t {
@ -77,27 +74,49 @@ public:
store_func(uint32)
store_func(uint64)
inline size_t icache_index(reg_t addr)
{
// for instruction sizes != 4, this hash still works but is suboptimal
return (addr / 4) % ICACHE_SIZE;
}
// load instruction from memory at aligned address.
inline icache_entry_t* access_icache(reg_t addr)
icache_entry_t* access_icache(reg_t addr) __attribute__((always_inline))
{
reg_t idx = (addr / sizeof(insn_t)) % ICACHE_SIZE;
reg_t idx = icache_index(addr);
icache_entry_t* entry = &icache[idx];
if (likely(entry->tag == addr))
return entry;
void* iaddr = translate(addr, sizeof(insn_t), false, true);
insn_fetch_t fetch;
fetch.insn.pad = *(decltype(fetch.insn.insn.bits())*)iaddr;
fetch.func = proc->decode_insn(fetch.insn.insn);
char* iaddr = (char*)translate(addr, 2, false, true);
insn_bits_t insn = *(uint16_t*)iaddr;
if (unlikely(insn_length(insn) == 2)) {
insn = (int16_t)insn;
} else if (likely(insn_length(insn) == 4)) {
if (likely((addr & (PGSIZE-1)) < PGSIZE-2))
insn |= (insn_bits_t)*(int16_t*)(iaddr + 2) << 16;
else
insn |= (insn_bits_t)*(int16_t*)translate(addr + 2, 2, false, true) << 16;
} else if (insn_length(insn) == 6) {
insn |= (insn_bits_t)*(int16_t*)translate(addr + 4, 2, false, true) << 32;
insn |= (insn_bits_t)*(uint16_t*)translate(addr + 2, 2, false, true) << 16;
} else {
static_assert(sizeof(insn_bits_t) == 8, "insn_bits_t must be uint64_t");
insn |= (insn_bits_t)*(int16_t*)translate(addr + 6, 2, false, true) << 48;
insn |= (insn_bits_t)*(uint16_t*)translate(addr + 4, 2, false, true) << 32;
insn |= (insn_bits_t)*(uint16_t*)translate(addr + 2, 2, false, true) << 16;
}
insn_fetch_t fetch = {proc->decode_insn(insn), insn};
icache[idx].tag = addr;
icache[idx].data = fetch;
reg_t paddr = (char*)iaddr - mem;
if (!tracer.empty() && tracer.interested_in_range(paddr, paddr + sizeof(insn_t), false, true))
reg_t paddr = iaddr - mem;
if (!tracer.empty() && tracer.interested_in_range(paddr, paddr + 1, false, true))
{
icache[idx].tag = -1;
tracer.trace(paddr, sizeof(insn_t), false, true);
tracer.trace(paddr, 1, false, true);
}
return &icache[idx];
}

18
riscv/processor.cc

@ -129,13 +129,13 @@ static void commit_log(state_t* state, insn_t insn)
#ifdef RISCV_ENABLE_COMMITLOG
if (state->sr & SR_EI) {
if (state->log_reg_write.addr) {
fprintf(stderr, "0x%016" PRIx64 " (0x%08" PRIx32 ") %c%2u 0x%016" PRIx64 "\n",
fprintf(stderr, "0x%016" PRIx64 " (0x%08" PRIx64 ") %c%2u 0x%016" PRIx64 "\n",
state->pc, insn.bits(),
state->log_reg_write.addr & 1 ? 'f' : 'x',
state->log_reg_write.addr >> 1, state->log_reg_write.data);
}
else {
fprintf(stderr, "0x%016" PRIx64 " (0x%08" PRIx32 ")\n",
fprintf(stderr, "0x%016" PRIx64 " (0x%08" PRIx64 ")\n",
state->pc, insn.bits());
}
}
@ -153,8 +153,8 @@ inline void processor_t::update_histogram(size_t pc)
static reg_t execute_insn(processor_t* p, reg_t pc, insn_fetch_t fetch)
{
reg_t npc = fetch.func(p, fetch.insn.insn, pc);
commit_log(p->get_state(), fetch.insn.insn);
reg_t npc = fetch.func(p, fetch.insn, pc);
commit_log(p->get_state(), fetch.insn);
p->update_histogram(pc);
return npc;
}
@ -192,13 +192,13 @@ void processor_t::step(size_t n)
while (instret++ < n)
{
insn_fetch_t fetch = mmu->load_insn(pc);
disasm(fetch.insn.insn);
disasm(fetch.insn);
pc = execute_insn(this, pc, fetch);
}
}
else while (instret < n)
{
size_t idx = (pc / sizeof(insn_t)) % ICACHE_SIZE;
size_t idx = _mmu->icache_index(pc);
auto ic_entry = _mmu->access_icache(pc);
#define ICACHE_ACCESS(idx) { \
@ -251,9 +251,9 @@ void processor_t::deliver_ipi()
void processor_t::disasm(insn_t insn)
{
// the disassembler is stateless, so we share it
fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIx32 ") %s\n",
id, state.pc, insn.bits(), disassembler->disassemble(insn).c_str());
uint64_t bits = insn.bits() & ((1ULL << (8 * insn_length(insn.bits()))) - 1);
fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIx64 ") %s\n",
id, state.pc, bits, disassembler->disassemble(insn).c_str());
}
void processor_t::set_pcr(int which, reg_t val)

6
spike/riscv-dis.cc

@ -32,10 +32,8 @@ int main(int argc, char** argv)
break;
size_t numstart = start + strlen("DASM(");
uint32_t n = strtoul(&s[numstart], NULL, 16);
string dis = d.disassemble(*(insn_t*)&n);
insn_bits_t bits = strtoull(&s[numstart], NULL, 16);
string dis = d.disassemble(bits);
s = s.substr(0, start) + dis + s.substr(end+1);
start += dis.length();
}

Loading…
Cancel
Save