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でコンパイルしシミュレーションすると下記のような結果が得られます。
実行環境:Win10 python3.7.1 polyphony0.3.4 iverilog0.9.7