Das 2015

Post on 10-Jan-2017

1.057 views 1 download

Transcript of Das 2015

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

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

2015.08.27

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

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

2015.08.27

Synthesijer

2

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

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

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

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

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

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

Synthesijerエンジン

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

合成配置配線

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

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

Open-source

3

Synthesijer クイックスタート

4

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

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

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

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

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

5

クイックスタート 2/8

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

6

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

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

ビルドパスに追加

準備完了!!

7

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

クラス生成ダイアログ

8

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

Lチカに相当する変数

適当なウェイト点滅

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

9

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

10

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

11

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

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

制約ファイルは別途用意

12

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

1

コンテンツ

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

✔ Synthesijerの設計と実装

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

2

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

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

3

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

✔ FPGAを使うのをやめる

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

4

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

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

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

5

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

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

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

2014 年 8 月 2015 年 1 月0

5

10

15

20

25

30

35

40

45

x20

2014 年 8 月 2015 年 1 月0

5

10

15

20

25

30

35

40

45

x20

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

高位合成友の会# 2015.1.16

1

高位合成処理系あれこれ

2

ESLSynthesisの市場規模

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

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

5

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

6

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

1

Synthesijer 設計と実装 

2

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

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

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

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

3

Synthesijer

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

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

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

✔ Thread,wait-notify

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

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

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

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

4

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

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

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

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

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

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

なぜJavaなのか?

Write Once, Run Anywhere!!

Write Once, Run Anywhere!!

たとえCPUがなくても!!

8

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

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

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内に処理をぶら下げればよい

10

Javaコードの内部表現

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

JCMethodDecl

JCVariableDecl

JCMethodDecl

JCMethodDecl

・・・

・・・

JCVariableDecl

JCVariableDecl

JCExpressionState

JCBlock

JCIf

JCFor

JCAssign

JCArrayAccess

JCBinary

JCIdent

JCLiteral

・・・

・・・

JSCtatementJSExpression

JCClassDecl

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に出会ったら呼び出してくれる

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に出会ったら呼び出してくれる

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に出会ったら呼び出してくれる

14

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

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

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

15

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

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

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

16

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

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

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

(1) (2)

(3)

17

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

(1)言語デザイン

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

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

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

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

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

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

✔ 適切な物理マッピング

✔ 符号化

18

Synthesijerでは

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

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

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

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

✔ 適切な物理マッピング

✔ 符号化

(1)言語デザイン

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

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

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

ISE/Vivado,QuartusIIにお任せ

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

19

Synthesijerオーバービュー

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

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

最適化

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

Javaコード

VHDL/Verilog HDLコード

VHDL/Verilog HDLコード

中間表現(S式)

フロントエンド

ミドルエンド

バックエンド

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++) ; } }}

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モジュールを生成

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

23

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

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

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

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

24

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

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

whilefor

for

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

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

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

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

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

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

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

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

33

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

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

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), … );

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

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では使わない,という印

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

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)); }

}

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

41

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

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

43

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

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++; } }

}

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(); } } }}

46

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

47

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

48

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

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

49

Synthesijerオーバービュー

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

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

最適化

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

Javaコード

VHDL/Verilog HDLコード

VHDL/Verilog HDLコード

中間表現(S式)

フロントエンド

ミドルエンド

バックエンド

50

Synthesijer as a Compiler-Infrastructure

Javaコード

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

最適化

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

X言語

VHDL/Verilog HDLコード

中間表現(S式)

フロントエンド

ミドルエンド

バックエンド

X言語パーザー 最適化器

Y言語

1

Synthesijer サンプル・事例 

2

2

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

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

→ hoge

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

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

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

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

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 ']': … } }

4

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

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

5

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

6

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

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

BF

IO

Console

合成して実機で動かす時

IO

+ putchar()+ getchar()

+ putchar()+ getchar()

RS232Wrapper

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');

}

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); } }}

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); // 適当なウェイト } }

10

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

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

11

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

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

12

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

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

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

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

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

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

13

サンプル3: AHCI

PC

FPGAディスク?

何かの処理

