Polyphonyって何だろう(5)使用例:固定小数点掛け算

Python で書いた関数を Verilog HDL に変換する高位合成コンパイラである Polyphony を使ってみたいと思います。今回は固定小数点の掛け算を書いてみたいと思います。前回と同じく @pure を使って pythonアルゴリズムの検証を行います。

固定小数点掛け算のコード (fix32.py) と簡単な説明

固定小数点の掛け算は、次の polyphony の本家に出てきますが、2の補数表現になっていないようなので、作り直しています。
[簡易版]固定小数点 浮動小数点 - Qiita

32bit 固定小数点(小数点以下9bit) の掛け算回路を作ります。fix32_mul は、小数点以下の bit 数可変の記述として、それを fix32_mul_9_23 で呼び出して、符号 1bit 整数部 8bit 小数部 23bit の固定小数点掛け算を行う回路を記述しています。
2の補数表現を考慮しながら 32bit を 64bit に拡張して掛け算を行い、その後 32bit に戻しています。
このアルゴリズムを検証するために、少数を固定小数点32bitに変換するpython関数が x_fix32 で、その反対に固定小数点32bitを少数に直すのが fix32_x です。この二つを使うことで、testbench でアルゴリズムの検証を行う事ができます。
気を付けないといけないのは、polyphony コンパイル時には @pure デコレータを付けたこれらの関数は、コンパイル前に実行されて値に変換されるという事です。ですので、testbench の結果を確認するのには使えないという事です。Verilogシミュレーション後の結果を自動確認するようにするためには、あらかじめ入力と正解を @pure 関数を使って事前に計算してlistにしておいて、それを使用する必要があります。

import math
import polyphony
from polyphony import module, pure, is_worker_running, rule, unroll
from polyphony import testbench
from polyphony.io import Port
from polyphony.typing import bit32,bit64
from polyphony.timing import clksleep, clkfence, wait_value, wait_rising

@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)

def fix32_mul(a:bit32, b:bit32, fn) -> bit32:
    if((a >> 31) & 1):
        a64:bit64 = a | 0xFFFFFFFF80000000
    else:
        a64 = a & 0xFFFFFFFFFFFFFFFF
#    print(format(a,'032b')) #for python only
#    print(format(a64,'064b')) #for python only
    if((b >> 31) & 1):
        b64:bit64 = b | 0xFFFFFFFF80000000
    else:
        b64 = b & 0xFFFFFFFFFFFFFFFF
#    print(format(b,'032b')) #for python only
#    print(format(b64,'064b')) #for python only
    rv32:bit32 = ((a64 * b64) >> fn) & 0xFFFFFFFF
#    print(format(rv32,'032b')) #for python only
    return(rv32)

def fix32_mul_9_23(a:bit32,b:bit32) -> bit32:
    return(fix32_mul(a,b,23))

@testbench
def fix32_test():
    c = x_fix32(8.25,23)
    d = x_fix32(-8.25,23)
    e = fix32_mul_9_23(c,d)
    print(c,d,e)

#    print(fix32_x(e,23))

if __name__ == '__main__':
    fix32_test()

実行結果

上記コードを、print 文をアンコメントしてpythonとして実行すると、埋め込んだprint文によって、次のような結果が表示されます。2つの少数が32bit固定小数点に変換され、この場合8.25と-8.25なので、単純にこの32bitを足し算すると0になることから、2の補数表現になっていることが確認できます。掛け算結果を少数に戻して表示すると、-68.0625と表示され、計算が正しく行われていることが分かります。testbench のcとdをいろいろと変えて確認することができますし、forループでlist として入力と結果を事前に作っておいて比較して確認することもできます。

8.25 : 00000100001000000000000000000000
-8.25 : 11111011111000000000000000000000
00000100001000000000000000000000
0000000000000000000000000000000000000100001000000000000000000000
11111011111000000000000000000000
1111111111111111111111111111111111111011111000000000000000000000
11011101111110000000000000000000
69206016 4225761280 3724017664
11011101111110000000000000000000 : -68.0625
-68.0625

コードからformatを使っているprint文とtestbenchでfix32_xを使っているprint文をコメントアウトしてからpolyphonyでコンパイルしシミュレーションすると下記のような結果が得られます。
f:id:feynman911:20190124204618j:plain

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