FPGA開発日記

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

RISC-Vの命令セットシミュレータSpikeでRoCCインタフェースを実装する2.

f:id:msyksphinz:20170201002310j:plain

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

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

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

今回はmemtotal_rocc.cc を作成してディレクトリを作成しビルドした。32ビット整数のDotProductを計算する回路(Chiselで作成したもの)と同一のものをビルド作成した。

  • riscv-tools/riscv-isa-sim/memtotal_rocc/memtotal_rocc.cc
class memtotal_rocc_t : public rocc_t
{
 public:
  const char* name() { return "memtotal_rocc"; }

  reg_t custom0(rocc_insn_t insn, reg_t xs1, reg_t xs2)
  {
    reg_t total = 0;
    switch (insn.funct)
    {
    case 0:
      c0_reg_lengthM = xs1;
      break;
    case 1:
      c0_reg_lengthK = xs1;
      break;
    case 2: {
      reg_t total_lo = 0, total_hi = 0;
      reg_t xs1_p = xs1, xs2_p = xs2;
      for (reg_t i = 0; i < c0_reg_lengthM; i++) {
        total_lo += p->get_mmu()->load_int32(xs1_p) * p->get_mmu()->load_int32(xs2_p);
        xs1_p += sizeof(int32_t);
        xs2_p += c0_reg_lengthK * sizeof(int32_t);
      }
      xs1_p = xs1;
      xs2_p = xs2 + sizeof(int32_t);
      for (reg_t i = 0; i < c0_reg_lengthM; i++) {
        total_hi += p->get_mmu()->load_int32(xs1_p) * p->get_mmu()->load_int32(xs2_p);
        xs1 += sizeof(int32_t);
        xs2 += c0_reg_lengthK * sizeof(int32_t);
      }
      total = ((total_hi & 0x0ffffffffUL) << 32) | (total_lo & 0x0ffffffffUL);
      break;
    }
    }
    return total;
  }

一度build.shを実行しないとちゃんと動作しなかった。このへんはもうちょっとビルドフローを調査する必要がある。ともかく、roccインタフェースをC++で記述するとRoCCの動作をC++で記述することが出来る。

拡張として memtotal_roccのライブラリを指定するとちゃんとRoCCアクセラレータの実装をSpikeで実行することが出来る。これは面白い。

./spike --extension=memtotal_rocc /home/msyksphinz/riscv64/riscv64-unknown-elf/share/riscv-tests/benchmarks/test-matrixmul32.riscv```
f:id:msyksphinz:20171101021825p:plain

追記: libmemtotal_rocc.so のビルドが可能なプロジェクトをgithubに置きました。

github.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シミュレータではシミュレーションすることが出来るようだ。

まず、サンプルプログラムとして dummy_rocc/ディレクトリにサンプルが存在している。

dummy_rocc/
├── dummy_rocc.ac
├── dummy_rocc.cc
├── dummy_rocc.mk.in
└── dummy_rocc_test.c

0 directories, 4 files

この実装 dummy_rocc.ccrocc_t を継承する形で実装されている。

  • https://github.com/riscv/riscv-isa-sim/blob/master/dummy_rocc/dummy_rocc.cc
class dummy_rocc_t : public rocc_t
{
 public:
  const char* name() { return "dummy_rocc"; }

  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)
...

これを拡張して memtotal_rocc.ccを作成してビルドを行ってみる。そのために memtotal_rocc/ ディレクトリを作成する。

$ tree memtotal_rocc/ -L 2
memtotal_rocc/
├── memtotal_rocc.ac
├── memtotal_rocc.cc
├── memtotal_rocc.mk.in
└── memtotal_rocc_test.c

0 directories, 4 files
class memtotal_rocc_t : public rocc_t
{
 public:
  const char* name() { return "memtotal_rocc"; }

...

  reg_t custom0(rocc_insn_t insn, reg_t xs1, reg_t xs2)
  {
    switch (insn.funct)
    {
      case 0:
        c0_reg_lengthM = xs1;
        break;
      case 1:
        c0_reg_lengthK = xs1;
        break;
      case 2:

        reg_t total = 0;
        for (reg_t i = 0; i < c0_reg_lengthK; i++) {
          total += p->get_mmu()->load_int32(xs1) * p->get_mmu()->load_int32(xs2);
          xs1 += sizeof(int32_t);
          xs2 += c0_reg_lengthK * sizeof(int32_t);
        }
        break;
    }
    return 0;
  }

ここで custom0 () の実装で、メモリアクセスを p->get_mmu()->load_int32(xs1) * p->get_mmu()->load_int32(xs2) としている。

さらにこの memtotal_rocc をコンパイルするようにサブモジュールとして登録する。

  • riscv-isa-sim/configure.ac
diff --git a/configure.ac b/configure.ac
index e361877..64759fa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -86,7 +86,7 @@ AC_SUBST([CXXFLAGS],["-Wall -Wno-unused -g -O2 -std=c++11"])
 # The '*' suffix indicates an optional subproject. The '**' suffix
 # indicates an optional subproject which is also the name of a group.

-MCPPBS_SUBPROJECTS([ riscv, dummy_rocc, softfloat, spike_main ])
+MCPPBS_SUBPROJECTS([ riscv, dummy_rocc, memtotal_rocc, softfloat, spike_main ])

 #-------------------------------------------------------------------------
 # MCPPBS subproject groups

これでビルドするとlibmemtotal.soが作成される。

build$ ls -1 *.so
libdummy_rocc.so
libmemtotal_rocc.so
libriscv.so
libsoftfloat.so
libspike_main.so

これをオプションとして指定することで、extensionとして使用できるようになる。が、--extension=memtotal_roccで何故か認識されなかった。要解析。

$ ./spike --extension=dummy_rocc
usage: spike [host options] <target program> [target options]
Host Options:
  -p<n>                 Simulate <n> processors [default 1]
  -m<n>                 Provide <n> MiB of target memory [default 2048]
  -m<a:m,b:n,...>       Provide memory regions of size m and n bytes
                          at base addresses a and b (with 4 KiB alignment)
  -d                    Interactive debug mode
  -g                    Track histogram of PCs
  -l                    Generate a log of execution
  -h                    Print this help message
  -H                 Start halted, allowing a debugger to connect
  --isa=<name>          RISC-V ISA string [default rv32ima]
  --pc=<address>        Override ELF entry point
  --ic=<S>:<W>:<B>      Instantiate a cache model with S sets,
  --dc=<S>:<W>:<B>        W ways, and B-byte blocks (with S and
  --l2=<S>:<W>:<B>        B both powers of 2).
  --extension=<name>    Specify RoCC Extension
  --extlib=<name>       Shared library to load
  --rbb-port=<port>     Listen on <port> for remote bitbang connection
  --dump-dts  Print device tree string and exit
$ ./spike --extension=memtotal_rocc
couldn't find extension 'memtotal_rocc' (or library 'libmemtotal_rocc.so')

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

Rocket ChipにはDual Coreのコンフィグレーションがある(1. シミュレーション試行 & Vivado合成試行)

UCBの開発したRISC-V実装であるRocket Chipは基本的にシングルコアのモードで動作させるが、よく見るとデュアルコアのモードも存在する。

class DualCoreConfig extends Config(
  new WithNBigCores(2) ++ new BaseConfig)

つまり、emulator/ディレクトリで以下のようにすれば一応デュアルコアのRocket Chipが作れることになる。

make CONFIG=DualCoreConfig

さらに、デュアルコア Rocket Chipの環境でDhrystoneを動かすためには、outputファイルを指定すればよい。

make CONFIG=DualCoreConfig output/dhrystone.riscv.out
  • 実行結果 :
$ make CONFIG=DualCoreConfig output/dhrystone.riscv.out
mkdir -p ./output
ln -fs /home/msyksphinz/riscv64//riscv64-unknown-elf/share/riscv-tests/benchmarks/dhrystone.riscv output/dhrystone.riscv
./emulator-freechips.rocketchip.system-DualCoreConfig +max-cycles=100000000 +verbose output/dhrystone.riscv 3>&1 1>&2 2>&3 | /home/msyksphinz/riscv64/bin/spike-dasm  > output/dhrystone.riscv.out && [ $PIPESTATUS -eq 0 ]
Microseconds for one run through Dhrystone: 532
Dhrystones per Second:                      1877
mcycle = 266427
minstret = 203319

実行してみると、output/dhrystone.riscv.out に2コア分の情報が出力された。C0がコア0、C1がコア1の情報のようだ。

C1:        901 [0] pc=[000001004c] W[r 0=ffffffea5d08a67f][0] R[r 0=a2550095c80cb9b7] R[r 5=9b4288d9bf70827b] inst=[10500073] wfi (args unknown)
C0:        902 [1] pc=[0000000828] W[r 8=0000000000000000][1] R[r 0=0000000000000000] R[r20=0000000000000003] inst=[f1402473] csrr    s0, mhartid
C1:        902 [0] pc=[000001004c] W[r 0=ffffffea5d08a67f][0] R[r 0=a2550095c80cb9b7] R[r 5=9b4288d9bf70827b] inst=[10500073] wfi (args unknown)
C0:        903 [0] pc=[0000000828] W[r 0=0000000000000000][0] R[r 0=0000000000000000] R[r20=0000000000000003] inst=[f1402473] csrr    s0, mhartid
C1:        903 [0] pc=[000001004c] W[r 0=ffffffea5d08a67f][0] R[r 0=a2550095c80cb9b7] R[r 5=9b4288d9bf70827b] inst=[10500073] wfi (args unknown)
C0:        904 [0] pc=[0000000828] W[r 0=0000000000000000][0] R[r 0=0000000000000000] R[r20=0000000000000003] inst=[f1402473] csrr    s0, mhartid
C1:        904 [1] pc=[0000010050] W[r 0=0000000000010052][1] R[r31=a2550095c80cb9b7] R[r29=9b4288d9bf70827b] inst=[0000bff5] j       pc - 4
C0:        905 [1] pc=[000000082c] W[r 0=0000000000000400][1] R[r 8=0000000000000000] R[r 0=0000000000000000] inst=[40044403] lbu     s0, 1024(s0)
C1:        905 [0] pc=[0000010050] W[r 0=0000000000010052][0] R[r31=a2550095c80cb9b7] R[r29=9b4288d9bf70827b] inst=[0000bff5] j       pc - 4
C0:        906 [0] pc=[000000082c] W[r 0=0000000000000400][0] R[r 8=0000000000000000] R[r 0=0000000000000000] inst=[40044403] lbu     s0, 1024(s0)
C1:        906 [1] pc=[000001004c] W[r 0=ffffffea5d08a67f][0] R[r 0=0000000000000000] R[r 5=9b4288d9bf70827b] inst=[10500073] wfi (args unknown)
C0:        907 [0] pc=[000000082c] W[r 0=0000000000000400][0] R[r 8=0000000000000000] R[r 0=0000000000000000] inst=[40044403] lbu     s0, 1024(s0)
C1:        907 [0] pc=[000001004c] W[r 0=ffffffea5d08a67f][0] R[r 0=a2550095c80cb9b7] R[r 5=9b4288d9bf70827b] inst=[10500073] wfi (args unknown)
C0:        908 [0] pc=[000000082c] W[r 0=0000000000000400][0] R[r 8=0000000000000000] R[r 0=0000000000000000] inst=[40044403] lbu     s0, 1024(s0)
C1:        908 [0] pc=[000001004c] W[r 0=ffffffea5d08a67f][0] R[r 0=a2550095c80cb9b7] R[r 5=9b4288d9bf70827b] inst=[10500073] wfi (args unknown)
C0:        909 [0] pc=[000000082c] W[r 0=0000000000000400][0] R[r 8=0000000000000000] R[r 0=0000000000000000] inst=[40044403] lbu     s0, 1024(s0)

それぞれ分割してみる。

grep "\[1\] pc" output/dhrystone.riscv.out | grep C0 > output/dhrystone.c0.out
grep "\[1\] pc" output/dhrystone.riscv.out | grep C1 > output/dhrystone.c1.out

どうやらコア0はDhrystoneを普通に実行して、コア1は休んでいるようだ。

  • output/dhrystone.c0.out (抜粋)
...
C0:     539000 [1] pc=[0000000804] W[r 0=0000000000000808][1] R[r 0=0000000000000000] R[r12=0000000000000003] inst=[04c0006f] j       pc + 0x4c
C0:     539023 [1] pc=[0000000850] W[r 8=0000000000000000][1] R[r 0=0000000000000000] R[r20=0000000000000003] inst=[f1402473] csrr    s0, mhartid
C0:     539026 [1] pc=[0000000854] W[r 0=0000000000000108][0] R[r 0=0000000000000000] R[r 8=0000000000000000] inst=[10802423] sw      s0, 264(zero)
C0:     539027 [1] pc=[0000000858] W[r 8=00000000800050f0][1] R[r 0=0000000000000000] R[r18=0000000000000003] inst=[7b202473] csrr    s0, dscratch
C0:     539028 [1] pc=[000000085c] W[r 0=00000000800050f0][0] R[r 0=0000000000000000] R[r18=0000000000000003] inst=[7b200073] dret (args unknown)
  • output/dhrystone.c1.out` (抜粋)
C1:     163725 [1] pc=[0080000104] W[r 4=0000000080005140][1] R[r 4=000000008000517f] R[r 0=0000000000000000] inst=[fc027213] andi    tp, tp, -64
C1:     163726 [1] pc=[0080000108] W[r10=0000000000000001][1] R[r 0=0000000000000000] R[r20=0000000000000003] inst=[f1402573] csrr    a0, mhartid
C1:     163727 [1] pc=[008000010c] W[r11=0000000000000001][1] R[r 0=0000000000000000] R[r 1=0000000000000003] inst=[00004585] li      a1, 1
C1:     163729 [1] pc=[008000010e] W[r 0=0000000000000000][0] R[r10=0000000000000001] R[r11=0000000000000001] inst=[00b57063] bgeu    a0, a1, pc + 0
C1:     163734 [1] pc=[008000010e] W[r 0=0000000000000000][0] R[r10=0000000000000001] R[r11=0000000000000001] inst=[00b57063] bgeu    a0, a1, pc + 0
C1:     163739 [1] pc=[008000010e] W[r 0=0000000000000000][0] R[r10=0000000000000001] R[r11=0000000000000001] inst=[00b57063] bgeu    a0, a1, pc + 0
C1:     163741 [1] pc=[008000010e] W[r 0=0000000000000000][0] R[r10=0000000000000001] R[r11=0000000000000001] inst=[00b57063] bgeu    a0, a1, pc + 0
C1:     163744 [1] pc=[008000010e] W[r 0=0000000000000000][0] R[r10=0000000000000001] R[r11=0000000000000001] inst=[00b57063] bgeu    a0, a1, pc + 0
C1:     163749 [1] pc=[008000010e] W[r 0=0000000000000000][0] R[r10=0000000000000001] R[r11=0000000000000001] inst=[00b57063] bgeu    a0, a1, pc + 0

デュアルコア構成でZedBoard向けビルドを実行してみる

以下の変更を加えて、DualCore向けの構成を作成する。 FPGAのビルド用に、 ZynqDualConfigを作成した。

diff --git a/common/src/main/scala/Configs.scala b/common/src/main/scala/Configs.scala
index 7ae2c38..40addd2 100644
--- a/common/src/main/scala/Configs.scala
+++ b/common/src/main/scala/Configs.scala
@@ -39,6 +39,7 @@ class WithSmallCores extends Config(
   })

 class ZynqConfig extends Config(new WithZynqAdapter ++ new DefaultFPGAConfig)
