FPGA開発日記

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

Google初のカスタムデザインチップPixel Visual Coreから考えるソフトウェアプログラミングモデル

GoogleフラグシップスマートフォンPixel 2が発表された。スマートフォン自体についてはあまり興味が湧かなかったのだが、搭載されているチップについては興味をそそる内容だ。

www.blog.google

Googleが初めて作ったカスタムデザインのチップということだが(あれ、ではTensor Processing Unitはどうなった?)、構成としてはIPUという画像処理用のプロセッサが8つ、その中には512個のALUが搭載されている。 1秒間に3兆回の演算ということは、3Topsということか?512器の演算器が8セット入っているため、4096個の演算器が入っているものとすると1演算器あたり秒間に750M回の実行となる。そうすると動作周波数は単純に考えて750MHzかなあ。

私が考えるPixel Visual Coreの特徴は、半導体チップ自体の特徴というよりもそのプログラミングモデルだと思う。演算器およびコアをよりシンプルなものにし、ソフトウェア側に詳細な制御をゆだねるという構造になっているからだ。 こういうことは、ソフトウェアで力をつけた組織でないとなかなかできないことだと思う(なぜなら、だいたいの半導体メーカーは最初にチップを作り、ソフトウェアはあとで作るという形を取ってしまうからだ)。

それに引き換えGoogleのPixel Visual Coreは、TensorFlowのサポートと、さらにDSL(Domain Specific Language)を使うようになっている。 画像処理用のオリジナル言語に対応してそちらに制御を委ねるというのは、ソフトウェアベンダでないと出来ないことだし、プログラミングモデルとソフトウェアを詳細に理解していないとなかなかできないことだと思う。

Halide

ターゲットのアプリケーションを最初に明確にすると、このような解が出てくるのだ。 半導体メーカのように、とりあえずチップありきで話をしてしまっては、チップが出来た後にソリューションを考えたって出てきっこない。

UCBのRISC-V FPGAシミュレーション加速環境 "MIDAS"

2017/10/14-10/15にかけて、アメリカのボストンにてRISC-Vの学術的なトピックに焦点を当てたワークショップ "CARRV (Computer Architecture Research with RISC-V)" が開催された。 その際の発表資料などは、CARRVのウェブサイトにて見ることが出来る。

carrv.github.io

この中でUCBの発表した内容(BOOM v2を含め)を中心に見ていたのだが、ちょっとこれまでに聞いたことのない発表が含まれていた。 FPGAを用いてRTLシミュレーションを高速化する、という目的のもとで開発された "MIDAS" という環境だ。

このMIDAS、RTLシミュレーションが遅いのでFPGAを使って加速しようぜ、という話なのだったら特段発表するほどの話ではないのだが、いったい何が新しいのだろうか。

  • Evaluation of RISC-V RTL Designs with FPGA Simulation

https://carrv.github.io/papers/kim-midas-carrv2017.pdf

f:id:msyksphinz:20171018015418p:plain

ザックリと資料を読んだだけなのだが、基本的なアイデアは正直まだ理解できていない。 FPGAを使ってRTLシミュレーションを高速化した、という話なのだろうが、それのどこが新しいのだろうか。 文献を一生懸命読むと、FPGAで高速化した、という話と、さらにI/O周りのソフトウェア群のAPIを整えて一体化した環境を作った、というところに焦点を当てているように見える。 ただしそれもオレオレ環境を作ればいくらでもそんなものは構築できる話で、何が新しいのかまだ理解できていない。

と思ったら、Github上にこのMIDASの環境が一式置かれていた。どうもREADMEを見た方が分かりやすそうだ。

github.com

  • FPGAシミュレーション用のRTLを生成する (Generate Verilog for FPGA Simulation) どうやらRocket Chipだけでなく、周りの環境も含めてVerilogを生成するのだろうか。これをRTLシミュレーションしますよ、という話になる。

  • Verilator/VCS でシミュレーションを実行する (Run Verilator/VCS tests) FPGAの環境をVerilator/VCSでシミュレーションする。

  • FPGA上でRTLを動作させる (Run FPGA Simulation) まだ試していないので良く分かっていないのだが、

