読者です 読者をやめる 読者になる 読者になる

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 内のファイル名も変更。
どうやら合成はできるようです。ただ、ここだとシミュレーションもできないようなのでここはここまで。

f:id:ryos36:20170319230532p:plain

Vivado でシミュレーション

ディレクトリを examples/fibonacci/sim/vivado/polyphony に変えてシミュレーションにいどむ。ここでも tcl を書き直し。
f:id:ryos36:20170319231319p:plain

どうもシミュレーションは失敗した模様。
これは道は険しい。

オリジナルに戻ってチェック

git reset --hard
でオリジナルに戻ってチェック。こっちはさすがにちゃんと動く。本日は、ここまでか、、、手順がわかったのでよしとする。
f:id:ryos36:20170319231607p:plain