+class ZynqDualConfig extends Config(new WithZynqAdapter ++ new DefaultFPGADualConfig)
 class ZynqSmallConfig extends Config(new WithSmallCores ++ new ZynqConfig)

 class WithIntegrationTest extends Config(
  • rocket-chip/src/main/scala/rocketchip/Configs.scala
diff --git a/src/main/scala/rocketchip/Configs.scala b/src/main/scala/rocketchip/Configs.scala
index e253bfb..5387c14 100644
--- a/src/main/scala/rocketchip/Configs.scala
+++ b/src/main/scala/rocketchip/Configs.scala
@@ -92,6 +92,8 @@ class FPGAConfig extends Config (
 )

 class DefaultFPGAConfig extends Config(new FPGAConfig ++ new BaseConfig)
+class DefaultFPGADualConfig extends Config(new FPGAConfig ++ new WithNCores(2) ++ new BaseConfig)
 class DefaultL2FPGAConfig extends Config(
   new WithL2Capacity(64) ++ new WithL2Cache ++ new DefaultFPGAConfig)

で、makeを実行する。 zedboard ディレクトリで、 make CONFIG=ZynqDualConfig rocket && make CONFIG=ZynqDualConfigを実行した。

しかし、2コアはZedBoardのFPGAで入りきらなかった。残念。

...
Netlist sorting complete. Time (s): cpu = 00:00:00.09 ; elapsed = 00:00:00.09 . Memory (MB): peak = 2660.359 ; gain = 0.000 ; free physical = 3793 ; free virtual = 8269

Phase 1.2 IO Placement/ Clock Placement/ Build Placer Device
ERROR: [Place 30-640] Place Check : This design requires more Slice LUTs cells than are available in the target device. This design requires 58438 of such cell types but only 53200 compatible sites are available in the target device. Ple
ase analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device. Please set tcl parameter "drc.disableLUTOverUtilError" to 1 to change t
his error to warning.
ERROR: [Place 30-640] Place Check : This design requires more LUT as Logic cells than are available in the target device. This design requires 57297 of such cell types but only 53200 compatible sites are available in the target device. P
lease analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device. Please set tcl parameter "drc.disableLUTOverUtilError" to 1 to change
 this error to warning.
INFO: [Timing 38-35] Done setting XDC timing constraints.
Phase 1.2 IO Placement/ Clock Placement/ Build Placer Device | Checksum: e1670006

Time (s): cpu = 00:00:19 ; elapsed = 00:00:12 . Memory (MB): peak = 2660.359 ; gain = 0.000 ; free physical = 3699 ; free virtual = 8179
Phase 1 Placer Initialization | Checksum: e1670006

Time (s): cpu = 00:00:19 ; elapsed = 00:00:12 . Memory (MB): peak = 2660.359 ; gain = 0.000 ; free physical = 3699 ; free virtual = 8179
ERROR: [Place 30-99] Placer failed with error: 'Implementation Feasibility check failed, Please see the previously displayed individual error or warning messages for more details.'
Please review all ERROR, CRITICAL WARNING, and WARNING messages during placement to understand the cause for failure.
Ending Placer Task | Checksum: e1670006

Time (s): cpu = 00:00:19 ; elapsed = 00:00:12 . Memory (MB): peak = 2660.359 ; gain = 0.000 ; free physical = 3700 ; free virtual = 8180
49 Infos, 0 Warnings, 0 Critical Warnings and 4 Errors encountered.
place_design failed

f:id:msyksphinz:20171029221532p:plain

SmallConfigを使うと一応入った。

diff --git a/common/src/main/scala/Configs.scala b/common/src/main/scala/Configs.scala
index 7ae2c38..3a30d2c 100644
--- a/common/src/main/scala/Configs.scala
+++ b/common/src/main/scala/Configs.scala
@@ -39,6 +39,8 @@ class WithSmallCores extends Config(
   })

 class ZynqConfig extends Config(new WithZynqAdapter ++ new DefaultFPGAConfig)