For ZC706 with MIG, we generate bitstream midas_wrapper.bit, which will be loaded through JTAG:
$ make bitstream [STROBER=1] [BOARD=<zc706_MIG>] [DESIGN=<your design>] [CONFIG=<your config>]

To generate boot.bin, which will be copied to the SD card, run:
$ make fpga [STROBER=1] [BOARD=<zc706|zedboard|zybo>] [DESIGN=<your design>] [CONFIG=<your config>]

To compile the simulation driver, run:
$ make zynq [STROBER=1] [DESIGN=<your design>] [CONFIG=<your config>]

うそ、これだけでFPGAにデザインを書き込んでベンチマークプログラムを走らせることが出来るようになるってこと?

どうやらZynqのARMの立ち上げは必要らしいが、ソフトウェア(ARMで動作する)と協調して動作しながらRocket Chipを動かすことが出来る環境なのだろうか。そうすると実作業としては非常に楽になるよね。

  • Replay Samples in RTL/Gate-level Simulation

ついでに、Design Compilerで合成したネットとスナップショットを一致させて電力検証もできるらしい。もうわけわからん。

fpgp-zynqの環境とよく似ているということだし、これはうまく使いこなすとかなり便利かもしれない。

オープンソースGPGPUの論理合成試行

オープンソースGPGPUをいろいろと調査しているが、その中でなかなか完成度の高いもののひとつであるNyuziProcessor、テストパタンの動作までは上手く行ったが、FPGAインプリメントしてみたい。

少し調べてみたのだが、どうやらALTERAのFPGA、そしてDE2-115というFPGAボードでのみ動作確認をしているようだ。しまった!ALTERAの実装経験が無い!

だが、プロジェクトファイルはあるようだし、論理合成とインプリメントまでは出来るはずだ。そうすると、もっと安いALTERAのボードでも入るか分かるかもしれない。やってみよう。

Intel Quartusのインストール

Intel Quartusは普通にIntelのページからダウンロードしてくる。どうやら13.1以上をサポートしているようだが、現在は17.0がリリースされている。別に最新バージョンでも論理合成は問題なく実行できた。

https://www.altera.co.jp/downloads/download-center.html

ここで最新のLinux版をダウンロードしてインストールした。例によってVirtualBox上のUbuntuにインストールしている。GUIを使ってインストールして、だいたいインストール完了まで30分程度だった。

NyuziProcessorのFPGA論理合成とインプリメント

論理合成とインプリメントと言っても、実はすべてMakefileが用意されており一発makeを叩くだけなので何の問題もない。以下のようにして論理合成を実行した。

cd hardware/fpga/de2-115/
make

終わりである。すると、Quartusが実行されて論理合成とインプリメントを実行した。しばらく待っていると(1時間くらい?)無事に終了したようだ。

f:id:msyksphinz:20171017004021p:plain

最後にサマリが表示されている。

Fmax 53.51MHz
71,728 Logic elements

Intel(ALTERA) FPGAのグレードについて詳しくないので、これがどれくらいのものなのかよく分からない。

ただし、上記のDE2-115のスペックを見てみると、

  • Cyclone® IV EP4CE115
  • 114,480 logic elements (LEs)
  • 3,888 Embedded memory (Kbits)
  • 266 Embedded 18 x 18 multipliers
  • 4 General-purpose PLLs
  • 528 User I/Os

というわけでLE(XilinxでいうLUTのことかな?)の半分以上を消費している。

このDE2-115というボード以外でどのようなボードがあるのかちょっと調査したのだが、まあ同じくらいのデバイスを搭載していて多少安いものもあるにはあるようだ。

www.terasic.com.tw

もし入手できる機会があれば、やってみても良いかもしれない。まあALTERA初心者なのでものすごく苦労すると思うけど。

プログラミング言語Rustに入門中(1. Rustのイントロダクション)

