アラン・シリトーが描く worker は月曜から土曜まで働いて、宵越しの金は持たずに日曜の朝を迎える。「働く」ということはいまだにピンとこない。どちらかというと目の前の事に四苦八苦している。だから、金曜の夜も土曜の夜も PC の前だ。
Polyphony の worker は文句も言わずに常駐してくれる。worker は関数(メソッド)で表現され、init 内で append_worker でworker として登録される。
上の図のように、これだけだとただの無限ループで何も生産しない。Port や Queue を使って外部あるいは別の Worker と通信をする。ソフトウェアの意味的には Port よりも Queue の方がわかりやすい。Port はどんなタイミングで、どんな情報が来るかをデザイナーは把握してなければならない。HDL が必要とするクロック的な概念に縛られている。高位合成はクロックからの解放に向かおうとする技術だから、この手の時間的概念を扱うインタフェースとすこぶる相性が悪い。
ここでは簡単な平均フィルターを書いてみよう。Queue をつかって入力と出力をしている。
from polyphony import rule
import polyphony
from polyphony import is_worker_running
from polyphony.io import Port, Queue
from polyphony.typing import bit, int8, int16, int18
from polyphony.timing import clksleep, clkfence, wait_rising, wait_falling
@polyphony.module
class Filter:
def __init__(self):
self.q = Queue(int16, 'out', maxsize=10)
self.din = Queue(int16, 'in', maxsize=10)
self.append_worker(self.proc)
def proc(self):
a0:int18 = 0
a1:int18 = 0
a2:int18 = 0
a3:int18 = 0
while is_worker_running():
self.q.wr(a0 >> 2)
data:int16 = self.din.rd()
a0 = a1 + data
a1 = a2 + data
a2 = a3 + data
a3 = data
@polyphony.testbench
def test(m):
datas = (0x5ead, 0x3eef, 0x7fff, 0x0000, 0x800)
for data in datas:
m.din.wr(data)
for i in range(5):
print(m.q.rd())
clksleep(10)
if __name__ == '__main__':
filter = Filter()
test(filter)
Polyphony のコードは Python なので Python で実行可能だ。本来なら Python でデバッグを完了してシミュレーションする
$ python ave_filter.py
0
6059
10087
18278
18278
生成された Verilog-HDL はちょっと複雑だ。わかりやすいように Vivado に絵を描いてもらうことにしよう。
module Filter_filter
(
input wire clk,
input wire rst,
input wire signed [15:0] din_dout,
input wire din_empty,
output reg din_read,
input wire q_full,
output reg signed [15:0] q_din,
output reg q_write
);