Das 2015

155
JavaベースのFPGA向け高位合成処理系の 実装と活用事例 イーツリーズ・ジャパン/わさらぼ 三好 健文 2015.08.27

Transcript of Das 2015

Page 1: Das 2015

JavaベースのFPGA向け高位合成処理系の実装と活用事例

イーツリーズ・ジャパン/わさらぼ 三好健文

2015.08.27

Page 2: Das 2015

JavaベースのFPGA向け高位合成処理系の実装と活用事例

イーツリーズ・ジャパン/わさらぼ 三好健文

2015.08.27

Synthesijer

Page 3: Das 2015

2

Synthesijer とは✔ JavaプログラムをFPGA上のハードウェアに変換

✔ 複雑なアルゴリズムのハードウェア実装を楽に✔ オブクジェクト指向設計による再利用性の向上

✔ 特殊な記法,追加構文はない✔ ソフトウェアとして実行可能.動作の確認、検証が容易

✔ 書けるプログラムに制限は加える

(動的なnew,再帰は制限付きで可など)

Javaコンパイラフロントエンド

Synthesijerエンジン

Javaコンパイラバックエンド

合成配置配線

while(){if(...){ … }else{ … … }….}

複雑な状態遷移も,Javaの制御構文を使って楽に設計できる同じJavaプログラムをソフトウェアとしてもFPGA上のハードウェアとしても実行可能

Open-source

Page 4: Das 2015

3

Synthesijer クイックスタート

Page 5: Das 2015

4

クイックスタート 1/8(1) バイナリパッケージをダウンロードします

http://synthesijer.github.io/web/

なるべく日付の新しいものをDLしてください

ダウンロードページに遷移します

✔ JDK8が必要です.✔ Eclipseを使って開発してみます.

Page 6: Das 2015

5

クイックスタート 2/8

(2) Eclipseでプロジェクトを作ります

Page 7: Das 2015

6

クイックスタート 3/8(3) libフォルダを作ってDLしたJARをコピー,ビルドパスに追加する

DLしたJARをD&Dでコピー

ビルドパスに追加

準備完了!!

Page 8: Das 2015

7

クイックスタート 4/8(4) Javaのクラスを作る

クラス生成ダイアログ

Page 9: Das 2015

8

クイックスタート 5/8(5) 間隔をおいて変数ledをtrue/falseするプログラムを書く

Lチカに相当する変数

適当なウェイト点滅

自動コンパイルが裏で動くので,Javaコードとしての正しさは即座にチェックされる

Page 10: Das 2015

9

クイックスタート 6/8(6) Synthesijerを使ってJavaコードをHDLに変換

Page 11: Das 2015

10

クイックスタート 6/8(6) Synthesijerを使ってJavaコードをHDLに変換

Page 12: Das 2015

11

クイックスタート 7/8(7) ISEで合成,配置配線

生成したモジュールのインスタンスを生成

制約ファイルは別途用意

Page 13: Das 2015

12

クイックスタート 8/8(8) FPGAをコンフィギュレーションして動作を確認

Page 14: Das 2015

1

コンテンツ

✔ 高位合成処理系のあれこれ

✔ Synthesijerの設計と実装

✔ Synthesijerのサンプル/使用事例✔ Synthesijer.Scala✔ これからについて

Page 15: Das 2015

2

“FPGAにキラーアプリはないの定理”への挑戦Florent de Dinechin, "Building Custom Arithmetic Operators with the FloPoCo Generator”,http://www.hipeac.net/conference/berlin/tutorial/flopoco

なんとかして開発スピードを速くしたい

Page 16: Das 2015

3

デメリット克服のためには

✔ FPGAを使うのをやめる

✔ より抽象度の高い設計手法/処理系を使う

Page 17: Das 2015

4

デメリット克服のためには

✔ FPGAを使うのをやめる✔ 別にプロセッサを持ってくる✔ FPGAの上にプロセッサを作る

✔ より抽象度の高い設計手法/処理系を使う✔ 既存のプログラミング言語を借用する✔ 新しい記述言語を模索する✔ 新しい設計概念を模索する

Page 18: Das 2015

5

デメリット克服のためには

✔ FPGAを使うのをやめる✔ 別にプロセッサを持ってくる✔ FPGAの上にプロセッサを作る

✔ より抽象度の高い設計手法/処理系を使う✔ 既存のプログラミング言語を借用する✔ 新しい記述言語を模索する✔ 新しい設計概念を模索する

Page 19: Das 2015

2014 年 8 月 2015 年 1 月0

5

10

15

20

25

30

35

40

45

x20

Page 20: Das 2015

2014 年 8 月 2015 年 1 月0

5

10

15

20

25

30

35

40

45

x20

高位合成を実装する人達の飲み会 # 2014.8.3

高位合成友の会# 2015.1.16

Page 21: Das 2015

1

高位合成処理系あれこれ

Page 22: Das 2015

2

ESLSynthesisの市場規模

Page 23: Das 2015

3

Source Abstraction Level

Ease of Impl.

Learning curve

Document-ation

Floating/Fixed Pt.

DesignExploration

Testbenchgeneration Verification Size FPGA

Slices

AccelDSP Matlab untimed ++ ++ + Auto conversion + yes ++ 2411

Agility Compiler

SystemC Cycle accurate

+ - + Fixed pt + no -

AutoPilotC, C++,SystemC

Cycle+untimed ++ ++ ++ yes ++ yes ++ 232

Bluespec BSV Cycle accurate +/- - + no limited no - 120

Catapult C C, C++,SystemC

Cycle+untimed ++ ++ ++ Fixed pt ++ yes ++ 370/560

Compaan C, Matlab untimed + + - automatic ++ no + 3398

CyberWorkBench

SystemC, C, BDL

Cycle + untimed + + + yes ++ yes + 243/292

DK Design Suite HandelC Cycle

accurate +/- - +/- Fixed pt limited no +/- 311/945

Impluse CoDeveloper

ImpulseC Cycle + unlimited

- + - Fixed pt + no + 4611

ROCCC C subst untimed - +/- + no + no -

Synphony C C, C++ untimed ++ + + Fixed pt + yes ++ 1047

いろいろな高位合成

Wim Meeus, Kristof Van Beeck, Toon Goedemé, Jan Meel, Dirk Stroobandt, "An overview of today’s high-level synthesis tools", Design Automation for Embedded Systems, September 2012, Volume 16, Issue 3, pp 31-51

Page 24: Das 2015

4

高位合成処理系,躍進のわけ✔ Embedded processors are in almost every SoC✔ Huge silicon capacity✔ Behavioral IP reuse✔ Verification drives the acceptance of HLS Specification✔ Accelerators and heterogeneous SoC

✔ Less pressure for formal verification✔ Ideal for platform-based synthesis✔ More pressure for time-to-market✔ Accelerated or reconfigurable computing

Cong, J.; Bin Liu; Neuendorffer, S.; Noguera, J.; Vissers, K.; Zhiru Zhang, "High-Level Synthesis for FPGAs: From Prototyping to Deployment," in Computer-Aided Design of Integrated Circuits and Systems, IEEE Transactions on , vol.30, no.4, pp.473-491, April 2011

Page 25: Das 2015

5

すみません,これはここまで

Page 26: Das 2015

6

実際のところ高位合成は身近

Page 27: Das 2015

1

Synthesijer 設計と実装 

Page 28: Das 2015

2

高位合成処理系に何を求めるか?✔ 記述コストの軽減

✔ 高抽象度の表現方法を利用したい✔ 言語習得にコストをかけたくない

✔ 動作検証/デバッグ効率の向上✔ 短時間で動作を確認したい✔ 見通しよく,手軽なデバッグをしたい

✔ FPGAのパフォーマンスの活用✔ 粗粒度,細粒度の並列性の活用✔ IPコアや内部機能ユニットを活用したい✔ 設計空間の探索

Page 29: Das 2015

3

Synthesijer

✔ クラスによるオブジェクト指向設計言語

 ← HWのモジュール設計との親和性は高そう

✔ 言語仕様で並列処理をサポート

✔ Thread,wait-notify

✔ (Cと違い)明示的なポインタの扱いが不要

✔ 言語の想定するメモリ構造から解放され得るかも

✔ 動的な振る舞いは厄介そう

Javaベースの高位合成処理系

Page 30: Das 2015

4

Synthesijer 設計基本方針✔ JavaプログラムをそのままHDL→HW化

✔ 追加構文,データ型は導入しない

✔ 使える記述には制限を導入

HDLで書けることをすべてJavaで書けるようにするではない

Javaで書けることををすべてHDL(HW)にするではない