突然新しいプログラミング言語に入門したくなることもある。自分としてはスクリプト言語Rubyをメインに使っているのだけれども(最近Rubyistとしては隠れすぎていてRubyを使っておらず、シェルスクリプトで何とかしようとする傾向があるが)、最近徐々に知名度を上げつつあるプログラミング言語Rustに入門してみようかと思い、日本語の資料をいろいろ探していた。

githubにいろんな文書がアップロードされている。ありがたい。

github.com

プログラミング言語Rust

このへんから入門してみようかと思う。とりあえず、やってみた成果はなるべく日記にアップロードしてみようと思うので、ここから数日の日記はRustのゴミみたいな備忘録になる事こと間違いなし。

とりあえず、Cargoというシステムはなかなか便利だなと思った。ついでに、RISC-Vにも対応させるべく移植にトライしてみるのも、面白いのかもしれない。

github.com

あと、Emacsなどの環境構築は、このあたりを参考にした。MacじゃなくてUbuntu on VirtualBoxだけど。

qiita.com

とりあえず今日やったのは第3章まで。

extern crate rand;

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    loop {
        println!("Please input your guess.");

        let mut guess = String::new();
        io::stdin().read_line(&mut guess)
            .expect ("Failed to read line");

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };
        
        println!("You guessed: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less    => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal   => {
                println!("You win!");
                break;
            }
        }
    }
}

RISC-Vにおける命令拡張についてのポリシー

先日Design Solution Forumで発表をしてきたのだが、その時にRocket Chipを拡張して独自命令を追加した話をした。

そのあとで他の発表の方から、RISC-Vの拡張方法についていろんな考え方が聞けたため、そういえばRISC-Vって独自の命令を追加する場合に何か考え方でもあるんだろうかと思って調査してみた。

参考にしたのは、User's ManualのRISC-Vの拡張についての説明部分だ(Chapter-21. RISC-V Extension)

ここでは命令拡張というよりも、RISC-Vの命令形態がどのような考え方に基づいて構成されているのかについて見ることが出来る。

まず大前提として、RISC-Vの命令長は基本的に32ビットをベースとしており、Compressed命令の場合は下位の2bitを見て32bitか16bitかを判定している。

f:id:msyksphinz:20171015224122p:plain

ここで、命令セットにはGreenFieldの命令とBrownFieldの命令があるというのを見ておかなければならない。

  • GreenField命令

最も基本となる命令セットで、RV32I、RV64Iなどがそれに該当する。RISC-Vの中で最も基本的な命令群となる。

  • BrownField命令

BrownField命令は、GreenField命令の上に拡張される命令群で、BrownFieldな命令をサポートする場合はそのベースとなるGreenFieldの命令がサポートされていることが大前提となる。 例えば、浮動小数点命令のFDQ拡張は、ベースとなる命令群の拡張なのでBrownFieldの命令となる。

この図は手前味噌で自分で作った未確認の表なのだが、おそらくGreenFieldとBrownFieldは以下のように分けられると思われる。この場合に注意なのは、"A"拡張であるAtomic命令はBrownFieldではないということだ。

f:id:msyksphinz:20171015225617p:plain

命令の拡張についての基本的な考え方

RISC-Vの命令の拡張において、最低限の実装であるRV32I, RV64Iを実装していれば、それ以外の拡張を取り除いてそこに自分の独自命令を追加しても構わないとなっている。 ただしこれは研究や学術教育用の話であって、これをするともちろんRISC-Vの実装としてはRV32I/RV64Iまでしか認定されないだろうし、それ以上の拡張はすべて独自のものとなっている。

また、32bit命令の拡張を行う場合でも、Compressed命令を使用しない場合であれば、命令フィールドの下位2bitの2'b00,2'b01,2'b10(Compressed命令の使用しているフィールド)を利用しても構わないという構成になっている。 従って、RISC-Vは最低限の命令セットさえ残せば、それ以外の命令セット拡張は廃止した場合その場所に自由な命令を配置しても良いという構成になっていると考えることが出来る。

オープンソースGPGPU "NyuziProcessor"を試行する (2.テストパタンの構成調査)

