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
でオリジナルに戻ってチェック。こっちはさすがにちゃんと動く。本日は、ここまでか、、、手順がわかったのでよしとする。