Javaとして実行可能なプログラムをHWにする≒ フェッチのないJavaプロセッサをHDLで直接生成する

Page 31: Das 2015

なぜJavaなのか?

Page 32: Das 2015

Write Once, Run Anywhere!!

Page 33: Das 2015

Write Once, Run Anywhere!!

たとえCPUがなくても!!

Page 34: Das 2015

8

[朗報] OpenJDKが素敵!!✔ オープンソースで開発されているJavaコンパイラ/実行環境✔ 字句解析,構文解析はお任せ✔ 最適化後の内部構造へのアクセスが簡単

✔ 実行時に型情報に触りやすい✔ printfデバッグのノリで動作を追うことができる✔ C/C++だとちょっと厳しい(と思う)

Page 35: Das 2015

9

Javaプログラムへのアクセス

/** Generate code and emit a class file for a given class * @param env The attribution environment of the outermost class * containing this class. * @param cdef The class definition from which code is generated. */ JavaFileObject genCode(Env<AttrContext> env, JCClassDecl cdef) throws IOException { synthesijer.jcfrontend.Main.newModule(env, cdef); // add hook for synthesijer try { if (gen.genClass(env, cdef) && (errorCount() == 0)) return writer.writeClass(cdef.sym); } catch (ClassWriter.PoolOverflow ex) { log.error(cdef.pos(), "limit.pool"); } catch (ClassWriter.StringOverflow ex) { log.error(cdef.pos(), "limit.string.overflow", ex.value.substring(0, 20)); } catch (CompletionFailure ex) { chk.completionError(cdef.pos(), ex); } return null; }

JavaCompiler.javaのgenCode内に処理をぶら下げればよい

Page 36: Das 2015

10

Javaコードの内部表現

この構造をたどるOpenJDKの仕組みを利用

JCMethodDecl

JCVariableDecl

JCMethodDecl

JCMethodDecl

・・・

・・・

JCVariableDecl

JCVariableDecl

JCExpressionState

JCBlock

JCIf

JCFor

JCAssign

JCArrayAccess

JCBinary

JCIdent

JCLiteral

・・・

・・・

JSCtatementJSExpression

JCClassDecl

Page 37: Das 2015

11

たとえばclass

public class JCTopVisitor extends Visitor{

private final Module module;

...

public void visitClassDef(JCClassDecl that){for (JCTree def : that.defs) {

if(def == null){;

}else if(def instanceof JCMethodDecl){def.accept(this);

}else if(def instanceof JCVariableDecl){def.accept(new JCStmtVisitor(module));

}else{System.err.printf("Unknown class: %s (%s)", def, def.getClass());

}}

}

...

構文解析器がJCClassDeclに出会ったら呼び出してくれる

Page 38: Das 2015

12

たとえばmethod

public void visitMethodDef(JCMethodDecl decl){String name = decl.getName().toString();Type type;if(JCFrontendUtils.isConstructor(decl)){

type = new MySelfType();}else{

type = TypeBuilder.genType(decl.getReturnType());}Method m = new Method(module, name, type);

m.setArgs(parseArgs(decl.getParameters(), m));...

m.setPrivateFlag(JCFrontendUtils.isPrivate(decl.mods));m.setParallelFlag(JCFrontendUtils.isAnnotatedBy(decl.mods.annotations, "parallel"));

...m.setConstructorFlag(JCFrontendUtils.isConstructor(decl));

...for(JCStatement stmt: decl.body.getStatements()){

JCStmtVisitor visitor = new JCStmtVisitor(m);stmt.accept(visitor);m.getBody().addStatement(visitor.getStatement());

}

module.addMethod(m);}

構文解析器がJCMethodDeclに出会ったら呼び出してくれる

Page 39: Das 2015

13

たとえば制御構文

public void visitIf(JCIf that){IfStatement tmp = new IfStatement(scope);tmp.setCondition(stepIn(that.cond, scope));tmp.setThenPart(wrapBlockStatement(stepIn(that.thenpart, scope)));if(that.elsepart != null){

tmp.setElsePart(wrapBlockStatement(stepIn(that.elsepart, scope)));}stmt = tmp;

}

public void visitForLoop(JCForLoop that){ForStatement tmp = new ForStatement(scope);for(JCStatement s: that.init){

//tmp.addInitialize(stepIn(s, scope));tmp.addInitialize(stepIn(s, tmp));

}tmp.setCondition(stepIn(that.cond, tmp));for(JCStatement s: that.step){

tmp.addUpdate(stepIn(s, tmp));}tmp.setBody(wrapBlockStatement(stepIn(that.body, tmp)));stmt = tmp;

}

構文解析器がJCIfDeclに出会ったら呼び出してくれる

Page 40: Das 2015

14

高位合成処理系は何をするか?

http://www.ida.liu.se/~petel/SysSyn/lect3.frm.pdf

基本はこれに書いてありますHigh-Level Synthesis

Page 41: Das 2015

15

高位合成処理系は何をするか

入力言語 データフロー スケジューリングデータパス生成

ステート生成物理マッピング

Page 42: Das 2015

16

高位合成処理系は何をするか

入力言語 データフロー スケジューリングデータパス生成

ステート生成物理マッピング

(1) (2)

(3)

Page 43: Das 2015

17

高位合成処理系の3つのレイヤ

(1)言語デザイン

(2)アーキテクチャの生成

(3)テクノロジ・マッピング

✔ 設計コストが小さくなるように

✔ パフォーマンスを出しやすいように

✔ 命令スケジューリング,並列性の抽出

✔ 動作周波数,リソース使用量,レイテンシの考慮

✔ 適切な物理マッピング

✔ 符号化

Page 44: Das 2015

18

Synthesijerでは

✔ 設計コストが小さくなるように

✔ パフォーマンスを出しやすいように

✔ 命令スケジューリング,並列化の抽出

✔ 動作周波数,リソース使用量,レイテンシの考慮

✔ 適切な物理マッピング

✔ 符号化

(1)言語デザイン

(2)アーキテクチャの生成

(3)テクノロジ・マッピング

Java - オブジェクト指向設計 - スレッドで並列化 - 既存資産の活用

ISE/Vivado,QuartusIIにお任せ

Javaから変換するエンジンを実装

Page 45: Das 2015

19

Synthesijerオーバービュー

JavaコードJavaプログラムの解析

スケジューリング表を作成

最適化

HDL構文の組み立てVHDL/Verilog HDLコード

Javaコード

VHDL/Verilog HDLコード

VHDL/Verilog HDLコード

中間表現(S式)

フロントエンド

ミドルエンド

バックエンド

Page 46: Das 2015

20

Synthesijer でのHDL生成: 例題

package blink_led;

public class BlinkLED { public boolean led; public void run(){ while(true){ led = true; for(int i = 0; i < 5000000; i++) ; led = false; for(int i = 0; i < 5000000; i++) ; } }}

Page 47: Das 2015

21

Synthesijer でのHDL生成: 大枠

class

entity blink_led_BlinkLED is port ( clk : in std_logic; reset : in std_logic; field_led_output : out std_logic; field_led_input : in std_logic; field_led_input_we : in std_logic; run_req : in std_logic; run_busy : out std_logic );end blink_led_BlinkLED;

class BlinkLED {

public boolean led;

public void run(){ while(true){ … } }}

module

クラス毎にHWモジュールを生成

Page 48: Das 2015

22

Synthesijer でのHDL生成: スケジュール表

メソッド毎にスケジュール表を生成

run_0000: op=METHOD_EXIT, src=, dest=, next=0001run_0001: op=METHOD_ENTRY, src=, dest=, next=0002 (name=run)run_0002: op=JT, src=true:BOOLEAN, dest=, next=0004, 0003run_0003: op=JP, src=, dest=, next=0023run_0004: op=ASSIGN, src=true:BOOLEAN, dest=class_led_0000:BOOLEAN, next=0005run_0005: op=ASSIGN, src=0:INT, dest=run_i_0001:INT, next=0006run_0006: op=LT, src=run_i_0001:INT, 5000000:INT, dest=binary_expr_00002:BOOLEAN, next=0007run_0007: op=JT, src=binary_expr_00002:BOOLEAN, dest=, next=0012, 0008run_0008: op=JP, src=, dest=, next=0013run_0009: op=ADD, src=run_i_0001:INT, 1:INT, dest=unary_expr_00003:INT, next=0010run_0010: op=ASSIGN, src=unary_expr_00003:INT, dest=run_i_0001:INT, next=0011run_0011: op=JP, src=, dest=, next=0006run_0012: op=JP, src=, dest=, next=0009run_0013: op=ASSIGN, src=false:BOOLEAN, dest=class_led_0000:BOOLEAN, next=0014run_0014: op=ASSIGN, src=0:INT, dest=run_i_0004:INT, next=0015run_0015: op=LT, src=run_i_0004:INT, 5000000:INT, dest=binary_expr_00005:BOOLEAN, next=0016run_0016: op=JT, src=binary_expr_00005:BOOLEAN, dest=, next=0021, 0017run_0017: op=JP, src=, dest=, next=0022run_0018: op=ADD, src=run_i_0004:INT, 1:INT, dest=unary_expr_00006:INT, next=0019run_0019: op=ASSIGN, src=unary_expr_00006:INT, dest=run_i_0004:INT, next=0020run_0020: op=JP, src=, dest=, next=0015run_0021: op=JP, src=, dest=, next=0018run_0022: op=JP, src=, dest=, next=0002run_0023: op=JP, src=, dest=, next=0000

Page 49: Das 2015

23

スケジューラ表の疑似命令

✔ 四則演算,論理演算,シフト✔ 比較✔ 代入✔ 配列のアドレス指定,配列アクセス✔ Call/Return

✔ CallしたらReturnされるまで待つ(基本)✔ Thread.startの呼び出し時は待たずに次へ

✔ 無条件ジャンプ,条件分岐,SELECT✔ フィールドアクセス指示命令

Page 50: Das 2015

24

Synthesijer でのHDL生成: 出力(状態遷移)

メソッド毎にスケジュール表を生成 → 状態遷移機械

whilefor

for

Page 51: Das 2015

25

Synthesijer でのHDL生成: 状態遷移

状態遷移機械に相当するステートマシンをベタに作る always @(posedge clk) begin if(reset == 1'b1) begin run_method <= run_method_IDLE; run_method_delay <= 32'h0; end else begin case (run_method) run_method_IDLE : begin run_method <= run_method_S_0000; end run_method_S_0000 : begin run_method <= run_method_S_0001; run_method <= run_method_S_0001; end run_method_S_0001 : begin if (run_req_flag == 1'b1) begin run_method <= run_method_S_0002; end end run_method_S_0002 : begin if (tmp_0003 == 1'b1) begin run_method <= run_method_S_0004; end else if (tmp_0004 == 1'b1) begin run_method <= run_method_S_0003; end end run_method_S_0003 : begin...

Page 52: Das 2015

26

Synthesijer でのHDL生成: 出力(データ操作)

メソッド毎にスケジュール表を生成 → 文/式相当のデータ操作

run_0000: op=METHOD_EXIT, src=, dest=, next=0001run_0001: op=METHOD_ENTRY, src=, dest=, next=0002 (name=run)run_0002: op=JT, src=true:BOOLEAN, dest=, next=0004, 0003run_0003: op=JP, src=, dest=, next=0023run_0004: op=ASSIGN, src=true:BOOLEAN, dest=class_led_0000:BOOLEAN, next=0005run_0005: op=ASSIGN, src=0:INT, dest=run_i_0001:INT, next=0006run_0006: op=LT, src=run_i_0001:INT, 5000000:INT, dest=binary_expr_00002:BOOLEAN, next=0007run_0007: op=JT, src=binary_expr_00002:BOOLEAN, dest=, next=0012, 0008run_0008: op=JP, src=, dest=, next=0013run_0009: op=ADD, src=run_i_0001:INT, 1:INT, dest=unary_expr_00003:INT, next=0010run_0010: op=ASSIGN, src=unary_expr_00003:INT, dest=run_i_0001:INT, next=0011run_0011: op=JP, src=, dest=, next=0006run_0012: op=JP, src=, dest=, next=0009run_0013: op=ASSIGN, src=false:BOOLEAN, dest=class_led_0000:BOOLEAN, next=0014run_0014: op=ASSIGN, src=0:INT, dest=run_i_0004:INT, next=0015run_0015: op=LT, src=run_i_0004:INT, 5000000:INT, dest=binary_expr_00005:BOOLEAN, next=0016run_0016: op=JT, src=binary_expr_00005:BOOLEAN, dest=, next=0021, 0017run_0017: op=JP, src=, dest=, next=0022run_0018: op=ADD, src=run_i_0004:INT, 1:INT, dest=unary_expr_00006:INT, next=0019run_0019: op=ASSIGN, src=unary_expr_00006:INT, dest=run_i_0004:INT, next=0020run_0020: op=JP, src=, dest=, next=0015run_0021: op=JP, src=, dest=, next=0018run_0022: op=JP, src=, dest=, next=0002run_0023: op=JP, src=, dest=, next=0000

Page 53: Das 2015

27

Synthesijer でのHDL生成: 出力(データ操作)

メソッド毎にスケジュール表を生成 → 文/式相当のデータ操作

run_0000: op=METHOD_EXIT, src=, dest=, next=0001run_0001: op=METHOD_ENTRY, src=, dest=, next=0002 (name=run)run_0002: op=JT, src=true:BOOLEAN, dest=, next=0004, 0003run_0003: op=JP, src=, dest=, next=0023run_0004: op=ASSIGN, src=true:BOOLEAN, dest=class_led_0000:BOOLEAN, next=0005run_0005: op=ASSIGN, src=0:INT, dest=run_i_0001:INT, next=0006run_0006: op=LT, src=run_i_0001:INT, 5000000:INT, dest=binary_expr_00002:BOOLEAN, next=0007run_0007: op=JT, src=binary_expr_00002:BOOLEAN, dest=, next=0012, 0008run_0008: op=JP, src=, dest=, next=0013run_0009: op=ADD, src=run_i_0001:INT, 1:INT, dest=unary_expr_00003:INT, next=0010run_0010: op=ASSIGN, src=unary_expr_00003:INT, dest=run_i_0001:INT, next=0011run_0011: op=JP, src=, dest=, next=0006run_0012: op=JP, src=, dest=, next=0009run_0013: op=ASSIGN, src=false:BOOLEAN, dest=class_led_0000:BOOLEAN, next=0014run_0014: op=ASSIGN, src=0:INT, dest=run_i_0004:INT, next=0015run_0015: op=LT, src=run_i_0004:INT, 5000000:INT, dest=binary_expr_00005:BOOLEAN, next=0016run_0016: op=JT, src=binary_expr_00005:BOOLEAN, dest=, next=0021, 0017run_0017: op=JP, src=, dest=, next=0022run_0018: op=ADD, src=run_i_0004:INT, 1:INT, dest=unary_expr_00006:INT, next=0019run_0019: op=ASSIGN, src=unary_expr_00006:INT, dest=run_i_0004:INT, next=0020run_0020: op=JP, src=, dest=, next=0015run_0021: op=JP, src=, dest=, next=0018run_0022: op=JP, src=, dest=, next=0002run_0023: op=JP, src=, dest=, next=0000

... assign tmp_0010 = run_i_0001 + 32'h00000001;... always @(posedge clk) begin if(reset == 1'b1) begin run_i_0001 <= 32'h00000000; end else begin if (run_method == run_method_S_0004) begin run_i_0001 <= 32'h00000000; end else if (run_method == run_method_S_0010) begin run_i_0001 <= unary_expr_00003; end end end... always @(posedge clk) begin if(reset == 1'b1) begin unary_expr_00003 <= 0; end else begin if (run_method == run_method_S_0009) begin unary_expr_00003 <= tmp_0010; end end end...

Page 54: Das 2015

28

最適化の例

べたなプログラムを用意

public class SimpleProgram{

public int sum(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l){ c = a + b; f = d + e; i = g + h; l = j + k; c = c + f; l = l + i; c = c + l; return c; }}

Page 55: Das 2015

29

最適化の例

べたなプログラムを用意sum_0000: op=METHOD_EXIT, src=, dest=, next=0001sum_0001: op=METHOD_ENTRY, src=, dest=, next=0002 (name=sum)sum_0002: op=ADD, src=sum_a_0000:INT, sum_b_0001:INT, dest=binary_expr_00012:INT, next=0003sum_0003: op=ASSIGN, src=binary_expr_00012:INT, dest=sum_c_0002:INT, next=0004sum_0004: op=ADD, src=sum_d_0003:INT, sum_e_0004:INT, dest=binary_expr_00013:INT, next=0005sum_0005: op=ASSIGN, src=binary_expr_00013:INT, dest=sum_f_0005:INT, next=0006sum_0006: op=ADD, src=sum_g_0006:INT, sum_h_0007:INT, dest=binary_expr_00014:INT, next=0007sum_0007: op=ASSIGN, src=binary_expr_00014:INT, dest=sum_i_0008:INT, next=0008sum_0008: op=ADD, src=sum_j_0009:INT, sum_k_0010:INT, dest=binary_expr_00015:INT, next=0009sum_0009: op=ASSIGN, src=binary_expr_00015:INT, dest=sum_l_0011:INT, next=0010sum_0010: op=ADD, src=sum_c_0002:INT, sum_f_0005:INT, dest=binary_expr_00016:INT, next=0011sum_0011: op=ASSIGN, src=binary_expr_00016:INT, dest=sum_c_0002:INT, next=0012sum_0012: op=ADD, src=sum_l_0011:INT, sum_i_0008:INT, dest=binary_expr_00017:INT, next=0013sum_0013: op=ASSIGN, src=binary_expr_00017:INT, dest=sum_l_0011:INT, next=0014sum_0014: op=ADD, src=sum_c_0002:INT, sum_l_0011:INT, dest=binary_expr_00018:INT, next=0015sum_0015: op=ASSIGN, src=binary_expr_00018:INT, dest=sum_c_0002:INT, next=0016sum_0016: op=RETURN, src=sum_c_0002:INT, dest=, next=0000sum_0017: op=JP, src=, dest=, next=0000

Page 56: Das 2015

30

最適化の例

べたなプログラムを用意sum_0000: op=METHOD_EXIT, src=, dest=, next=0001sum_0001: op=METHOD_ENTRY, src=, dest=, next=0002 (name=sum)sum_0002: op=ADD, src=sum_a_0000:INT, sum_b_0001:INT, dest=binary_expr_00012:INT, next=0003sum_0002: op=ADD, src=sum_d_0003:INT, sum_e_0004:INT, dest=binary_expr_00013:INT, next=0003sum_0002: op=ADD, src=sum_g_0006:INT, sum_h_0007:INT, dest=binary_expr_00014:INT, next=0003sum_0002: op=ADD, src=sum_j_0009:INT, sum_k_0010:INT, dest=binary_expr_00015:INT, next=0003sum_0003: op=ASSIGN, src=binary_expr_00012:INT, dest=sum_c_0002:INT, next=0010sum_0003: op=ASSIGN, src=binary_expr_00013:INT, dest=sum_f_0005:INT, next=0010sum_0003: op=ASSIGN, src=binary_expr_00014:INT, dest=sum_i_0008:INT, next=0010sum_0003: op=ASSIGN, src=binary_expr_00015:INT, dest=sum_l_0011:INT, next=0010sum_0010: op=ADD, src=sum_c_0002:INT, sum_f_0005:INT, dest=binary_expr_00016:INT, next=0011sum_0010: op=ADD, src=sum_l_0011:INT, sum_i_0008:INT, dest=binary_expr_00017:INT, next=0011sum_0011: op=ASSIGN, src=binary_expr_00016:INT, dest=sum_c_0002:INT, next=0014sum_0011: op=ASSIGN, src=binary_expr_00017:INT, dest=sum_l_0011:INT, next=0014sum_0014: op=ADD, src=sum_c_0002:INT, sum_l_0011:INT, dest=binary_expr_00018:INT, next=0015sum_0015: op=ASSIGN, src=binary_expr_00018:INT, dest=sum_c_0002:INT, next=0016sum_0016: op=RETURN, src=sum_c_0002:INT, dest=, next=0000sum_0017: op=JP, src=, dest=, next=0000

Page 57: Das 2015

31

最適化の例

べたなプログラムを用意sum_0000: op=METHOD_EXIT, src=, dest=, next=0001sum_0001: op=METHOD_ENTRY, src=, dest=, next=0002 (name=sum)sum_0002: op=ADD, src=sum_a_0000:INT, sum_b_0001:INT, dest=binary_expr_00012:INT, next=0016sum_0002: op=ADD, src=sum_d_0003:INT, sum_e_0004:INT, dest=binary_expr_00013:INT, next=0016sum_0002: op=ADD, src=sum_g_0006:INT, sum_h_0007:INT, dest=binary_expr_00014:INT, next=0016sum_0002: op=ADD, src=sum_j_0009:INT, sum_k_0010:INT, dest=binary_expr_00015:INT, next=0016sum_0002: op=ASSIGN, src=binary_expr_00012:INT:chain, dest=sum_c_0002:INT, next=0016sum_0002: op=ASSIGN, src=binary_expr_00013:INT:chain, dest=sum_f_0005:INT, next=0016sum_0002: op=ASSIGN, src=binary_expr_00014:INT:chain, dest=sum_i_0008:INT, next=0016sum_0002: op=ASSIGN, src=binary_expr_00015:INT:chain, dest=sum_l_0011:INT, next=0016sum_0002: op=ADD, src=sum_c_0002:INT:chain, sum_f_0005:INT:chain, dest=binary_expr_00016:INT, sum_0002: op=ADD, src=sum_l_0011:INT:chain, sum_i_0008:INT:chain, dest=binary_expr_00017:INT, sum_0002: op=ASSIGN, src=binary_expr_00016:INT:chain, dest=sum_c_0002:INT, next=0016sum_0002: op=ASSIGN, src=binary_expr_00017:INT:chain, dest=sum_l_0011:INT, next=0016sum_0002: op=ADD, src=sum_c_0002:INT:chain, sum_l_0011:INT:chain, dest=binary_expr_00018:INT, sum_0002: op=ASSIGN, src=binary_expr_00018:INT:chain, dest=sum_c_0002:INT, next=0016sum_0016: op=RETURN, src=sum_c_0002:INT, dest=, next=0000sum_0017: op=JP, src=, dest=, next=0000

Page 58: Das 2015

32

最適化の例

内部はS式.S式を外でいじって処理系に戻すことも可 (SEQUENCER sum (SLOT 0 (METHOD_EXIT :next (1)) ) (SLOT 1 (METHOD_ENTRY :next (2)) ) (SLOT 2 (SET binary_expr_00012 (ADD sum_a_0000 sum_b_0001) :next (16)) (SET binary_expr_00013 (ADD sum_d_0003 sum_e_0004) :next (16)) (SET binary_expr_00014 (ADD sum_g_0006 sum_h_0007) :next (16)) (SET binary_expr_00015 (ADD sum_j_0009 sum_k_0010) :next (16)) (SET sum_c_0002 (ASSIGN binary_expr_00012) :next (16)) ...略... (SET sum_c_0002 (ASSIGN binary_expr_00016) :next (16)) (SET sum_l_0011 (ASSIGN binary_expr_00017) :next (16)) (SET binary_expr_00018 (ADD sum_c_0002 sum_l_0011) :next (16)) (SET sum_c_0002 (ASSIGN binary_expr_00018) :next (16)) ) (SLOT 16 (RETURN sum_c_0002 :next (0)) ) (SLOT 17 (JP :next (0)) ) )

Page 59: Das 2015

33

SynthesijerはJavaベースなので...オブジェクト指向的プログラミング!!… … …

Page 60: Das 2015

34

インスタンス生成クラスのインスタンスを生成/利用できる

public class Test002 { public static final int DEFAULT_VALUE = 0x20; public int[] a = new int[128]; public int x, y; public void init(){ for(int i = 0; i < a.length; i++){ a[i] = 0; } }

public void set(int i, int v){ a[i] = v; }…}

ex. sample/test/Test002.javaとsample/test/Test003.java

public class Test003 { private final Test002 t = new Test002(); public void test(){ t.init(); t.set(0, 100); // 0 <- 100 … }…}

Page 61: Das 2015

35

インスタンス生成クラスのインスタンスを生成/利用できるex. sample/test/Test002.javaとsample/test/Test003.java

Test002 class_t_0000 ( .clk(class_t_0000_clk), .reset(class_t_0000_reset), .a_address(class_t_0000_a_address), .a_we(class_t_0000_a_we), .a_oe(class_t_0000_a_oe), .a_din(class_t_0000_a_din), .a_dout(class_t_0000_a_dout), .a_length(class_t_0000_a_length), … .set_i(class_t_0000_set_i), .set_v(class_t_0000_set_v), … .init_req(class_t_0000_init_req), .init_busy(class_t_0000_init_busy), … .set_req(class_t_0000_set_req), .set_busy(class_t_0000_set_busy), … );

Page 62: Das 2015

36

インスタンス生成・拡張モジュールVHDL/Verilog HDLで書いたモジュールのインスタンス化

module hoge( input wire clk, input wire reset,

input signed [31:0] a_address, input signed [31:0] a_din, input wire we, input wire oe, output signed [31:0] a_dout, output signed [31:0] a_length,

input wire func_req, output wire func_busy,

output wire [31:0] q);

endmodule

clkreset

a_addressa_dina_wea_oe

a_din

a_dout

func_reqfunc_busy

hoge q

ex. Verilog HDLで書いたhogeをJavaから呼び出す

a_length

Page 63: Das 2015

37

インスタンス生成・拡張モジュールVHDL/Verilog HDLで書いたモジュールのインスタンス化

class HogeIface extends HDLModule{

int[] a; void func(){}

public HogeIface(String... args){ newPort("a_address", HDLPort.DIR.IN, genSignedType(32)); newPort("a_din", HDLPort.DIR.IN, genSignedType(32)); newPort("a_we", HDLPort.DIR.IN, genBitType()); newPort("a_oe", HDLPort.DIR.IN, genBitType()); newPort("a_dout", HDLPort.DIR.OUT, genSignedType(32)); newPort("a_length", HDLPort.DIR.OUT, genSignedType(32)); newPort("func_req", HDLPort.DIR.IN, genBitType()); newPort("func_busy", HDLPort.DIR.OUT, genBitType());

newPort("q", HDLPort.DIR.OUT, genVectorType(32), Enum.of(OPTION.EXPORT)); } … }

↓のようなJava用のダミーコードを書いて,使う… private final HogeIface obj = new HogeIface();… private void func(){ obj.a[10] = 100; obj.func();…

a[]

func()

これはJavaでは使わない,という印

Page 64: Das 2015

38

インスタンス生成の例乱数生成のハードウェアをJavaから使う

// xorshift RNG, period 2^128-1// cf. http://www.jstatsoft.org/v08/i14/papermodule xor128 ( input wire clk, input wire reset, output reg [63:0] q ); reg[31:0] x = 123456789, y = 362436069, z = 521288629; reg[31:0] w = 88675123; reg[31:0] t; always @(posedge clk) begin q[63:32] <= 32'h0; if(reset == 1'b1) begin x <= 123456789; y <= 362436069; z <= 521288629; w <= 88675123; end else begin t = x ^ (x << 11); x = y; y = z; z = w; w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); q[31:0] <= w; end endendmodule

Page 65: Das 2015

39

インスタンス生成の例乱数生成のハードウェアをJavaから使う

/** * リスト15: XOR128.java * xor128をJavaから利用するためのダミークラス */import synthesijer.hdl.HDLModule;import synthesijer.hdl.HDLPort;import synthesijer.hdl.HDLPrimitiveType;

public class XOR128Iface extends HDLModule{ // Javaプログラムのためのダミー変数 // xor128の出力qに相当する public long q;

public XOR128Iface(String... args){ // - 実際に使用するモジュール名 // - クロック信号名 // - リセット信号名 // の順に指定する. super("xor128", "clk", "reset"); // メンバ変数"q"に相当する64bit幅の出力ポートを生成する newPort("q", HDLPort.DIR.OUT, HDLPrimitiveType.genSignedType(64)); }

}

Page 66: Das 2015

40

インスタンス生成の例乱数生成のハードウェアをJavaから使う

/** * リスト16: XOR128Test.java * xor128をJavaから利用する例 */public class XOR128Test{ // XOR128モジュールのインスタンスを生成 private final XOR128Iface rng = new XOR128Iface();

public boolean test(){ long v = rng.q; // 乱数を読み出す // 処理の例.たとえば1000より大きければ真,とか. if(v > 1000){ return true; }else{ return false; } }}

Page 67: Das 2015

41

インスタンス生成の例乱数生成のハードウェアをJavaから使う

Page 68: Das 2015

42

クラスの継承も可継承したクラスのメソッドも利用可能

public class Test012 extends Test010{ public void run(){ test(); }}

public class Test010{

public void test(){ int a = 20; int b = 30; int c = 0;

c = a & b; c = a | b; c = a ^ b;

}

}

library IEEE;use IEEE.std_logic_1164.all;use IEEE.numeric_std.all;

entity Test012 is port ( clk : in std_logic; reset : in std_logic; run_busy : out std_logic; run_req : in std_logic; test_busy : out std_logic; test_req : in std_logic );end Test012;

architecture RTL of Test012 is...

Page 69: Das 2015

43

SynthesijerはJavaベースなので...Threadを使って並列化できる… … …

Page 70: Das 2015

44

スレッドの利用例JavaではThreadクラスを継承して並列動作を記述

/* * リスト11: ThreadCounter.java * Threadの活用例 */// JavaではThreadクラスを継承したクラスを作るpublic class ThreadCounter extends Thread{

public int counter = 0;

// Threadの処理はrunメソッドで実装する public void run(){ while(true){ // 変数をインクリメントする無限ループ counter++; } }

}

Page 71: Das 2015

45

スレッドの利用例JavaではThreadクラスを継承して並列動作を記述

import synthesijer.rt.unsynthesizable;public class ThreadTest{ private final ThreadCounter t0 = new ThreadCounter(); private final ThreadCounter t1 = new ThreadCounter(); public void test(){ t0.start(); t1.start(); } // このメソッドはSynthesijerでは無視する @unsynthesizable public static void main(String... args){ ThreadTest t = new ThreadTest(); t.test(); while(true){ System.out.println("t0:" + t.t0.counter); System.out.println("t1:" + t.t1.counter); try{ Thread.sleep(1); }catch(Exception e){ e.printStackTrace(); } } }}

Page 72: Das 2015

46

スレッドの利用例PCで実行したところ

Page 73: Das 2015

47

スレッドの利用例シミュレーションで確認したところ

Page 74: Das 2015

48

ここまでのまとめ✔ Synthesijer=JavaをVHDL/Verilog HDLに変換する処理系

✔ Javaなりのオブジェクト指向設計をサポート✔ Threadによる並列処理の記述とHW化

Page 75: Das 2015

49

Synthesijerオーバービュー

JavaコードJavaプログラムの解析

スケジューリング表を作成

最適化

HDL構文の組み立てVHDL/Verilog HDLコード

Javaコード

VHDL/Verilog HDLコード

VHDL/Verilog HDLコード

中間表現(S式)

フロントエンド

ミドルエンド

バックエンド

Page 76: Das 2015

50

Synthesijer as a Compiler-Infrastructure

Javaコード

スケジューリング表を作成

最適化

Y構文の組み立てVHDL/Verilog HDLコード

X言語

VHDL/Verilog HDLコード

中間表現(S式)

フロントエンド

ミドルエンド

バックエンド

X言語パーザー 最適化器

Y言語

Page 77: Das 2015

1

Synthesijer サンプル・事例 

Page 78: Das 2015

2

2

✔ Javaといえば,スクリプト言語のホストとしても魅力的

✔ JRuby,Scala,Clojure,and etc.✔ BrainF**k: とても小さなスクリプト言語処理系

→ hoge

サンプル1: プログラムインタプリタ

+, -, >, <, [, ], ., ,の記号からなるインタプリタ

++++++++++[>++++++++++<-]>++++.+++++++.--------.--.

http://www.kmonos.net/alang/etc/brainfuck.php

Page 79: Das 2015

3

サンプル1: BFのコード抜粋private IO io = new IO();private byte[] prog = new byte[CODESIZE];private byte[] data = new byte[ARRAYSIZE];private int ptr, pc;

public boolean step() {byte cmd = prog[pc];byte tmp;int nlvl = 0;switch (cmd) {case 0: return false;case '>': ptr++; break;case '<': ptr--; break;case '+': data[ptr] = (byte) (data[ptr] + 1); break;case '-': data[ptr] = (byte) (data[ptr] – 1); break;case '.': io.putchar(data[ptr]); break;case ',': data[ptr] = io.getchar(); break;case '[':

if (data[ptr] == (byte) 0) {while (true) {

pc++;if(prog[pc] == ']' && nlvl == 0) break;if(prog[pc] == '[') nlvl++;if(prog[pc] == ']') nlvl--;

}}break;

case ']': … } }

Page 80: Das 2015

4

サンプル1: ソフトウェアとして実行✔ 標準入出力越しでBrainF**kインタプリタを実行

✔ +, -, >, <, [, ], ., ,の記号からなるインタプリタ

Page 81: Das 2015

5

サンプル1: FPGAで実行✔ 標準入出力の代わりにシリアル通信越しで実行

Page 82: Das 2015

6

SWとHWで同じコードを利用

ソフトウェアで実行する時

BF

IO

Console

合成して実機で動かす時

IO

+ putchar()+ getchar()

+ putchar()+ getchar()

RS232Wrapper

Page 83: Das 2015

7

サンプル1: 文字の入出力ルーチンpublic void read() {

prompt();for (int i = 0; i < CODESIZE; i++) {

byte b;b = io.getchar();//io.putchar(b);if (b == '\n' || b == '\r') {

prog[i] = (byte) 0;break;

} else {prog[i] = b;

}}

}

public void print() {boolean flag = true;for (int i = 0; i < CODESIZE; i++) {

byte b = prog[i];if (b == 0) {

break;}io.putchar(b);

}io.putchar((byte) '\n');

}

Page 84: Das 2015

8

サンプル1: SW版とHW版の入出力public class IO {

private rs232c obj = new rs232c();

public void putchar(byte c) { obj.write(c); }

public byte getchar() { byte b; b = obj.read(); return b; }}

public class IO{

public void putchar(byte c){ System.out.print((char)c); }

public byte getchar(){ try{ return (byte)(System.in.read()); }catch(Exception e){ throw new RuntimeException(e); } }}

Page 85: Das 2015

9

サンプル2: ディスプレイに絵を書くpublic class BitMapTest extends Thread{... public void run(){ // 座標(100, 100)の位置にサイズ20の正方形を描画 canvas.rect(100, 100, 20, 20, BitMapCanvas.RED, 0); int c0 = 0, c = 0; // 画面上にサイズ40の正方形を色を変えながら敷き詰める for(int i = 0; i < 16; i++){ // (/ 640 40)16 c = c0; for(int j = 0; j < 12; j++){ // (/ 480 40) canvas.rect(40*i, 40*j, 40, 40, color(c), 1); c = (c + 1) & 0x7; } c0 = (c0 + 1) & 0x7; } // 線を描画するメソッドのテスト canvas.line(0, 0, 639, 479, color(7), 1); canvas.line(639, 0, 0, 479, color(7), 1);

// フレームバッファ1をスクロールさせる int v = 0, h = 0; while(true){ canvas.set_offset(0, v, 1); v = (v == 479) ? 0 : v + 1; t.sleep(1); // 適当なウェイト } }

Page 86: Das 2015

10

✔ ソフトウェアとして実行して動作を確認

サンプル2: ディスプレイに絵を書く

Page 87: Das 2015

11

✔ 実FPGAとディスプレイで同じ動作を確認

サンプル2: ディスプレイに絵を書く

Page 88: Das 2015

12

サンプル3: AHCI✔ Advanced Host Controller Interface

✔ SATAなHDDやSSDを接続するためのインターフェース

✔ 最近のOSのほとんどでサポートされている

= 標準のデバドラでブロックデバイスにみえる

✔ SATAなHDDやSSD以外に接続してはいけない,ことはない

✔ AHCI HBAをFPGAに実装してディスクアクセスしてみよう

Page 89: Das 2015

13

サンプル3: AHCI

PC

FPGAディスク?

何かの処理

ディスク

インターネット

✔ なんでもディスクに見せてしまうのは楽しそう

✔ たとえば

BRAM

Page 90: Das 2015

14

一般的なAHCIによるディスク接続

ディスクに送るコマンドを預かるディスクからのリプライを返す作業用メモリのポインタの確認複数コマンドをOoOで発行(NCQ)

ディスクへのFISの発行ディスクからのFISの受信ステータス管理

Page 91: Das 2015

15

AHCIのメモリチェイン

どのコマンドが有効かはPxCIレジスタをみる

ディスクに送るべきコマンド

PCIeのBAR5にレジスタ空間へのポインタがある

ディスクとのやり取りに使うメモリ領域へのポインタ

近代的なOSでは大量の仮想メモリを複数の細かい実メモリに分割して扱う

ディスクとのやり取りに使うメモリ領域

P0CLB

HBAからはP0CLBの値でたどる

Page 92: Das 2015

16

AHCI越しのデータ授受のフロー

PCIe越しの通信

Page 93: Das 2015

17

HBAの動作フロー/FIS取得までprivate int wait_for_command(){ while(reg.values[P0CI] == 0) ; reg.values[P0CMD] = 0x10110007; // 処理開始 int id = get_port_id(reg.values[P0CI]); // '1'のbitを選ぶ

axi.fetch(reg.values[P0CLB] + (id << 5), 4); // command headerを取得 dw0 = axi.read(0); // PRDTL, PMP, RCBRPWA, CFL dw1 = axi.read(1); // PRD Byte Count dw2 = axi.read(2); // CTBA0 dw3 = axi.read(3); prdtl = (dw0 >> 16) & 0x0000FFFF;

ctba = dw2; axi.fetch(ctba, 5);// CFIS取得 cfis_dw0 = axi.read(0); // Features, // Command,… cfis_dw1 = axi.read(1); cfis_dw2 = axi.read(2); cfis_dw3 = axi.read(3); return (dw0 >> 16) & 0x000000FF;}

P0CLB

Page 94: Das 2015

18

動作例PCIデバイス→AHCIデバイス→ディスクに見えている

うまくOSをだませている/dev/sdbらしい

ddとかできる

Page 95: Das 2015

19

Synthesijer 活用方法 - 3つのパタン

Page 96: Das 2015

20

3つの開発パタン

(1) Javaによるモジュールだけでシステムを構成

(2) HDLによるモジュールを部品として利用.全体管理はJavaで記述 = 既存IPコア,FPGAの性能を活用

(3) Javaによるモジュールを部品として利用.全体管理はHDLで記述 = 複雑なアルゴリズムをSWプログラマに書いてもらう.SW資産の活用

(1)Javaでシステムを構成 (2)Java + HDL記述の部品 (3)HDL + Java記述の部品

Page 97: Das 2015

21

(1) Javaでシステムを構成

✔ すべてをJavaで記述✔ トップモジュール,UCF/XDCなんかは必要✔ I/Oなどは用意されたライブラリを利用

✔ シリアルI/O,SC1602,純粋なInput/Output, ...✔ AXI(32bit逐次,シンプルなキャッシュ),UPL,...

Page 98: Das 2015

22

(2) Java+HDL記述の部品

✔ JavaプログラムにHDL記述の部品を埋め込む✔ Javaで記述したオブジェクトのようにHDL記述部品をインスタンス化して使う✔ HDLモジュールを簡単に使い回せる

✔ Synthesijer内部でどうオブジェクトが扱われるかを知る必要がある✔ Javaとのブリッジのために HDLModule を継承したクラスを実装

✔ 合成時にはHDL記述のコードと組み合わせる

✔ HWプログラマがサポートしつつSWプログラマにFPGAを活用してもらう!!

HogeModule obj = new HogeModule();

obj.x = 0;obj.data[0] = 100;

class HogeModule extends HDLModule{

int x; int[] data;

HogeModule(){ super("hoge_module", ...); }

entity hoge_module is port( x : ... data_waddr : … data_wdata : ...

見かけ上の接続

実際の接続

SWプログラマにHWモジュールを有効活用してもらう

Page 99: Das 2015

23

インスタンス生成・拡張モジュールVHDL/Verilog HDLで書いたモジュールのインスタンス化

module hoge( input wire clk, input wire reset,

input signed [31:0] a_address, input signed [31:0] a_din, input wire we, input wire oe, output signed [31:0] a_dout, output signed [31:0] a_length,

input wire func_req, output wire func_busy,

output wire [31:0] q);

endmodule

clkreset

a_addressa_dina_wea_oe

a_din

a_dout

func_reqfunc_busy

hoge q

ex. Verilog HDLで書いたhogeをJavaから呼び出す

a_length

Page 100: Das 2015

24

インスタンス生成・拡張モジュールVHDL/Verilog HDLで書いたモジュールのインスタンス化

class HogeIface extends HDLModule{

int[] a; void func(){}

public HogeIface(String... args){ newPort("a_address", HDLPort.DIR.IN, genSignedType(32)); newPort("a_din", HDLPort.DIR.IN, genSignedType(32)); newPort("a_we", HDLPort.DIR.IN, genBitType()); newPort("a_oe", HDLPort.DIR.IN, genBitType()); newPort("a_dout", HDLPort.DIR.OUT, genSignedType(32)); newPort("a_length", HDLPort.DIR.OUT, genSignedType(32)); newPort("func_req", HDLPort.DIR.IN, genBitType()); newPort("func_busy", HDLPort.DIR.OUT, genBitType());

newPort("q", HDLPort.DIR.OUT, genVectorType(32), Enum.of(OPTION.EXPORT)); } … }

↓のようなJava用のダミーコードを書いて,使う… private final HogeIface obj = new HogeIface();… private void func(){ obj.a[10] = 100; obj.func();…

a[]

func()

これはJavaでは使わない,という印

Page 101: Das 2015

25

(3) HDL+Java記述の部品

✔ HDLで設計したシステムにJava記述の部品を埋め込む✔ 複雑なアルゴリズムの処理でもJavaなら(比較的)手軽に実装✔ HDLで自然に実装できるストリーム処理,並列処理を活用✔ Synthesijerで合成した回路がどういう構成か知る必要がある✔ SWプログラマ/資産をFPGA設計に活用!!

class HogeModule{

int hoge_func(int a){ … }

entity hoge_module is port( hoge_func_a : in ... hoge_func_return : out … hoge_func_req : in … hoge_func_busy : out ...

Synthesijer

hoge_modulehoge_func_req

hoge_func_a hoge_func_return

hoge_func_busy

Page 102: Das 2015

26

複雑/面倒なアルゴリズム処理の実装✔ 例: バブルソートじゃなくてマージソートの採用,など

✔ バブルソート(512個の降順→昇順)

✔ マージソート(512個の降順→昇順) x15

Page 103: Das 2015

27

複雑なアルゴリズム処理はJavaで実装✔ バブルソートの状態遷移 ✔ マージソートの状態遷移

Page 104: Das 2015

28

最近よく実装しているパタン

FIFO

実行ステージ

FIFO

実行ステージ

FIFO

スループット T [bps]

実行ステージ

FIFO

実行ステージ

FIFO

実行ステージ

FIFO

実行ステージ

FIFOここをJavaで書く

Page 105: Das 2015

1

今できていること

✔ とりあえず使える

✔ Javaの制御構文,メソッド呼び出し,演算✔ とてもシンプルな最適化

✔ Vivado向けIPコアテンプレートの自動生成

Source Abstraction Level

Ease of Impl.

Learning curve

Document-ation

Floating/Fixed Pt.

DesignExploration

Testbenchgeneration Verification Size FPGA

Slices

Synthesijer Java untimed + + - Auto conversion +/- - -

Page 106: Das 2015

2

手が回っていないこと

✔ もう少し賢い最適化

✔ メモリアクセスレイテンシの隠蔽✔ 演算レイテンシを考慮したチェイニング✔ リソースシェアリング

Page 107: Das 2015

3

今後挑戦したいこと

✔ メソッドのパイプライン化

✔ Javaプログラムとしてどう扱うか✔ I/Oストリームへの対応

✔ コンストラクタに対応

✔ コンパイル時のJavaプログラム実行✔ lambda式対応✔ Streamへの対応

Page 108: Das 2015

1

+α Synthesijer.scala

Page 109: Das 2015

2

RTL設計の辛さ

✔ “ ”信号の流れ を記述する

✔ “ ” “ ”処理の流れ を 信号の流れ に変換

↔ “ ”書きたいのは 処理の流れ

✔たくさんの似たような構造

✔ たくさんの似たような名前

✔ 記述の再利用をしたいが難しい

本質としては

実務的には

Page 110: Das 2015

3

手軽なFPGA開発のための取り組み

✔ 高位合成言語処理系

✔ DSL

Page 111: Das 2015

4

高位合成処理系✔ CやJava,C#などを入力言語とした開発環境

✔ 開発コストを小さくできる

✔ 生成したHWの質は処理系依存

✔ 良いHW生成にはコツが必要

✔ ディレクティブの活用

✔ 書き方を考える必要がある

Page 112: Das 2015

5

高位合成処理系✔ Vivado HLS

✔ OpenCL対象の処理系

✔ CyberWorkBench

✔ Cynthesizer

✔ Symphony C Compiler

✔ ImpulseC

✔ Lime

✔ Synthesijer

などなどなど

Page 113: Das 2015

6

DSLベースの設計手法✔ RTL設計を楽にするためのDSL

✔ 実装したいアプリケーション別のDSL

✔ プログラミングモデルに特化したDSL

Page 114: Das 2015

7

様々なDSLベースのシステム開発(1)

✔ MyHDL (Pythonベース)

✔ JHDL (Javaベース)

✔ Chisel (Scalaベース)

✔ PyVerilog (Pythonベース)

などなどなど

RTL設計(あるいは少し抽象的なHW設計)をより楽にするためのDSL

Page 115: Das 2015

8

様々なDSLベースのシステム開発(2)

✔ Spiral ←Linear Digital Processing

✔ HDL Coder ← 信号処理(Matlab)

✔ Optimus ← ストリーム処理

✔ LINQ ← クエリプロセッシング

などなどなど

✔ DSLを作るツールも.(たとえば,LMS/Scala)

実装したいアプリケーションに特化したDSL

Page 116: Das 2015

9

様々なDSLベースのシステム開発(3)

✔ Bluespec (並行プログラミング)

✔ MaxCompiler (データフロープログラミング)

✔ FloPoCo (浮動小数点数パイプライン)

✔ SPGen (パイプライン)

などなどなど

使用するプログラミングモデルに特化したDSL

Page 117: Das 2015

10

DSLベースの設計手法✔ RTL設計を楽にするためのDSL

←依然RTLであることに代わりはない

✔ 実装したいアプリケーション別のDSL

←HWの設計探索をしたい場合には不向き

✔ プログラミングモデルに特化したDSL

→さて,どんなモデルがよいだろうか?

Page 118: Das 2015

11

FPGAが有用なのはなぜか?

✔ 論理回路・データパスを自由に作り込めるLSI

✔ I/Oを自由に使える

✔ クロックレベルの同期と並列性を活用した処理を実現

Field Programmable Gate Array

Page 119: Das 2015

12

FPGAが有用なのはなぜか?

10G if

DRAM

Networkadapter

FPGA

Network stack Memcached

x86 DRAM

motherboard

Hash table Value store

図は https://www.usenix.org/sites/default/files/conference/protected-files/blott_hotcloud13_slides.pdf より

データはどうせ移動させる移動途中で副次的に処理できる

Page 120: Das 2015

13

鍵はパイプライン並列化

FIFO

スループット T [bps]

w [byte]f [Hz]

実行ステージ

FIFO

実行ステージ

実行ステージ

Page 121: Das 2015

14

データが来たら迎え撃つ

Page 122: Das 2015

15

鍵はパイプライン並列化

パケットデータが d [byte] のとき全データ入力を受け取るのにかかる時間 = (d/w)*(1/f) [sec] 同様に、全データの出力にかかる時間 = (d/w)*(1/f) [sec]

スループットT [bps]を実現するとき、パケットデータを(8*d)*(1/T) [sec]内で処理し続ける必要がある

→ 各モジュールで処理に使える時間 t は 8*d/T-2*d/(w*f) [sec] → (8*d/T-2*d/(w*f))/(1/f) [cycle]

 たとえば、d=1500, T=1G, w=4, f=100Mのとき 1パケットあたりの処理にかけられるサイクル数は450サイクル.

    f=200Mなら1650サイクル,w=16なら1012サイクル T=10Gの場合,w=16,f=200M でも 52サイクル

FIFO

スループット T [bps]

w [byte]f [Hz]

実行ステージ

FIFO

実行ステージ

実行ステージ

とはいえ,各ステージで状態遷移のある処理が必要

Page 123: Das 2015

16

鍵はパイプライン並列化

パケットデータが d [byte] のとき全データ入力を受け取るのにかかる時間 = (d/w)*(1/f) [sec] 同様に、全データの出力にかかる時間 = (d/w)*(1/f) [sec]

スループットT [bps]を実現するとき、パケットデータを(8*d)*(1/T) [sec]内で処理し続ける必要がある

→ 各モジュールで処理に使える時間 t は 8*d/T-2*d/(w*f) [sec] → (8*d/T-2*d/(w*f))/(1/f) [cycle]

 たとえば、d=1500, T=1G, w=4, f=100Mのとき 1パケットあたりの処理にかけられるサイクル数は450サイクル.

    f=200Mなら1650サイクル,w=16なら1012サイクル T=10Gの場合,w=16,f=200M でも 52サイクル

FIFO

スループット T [bps]

w [byte]f [Hz]

実行ステージ

FIFO

実行ステージ

実行ステージ

とはいえ,各ステージで状態遷移のある処理が必要

限られた許容サイクルを効率よく使う必要

Page 124: Das 2015

17

組み合わせ回路/データ並列性の活用✔ プロセッサと比べてFPGA回路のφは圧倒的に低速

✔ 高速データ処理では1クロックでデータを捌く必要も

✔ 時にはソフトウェア実装とは全く違う場合も

条件

・・・

条件

v.s

Page 125: Das 2015

18

欲しいDSL

✔ Bluespec (並行プログラミング)

✔ MaxCompiler (データフロープログラミング)

✔ FloPoCo (浮動小数点数パイプライン)

などなどなど

使用するプログラミングモデルに特化したDSL

状態遷移および各状態での処理設計がやりやすく組み合わせ回路が書きやすい言語

Page 126: Das 2015

19

設計: Synthesijer.scala✔ 組み合わせ回路の設計の容易は失わないまま

✔ “処理の流れ”の記述を容易にする

✔ クロックベースの状態管理と状態遷移を言語機能に

✔ 内部DSLとして設計.回路設計にScalaの機能を活用.

✔ 設計資産の再利用性を高める

✔ 見通しの良い設計を可能にする

Page 127: Das 2015

20

Synthesijer.scala とは✔ Synthesijerのバックエンドを使ってScala“で”HDLを書く

✔ signal, port: 状態を変更可能なオブジェクト

✔ expr: 副作用なしの式

✔ sequencer: 状態遷移機械

✔ module: モジュール全体

✔ 上記のオブジェクトをScalaでインスタンス化.つなぎ合わせる.

Page 128: Das 2015

21

Synthesijer.Scalaの立ち位置

JavaコードJavaプログラムの解析

スケジューリング表を作成

最適化

HDL構文の組み立てVHDL/Verilog HDLコード

Javaコード

VHDL/Verilog HDLコード

VHDL/Verilog HDLコード

中間表現(S式)

フロントエンド

ミドルエンド

バックエンド

Scalaで自分で組立て

Page 129: Das 2015

22

開発フロー

Scalaプログラムとして実装 → VHDLまたはVerilog HDLを生成

RTLシミュレーションによる動作検証

使用するFPGAにあわせた制約を定義

ツールで合成,配置配線,FPGA構成情報生成

実機で動作確認

Synthesijer.Scalaを使ってモジュールを実装

Page 130: Das 2015

23

モデル

モジュールの設計単位

入出力.クロック単位で状態保持

組み合わせ回路.状態は持たない

クロック単位での状態保持状態遷移マシン

Page 131: Das 2015

24

はじめてのSynthesijer.scala✔ Lチカの例

def generate_led() : Module = { val m = new Module("led") val q = m.outP("q") val counter = m.signal(32) q <= counter.ref(5) val seq = m.sequencer("main") counter <= (seq.idle, VECTOR_ZERO) val s0 = seq.idle -> seq.add() counter <= s0 * (counter + 1) return m}

def generate_sim(target:Module, name:String) : SimModule = { val sim = new SimModule(name) val inst = sim.instance(target) val (clk, reset, counter) = sim.system(10) inst.sysClk <= clk inst.sysReset <= reset return sim}

Page 132: Das 2015

25

ベンディングマシンの例

ドリンク

自動販売機¢5

¢10

ドリンク一杯¢20お釣りは出ません

Page 133: Das 2015

26

RTLでの設計の場合

… type stateType is (NONE, CHARGE_5, CHARGE_10, CHARGE_15, OK) signal state, next_state: stateType := NONE;… case (state) is when NONE => if nickel then next_state <= CHARGE_5; elsif dime then next_state <= CHARGE_10; else next_state <= NONE; end if; when CHARGE_5 => if nickel then next_state <= CHARGE_10; elsif dime then next_state <= CHARGE_15; else next_state <= NONE; end if;…

Page 134: Das 2015

27

ベンディングマシンの例class VendingMachine(n:String,c:String,r:String) extends Module(n,c,r){ val nickel = inP("nickel") val dime = inP("dime") val rdy = outP("rdy") val seq = sequencer("main") val s5,s10,s15,s_ok = seq.add() rdy <= (seq.idle, LOW) rdy <= (s_ok, HIGH) seq.idle -> (nickel, s5) seq.idle -> (dime, s10)

s5 -> (nickel,s10) s5 -> (dime, s15)

s10 -> (nickel, s15) s10 -> (dime, s_ok)

s15 -> (nickel, s_ok) s15 -> (dime, s_ok)

s_ok -> seq.idle}

val m = new VendingMachine("machine", "clk", "reset")m.genVHDL()m.visuallize_statemachine()

Page 135: Das 2015

28

ベンディングマシンのステートマシン

Page 136: Das 2015

29

Scalaの機能を使った設計効率化の向上

✔ 設計資産をメソッドやクラスとして保存,再利用

✔ 類似オブジェクトや類似オブジェクトの生成

✔ ループとパタンマッチによる直列化/復元コードの生成

Page 137: Das 2015

30

設計資産の再利用

例: デコーダ (7セグメントLEDの点灯)

4

0

2

51

6

73

Page 138: Das 2015

31

設計資産の再利用

process (data)begin case (data) is when 0 => segment <= X"7e"; when 1 => segment <= X"30"; … when 9 => segment <= X"79"; when others => null; end case;end process;

4

0

2

51

6

73

毎回このパターンを書かないといけない.

従来のHDLによるRTL設計の場合

Page 139: Das 2015

32

設計資産の再利用

def decoder(s:ExprItem, l:List[(Int,Int)], w:Int) = { l.foldRight[ExprItem](value(0,w)){ (a, z) => ?(s == a._1, value(a._2, w), z)}

val tbl = List((0, 0x7e), (1, 0x30), …, (9, 0x79)) segment := decoder(data, tbl, segment.width())

パターンを作るメソッド.回路のレシピ.

Synthesijer.scalaを使う場合

実データに合わせた回路の生成

Page 140: Das 2015

33

自分なりのパタン,最適化の埋め込みも

✔ 組み合わせ回路の段数に応じたFF(状態)の追加,除去

✔ 入出力データに対するパタンマッチ

✔ よく使うモジュールのテンプレート化

例えば...

などなど

Page 141: Das 2015

34

自分なりの実装,例

例えば...ANDゲートをLUTじゃなくてMUXで作りたい

Page 142: Das 2015

35

自分なりの実装,例

継承とオーバーライドを使って,自然に表現可能

val x0 = inP("x0") val y0 = inP("y0") val z0 = outP("z0") val z1 = outP("z1") val z2 = outP("z2") z0 := x0 and y0

val x1c = new CARRY4Sig(x0) val y1c = new CARRY4Sig(y0) z1 := x1c and y1c z2 := z0 or z1

Page 143: Das 2015

36

自分なりの実装,例

例えば...ANDゲートをLUTじゃなくてMUXで作りたい

Page 144: Das 2015

37

自分なりの実装,例

例えば...ANDゲートをLUTじゃなくてMUXで作りたい

Page 145: Das 2015

1

さて,未来はどうなる?

Page 146: Das 2015

2

ポテンシャルをどう活かすか!!

https://www.youtube.com/watch?v=FCmfcfPt4T4

Page 147: Das 2015

3

各社の取り組み

✔ Xilinx SDx (特にSDSoC)

✔ Altera SDP

✔ IBM Lime

✔ Microsoft Kiwi

✔ CoRAM/PyCoRAM

“システムをどう作るか”,がターゲット= システムとLSI

Page 148: Das 2015

4

Xilinx SDSoCZynq向けのSW/HWコデザインツール

Page 149: Das 2015

5

Altera SDP

SPCS005 — Technology Insight: Workload Acceleration, IDF2015

Page 150: Das 2015

6

IBM LimeJava + Isolation + Abstract Parallelism + Extraction

Page 151: Das 2015

7

Microsoft Kiwi (Distributing C#)

Greaves, D.; Singh, S., "Distributing C# methods and threads over Ethernet-connected FPGAs using Kiwi," in Formal Methods and Models for Codesign (MEMOCODE), 2011 9th IEEE/ACM International Conference on , vol., no., pp.1-9, 11-13 July 2011

Page 152: Das 2015

8

CoRAM/PyCoRAM

高前田先生@NAIST

Page 153: Das 2015

9

未来で,どう開発したい?

Page 154: Das 2015

1

まとめ✔ Javaベースの高位合成処理系を作って/使っています✔ まだまだですが,こういう人もいる,と.✔ 中間表現のS式を操作して処理系に戻せるので,

最適化器実装してみたいという人がいたら是非

✔ 結構,楽しい時代だと思っている✔ ...FPGAって米国産なんだよなあ...

Javaコンパイラフロントエンド

Synthesijerエンジン

Javaコンパイラバックエンド

.classソフトとして実行

同じJavaプログラムをソフトウェアとしてもFPGA上のハードウェアとしても実行可能

Page 155: Das 2015

2

参考✔ http://synthesijer.github.io/web

✔ リソース一式,クイックスタートガイドなど

✔ http://qiita.com/kazunori279/items/4951ca5f6164040878ce

✔ Synthesijer関連資料まとめ Qiita

✔ http://labs.beatcraft.com/ja/index.php?Synthesijer

✔ Altera DE0-nanoでのサンプルの動作手順など

✔ http://wasa-labo.com/wp/

✔ わさらぼ ブログ – 開発状況,Tipsなど