オープンソースGPGPUであるNyuziProcessorの話続き。NyuziProcessorは、ビルドした後のリグレッションテストを自動的に実行することできる環境が用意されている。 こういうハードウェア関係のプロジェクトでリグレッションテストの環境まで提供されているのはなかなか無いし、面白そうなので勉強のためにどのような構成を取っているのか調査した。

図. NyuziProcessorの構成

NyuziProcessorのテストパタンは、ホームディレクトリでmake testをタイプすると実行される。これは実際には、

test:
        cd remote-gdb && $(PYTHON) ./runtest.py
        cd core/isa/ && $(PYTHON) ./runtest.py
        cd core/cache_control/ && $(PYTHON) ./runtest.py
...

ここでは、core/isa/のテストパタンの中身を見てみた。cd core/isa/ && python ./runtest.pyが実行される。 ./runtest.pyが実行されると、テストパタンの登録がされ、次にテストパタンが次々に実行される。

  • tests/core/isa/runtest.py
sys.path.insert(0, '../..')
import test_harness

test_harness.register_generic_assembly_tests([
    'int_arithmetic',
    'branch',
    'compare',
    'shuffle',
    'load_store',
    'atomic',
    'float_ops',
    'float'
])

test_harness.execute_tests()

これは実際には、tests/test_harness.py が実行される。まずはテストパタンを登録して、実行するわけだ。_run_generic_assembly_testというルーチンを呼ぶらしく、これは普通のアセンブリのテストセットを実行するものらしい。

  • tests/test_harness.py
def register_generic_assembly_tests(tests):
...
    for name in tests:
        register_tests(_run_generic_assembly_test, [name + '_verilator'])
        register_tests(_run_generic_assembly_test, [name + '_emulator'])

def _run_generic_assembly_test(name):
    underscore = name.rfind('_')
    if underscore == -1:
        raise TestException(
            'Internal error: _run_generic_test did not have type')

    environment = name[underscore + 1:]
    basename = name[0:underscore]

    build_program([basename + '.S'])
    result = run_program(environment=environment)
    if 'PASS' not in result or 'FAIL' in result:
        raise TestException('Test failed ' + result)

_run_generic_assembly_testでは、まずはプログラムのビルド(build_program)と、次にプログラムの実行(run_program)が行われる。テストパタンの内容は、それぞれのテストディレクトリに存在するテストパタンを見ればよいのだが、

  • tests/core/isa/int_arithmetic.S
# This file auto-generated by ./generate_arithmetic.py. Do not edit.
            #include "arithmetic_macros.inc"

            .globl _start
_start:
        test_sss or, 0x6f7dfdd7, 0x2069e1d7, 0x4f349d96
        test_vvs or, result0, voperand1, 0x7285a272
        test_vvsm or_mask, result1, 0xdd14, voperand1, 0x9d80748d
        test_vvv or, result2, voperand1, voperand2
        test_vvvm or_mask, result3, 0x9a44, voperand1, voperand2
        test_ssi or, 0x3e9cde49, 0x3e9cde49, 0x1
...
  • tests/core/isa/arithmetic_macros.inc
...
.macro test_sss operation, result, operand1, operand2
                li s0, \operand1
                li s1, \operand2
                \operation s2, s0, s1
                li s3, \result
                cmpeq_i s4, s2, s3
                bnz s4, 1f
                call fail_test
1:
.endmacro
...

まあこんな感じで、セルフチェックを行うようなテストパタンとなっている。別にシミュレータがあって、それと一致検証をしているわけではなさそうだな。

オープンソースGPGPU "NyuziProcessor"を試行する (1. ダウンロードとビルド, サンプル実行)

前回紹介したオープンソースGPGPU "NyuziProcessor" はどうやらダウンロードして簡単に試行できるようだ。試してみよう。

Ubuntuで試行する場合は、必要なパッケージをダウンロードした。

sudo apt-get -y install autoconf cmake make gcc g++ bison flex python \
    python3 perl emacs openjdk-8-jdk swig zlib1g-dev python-dev \
    libxml2-dev libedit-dev libncurses5-dev libsdl2-dev gtkwave imagemagick

