FPGA開発日記

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

CARRV 2021での発表論文を概観する

CARRVというのはComputer Architecture Research with RISC-Vのことで、RISC-Vネタにした研究に関するワークショップである。 毎年ISCAと併設して実施されている。

carrv.github.io

なんかまじめに論文を読んでいなかったので、とりあえず概観してみることにした。


RISKA : SKAデータ処理向けのRISC-Vをベースとしたオープンソースドメイン特化System-on-Chipに向けて

SKAとはSquare Kilometer Arrayの意味。RISKAというのはオープンソースのメニーコアSoCで、CPUコアとしてRISC-Vを採用している。このプロジェクトの目的は電波望遠鏡で取得した画像を解析するためのシステムを構築することである。SKAはRISC-V CPUコアとカスタムデザインアクセラレータから構成されている。

NeuralScale: クラウド上でAIの推論をブーストするためのRISC-Vベースのニューラルプロセッサ

NeuralScaleというAI推論向けのチップを開発した。

  • 動作周波数 1.0GHz
  • 性能 256TOPs (INT8) / 128TFLOPS (FP16)
  • テクノロジ : TSMC 12nm
  • RISC-Vベクトル拡張をサポート
f:id:msyksphinz:20210911011351p:plain

RISC-V データフローアーキテクチャ

データフローアーキテクチャはフォンノイマンアーキテクチャとは別のもので、大きく分けて2種類のものがある。 - 明示的なデータフローアーキテクチャ: データフロー的な処理に対して高い性能を示す。 - ハイブリッドアーキテクチャ: フォンノイマンとデータフローのハイブリッド型

本研究で開発したのは、明示的なデータフローアーキテクチャに対してRISC-Vの移植を行ったものである。Gem5に対してこれを実装し、最大で7.5%の性能向上を達成した。

f:id:msyksphinz:20210911011409p:plain

暗号および誤り訂正符号のための汎用RISC-V ISAガロアフィールド演算拡張機能

暗号処理のためにはガロア体演算をベースとしたアルゴリズムが求めらている。ガロア体演算乗算用RISC-V命令セットを実装して動作させた。 AESやReed-Solomonコードのようないくつかのアルゴリズムでは性能の向上が確認できた。

f:id:msyksphinz:20210911011425p:plain

機能指向プログラミングによるRocket-Chipの性能カウンタ設計のバリエーション

Rocket-ChipのTLBがメモリの属性を取得する仕組み

Rocket-ChipのTLBは、TLBミスが発生すると外部のPTWにアクセスして当該メモリ領域の属性を取得する。