+class ZynqDualConfig extends Config(new WithZynqAdapter ++ new DefaultFPGADualConfig)
+class ZynqDualSmallConfig extends Config(new WithZynqAdapter ++ new DefaultFPGASmallDualConfig)
 class ZynqSmallConfig extends Config(new WithSmallCores ++ new ZynqConfig)
  • rocket-chip/src/main/scala/rocketchip/Configs.scala
diff --git a/src/main/scala/rocketchip/Configs.scala b/src/main/scala/rocketchip/Configs.scala
index e253bfb..a4c0aa3 100644
--- a/src/main/scala/rocketchip/Configs.scala
+++ b/src/main/scala/rocketchip/Configs.scala
@@ -92,6 +92,9 @@ class FPGAConfig extends Config (
 )

 class DefaultFPGAConfig extends Config(new FPGAConfig ++ new BaseConfig)
+class DefaultFPGADualConfig extends Config(new FPGAConfig ++ new WithNCores(2) ++ new BaseConfig)
+class DefaultFPGASmallDualConfig extends Config(new FPGAConfig ++ new WithSmallCores ++ new WithNCores(2) ++ new BaseConfig)
 class DefaultL2FPGAConfig extends Config(
   new WithL2Capacity(64) ++ new WithL2Cache ++ new DefaultFPGAConfig)