次に、リポジトリをダウンロードする。非常に軽いリポジトリだ。こんなので本当に動くのかなあ?

git clone https://github.com/jbush001/NyuziProcessor.git

そして、おもむろにsetupのスクリプトを走らせると、リポジトリのダウンロードが始まった。そういうことか。

./build/setup_tools.sh

あああ、と思ったらCMakeのバージョンを上げなければならないらしい。というか、結論としてはXilinxのツールとしてデフォルトでインストールされるものと被っていた。

CMake Error at CMakeLists.txt:3 (cmake_minimum_required):
  CMake 3.4.3 or higher is required.  You are running version 3.3.2

というわけでバージョンアップするとビルド出来るようになったのだが、ずいぶんと時間がかかる。時間切れ。。。

CMakeの環境が整うと、あとはビルドを実行するだけだ。ただし、非常に時間がかかるのでCPUを1つ以上持っている場合は並列化することをお勧めする。

  • ./build/setup_tools.sh
diff --git a/build/setup_tools.sh b/build/setup_tools.sh
index a6edf4b..d724be3 100755
--- a/build/setup_tools.sh
+++ b/build/setup_tools.sh
@@ -39,7 +39,7 @@ if [ ! -f Makefile ]; then
        ./configure || fail "Error configurating Verilator"
 fi

-make || fail "Error building verilator"
+make -j3 || fail "Error building verilator"
 sudo make install || fail "Error installing verilator"
 )

@@ -58,5 +58,5 @@ else
        cd tools/NyuziToolchain/build
 fi

-make || fail "Error building toolchain"
+make -j3 || fail "Error building toolchain"
 sudo make install || fail "Error installing toolchain"

ビルドが完了すると次はテストを動かしてみる。テストはmake testで実行する。何本もテストが流れていくので、それを眺めているだけである。これはおそらくCTestを使っているのかな?

make[1]: ディレクトリ '/home/msyksphinz/work/NyuziProcessor/tests' に入ります
cd remote-gdb && python3 ./runtest.py
gdb_breakpoint                          [PASS]
gdb_remove_breakpoint                   [PASS]
gdb_breakpoint_errors                   [PASS]
gdb_single_step                         [PASS]
gdb_single_step_breakpoint              [PASS]
gdb_read_write_memory                   [PASS]
gdb_read_write_register                 [PASS]
gdb_register_info                       [PASS]
gdb_select_thread                       [PASS]
gdb_thread_info                         [PASS]
gdb_invalid_command                     [PASS]
gdb_big_command                         [PASS]
gdb_queries                             [PASS]
gdb_vcont                               [PASS]
gdb_crash                               [PASS]
0/15 tests failed
cd core/isa/ && python3 ./runtest.py
int_arithmetic_verilator                [PASS]
int_arithmetic_emulator                 [PASS]
branch_verilator                        [PASS]
branch_emulator                         [PASS]
compare_verilator                       [PASS]
compare_emulator                        [PASS]
shuffle_verilator                       [PASS]
shuffle_emulator                        [PASS]
load_store_verilator                    [PASS]
load_store_emulator                     [PASS]
atomic_verilator                        [PASS]
atomic_emulator                         [PASS]
float_ops_verilator                     [PASS]
float_ops_emulator                      [PASS]
float_verilator                         [PASS]
float_emulator                          [PASS]
0/16 tests failed
cd core/cache_control/ && python3 ./runtest.py
dflush                                  [PASS]
dinvalidate                             [PASS]
...

サンプルプログラムを動かす

テストが通るようになったので、サンプルプログラムを動かしてみよう。 その前に一つ注意事項だが、私はVirtual Box上のUbuntuでシミュレーションをしている。この場合、Virtual Boxの設定を変更して、3DアクセラレーションをOFFにしておく必要がある。

f:id:msyksphinz:20171014003913p:plain

サンプルプログラムのディレクトリに移って、実行してみる。

cd software/apps/sceneview/
make run

GUIが表示されて、アプリケーションが動作する。なにこれ面白い!

f:id:msyksphinz:20171014004144g:plain