その実装を探していたのだが、Chiselで書いてあって非常に分かりにくい。PTWの実装のうち、おそらくこの辺がPMAのテーブルを作っているものと思われる。

  • sims/verilator/generated-src/chipyard.TestHarness.RocketConfig/chipyard.TestHarness.RocketConfig.top.v
 module PTW(
   input         clock,
   input         reset,
   output        io_requestor_0_req_ready,
/* ... 中略 ... */
   wire [16:0] s2_entry_vec_0_tag = uncorrected[42:26]; // @[PTW.scala 261:59]
   wire  s2_hit_vec_0 = s2_valid_vec & r_tag == s2_entry_vec_0_tag; // @[PTW.scala 262:83]
   wire  s2_hit = s2_valid & s2_hit_vec_0; // @[PTW.scala 263:27]
   wire [65:0] _pmaPgLevelHomogeneous_T_6 = pte_addr ^ 66'hc000000; // @[Parameters.scala 137:31]
   wire [66:0] _pmaPgLevelHomogeneous_T_7 = {1'b0,$signed(_pmaPgLevelHomogeneous_T_6)}; // @[Parameters.scala 137:49]
   wire [66:0] _pmaPgLevelHomogeneous_T_9 = $signed(_pmaPgLevelHomogeneous_T_7) & -67'sh4000000; // @[Parameters.scala 137:52]
   wire  _pmaPgLevelHomogeneous_T_10 = $signed(_pmaPgLevelHomogeneous_T_9) == 67'sh0; // @[Parameters.scala 137:67]
   wire [65:0] _pmaPgLevelHomogeneous_T_11 = pte_addr ^ 66'h80000000; // @[Parameters.scala 137:31]
   wire [66:0] _pmaPgLevelHomogeneous_T_12 = {1'b0,$signed(_pmaPgLevelHomogeneous_T_11)}; // @[Parameters.scala 137:49]
   wire [66:0] _pmaPgLevelHomogeneous_T_14 = $signed(_pmaPgLevelHomogeneous_T_12) & -67'sh10000000; // @[Parameters.scala 137:52]
   wire  _pmaPgLevelHomogeneous_T_15 = $signed(_pmaPgLevelHomogeneous_T_14) == 67'sh0; // @[Parameters.scala 137:67]
   wire  pmaPgLevelHomogeneous_1 = _pmaPgLevelHomogeneous_T_10 | _pmaPgLevelHomogeneous_T_15; // @[TLBPermissions.scala 98:65]
   wire [66:0] _pmaPgLevelHomogeneous_T_20 = {1'b0,$signed(pte_addr)}; // @[Parameters.scala 137:49]
   wire [66:0] _pmaPgLevelHomogeneous_T_38 = $signed(_pmaPgLevelHomogeneous_T_20) & -67'sh5000; // @[Parameters.scala 137:52]
/* ... 中略 ... */

この辺の実装についてはChisel側を眺めてみると、edge.managerなどが出てくるのでおそらくDiplomacyで接続された各デバイスからメモリアクセスの属性の情報を取得して、テーブルに纏めているものと思われる。

  • rocket-chip/src/main/scala/rocket/PTW.scala
   val pageGranularityPMPs = pmpGranularity >= (1 << pgIdxBits)
   val pmaPgLevelHomogeneous = (0 until pgLevels) map { i =>
     val pgSize = BigInt(1) << (pgIdxBits + ((pgLevels - 1 - i) * pgLevelBits))
     if (pageGranularityPMPs && i == pgLevels - 1) {
       require(TLBPageLookup.homogeneous(edge.manager.managers, pgSize), s"All memory regions must be $pgSize-byte aligned")
       true.B
     } else {
       TLBPageLookup(edge.manager.managers, xLen, p(CacheBlockBytes), pgSize)(pte_addr).homogeneous
     }
   }
   val pmaHomogeneous = pmaPgLevelHomogeneous(count)
   val pmpHomogeneous = new PMPHomogeneityChecker(io.dpath.pmp).apply(pte_addr >> pgIdxBits << pgIdxBits, count)
   val homogeneous = pmaHomogeneous && pmpHomogeneous

"libmpfr.so.4: cannot open shared object file" エラーメッセージの対処方法

完全な作業メモ。WSL上でChipyard向けのGNUツールのビルドを行っていると時々こういうエラーに遭遇する。

$ ./scripts/build-toolchains.sh --ignore-qemu ec2fast
==>  Removing existing riscv-pk/build directory
==>  Configuring riscv-pk
checking build system type... x86_64-pc-linux-gnu
checking host system type... riscv64-unknown-elf
checking for riscv64-unknown-elf-gcc... riscv64-unknown-elf-gcc
checking whether the C compiler works... no
configure: error: in `/home/msyksphinz/work2/riscv/chipyard/toolchains/riscv-tools/riscv-pk/build':
configure: error: C compiler cannot create executables
See `config.log' for more details
zsh: exit 77    ./scripts/build-toolchains.sh --ignore-qemu ec2fast

このときに chipyard/toolchains/riscv-tools/riscv-pk/build/config.logを確認する。以下のようになっていた時は環境の問題と考えてよい。

riscv64-unknown-elf-gcc: error: unrecognized command line option '-V'
riscv64-unknown-elf-gcc: fatal error: no input files
compilation terminated.
configure:2377: $? = 1
configure:2366: riscv64-unknown-elf-gcc -qversion >&5
riscv64-unknown-elf-gcc: error: unrecognized command line option '-qversion'; did you mean '--version'?
riscv64-unknown-elf-gcc: fatal error: no input files
compilation terminated.
configure:2377: $? = 1
configure:2397: checking whether the C compiler works
configure:2419: riscv64-unknown-elf-gcc    conftest.c  >&5
/home/msyksphinz/work2/riscv/chipyard/riscv-tools-install/bin/../libexec/gcc/riscv64-unknown-elf/9.2.0/cc1: error while loading shared libraries: libmpfr.so.4: cannot open shared object file: No such file or directory

以下の記事を参考にして修正する。

qiita.com

ldd /home/msyksphinz/work2/riscv/chipyard/riscv-tools-install/bin/../libexec/gcc/riscv64-unknown-elf/9.2.0/cc1
        linux-vdso.so.1 (0x00007ffc7c782000)
        libmpc.so.3 => /lib/x86_64-linux-gnu/libmpc.so.3 (0x00007f2aaa235000)
        libmpfr.so.4 => not found
        libgmp.so.10 => /lib/x86_64-linux-gnu/libgmp.so.10 (0x00007f2aaa1b1000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f2aaa1ab000)
        libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f2aaa18f000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f2aaa03e000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2aa9e4c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f2aaa45a000)
        libmpfr.so.6 => /lib/x86_64-linux-gnu/libmpfr.so.6 (0x00007f2aa9dcb000)
$ cd /lib/x86_64-linux-gnu
$ sudo ln -s libmpfr.so.6 libmpfr.so.4

これで修正できる。

Verilatorのリグレッションテスト用にGitHub Actionsの設定を試行

GitHub Actionsの使い方がだんだんわかってきた。いろんなサンプルプログラムを見ながらVerilatorを使ったリグレッション環境を作ろうとしている。

色々試した結果、私の自作CPUのデザインではかなり規模の問題で厳しいことが分かってきた。 最初はこんな感じでビルド環境を作っていた。

       - name: RV64 Tiny Build
         run: make -C verilator_sim/ rv64_tiny 

       - name: RV64 Small Build
         run: make -C verilator_sim/ rv64_small 

       - name: RV64 Standard Build
         run: make -C verilator_sim/ rv64_standard 

       - name: RV64 Big Build
         run: make -C verilator_sim/ rv64_big 

       - name: RV64 Giant Build
         run: make -C verilator_sim/ rv64_giant 

途中でなぜか知らないけど落ちてしまう。

f:id:msyksphinz:20210906003257p:plain

そこで、長い時間が必要なビルドではなく、短い時間で良いLINTに変更してみた。

       - name: RV64 Tiny Build
         run: make -C verilator_sim/ rv64_tiny LINT=1

       - name: RV64 Small Build
         run: make -C verilator_sim/ rv64_small LINT=1

       - name: RV64 Standard Build
         run: make -C verilator_sim/ rv64_standard LINT=1

       - name: RV64 Big Build
         run: make -C verilator_sim/ rv64_big LINT=1

       - name: RV64 Giant Build
         run: make -C verilator_sim/ rv64_giant LINT=1

しかしこちらでも同様に怒られてしまった。原因を調査していく。

Verilatorのリグレッションテスト用にGitHub Actionsの設定を試行

Verilatorを使った自作CPUのリグレッションテスト用に、Dockerでの環境構築と同時にGitHub Actionsを使ってCI環境を作っておきたい。 GitHub Actionsはこれまで全く使ったことが無かったので、まずは使い方を覚えるところから。

.github/workflowsというところにYAMLファイルを置いておくらしい。やりたいこととしては

  • 必要なパッケージのインストール(Ubuntu)
  • RISC-Vツールのインストール(GNU GCCとか)
  • Verilatorのインストール
  • 各コンフィグレーションでのビルド
  • 各コンフィグレーションでのリグレッションテスト

まで出来るようにしてみたい。まずは簡単にGitHub Actionsを使えるようになるまでを試行した。

主に以下を参考にしながら進めている。

www.tech-diningyo.info

github.com

なるほど、DockerやVagrantと似ていて、環境を構築するためのスクリプトを用意しておく感じか。GitHub Actionsの場合はYAMLを使うということになっている。

とりあえずいろんなものをコピペしながら以下のようなmain.ymlファイルを作成した。

# いろいろ省略

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    name: ci
    strategy:
      matrix:
        system: ["ubuntu-20.04"]
        verilator: ["4.204"]

    # The type of runner that the job will run on
    runs-on: ${{ matrix.system }}

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2

      # Apt updates
      - name: apt_updates
        run: |
          sudo apt update
          sudo apt install -y tzdata
          sudo apt install -y git autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev\
 libexpat-dev libfl-dev

      # Build Verilator
      - name: Cache Verilator ${{ matrix.verilator }}
        uses: actions/cache@v2.1.6
        id: cache-verilator
        with:
          path: verilator-${{ matrix.verilator }}
          key: ${{ matrix.system }}-verilator-${{ matrix.verilator }}

      - name: Compile Verilator ${{ matrix.verilator }}
        if: steps.cache-verilator.outputs.cache-hit != 'true'
        run: |
          wget https://github.com/verilator/verilator/archive/refs/tags/v${{ matrix.verilator }}.tar.gz
          tar xvf v${{ matrix.verilator }}.tar.gz
          cd verilator-${{ matrix.verilator }}
          autoconf
          ./configure
          make
      - name: Install Verilator ${{ matrix.verilator }}
        run: |
          cd verilator-${{ matrix.verilator }}
          sudo make install
          verilator --version

ここまでをリポジトリにコミット、pushしたら自動的にGitHub Actionsが動き始める。その結果は自動的にGitHub Actionsのページに記録される。便利だなあ。

f:id:msyksphinz:20210905002201p:plain

これに、さらに各コンフィグレーションでのVerilatorビルドのコマンドをくっつけるとビルドしてくれる?試してみる。

Verilatorのリグレッションテスト用にWSL2上にDocker環境を構築する試行

Verilatorを使った自作CPUのリグレッションテスト用に、Dockerを使った仮想環境を作っておきたい。Windowsを使っているので、WSL2のDockerを使って構築することにした。

ここを参考にしてインストールした。WindowsならばWindowsバイナリをダウンロードしたら自動的にWSL2を検出してくれるらしい。便利だ。

kahoo.blog

一応インストールできたので確認してみる。WSL2上でdockerを実行してみた。

$ docker --version
Docker version 20.10.8, build 3967b7d

ここから先はRISC-V+Chiselの本を参考にしながら組み立てていく。

FROM ubuntu:20.04

ENV RISCV=/opt/riscv
ENV PATH=$RISCV/bin:$PATH
ENV MAKEFLAGS=-j4

WORKDIR $RISCV

RUN apt update && \
    apt install -y tzdata && \
apt install -y autoconf automake autotools-dev curl libmpc-dev libmpfr-dev \
libgmp-dev gawk build-essential bison flex texinfo \
gperf libtool patchutils bc zlib1g-dev libexpat-dev \
pkg-config git libusb-1.0-0-dev device-tree-compiler default-jdk gnupg vim

RUN git clone -b rvv-0.9.x --single-branch https://github.com/riscv/riscv-gnu-toolchain.git && \
cd riscv-gnu-toolchain && \
git submodule update --init --recursive

RUN cd riscv-gnu-toolchain && mkdir build && \
cd build && \
../configure --prefix=${RISCV} --enable-multilib && \
make

# riscv-tests

RUN git clone -b master --single-branch https://github.com/riscv/riscv-tests && \
cd riscv-tests && \
git submodule update --init --recursive

これでdockerコマンドを実行して立ち上げてみた。ビルドには時間がかかる。

sudo docker build . -t riscv/regression
f:id:msyksphinz:20210902224521p:plain

文字起こしサイト"LogMi Tech" にカーネルVM勉強会の発表内容が掲載されました

文字起こしサイト「ログミーTech」に、私のカーネルVM勉強会Onlineで発表した「Rustで作るQEMUもどき」の発表内容が掲載されました。

文字起こしは2回に分けられていて、前半はQEMUもどきの実装のところまで、後半なスピードアップの話題になっている。

logmi.jp

logmi.jp

久しぶりの発表で、しかも強者の多いカーネルVM勉強会とあってかなり緊張したのを覚えているが、文字起こしされた内容を見直してみると余計な部分が多いなと反省するところがある。

この発表は本当に興味本位で始めたものをまとめたもので、QEMUを自作するというのは当初の目的ではなかった。

  1. Linuxがブートする様子をみたい
  2. シミュレータでLinuxをブートする方法を調べる
  3. Spike(RISC-VのISS)とQEMUでものすごく実行速度が違うな?
  4. QEMUの実装調べよう
  5. じゃあQEMUクローンを自分で作ろう

ということで、結局当初の目的は達成されず、RustでQEMUもどきを書いてそれで終わりになってしまっていた。 でもこれを勉強する中でx86のISAにも多少詳しくなったので良しとしようかな。

こういう勉強会には、今後も積極的に参加していきたいので、参加するために何かネタを集めなければ。