Browse Source

Implement RoCC and add a dummy RoCC

Enable it with --extension=dummy
confprec
Andrew Waterman 13 years ago
parent
commit
bbb0f2179c
  1. 46
      riscv/dummy-rocc.h
  2. 29
      riscv/extension.cc
  3. 33
      riscv/extension.h
  4. 1
      riscv/pcr.h
  5. 56
      riscv/processor.cc
  6. 37
      riscv/processor.h
  7. 5
      riscv/riscv.mk.in
  8. 44
      riscv/rocc.cc
  9. 28
      riscv/rocc.h
  10. 2
      riscv/sim.cc
  11. 8
      riscv/spike.cc

46
riscv/dummy-rocc.h

@ -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

29
riscv/extension.cc

@ -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);
}

33
riscv/extension.h

@ -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

1
riscv/pcr.h

@ -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

56
riscv/processor.cc

@ -1,6 +1,7 @@
// See LICENSE for license details.
#include "processor.h"
#include "extension.h"
#include "common.h"
#include "config.h"
#include "sim.h"
@ -13,15 +14,12 @@
#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);
}

37
riscv/processor.h

@ -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

5
riscv/riscv.mk.in

@ -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 =

44
riscv/rocc.cc

@ -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;
}

28
riscv/rocc.h

@ -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

2
riscv/sim.cc

@ -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;
}

8
riscv/spike.cc

@ -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);

Loading…
Cancel
Save