FPGA
python3
Polyphony

ドイツで考えたこと:Polyphony の論文発表(class, tuple, list)

堀田善衞はインドで考えた。椎名誠もインドで考えた。インドで飲むビールはさぞうまいのだろう。

論文発表はフランクフルトで行った。その時の発表パワーポイントはここにある。内容としてはコンパイラの概要とその詳細、Python3 としては Class と Tuple、List についてカバーしている。

Class

Polyphony はこの1年で大幅に機能アップした。関数呼び出しはすべてインライン化される。すべてというところには異論もあるだろうが現時点ではすべてである。Class もすべてインライン化される。正確には Polyphony は高位合成のコンパイラであり、オブジェクトを動的に生成することは出来ない。であるから、Class のオブジェクトは静的にインライン化される。

結果として何が起こったか?

__init__ で表されるコンストラクタ内のコードはコンパイラによって"実行"され各メソッドはコンパイラによって静的に配置(インライン化)される。

ソースコードで見て行こう。

__init__
    def __init__(self):
        self.uart = uart.Transmitter()
        self.spi_in = AD7091R_SPIC()
        self.q = Queue(int16, 'any', maxsize=10)
        self.append_worker(self.read_proc)
        self.append_worker(self.proc)

このコードはフィルターの節で書いた Filter クラスのコンストラクタである。Tranmitter と AD7091R_SPIC は一見、動的にオブジェクトを作っているように見えるが実際はコンパイラによって解釈(インタプリット)されて静的に配置される。

Tuple

Tuple のすべての機能をカバーしているわけではない。Tuple は複数の値をまとめることが出来るのだが、通常の CPU 用のコンパイラが Tuple を実現しようとするとアセンブラの関数呼び出し(関数の戻り)の生成でいろいろ工夫しないといけないだろう。その点、RTL 的には複数の値を同時に返すことは自然に実現できる。

ちょっと複雑な例だが、Polyphony の tests/tuple/tuple07.py に含まれる例を示そう。

tuple07
from polyphony import testbench

def tuple07(p, x, y, z):
    def f(p, x, y, z):
        if p:
            return x, y
        else:
            return y, z
    a, b = f(p, x, y, z)
    return a + b

@testbench
def test():
    assert 1+2 == tuple07(True, 1, 2, 3)
    assert 2+3 == tuple07(False, 1, 2, 3)
    assert 4+5 == tuple07(True, 4, 5, 6)
    assert 5+6 == tuple07(False, 4, 5, 6)
test()

List

List も"なんとなく"使える。Polyphony を使いこなそうとすれば、デザイナはどのように List が解釈されるかを知っておいた方がよい。「動的には生成できない」という点にも理解が必要であろう。

sum.py
from polyphony import testbench

def sum(lst):
    rv = 0
    for i in range(len(lst)):
        rv += lst[i]

    return rv

@testbench
def test():
    data = [1, 2, 3, 4, 5]
    s = sum(data)
    assert(s == 15)

test()

上の例ではインタフェースとして list を使っているのでアドレスを持ったデータのアクセスのインタフェースを生成していた(過去形)。

sum.v
module sum
  (
    input wire clk,
    input wire rst,
    input wire sum_ready,
    input wire sum_accept,
    output reg sum_valid,
    input wire signed [31:0] sum_in_lst_q,
    input wire [3:0] sum_in_lst_len,
    output wire signed [3:0] sum_in_lst_addr,
    output wire signed [31:0] sum_in_lst_d,
    output wire sum_in_lst_we,
    output wire sum_in_lst_req,
    output reg signed [31:0] sum_out_0
  );

なお、今は 512bit(bit であることに注意) を下回るリストの場合次のコードを生成する。アドレスではなく直接値をやりとりする。512bit は調整可能な(env.py を書き換える)値なのでデザイナは上の性能要求によってインタフェースを選択することが出来る。(フレキシブルに選択するのは今後の課題)

sum.v
module sum
  (
    input wire clk,
    input wire rst,
    input wire sum_ready,
    input wire sum_accept,
    output reg sum_valid,
    input wire signed [31:0] sum_in_lst0,
    input wire signed [31:0] sum_in_lst1,
    input wire signed [31:0] sum_in_lst2,
    input wire signed [31:0] sum_in_lst3,
    input wire signed [31:0] sum_in_lst4,
    output wire signed [31:0] sum_out_lst0,
    output wire signed [31:0] sum_out_lst1,
    output wire signed [31:0] sum_out_lst2,
    output wire signed [31:0] sum_out_lst3,
    output wire signed [31:0] sum_out_lst4,
    output reg signed [31:0] sum_out_0
  );

内部でリストを使う例も示そう。動的にリストを作って"いない"(動的に作ることは出来ない)点に留意されたい。

list08.py
from polyphony import testbench

def list08(x, y, z):
    index = [x, y, z]
    data = [2, 3, 4]

    a = data[index[0]]
    b = data[index[1]]
    c = data[index[2]]
    return a + b * c

@testbench
def test():
    assert 14 == list08(0, 1, 2)
    assert 11 == list08(1, 2, 0)
    assert 10 == list08(2, 0, 1)
test()