DefaultFPGASmallDualConfigを使うようにする。make CONFIG=ZynqDualSmallConfigで合成とインプリメントを実行した。

f:id:msyksphinz:20171030011226p:plain

f:id:msyksphinz:20171030011251p:plain

MicroPython試行(2. RISC-V移植環境の調査)

MicroPythonは一つの題材なのだけれども、いろんなアプリケーションを移植してRISC-Vで動作させれば、少しはアプリケーションのことが分かるようなるかもしれない。

MicroPythonを題材にして、RISC-Vのソフトウェア実行環境とか、そのあたりについて勉強していこう。

MicroPython のアーキテクチャ移植における変更点

アーキテクチャの変更点においては、 py/nlr[ARCH].c にまとめてある。ここに今回はpy/nlrriscv32.cを作成した。

また、コンパイル時にターゲットアーキテクチャを決めるためのGCCのdefine文として、__riscvを使用した。これらのターゲットの定義defineは、以下で調査することが出来る。

riscv32-unknown-elf-cpp -dM /dev/null  | sort
...
#define __WINT_TYPE__ unsigned int
#define __WINT_WIDTH__ 32
#define __has_include(STR) __has_include__(STR)
#define __has_include_next(STR) __has_include_next__(STR)
#define __riscv 1
#define __riscv_atomic 1
#define __riscv_cmodel_medlow 1
#define __riscv_div 1
#define __riscv_float_abi_soft 1
#define __riscv_mul 1
#define __riscv_muldiv 1
#define __riscv_xlen 32
  • py/nlrriscv32.c (抜粋)
