Polyphonyって何だろう(6)使用例:FIRフィルター

Python で書いた関数を Verilog HDL に変換する高位合成コンパイラである Polyphony を使ってみたいと思います。固定小数点の掛け算もできるようになったので、FIRフィルターを記述して、pythonなので、グラフ表示で機能確認をしてみたいと思います。

FIRフィルターの係数

FIRフィルターの係数は
Parks-McClellan法の設計仕様(BPF,BEF)
このサイトを利用させてもらいました。

設計仕様を入力して右下の[設計する]をクリックすると
f:id:feynman911:20190204203427j:plain

次の様に、係数と特性が表示されます。
この係数をコピーして使わせていただきました。
f:id:feynman911:20190204203632j:plain

FIRローパスフィルターコード (FIRfilter.py) と簡単な説明

係数は32bit固定小数点として扱う事にします。19 タップとしているので、32x19=608bit 必要となります。
Polyphonyの標準設定として 512bit を超えると RAM を使用するような設定になっていて、読み出しに delay が入るようになっているので、それを回避するために env.py を修正します。
internal_ram_threashold_size = 1024

入力と係数の固定小数点掛け算には、前回作成した fix32.py から fix32_mul を import して使用します。
FIR係数の浮動小数点変数を32bit固定小数点に直すための関数 x_fix32 も fix32.py から import して使用したいところですが、@pure が無視されてしまうようで、polyphony コンパイル時にエラーが出てしまいます。そのため、コピーして使用することにしました。
python デバッグ時に、出力結果の 32bit固定小数点を通常の浮動小数点に戻す為の関数 fix32_x もコピーして使用します。

シミュレーション用の入力データは、2つのサイン波を足し合わせて、ベース周波数にノイズが乗っている波形を作成しています。その波形から、FIRローパスフィルターでノイズを除去する事を想定しています。

# -*- coding: utf-8 -*-
# FIRfilter.py
import math
import polyphony
from polyphony import pure, unroll, pipelined
from polyphony import testbench
from polyphony.typing import bit32,List
from fix32 import fix32_mul

TAPS = 19
FPPBIT = 23
TESTSIZE = 200

@pure
def x_fix32(x,fn):
    y = int(x * (2**fn)) & 0xFFFFFFFF
#    print(x,':',format(y,'032b'))
    return(y)

@pure
def fix32_x(x,fn):
    x = x & 0xFFFFFFFF
    s = x >> 31
    y = x & 0x7FFFFFFF
    if s != 0:
        y = -((2**32) - x)
    r = y / (2**fn)
#    print(format(x,'032b'),':',r)
    return(r)

@pure
def calcoeff(fp):
    coeff = [0] * TAPS
    coeff[0] =6.612020219528149e-04
    coeff[1] =1.831283662723811e-14
    coeff[2] =-5.277493642752464e-03
    coeff[3] =2.734654608024070e-15
    coeff[4] =2.263745261146082e-02
    coeff[5] =-3.303789990121387e-14
    coeff[6] =-7.405318429979151e-02
    coeff[7] =6.039613253960852e-14
    coeff[8] =3.060336228515318e-01
    coeff[9] =4.999999999999285e-01
    coeff[10] =3.060336228515318e-01
    coeff[11] =6.039613253960852e-14
    coeff[12] =-7.405318429979151e-02
    coeff[13] =-3.303789990121387e-14
    coeff[14] =2.263745261146082e-02
    coeff[15] =2.734654608024070e-15
    coeff[16] =-5.277493642752464e-03
    coeff[17] =1.831283662723811e-14
    coeff[18] =6.612020219528149e-04

    for i in range(19):
        coeff[i] = x_fix32(coeff[i], fp)
#        print(fix32_x(coeff[i], fp))
    return(coeff)

@pure
def caltestdata(fp):
    testdata = [0] * TESTSIZE
    for i in range(TESTSIZE):
        x = 7 * math.sin(0.01*3.14*i)+2*math.sin(0.8*3.14*i)
        testdata[i] = x_fix32(x, fp)
#        print(testdata)
    return(testdata)

COEFF = calcoeff(FPPBIT)
TESTDATA = caltestdata(FPPBIT)

def FIRfilter(input:bit32, coeff:List, previous:List)->bit32:
    for j in unroll(range(TAPS-1)):
        jj = TAPS-1 -j
        previous[jj] = previous[jj-1]
    previous[0] = input
    temp:bit32 = 0
    for j in pipelined(range(TAPS)):
        temp += fix32_mul(previous[TAPS-1-j], coeff[j], FPPBIT)
    return(temp)

@testbench
def FIRfilter_test():
    previous = [0] * TAPS
#    data = [] #python
#    result = [] #python
    for i in range(TESTSIZE):
        r = FIRfilter(TESTDATA[i], COEFF, previous) & 0xFFFFFFFF
        print(r)

#        print(format(TESTDATA[i],'10d'),':',format(r,'10d')) #python
#        print(fix32_x(TESTDATA[i],FPPBIT),fix32_x(r,FPPBIT)) #python
#        print(format(TESTDATA[i],'032b'),':',format(r,'032b')) #python
#        data.append(fix32_x(TESTDATA[i],FPPBIT)) #python
#        result.append(fix32_x(r,FPPBIT)) #python
#    import matplotlib.pyplot as plt #python
#    plt.plot(data) #python
#    plt.plot(result) #python

if __name__ == '__main__':
    FIRfilter_test()

実行結果

#python と書かれた行をアンコメントしてからpythonで実行すると、テストデータとFIRフィルターによって処理されたデータのグラフが表示され、FIRフィルターの効果が確認できます。

4276652763
4286795431 : 4276652763
-0.974162220954895 -2.1832624673843384
11111111100000110100111010100111 : 11111110111010001000101011011011

f:id:feynman911:20190130230928j:plain


polyphony でコンパイルする時には #python が付いた行をコメントアウトする必要があります。
こんな風に何かマークを付けた行をコンパイル時に読み飛ばしてくれる機能がpolyphonyにあると 非常に便利なのですが、今後に期待したいところです。

testbench の print(r) はシミュレーション時に必要なので、コメントアウトしないようにします。これが無いと、シミュレーション結果が保存されません。

polyphony でのコンパイルから 、シミュレーション、gtkwave での結果表示までは、
https://feynman.hatenablog.com/entry/2018/12/22/115738#簡単実行ユーティリティー
を使用してください。

f:id:feynman911:20190130231555j:plain

Verilogシミュレーション結果の .vcd ファイルを読み込んで、pythonシミュレーションと比較するようなスクリプトを書けば、python から Verilog への変換がうまくいているかどうかをもっと確実に確認できると思います。

実行環境:Win10 python3.7.1 polyphony0.3.4  iverilog0.9.7