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 です。
整理は必要ですが、現時点ではこんな感じ。あー自分のメモだな。こりゃ。
Polyphony で tests suite を試す。
Polyphony で tests suite を試す方法を記します。
Linux とか cygwin とか
まず Python 3 の環境 + pip3 の環境をいれてください。ubuntu なら apt-get でインストールできます。
> sudo apt-get install python3-pip
その後 pip3 でインストール
> sudo pip3 install polyphony Successfully installed polyphony-0.2.2 > ls -l /usr/local/bin/polyphony -rwxr-xr-x 1 root root 71 3月 1 16:16 /usr/local/bin/polyphony*
現時点でインストールされるのは 0.2.2 です。IO アクセスの機能が入っていないことに注意してください。(注意してもらえると助かります。)
無事インストールされれば /usr/local/bin に polyphony を見ることが出来るようになります。本来であればチュートリアル的なソースがあればよいのでしょうが、(すいません)現時点ではありません。github には polyphony の開発者用の tests suite があるのでそれを実行することが可能です。iverilog が必要になります。これも apt-get などでインストールしておいてください。
> git clone https://github.com/ktok07b6/polyphony > cd polyphony > ./suite.py
.tmp に verilog のファイルが生成されるはずです。
個々のファイルを実行する
個々のテストを実行することも可能です。
> ./simu.py tests/if/if01.py 0:if01_0_in_x= 0, if01_0_in_y= 0, if01_0_out_0= x 110:if01_0_in_x= 0, if01_0_in_y= 1, if01_0_out_0= x 140:if01_0_in_x= 0, if01_0_in_y= 1, if01_0_out_0= 721 150:if01_0_in_x= 1, if01_0_in_y= 1, if01_0_out_0= 721 180:if01_0_in_x= 1, if01_0_in_y= 1, if01_0_out_0= 2 190:if01_0_in_x= 0, if01_0_in_y= 2, if01_0_out_0= 2 220:if01_0_in_x= 0, if01_0_in_y= 2, if01_0_out_0=1442
0.3.0 の新機能を使う
polyphony の 0.3.0 では IO が使えるようになりました。現時点ではテスト的に入れている機能もあるので注意が必要です。
> git clone -b 0.3.0 https://github.com/ktok07b6/polyphony polyphony-0.3.0 > cd polyphony-0.3.0 > export PYTHONPATH=`pwd` > ./simu.py tests/io/port01.py 0:p01_in0= 0, p01_in_valid=0, p01_out0= x, p01_out_valid=x, p01_start=x 10:p01_in0= 0, p01_in_valid=0, p01_out0= 0, p01_out_valid=0, p01_start=0 110:p01_in0= 0, p01_in_valid=0, p01_out0= 0, p01_out_valid=0, p01_start=1 wait_rising out_valid 120:p01_in0= 2, p01_in_valid=1, p01_out0= 0, p01_out_valid=0, p01_start=1 140:p01_in0= 2, p01_in_valid=1, p01_out0= 4, p01_out_valid=0, p01_start=1 150:p01_in0= 2, p01_in_valid=1, p01_out0= 4, p01_out_valid=1, p01_start=1 4
tests/io に開発者のテスト用ソースがあります。興味のある方は確認してみてください。残念なことにまだチュートリアルがありません。チュートリアルはでき次第、公開します。
Windows10
Windows 用の Python3 をインストールすることで Windows でも(cygwin なしに)使うことが出来ます。
Python 3.6.0 あるいは 3.5.3 をインストールします。なお、Visual Studio 2013 から Python を使おうとすると 3.5.3 を使うことになるので(VS 2015 なら 3.6.0 が使えます。2017 は?不明)。
私の場合
C:\Users\<ユーザ名>\AppData\Local\Programs\Python\Python36
にインストールされました。
polyphony のインストールは pip3 を使用します(Python をインストールした後に)。pip3 は
C:\Users\<ユーザ名>\AppData\Local\Programs\Python\Python36\Scripts
の下にあるのでコマンド・プロンプトを開き pip3 install polyphony とします。Scripts の下に polyphony というファイルができていれば OK です。(exe ではないのはご容赦)
Python Tools for Visual Studio
これをインストールすると Visual Studio で(無料で使用可能な Express でも!!) Python の開発ができるようになります。Polyphony の開発は主に Visual Studio で行っています。Editor が便利なようです。
https://www.visualstudio.com/ja/vs/python/
PTVS を使用するには、あらかじめ Python をインストールすることになります。VS のバージョンと Python のバージョンには組み合わせがあるので(上記にも書きましたが)注意してください。