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


riscv-isa-sim(いわゆるspikeシミュレータ)は便利なツールだが、例えば外部デバイスとして任意の場所に自作のデバイスを追加することができる。 例えば、自分の場合だと0x5400_0000に自作のシリアルデバイスを追加したい場合はどうするか。いろいろ情報は少ないのだが、spikeのソースコードとhelpファイルを見ながらいろいろ試行してみる。

$ spike --help
Spike RISC-V ISA Simulator 1.0.1-dev

usage: spike [host options] <target program> [target options]
Host Options:
  --device=<P,B,A>      Attach MMIO plugin device from an --extlib library
                          P -- Name of the MMIO plugin
                          B -- Base memory address of the device
                          A -- String arguments to pass to the plugin
                          This flag can be used multiple times.
                          The extlib flag for the library must come first.
  --log-cache-miss      Generate a log of cache miss
  --extension=<name>    Specify RoCC Extension
                          This flag can be used multiple times.
  --extlib=<name>       Shared library to load
                        This flag can be used multiple times.


// See LICENSE for license details.

#include <riscv/mmio_plugin.h>

#include "abstract_device.h"
#include "mmu.h"

class sim_t;
class bus_t;

class serialdevice_t : public abstract_device_t {
  serialdevice_t(std::string name);
  bool load(reg_t addr, size_t len, uint8_t* bytes);
  bool store(reg_t addr, size_t len, const uint8_t* bytes);

serialdevice_t::serialdevice_t(std::string name)
  std::cerr << "serialdevice: " << name << " loaded\n";

bool serialdevice_t::load(reg_t addr, size_t len, uint8_t* bytes)
  // std::cerr << "serialdevice_t::load called : addr = " << std::hex << addr << '\n';
  switch (addr) {
    case 5 : return 0x20;
    default : return 0x0;

  return true;

bool serialdevice_t::store(reg_t addr, size_t len, const uint8_t* bytes)
  // std::cerr << "serialdevice_t::store called : addr = " << std::hex << addr << '\n';
  std::cout << bytes[0];
  return true;

static mmio_plugin_registration_t<serialdevice_t> serialdevice_mmio_plugin_registration("serialdevice");



g++ -o libserialdevice.so -fPIC -shared serialdevice.cc -I./riscv-isa-sim/riscv -I./riscv-isa-sim -I./riscv-isa-sim/softfloat -I./riscv-isa-sim/fesvr


  • serialdevice_tabstract_device_t を継承している。
  • 必要なルーチンはコンストラクタ、load()store() のメソッド群。
  • 最後にmmio_plugin_registration_t型のラッパーで囲む。


  • mmio_plugin.h
  mmio_plugin_registration_t(const std::string& name)
    mmio_plugin_t plugin = {

    register_mmio_plugin(name.c_str(), &plugin);

register_mmio_plugin()は何をしているのか一瞬分らなかったのだが、static mmio_plugin_map_tのマップを作ってデバイスのマップ(というか一覧)を作成し追加している。

この操作をしているのが、spike.ccの引数処理の部分。--extlibで追加したプラグインに対して、そのプラグインをどの名前、どのアドレスで使用するかを指定している。 device_parser()では最後にmmio_plugin_device_tインスタンスを作成しているのがポイント。

  • spike.cc
  parser.option(0, "device", 1, device_parser);
  auto const device_parser = [&plugin_devices](const char *s) {
    const std::string str(s);
    std::istringstream stream(str);

    // We are parsing a string like name,base,args.

    // Parse the name, which is simply all of the characters leading up to the
    // first comma. The validity of the plugin name will be checked later.
    std::string name;
    std::getline(stream, name, ',');
    if (name.empty()) {
      throw std::runtime_error("Plugin name is empty.");

    // Parse the base address. First, get all of the characters up to the next
    // comma (or up to the end of the string if there is no comma). Then try to
    // parse that string as an integer according to the rules of strtoull. It
    // could be in decimal, hex, or octal. Fail if we were able to parse a
    // number but there were garbage characters after the valid number. We must
    // consume the entire string between the commas.
    std::string base_str;
    std::getline(stream, base_str, ',');
    if (base_str.empty()) {
      throw std::runtime_error("Device base address is empty.");
    char* end;
    reg_t base = static_cast<reg_t>(strtoull(base_str.c_str(), &end, 0));
    if (end != &*base_str.cend()) {
      throw std::runtime_error("Error parsing device base address.");

    // The remainder of the string is the arguments. We could use getline, but
    // that could ignore newline characters in the arguments. That should be
    // rare and discouraged, but handle it here anyway with this weird in_avail
    // technique. The arguments are optional, so if there were no arguments
    // specified we could end up with an empty string here. That's okay.
    auto avail = stream.rdbuf()->in_avail();
    std::string args(avail, '\0');
    stream.readsome(&args[0], avail);

    plugin_devices.emplace_back(base, new mmio_plugin_device_t(name, args));


./riscv-isa-sim/spike --extlib=libserialdevice.so --device=serialdevice,1409286144,uart --isa=rv64imac --dtb ../dts/rv64imac.dtb /home/msyksphinz/work/riscv/linux/riscv64-linux/output_spike/images/fw_jump.elf
serialdevice: uart loaded