ディスク

インターネット

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

✔ たとえば

BRAM

14

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

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

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

15

AHCIのメモリチェイン

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

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

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

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

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

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

P0CLB

HBAからはP0CLBの値でたどる

16

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

PCIe越しの通信

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

18

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

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

ddとかできる

19

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

20

3つの開発パタン

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

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

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

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

21

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

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

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

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モジュールを有効活用してもらう

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

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では使わない,という印

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

26

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

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

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

27

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

28

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

FIFO

実行ステージ

FIFO

実行ステージ

FIFO

スループット T [bps]

実行ステージ

FIFO

実行ステージ

FIFO

実行ステージ

FIFO

実行ステージ

FIFOここをJavaで書く

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 +/- - -

2

手が回っていないこと

✔ もう少し賢い最適化

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

3

今後挑戦したいこと

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

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

✔ コンストラクタに対応

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

1

+α Synthesijer.scala

2

RTL設計の辛さ

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

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

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

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

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

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

本質としては

実務的には

3

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

✔ 高位合成言語処理系

✔ DSL

4

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

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

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

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

✔ ディレクティブの活用

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

5

高位合成処理系✔ Vivado HLS

✔ OpenCL対象の処理系

✔ CyberWorkBench

✔ Cynthesizer

✔ Symphony C Compiler

✔ ImpulseC

✔ Lime

✔ Synthesijer

などなどなど

6

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

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

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

7

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

✔ MyHDL (Pythonベース)

✔ JHDL (Javaベース)

✔ Chisel (Scalaベース)

✔ PyVerilog (Pythonベース)

などなどなど

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

8

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

✔ Spiral ←Linear Digital Processing

✔ HDL Coder ← 信号処理(Matlab)

✔ Optimus ← ストリーム処理

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

などなどなど

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

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

9

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

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

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

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

✔ SPGen (パイプライン)

などなどなど

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

10

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

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

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

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

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

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

11

FPGAが有用なのはなぜか?

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

✔ I/Oを自由に使える

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

Field Programmable Gate Array

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 より

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

13

鍵はパイプライン並列化

FIFO

スループット T [bps]

w [byte]f [Hz]

実行ステージ

FIFO

実行ステージ

実行ステージ

14

データが来たら迎え撃つ

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

実行ステージ

実行ステージ

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

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

実行ステージ

実行ステージ

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

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

17

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

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

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

条件

・・・

条件

v.s

18

欲しいDSL

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

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

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

などなどなど

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

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

19

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

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

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

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

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

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

20

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

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

✔ expr: 副作用なしの式

✔ sequencer: 状態遷移機械

✔ module: モジュール全体

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

21

Synthesijer.Scalaの立ち位置

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

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

最適化

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

Javaコード

VHDL/Verilog HDLコード

VHDL/Verilog HDLコード

中間表現(S式)

フロントエンド

ミドルエンド

バックエンド

Scalaで自分で組立て

22

開発フロー

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

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

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

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

実機で動作確認

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

23

モデル

モジュールの設計単位

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

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

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

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}

25

ベンディングマシンの例

ドリンク

自動販売機¢5

¢10

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

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;…

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

28

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

29

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

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

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

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

30

設計資産の再利用

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

4

0

2

51

6

73

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設計の場合

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を使う場合

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

33

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

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

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

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

例えば...

などなど

34

自分なりの実装,例

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

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

36

自分なりの実装,例

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

37

自分なりの実装,例

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

1

さて,未来はどうなる?

2

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

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

3

各社の取り組み

✔ Xilinx SDx (特にSDSoC)

✔ Altera SDP

✔ IBM Lime

✔ Microsoft Kiwi

✔ CoRAM/PyCoRAM

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

4

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

5

Altera SDP

SPCS005 — Technology Insight: Workload Acceleration, IDF2015

6

IBM LimeJava + Isolation + Abstract Parallelism + Extraction

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

8

CoRAM/PyCoRAM

高前田先生@NAIST

9

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

1

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

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

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

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

Synthesijerエンジン

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

.classソフトとして実行

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

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など