Polyphony で 64bit Fibonacci (本編スピンアウト 64bit Fibonacci the movie)
あらすじ ~ TV とは違う Movie ならではの展開 ~
32bit で気を良くした Polyphony サポートチームは 64bit の Fibonacci にチャレンジした。64bit は 32 を2つにしたというだけではない。次から次へと襲う難題。チームは問題を解決できるのか?本編からスピンアウトした 64bit 対応。最後に笑うのは誰だ?そして翔太は?
まずは Polyphony 0.3.0 の 64bit 化
env.py にある default_int_width=32 を 64 に変更。なお、64bit 対応は 0.3.0 から。master の 0.2.2 は未対応。
default_int_width = 64
test bench を 64bit 化
from polyphony import testbench def fib(n): if n <= 0: return 0 if n == 1: return 1 r0 = 0 r1 = 1 for i in range(n-1): prev_r1 = r1 r1 = r0 + r1 r0 = prev_r1 return r1 @testbench def test(): expect = [0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610] for i in range(len(expect)): result = fib(i) assert expect[i] == result print(i, "=>", result) expect2 = [ 0, 55, 6765, 832040, 102334155, 12586269025, 1548008755920, 190392490709135, 23416728348467685, 2880067194370816120, 354224848179261915075, 573147844013817084101, 927372692193078999176, 1500520536206896083277, 2427893228399975082453 ] j = 0 for i in range(105): if i % 10 != 0 and i < 100 : continue result = fib(i) #print("{i} => {result}({rhex}) vs {expect2}({ehex})".format(i = i, result = result, rhex = hex(result), expect2 = expect2[j], ehex = hex(expect2[j]))) print(i, "=>", result, "(", expect2[j], ")") #print(i, "=>", hex(result), "(", expect2[j], ")") assert expect2[j] == result j += 1 test()
そして、Python3 で動くことを確かめる。
> python3 fib.py 0 => 0 ざっくり中略 90 => 2880067194370816120 ( 2880067194370816120 ) 100 => 354224848179261915075 ( 354224848179261915075 ) 101 => 573147844013817084101 ( 573147844013817084101 ) 102 => 927372692193078999176 ( 927372692193078999176 ) 103 => 1500520536206896083277 ( 1500520536206896083277 ) 104 => 2427893228399975082453 ( 2427893228399975082453 )
64bit の int では 93 まで。(uint なら 94)。上の結果はなぜか 104 まで
msgpack-rpc vhdl を 64bit 化
たぶん create_project で generate する際のパラメタを渡せると思うだが、力及ばず、/src/test/vhdl/test_bench.vhd を直接編集。
> git diff ../../../src/test/vhdl/test_bench.vhd diff --git a/examples/fibonacci/src/test/vhdl/test_bench.vhd b/examples/fibonacci/src/test/vhdl/test_bench.vhd index 11f6861..7eabfbf 100644 --- a/examples/fibonacci/src/test/vhdl/test_bench.vhd +++ b/examples/fibonacci/src/test/vhdl/test_bench.vhd @@ -66,13 +66,13 @@ architecture MODEL of TEST_BENCH is constant CLOCK_PERIOD : time := 10 ns; constant DELAY : time := 1 ns; constant MATCH_PHASE : integer := 8; - constant I_BYTES : integer := 4; + constant I_BYTES : integer := 8; constant I_WIDTH : AXI4_STREAM_SIGNAL_WIDTH_TYPE := ( ID => 4, USER => 4, DEST => 4, DATA => 8*I_BYTES); - constant O_BYTES : integer := 4; + constant O_BYTES : integer := 8; constant O_WIDTH : AXI4_STREAM_SIGNAL_WIDTH_TYPE := ( ID => 4, USER => 4,
これは polyphony では入力も 64 ビットになるから。Synthesijer では入力 32ビット、出力 64bit みたいなことができる。
あとcreate_project.tcl 内のシナリオも test_42.snr から test_92.snr に変えておく
Vivado でシミュレーション
結果は 7540113804746346429 でウェブで検索したらあたったので、OK とします。
そのころ翔太は
Ikalog をとることに没頭していた。
PYNQ で fibonacci (完結編)
前回までのあらすじ
Polyphony で高位合成をして fib.py を verilog に落とし、ついでにインタフェース情報も JSON で吐き出したものを yml に(python で)書き直し、それを手で書き直したものをつかい vivado のシミュレーションに成功。さらに IPXACT 化して、PYNQ の FPGA のビットストリームを作ることに成功した。このバイナリは本当に動くのか?チームの命運は如何に?そして、翔太の恋愛のゆくえは?
PYNQ の環境を git clone する (lfs に注意!!)
https://github.com/ikwzm/PYNQ-Festival から clone する。ReadMe に書いてある通りにやればいいので、この ReadMe を最初によく読む(べきだった、、、)
特にここに含まれる大きめのファイルは git lfs をつかっているので、clone しただけだとただの ascii file です。
- boot/boot.bin
- boot/design_1_wrapper.bit
- boot/u-boot.img
- boot/uImage-4.8.17-armv7-fpga
- debian8-rootfs-4.8.17.tgz
私の環境では git lfs が使えなかったので、github の gui からこつこつクリックしてとってきました。あとパーティションを切るようになっているので、SD カードの 2nd Partition を適切に設定し、debian8-rootfs-4.8.17.tgz を展開しました。これも ReadMe の通り
うまくたちあがると login を見ることが出来るので fpga/fpga でログイン。examples/fibonacci に移動して fibonacci_server_install.sh をroot 権限で実行。これで準備は整いました。
jupyter からつかう
折角 PYNQ なので、web browser から使います。samba も動いているので pynq という hostname で繋がります。うまく、動きました。
jupyter 便利ですね。これの便利さは結局 history の永続性と視覚化の2点ですね。
ISSUE: time out
なんか時々動きが、、、そういう細かいところにこだわっていてはお重荷になれないので、大物になれないので、ざっくりとオミット。
はいいろぞうさん、いるといいなぁ~
追記
「timeout は、socat を起動するときに "socat -d -d tcp-listen:54321,fork /dev/zptty0,raw,nonblock,echo=0 &" のように echo=0 をつけると出なくなりました。」
とのこと。試してみよう。
そのころ翔太は、、、
ひとり家の中でスプラトゥーンに興じていた。
polyphony + msgpack-rpc を試す(なんかシミュレーションはうまくいきましたよ変)
自動生成の json とか yml とかに間違いが
いろいろ自動生成に間違いがありインタフェース名がまずかったようで、そこをなおしたら動いた。
以前より性能がよくなっているじゃん。
def fib じゃなくて def fibonacci にすべきだったんだね。
とりあえずは手で yml 等を修正
PYNQ 用のビットストリームを生成
ここまでいけば PYNQ でいけそうじゃありませんか?回路図はうまくいきましたね。(一部 tcl のファイル名を書き直しましたが)
理由はわかりませんが build_fsbl.tcl だけ失敗。まぁいいでしょう。うまくビットストリームを作るところまではいきました。
Polyphony + msgpack-rpc をつかってみる(未達成編)
Python base の高位合成と msgpack-rpc をつかって処理をやってみようという話
元記事は
フィボナッチを求める回路をPolyphonyとMessagePack-RPCでFPGAに実装してみた(シミュレーション編) - Qiita
あと、PYNQ 祭りの資料
Pynq祭り資料
msgpack-rpc を使うモチベーション
Python から簡単に FPGA を使いたいからというのが理由。msgpack-rpc を使えばほぼ Python だけを知っていれば、FPGA が使える環境が構築できそう(<=まだ、出来ていない。複雑な手順を追わないといけない)
まずは自分のメモ代わりにその複雑な手順を追ってみる。
参考までに書くと msgpack というのは json 風のシリアライゼーションの仕組み。仕様としてはその形式だけを定義しているので RPC の機能はない。msgpack-rpc というのがそれとは別にあって、こちらは RPC 。(という私の理解)
準備
まずは polyphony の準備。0.3.0 の branch を使っている。
GitHub - ktok07b6/polyphony: Polyphony is Python based High-Level Synthesis compiler.
そして、msgpack-vhdl
GitHub - ikwzm/msgpack-vhdl-examples: Example for msgpack-vhdl
msgpack-vhdl は git のサブモジュールを使っているのでそれらも clone する。
> git clone https://github.com/ikwzm/msgpack-vhdl-examples > cd msgpack-vhdl-examples > git submodule init > git submodule update
polyphony にはパッチを当てる.これで json を標準出力に出力する。
diff --git a/polyphony/compiler/__main__.py b/polyphony/compiler/__main__.py index e19b58e..65fb09b 100644 --- a/polyphony/compiler/__main__.py +++ b/polyphony/compiler/__main__.py @@ -1,5 +1,10 @@ ・ソimport os import sys + +import json +from .hdlinterface import port2ahdl + +from collections import OrderedDict from optparse import OptionParser from .builtin import builtin_names from .driver import Driver @@ -230,6 +235,33 @@ def reducestate(driver, scope): StateReducer().process(scope) +def print_json(driver, scope): + #print(scope.module_info) + #print(scope.module_info.name) + if not scope.is_module() and not scope.is_function_module() : + return + mod_if_dict = OrderedDict() + mod_if_dict["name"] = scope.module_info.get_name() + infs = [] + for inf in scope.module_info.get_interfaces().values(): + an_inf = OrderedDict() + an_inf["interface"] = inf.__class__.__name__ + an_inf["name"] = inf.get_if_name() + ports = [] + port= OrderedDict() + for p in inf.get_ports().all(): + #print(port2ahdl(inf, p[0])) + port["name"] = p[0] + port["width"] = p[1] + port["dir"] = p[2] + port["signed"] = p[3] + ports.append(port) + an_inf["ports"]=ports + infs.append(an_inf) + mod_if_dict["interfaces"] = infs + print(json.dumps(mod_if_dict)) + #print(scope.module_info.get_parameters()) + def transformio(driver, scope): IOTransformer().process(scope) @@ -396,6 +428,9 @@ def compile_plan(): transformio, dbg(dumpmodule), reducestate, + + print_json, + dbg(dumpmodule), genhdl, dbg(dumphdl),
msgpack-vhdl-examples/examples/fibonacci/src/main/polyphony で作業をする。
すでに *.v や *.yml があるので(それを自動生成する過程を追いましょうというの今回の話なので)
それらを削除。
PYTHONPATH を patch のあたった polyphony をつかってまずは fib.v を作る。
あれ~ Makefile とファイル名が違うので Makefile を修正する。
SOURCE_VERILOG_FILE = fib.v INTERFACE_VHDL_FILE = fib_interface.vhd SERVER_VHDL_FILE = fib_server.vhd
> make fib.v > fib.json > json2yaml.py fib.json > make ../../../../../msgpack-vhdl/tools/msgpack-rpc-ifgen -v fib.yml msgpack-rpc-ifgen 0.2.5 : read file : fib.yml msgpack-rpc-ifgen 0.2.5 : generate interface file : fib_interface.vhd msgpack-rpc-ifgen 0.2.5 : generate server file : fib_server.vhd > ls create_vivado_project.tcl* fib.v fib_server.vhd test.v fib.json fib.yml Makefile fib.py* fib_interface.vhd polyphony_out.v
json2yaml は今回作った python のやっつけソース
#!/usr/bin/env python3 import os import sys import json from collections import OrderedDict from functools import partial import yaml def main(): if len(sys.argv) <= 1: sys.exit(0) src_file = sys.argv[-1] if not os.path.isfile(src_file): print(src_file + ' is not valid file name') sys.exit(0) (dst_file,_) = os.path.splitext(src_file) dst_file += ".yml" with open(src_file, "r+") as f: data = json.load(f) yml_data = OrderedDict() name = data["name"] yml_data["name"] = data["name"] yml_data["generate"] = {"interface": { "name": name + "_Interface", "file": name + "_interface.vhd"}, "server": { "name" : name + "_Server", "file" : name + "_server.vhd"}} yml_data["port"] = {'reset': 'rst', 'clear': None, 'clock': 'clk'} meth = OrderedDict() meth["name"] = data["name"] meth["interface"] = {"type": "polyphony"} args = [] returns = [] infs = data["interfaces"] for i in infs: iname = i["interface"] if not iname == "SingleWriteInterface" and not iname == "SingleReadInterface" : continue pi = i["ports"][0] a = OrderedDict() a["name"] = i["name"] a["type"] = "Integer" ai = OrderedDict() ai["name"] = "Signal" ait = OrderedDict() if pi["signed"] == True : ait["name"] = "Signed" else : ait["name"] = "Unsigned" ait["width"] = pi["width"] ai["type"] = ait aip = {} aip["data"] = data["name"] + "_" + i["name"] ai["port"] = aip a["interface"] = ai if pi['dir'] == "in" : args.append(a) else: returns.append(a) meth["arguments"] = args meth["returns"] = returns yml_data["methods"] = [meth] def represent_odict(dumper, instance): return dumper.represent_mapping(u'tag:yaml.org,2002:map', instance.items()) yaml.add_representer(OrderedDict, represent_odict) with open(dst_file, "w") as f: f.write(yaml.dump(yml_data, default_flow_style=False)) main()
これで polyphony がつくった verilog のファイルと ruby で作った VHDL のソースができあがる。これでいいはずなのよ。
Vivado を立ち上げて合成してみる
create_vivado_project.tcl があるのでたぶんこれを実行するのでしょう。ということで Vivado の console から実行。
あーファイル名を変えていたので tcl 内のファイル名も変更。
どうやら合成はできるようです。ただ、ここだとシミュレーションもできないようなのでここはここまで。
Vivado でシミュレーション
ディレクトリを examples/fibonacci/sim/vivado/polyphony に変えてシミュレーションにいどむ。ここでも tcl を書き直し。
どうもシミュレーションは失敗した模様。
これは道は険しい。
オリジナルに戻ってチェック
git reset --hard
でオリジナルに戻ってチェック。こっちはさすがにちゃんと動く。本日は、ここまでか、、、手順がわかったのでよしとする。
Python の Parser
訳があって Python の Parser をしらべていたら、Python のコードがかなり整理されていることに気が付いた。文法もどうやら、adsl という形でちゃんと定義されている。ソース内(https://github.com/python/cpython)には Grammar/Grammar というファイルと Parser/Parser.asdl という形で整理されている。
Grammar はまさに Python の文法。これは BNF じゃなさそうだけど、素直に読める形式だ。Parser.asdl はなんだろう?IR だろうか?これ AST の Visitor で現れるキーワードだと思う。
よく整理されているので Grammar を適当に変えると、自分の言語が簡単に作れるのではないかと思った。公式ドキュメントもあるし、このいばらの道を突き進んだ強者もいるようだ。
24. Changing CPython’s Grammar — Python Developer's Guide
qiita.com
pf-siedler.hatenablog.com
pf-siedler.hatenablog.com
cygwin を更新したら、、、vim の動きが変わりましたよ
毎度のことだが、cygwin を更新したら、デフォルトの vim の動きが変わってしまった。
/etc/vimrc を読むらしい。
勝手に以前に編集した(直前に編集した)場所に戻るようになってしまった。
これが原因らしいよ。
if has("autocmd") augroup fedora autocmd! " In text files, always limit the width of text to 78 characters " autocmd BufRead *.txt set tw=78 " When editing a file, always jump to the last cursor position autocmd BufReadPost * \ if line("'\"") > 0 && line ("'\"") <= line("$") | \ exe "normal! g'\"" | \ endif " don't write swapfile on most commonly used directories for NFS mounts or USB sticks autocmd BufNewFile,BufReadPre /media/*,/run/media/*,/mnt/* set directory=~/tmp,/var/tmp,/tmp " start with spec file template autocmd BufNewFile *.spec 0r /usr/share/vim/vimfiles/template.spec augroup END endif
非常に鬱陶しい。ちゃんと先頭から表示してくれ(というか動きを変えるなよ)。ということで、.vimrc に
if has("autocmd") augroup fedora autocmd! endif
と書いて抑制。
Polyphony の開発の位置づけ
とりあえず master が clone されることが多いので stable 版になります。いまは 2.1.0。
最新版は branch を切って開発。現時点で 0.3.0 です。
pip3 でインストールされるのは現時点で 2.2.0 です。
整理は必要ですが、現時点ではこんな感じ。あー自分のメモだな。こりゃ。