#include "py/mpstate.h"

#if !MICROPY_NLR_SETJMP && defined(__riscv)

#undef nlr_push

unsigned int nlr_push(nlr_buf_t *nlr) {

    __asm volatile (
    "sw  x2,  8 (x10) \n" // save regs...
    "sw  x8,  12(x10) \n"
    "sw  x9,  16(x10) \n"
...

とりあえず、 nlr_push()nlr_jump() の2つをいろいろ調べて移植してみた。が、まだちゃんと動作していない。

make clean && make
spike pk build/firmware.elf

z  00000000 ra 00026664 sp 7fbe5d20 gp 000601a8
tp 00000000 t0 000451f8 t1 00040000 t2 00000000
s0 0005dbd0 s1 00000000 a0 00000001 a1 00000000
a2 00000000 a3 00000000 a4 00060648 a5 7fbe5d30
a6 0000000f a7 7fbe5c64 s2 00000000 s3 00000000
s4 00000000 s5 00000000 s6 00000000 s7 00000000
s8 00000000 s9 00000000 sA 00000000 sB 00000000
t3 00000000 t4 00000000 t5 00000000 t6 00000000
pc 00026664 va 00000000 insn       00000000 sr 80046020
An illegal instruction was executed!

Environment Modulesを使ってRISC-Vのツール群を整理する環境構築

RISC-Vのツール群、主にコンパイラツールは32bitと64bitのものが存在する。それぞれにライブラリが定義されており、別々にディレクトリを用意してインストールするのが良いと思われる。

ビルドスクリプトも別々に用意されている。

riscv-tools/build.sh # 64bit用 riscv-tools/build-rv32ima.sh # 32bit用

これらのツール群のパスを設定するために、いちいちスクリプトを変更するのは面倒だ。これをかんりするために、Environment Modulesを使ってパスとライブラリを管理しよう。

modules.sourceforge.net

RISC-Vのツール群で設定する必要があるのは、

の設定が必要だ。${HOME}/modules/riscv/ディレクトリに riscv32, riscv64 ファイルを作り、環境変数 MODULEPATH=${HOME}/modules を設定する。

  • ${HOME}/modules/riscv/riscv32
#%Module1.0
##
proc ModulesHelp { } {
    puts stderr "RISC-V 32-bit Toolchain\n"
}

module-whatis   "RISC-V 32-bit Toolchain"

# append pathes
setenv       RISCV           $::env(HOME)/riscv32/
prepend-path PATH            $::env(HOME)/riscv32/bin/
prepend-path LD_LIBRARY_PATH $::env(HOME)/riscv32/lib/
  • ${HOME}/modules/riscv/riscv64
#%Module1.0
##
proc ModulesHelp { } {
    puts stderr "RISC-V 64-bit Toolchain\n"
}

module-whatis   "RISC-V 64-bit Toolchain"

# append pathes
setenv       RISCV           $::env(HOME)/riscv64/
prepend-path PATH            $::env(HOME)/riscv64/bin
prepend-path LD_LIBRARY_PATH $::env(HOME)/riscv64/lib

module avail で、ツール群の存在を確認する。

module avail

------------------------- /home/msyksphinz/modules/ ---------------------------------------
riscv/riscv32 riscv/riscv64

例えば、32bitツール群をロードしてみよう。

$ module load riscv/riscv32
$ printenv RISCV
/home/msyksphinz/riscv32/
$ which riscv32-unknown-elf-gcc
/home/msyksphinz/riscv32/bin//riscv32-unknown-elf-gcc
$ which riscv64-unknown-elf-gcc

次に、64bitツール群をロードしてみる。

$ module load riscv/riscv64
$ printenv RISCV
/home/msyksphinz/riscv64/
$ which riscv32-unknown-elf-gcc
$ which riscv64-unknown-elf-gcc
/home/msyksphinz/riscv64/bin/riscv64-unknown-elf-gcc

ちゃんと設定できた!

MicroPython試行(1. ダウンロードとビルド → RISC-Vツールチェインでコンパイルするとどうなる?)

ちょっとやりたいことがあって、MicroPythonをダウンロードしてビルドした。

github.com

sudo apt install -y libffi-dev
git clone https://github.com/micropython/micropython.git
cd micropython/
git submodule update --init
make axtls
make

なるほど、MicroPython、それなりにでっかい。

size -A ./micropython
./micropython  :
section                size      addr
.interp                  28   4194872
.note.ABI-tag            32   4194900
.note.gnu.build-id       36   4194932
.gnu.hash               380   4194968
.dynsym                3864   4195352
.dynstr                1586   4199216
.gnu.version            322   4200802
.gnu.version_r          208   4201128
.rela.dyn               312   4201336
.rela.plt              3144   4201648
.init                    26   4204792
.plt                   2112   4204832
.plt.got                  8   4206944
.text                234112   4206960
.fini                     9   4441072
.rodata               76929   4441088
.eh_frame_hdr         11740   4518020
.eh_frame             57748   4529760
.init_array               8   6688208
.fini_array               8   6688216
.jcr                      8   6688224
.dynamic                528   6688232
.got                      8   6688760
.got.plt               1072   6688768
.data                  4288   6689856
.bss                   2464   6694144
.comment                 52         0
Total                401032

なるほど、次に、QEMU-ARMでは?

cd ../qemu-arm
make
...
CC ../../lib/libm/sf_ldexp.c
CC ../../lib/libm/asinfacosf.c
CC ../../lib/libm/atanf.c
CC ../../lib/libm/atan2f.c
CC ../../lib/utils/sys_stdio_mphal.c
CC main.c
/opt/Xilinx/SDK/2017.1/gnu/aarch32/lin/gcc-arm-none-eabi/bin/../lib/gcc/arm-none-eabi/6.2.1/../../../../arm-none-eabi/bin/ld: -lc_nano が見つかりません
collect2: error: ld returned 1 exit status

あれ、Xilinxのツール使ってしまった。ってか-lc_nanoって何だろう?

先に進む。RISC-V用のビルドディレクトリを作ってみる。

cd ../
cp qemu-arm qemu-riscv -r
cd qemu-riscv
make

うーん、途中で失敗した。

CC ../../py/emitinlinextensa.c
CC ../../py/formatfloat.c
CC ../../py/parsenumbase.c
CC ../../py/parsenum.c
CC ../../py/emitglue.c
CC ../../py/persistentcode.c
CC ../../py/runtime.c
CC ../../py/runtime_utils.c
CC ../../py/scheduler.c
CC ../../py/nativeglue.c
../../py/nativeglue.c:158:5: error: 'nlr_push' undeclared here (not in a function)
     nlr_push,
     ^~~~~~~~
../../py/nativeglue.c:159:5: error: 'nlr_pop' undeclared here (not in a function)
     nlr_pop,
     ^~~~~~~
../../py/mkrules.mk:47: ターゲット 'build/py/nativeglue.o' のレシピで失敗しました
make: *** [build/py/nativeglue.o] エラー 1

ちょっと先は長そうだ。