SWEST20 S5c FPGA ¤¡ FPGAQZ@WSPY*nsigboost
{ z sigboost]b¤¡

RubyA K9L K<=;/-Mulvery'"«L K<=;(FOJ<=;,§¦ \i fu \sFPGA-}1+2\a!)L K<=;x~\*:!4*hdw*5'!)#3©5*(v_*5(£&7j,-2'4^EHBV['-hdw,'IPA¢a`+¥8,pgw+4#&D M ?X> G+2m8 yª,ec chdw(FPGAq,tl960 ¤¡cpg8&7NCU;Y|R D'FPGAQZ@WSPY*nsigboost(Ruby,Reactive Programming+%
Mulvery+$&¨0hdw(FPGAq+47BDITx~,okr+.1032

FPGA>L/H@=JgU|xsigboost
FPGA'~tcp\l]>L/H@=J
gU|xsigboost>L/H@=JVP'1?:%QhOap\}sigboostHLS w
gU|xF2SXoI(J6*B~tcp\'N5J>8:'m[$K*9M3' %r$ %  f^Ys &#|xC-,M4;(IM/!7E;M/"q[&%VP >L/H@=JgU|x(9)
5: L48.'jz% &#q[v $
sigboost>L4+.:FPGA<4E(J>L/HAM/eu#Q
hOa"p\jzp\`k'RTZb"
7DKM4Ry(>I03GM^i|xd_PW{n&#(>L7w

3

SWEST20 2018 @下呂温泉 31, Sep., 2018

高位合成/FPGA活用技術の最前線
Rubyコードをハードウェアへ-Mulveryで打ち砕くハードウェアとソフトウェアの壁-

東京農工大学 工学府 情報工学専攻
2017年度 未踏事業 スーパークリエータ

照屋大地

2018/08/31

1

自己紹介

照屋 大地
東京農工大学 工学府
博士後期課程電子情報工学専攻
知能・情報工学専修
2017年度未踏事業
スーパークリエータ

2018/08/31

2

きっかけ：IoTデバイス開発の課題

センサデバイス

多数の高速な外部I/Oを扱いたい

FPGA

マイコン

ネットワークに接続したい

Internet

連携が面倒“ぜんぶRubyだけでサクサク書きたい”
CPU+FPGA環境におけるシステム開発の簡略化がしたい！
Mulveryプロジェクト
2018/08/31

3

Mulveryフレームワークとは？
開発段階開発するシステム

Mulvery
フレームワーク

InternetCPU
ソフトウェアRuby

ソースコードFPGA

ハード化対象を自動で抽出

回路設計の知識不要

ハードウェア()2018/08/31

アク
チセ
ュン
エサータ4

これまでの再構成可能計算システム
開発段階

開発するシステム

ソフトウェア
ソースコード群
ソフトウェア

Network

デバイス
ドライバ
CPU
ハードウェア
ソースコード群

ハードウェア
設計ファイル ハードウェア
制約ファイル

課題1
幅広い知識が必要
2018/08/31

ソフトウェアFPGA
ハードウェアメモリ
2

高位合成とその複雑さ• 高位合成(High Level Synthesis, HLS)
CやJava等の高級言語を，ハードウェア記述言語(HDL)によるレジスタ転送レベル(RTL)設計に変換する技術
ビヘイビア
記述（高級言語）

高位合成系

RTL設計
（HDL）

FPGA

課題2
「動作記述≠構造記述」→HW技術者による最適化が必要
シミュレータ
set_directive_pipeline 最適化“Interface::get_data 指示の記述による評価(専用命令)/get_data_loop0”
2018/08/31

3

目指すところ
開発段階

開発するシステム

Mulvery
フレームワーク

InternetCPU
ソフトウェアmRuby

ソースコードFPGAFPGAとのつなぎ込み不要

回路の知識不要

ハードウェア()2018/08/31

アク
チセ
ュン
エサータ

7

データ処理のアクセラレータにも開発するシステム

ネットワーク接続,データのパース・整形,条件分岐の多い処理, ...

Internet

CPU
ソフトウェアFPGA

ストリームデータのアグリゲーション, 操作,並列処理, ...
2018/08/31

ハードウェア8

HW-SW協調設計環境
Intel FPGA SDK
for OpenCL• C/C++コードの一部をハードウェア化• オフロード対象を明示的に指定する
高速化のための試行錯誤が必要⇒HW技術者向け

2018/08/31

9軽量言語によるメタプログラミング(Scala)CλaSH(Haskell)• 軽量言語をドメイン固有言語(DSL)として用いる• 生成されるHWを設計しておく必要がある（ScalaもHaskellも強い静的型付けでHW合成しやすそう）class Max2 extends Module {val io = new Bundlw {val x = UInt(INPUT, 8)
val y = UInt(INPUT, 8)
val z = UInt(OUTPUT, 8)}
io.z = Mux(io.x > io.y, io.x, io.y)}
2018/08/31

ハードウェア記述言語のメタプログラミング⇒HW技術者向け

10マイコン＋FPGAな環境？

ラズパイみたいなものもある！

マイコン＋FPGAな
Raspberry Pi
互換ボード
2018/08/31

Trenz Electronics社“Zynqberry”

11PythonでFPGAを便利に使うヤツ• ハードウェアをAPI的にPythonから呼び出せる• SDK＋ハードウェア• ハードは専門家が作る• DeepLearningやリアルタイム画像処理で遊べる
2018/08/31

こういうのRubyにもほしい！
12ReactiveXとハード合成のアイデア
2018/08/31

13

Mulveryのアプローチ
Reactive Programmingを用いたプログラムからの合成
Reactive Programmin (RP)
「データストリーム」をオブジェクトとして扱う
プログラミングパラダイム
今回はRxを使ったコードを速くする
Web開発

2018/08/31

Web開発
Android開発
14

ReactiveXとは次の3つの要素を組み合わせてデータフローを記述してプログラムを作る• Observer Pattern … 非同期の実現• LINQ ………………… 操作クエリの実装• Scheduler …………… スケジューリング

2018/08/31

15

Reactive Programmingの例
・データの到着回数を数える例：
Eventstimemap( event becomes 1 )

1

1

1

Source1mapscan(+)

1
2018/08/31

2

scan

3

4

Data Flow
Observable

16

Observer Pattern• Publish/Subscribeとも• ObserverがSubjectを観察する• RxではSubject=Observable
ライブラリ側イベントを購読2018/08/31

イベントを配信ユーザ実装

17

Observer PatternとHW合成アイデア
class Pin < SubjectBase<pin_watcherの定義>
def notify(event)@observers.each do |o|o.onNotify(event)
end
end
end
pin_watcher
ハードウェアPin pin_0
pin_0.addObserver do |e|<Observer1の定義>
end
pin_0.addObserver do |e|<Observer2の定義>
end
Observer1
ハードウェア
Observer2
ハードウェア…

2018/08/31

18

LINQ
• Language-INtegrated Query• データ列（DBとか）を操作するための標準クエリ

ラムダ抽象の内容を各イベントに適用
2018/08/31

図：Rxのページより

19

LINQ
• Language-INtegrated Query• データ列（DBとか）を操作するための標準クエリ

2つのデータフローを待ち合わせる
2018/08/31

図：Rxのページより

20

LINQ
• Language-INtegrated Query• データ列（DBとか）を操作するための標準クエリ

一定時間後にイベントを発生させる

2018/08/31

図：Rxのページより

21

LINQ on Reactive Extensions

2018/08/31

22

LINQとHW合成アイデア
・データの到着回数を数える例（再掲）
Eventstimemap( event becomes 1 )

1

1

1

Source1map HWscan(+)

1
2018/08/31

2

scan HW

3

4
Observable

各HWはHDLのテンプレートから合成
23

Scheduler• 動作スレッドを制御するための仕組み• たとえばTimerだと・・・
o1 = Observable.timer(1) dop “this is o1”
end

Current
ThreadLocal
ThreadLocalThreado2 = Observable.timer(1) dop “this is o2”
endtimer(len,scheduler=DefaultScheduler)
使い方に応じてschedulerを変える
2018/08/31

this iso1
this iso224

Scheduler• 同一スレッドで時間計測させる例
SchedulerBaseを使って任意の戦略が実装可能

m_th = LocalScheduler.new
o1 = Observable.timer(1, m_th) dop “this is o1”
endo2 = Observable.timer(1, m_th) dop “this is o2”
end
2018/08/31

Current
ThreadLocal
Threadthis iso1
this iso225

SchedulerとHW合成アイデア• 動的スケジューリングを制限すればそのままデータフローグラフとして扱えそう
Current
ThreadLocal
Threadthis iso1Source
o1 HW
CurrentThread
HW

o2 HW

this iso2
2018/08/3126

これまでの開発
2018/08/31

27

実装しなければならないもの

1. HW抽出機構
2. HW合成機構
3. ラムダ抽象の合成
4. Schedulerの合成
5. ソフト/ハード連携

2018/08/31

28

V2：動的解析による実装• 多段階計算を使った動的解析の実装への舵切り• フレームワークとして提供するので通常のRubyで合成も実行も全部できる

2018/08/31

29

ハード/ソフトの自動分割

ソフトウェア部

ハードウェア部

Rx記述⇒ハード

それ以外⇒ソフト

def initialize()
i2c_stream = Stream.new(I2C_BUS_A)
sensors = i2c_stream.group_by(2){ |d| d. sensor_ id }for sensor in sensors do@averages[sensor] =sensor.average(5)endenddef main()for average in @averages dop average[sensor].get_latest()
end
wait_ms(100) Rxで書きにくい部分は多く場合ソフト実行が速い
end

2018/08/31

I2 C Bus A
i2c_stream

groupBy
sensors[A]Sensors[B]averageaverageaverages[A]averages[B]0x12340x5678

データフローグラフ

ハード化

メモリに
マップ

30

ハードウェア合成の例
events = Stream.new(MIO::p0)
count = events.map(){ |event| 1 }.scan(){ |acc, x| acc + x }Streamオブジェクトがハードウェアを生成する

S_0.v

Map_0.v

module S_0(p0);…
Map_0 map_0(.din(w_0_0),.v_din(v_w_0_0),.dout(w_0_1),.v_dout(v_w_0_1));

module Map_0(…);…
dout <= 8’d1;
v_din <= 1’b1;…
テンプレート
endmodule

Scan_0 scan_0(…
endmodule

module Scan_0(…);…
endmodule

2018/08/31

Scan_0.vから生成

31

実装しなければならないもの✔1. HW抽出機構✔2. HW合成機構
3. ラムダ抽象の合成
4. Schedulerの合成
5. ソフト/ハード連携

2018/08/31

32

ラムダ抽象の合成について• LINQクエリの多くは高階関数• ラムダ抽象の合成はどうしよう・・・⇒現状は用途に合わせた多段階計算で誤魔化している

2018/08/31

33

サンプル：畳み込み演算• 128x128の画像に対するラプラシアンフィルタ適用∗
入力バッファ

…
128画素（1行）

2018/08/31=5行バッファ…
…
…
…
…

畳み込みx128

…
128画素
（1行）

34

コーディング例
buffer = input.sliding_buffer(5)line = buffer.map(){ |data|mats = Array.new()for i in (0…SIZE) domats.push(Matrix[data[0][i,5行ぶん待つ5], ¥data[1][i, 5], ¥data[2][i, 5], ¥data[3][i, 5], ¥5行毎に処理（畳み込み）
data[4][i, 5]])end
result_row = Array.new()
128行出力待つmats.map() do |mat|result_row.push(mat.conv(kernel))end}
result = line.buffer(128)2018/08/31

input
sliding_buffermap

bufferresult35

ラムダ抽象をメタプロ的に扱う
buffer = input.sliding_buffer(5)line = buffer.map(){ |data|mats = Array.new()for i in (0…SIZE) {mats.push(Matrix[data[0][i, 5], ¥data[1][i, 5], ¥data[2][i,5],Matrixは¥Array,オーバーロード
data[3][i,5], ¥data[4][i,されている5]])}
result_row = Array.new()mats.map() { |mat|result_row.push(mat.conv(kernel))}}result = line.buffer(128)2018/08/31

datamatmatskernel(convolution)

result_row36

IF文のハードウェア化
line = buffer.map(){ |data|check(data == 0) {do_something_1()}.elsewhen(data == 1) {do_something_2()}.otherwise {do_something_3()}.endcheck}

check
CheckContext#elsewhen
CheckContext#otherwise
CheckContext#endcheck

ハード合成時：すべて評価
ソフト実行時：IFとおなじ
2018/08/31

37実装しなければならないもの✔1. HW抽出機構✔2. HW合成機構✔3. ラムダ抽象の合成
4. Schedulerの合成
5. ソフト/ハード連携

2018/08/31

38

Schedulerの合成• Thread毎にステートマシンを持つ• CurrentThreadはかならずできる
CurrentThread

CurrentThread

LocalThread

this iso1

CurrentThread
HW

LocalThread_0
o1 HW

this iso2
2018/08/31

o2 HW39Schedulerの合成の問題• 複数のThreadを同時にキックしたい場合（未解決）

o1 = Observable.timer(1) dop “this is o1”
end

Current
ThreadLocal
ThreadLocal
Threado2 = Observable.timer(1) dop “this is o2”
end

違うstateでキックしてしまう
2018/08/31

this iso1
this iso240

実装しなければならないもの✔1. HW抽出機構✔2. HW合成機構✔3. ラムダ抽象の合成✔4. Schedulerの合成
5. ソフト/ハード連携

2018/08/31

41

ソフト/ハード自動連携

i2c_stream

groupBy
sensors[A]Sensors[B]averageaverageaverages[A]averages[B]0x12340x5678

データフローグラフ
2018/08/31

ハード化

メモリにマップ

Programmable
LogicI2 C Bus A

ProcessingSystem• 現状はかなり素朴な実装• 共有メモリでデータ共有のみ

ユーザアプリ(Ruby)
Ruby
ライブラリ

Ruby拡張(C言語)

Debian
OS管理
メモリ

OS管理外
メモリ

メモリアクセス機構
Mulvery合成回路

外部I/O
42

実装しなければならないもの✔1. HW抽出機構✔2. HW合成機構✔3. ラムダ抽象の合成✔4. Schedulerの合成✔5. ソフト/ハード連携

2018/08/31

43

Mulveryの楽しいところ
2018/08/3144透過性の高い記述
def func(data)…
end

Stream：
FPGAで実行stream_1 = Stream.new()grouped_stream_1 = stream_1.group_by{|d| func(d)}array_1 = [1, 2, 3, 4, 5, 6]
grouped_array_1 = array_1.group_by{|d| func(d)}非Stream：
CPUで実行
データに近い場所で処理が行われる

透過性の高い記述2018/08/3145透過性の高い記述の例２I2 C Bus A

i2c_stream = Stream.new(I2C_BUS_A)
sensors = i2c_stream.group_by(2){ |d| d. sensor_ id }averages = sensors.map{ |key, val|
[key, sensor.average(5)]}.to_h()⇒ハードにもソフトにもならないmap

i2c_stream

groupBy
sensors[A] Sensors[B]averageaverageaverages[A] averages[B]同じメソッドが「ソフトウェア記述」「ハードウェア記述」「generator文」の３役を果たす
2018/08/31

46

デバイスコントローラとしての活用
1024個のフルカラーLED(NeoPixel)を制御してみる
マイコン

Web
Server
FPGA

LED制御

LED
マトリクス

1.25us信号x24x1024
マイコンだとアセンブリが必要Rubyの資産を使ってシステム実装できる
2018/08/31

akiba LED ピカリ館さんにて1000個！？
一度にまとめて動かせるんですか！？

47

余談
2018/08/31

48

なぜRubyなのかRubyの方が開発が素早そうだったから
Pythonevents = Stream.from_pin(MIO::p0)t = events.map(lambda event: 1)
count = t.scan(lambda acc, x: acc + x)

VS
Ruby

events = Stream.from_pin(MIO::p0)
count = events.map(){ |event| 1 }.scan(){ |acc, x| acc + x }・Method chainがしにくい・ブロックが渡せない⇒ちょっとイマイチ

・Method chainできる・ラムダ抽象をブロックで⇒いいかんじ！

ML/DL向けに今後はPythonにも展開していきたい
2018/08/31

49

自動テストの話• RSpec+自作テスタ on Travis CI
RSpecを用いた各メソッド単体テスト
makefileからテストハードの合成
Icarus Verilogでテストハードの動作検証
Vivadoでテストハードの配置配線が通るかテスト
2018/08/31

on 自宅サーバ50

2018/08/31

51