FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

RISC-Vの命令セットシミュレータSpikeでもRoCCインタフェースがシミュレーションできる?

f:id:msyksphinz:20170201002310j:plain

UCBの開発しているシミュレータSpikeは、RISC-Vの命令セットをシミュレーションすることのできるISSで、サイクル精度を出すことはできないが最初のアプリケーションのデバッグに有用なツールだ。

そして、RISC-Vにはカスタム命令 (custom0 - custom3)が用意されており、Rocket Chipの場合にはこの命令フィールドを用いてRoCCインタフェース上にアクセラレータを搭載することが出来る。

どうせ元はChiselなのだから、この辺をうまくマージすることが出来ないだろうかといろいろ調査していたのだが、どうやらRoCCインタフェースをSpikeシミュレータではシミュレーションすることが出来るようだ。

github.com

riscv-isa-simのリポジトリを眺めていると、RoCCの型が定義されており、そこでダミーのRoCCを定義してシミュレーションをすることが出来る。

github.com

class extension_t
{
 public:
  virtual std::vector<insn_desc_t> get_instructions() = 0;
  virtual std::vector<disasm_insn_t*> get_disasms() = 0;
  virtual const char* name() = 0;
  virtual void reset() {};
  virtual void set_debug(bool value) {};
  virtual ~extension_t();

  void set_processor(processor_t* _p) { p = _p; }
 protected:
  processor_t* p;

  void illegal_instruction();
  void raise_interrupt();
  void clear_interrupt();
};
struct rocc_insn_t
{
  unsigned opcode : 7;
  unsigned rd : 5;
  unsigned xs2 : 1;
  unsigned xs1 : 1;
  unsigned xd : 1;
  unsigned rs1 : 5;
  unsigned rs2 : 5;
  unsigned funct : 7;
};

union rocc_insn_union_t
{
  rocc_insn_t r;
  insn_t i;
};

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();
  std::vector<disasm_insn_t*> get_disasms();
};

例えば、riscv-isa-sim/riscv/dummy_rocc.cc にはサンプルのデザインが書いてある。これは、Rocket-ChipにおけるAccumulatorExampleのデザインそのままだね。

  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]
  }