IEEE754はコンピュータ内での浮動小数点数の表現方法に関する決まり事です。FPGAでフィルタを実現するにあたって、計算結果の出力時にこの知識が必要です。Intel(ALTERA)のFPGAをQuartusで記述するなら、ALTFP_CONVERTっていうIPがありますが、自力でどうするかを考える。
C言語にて0以上1未満の浮動小数点数を指定したビット幅の整数に変換することができたので、VHDLで実装します。
一度C言語で実装しているので、ちゃちゃっと。 ただし16bit限定。16bitより小さくするなら、結果をさらに右シフトする、FPGAハード的には内部結線しないとか、外部結線しないとかってことで。
float2uint.vhd
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; --use ieee.numeric_std.all; entity Float2UI32 is port ( i_src : in std_logic_vector(31 downto 0); o_ui32 : out std_logic_vector(31 downto 0) ); end Float2UI32; architecture ppl_type of Float2UI32 is signal nbit : std_logic_vector(7 downto 0):=X"10"; signal sign1 : std_logic; signal expn8 : std_logic_vector(7 downto 0); signal frac23 : std_logic_vector(31 downto 0); -- "https://github.com/ghdl/ghdl/blob/master/libraries/ieee/numeric_std-body.vhdl" function XSLL (ARG: STD_LOGIC_VECTOR; COUNT: NATURAL) return STD_LOGIC_VECTOR is constant ARG_L: INTEGER := ARG'LENGTH-1; alias XARG: STD_LOGIC_VECTOR(ARG_L downto 0) is ARG; variable RESULT: STD_LOGIC_VECTOR(ARG_L downto 0) := (others => '0'); begin if COUNT <= ARG_L then RESULT(ARG_L downto COUNT) := XARG(ARG_L-COUNT downto 0); end if; return RESULT; end XSLL; function XSRL (ARG: STD_LOGIC_VECTOR; COUNT: NATURAL) return STD_LOGIC_VECTOR is constant ARG_L: INTEGER := ARG'LENGTH-1; alias XARG: STD_LOGIC_VECTOR(ARG_L downto 0) is ARG; variable RESULT: STD_LOGIC_VECTOR(ARG_L downto 0) := (others => '0'); begin if COUNT <= ARG_L then RESULT(ARG_L-COUNT downto 0) := XARG(ARG_L downto COUNT); end if; return RESULT; end XSRL; begin sign1<=i_src(31); expn8<=i_src(30 downto 23); frac23<="000000000"&i_src(22 downto 0); o_ui32<=X"00000000" when expn8+nbit<127 else XSLL(X"00000001",0) or XSRL(frac23,23) when expn8+nbit=127 else XSLL(X"00000001",1) or XSRL(frac23,22) when expn8+nbit=128 else XSLL(X"00000001",2) or XSRL(frac23,21) when expn8+nbit=129 else XSLL(X"00000001",3) or XSRL(frac23,20) when expn8+nbit=130 else XSLL(X"00000001",4) or XSRL(frac23,19) when expn8+nbit=131 else XSLL(X"00000001",5) or XSRL(frac23,18) when expn8+nbit=132 else XSLL(X"00000001",6) or XSRL(frac23,17) when expn8+nbit=133 else XSLL(X"00000001",7) or XSRL(frac23,16) when expn8+nbit=134 else XSLL(X"00000001",8) or XSRL(frac23,15) when expn8+nbit=135 else XSLL(X"00000001",9) or XSRL(frac23,14) when expn8+nbit=136 else XSLL(X"00000001",10) or XSRL(frac23,13) when expn8+nbit=137 else XSLL(X"00000001",11) or XSRL(frac23,12) when expn8+nbit=138 else XSLL(X"00000001",12) or XSRL(frac23,11) when expn8+nbit=139 else XSLL(X"00000001",13) or XSRL(frac23,10) when expn8+nbit=140 else XSLL(X"00000001",14) or XSRL(frac23,9) when expn8+nbit=141 else XSLL(X"00000001",15) or XSRL(frac23,8) when expn8+nbit=142 else X"FFFFFFFF"; end;
expn+nbit-127が0以上15未満であることが所望のビット数内に収めるための条件ってことは、expn+nbitは127, 128, 129, ..., 142しかあり得ないので、全条件を羅列して一発判定。左シフト、右シフトがうちのghdlには実装されてなかったので、functionとして追加。ここから持ってきた。このXSLLとXSRLは第2引数はconstantじゃないといけないってのもあって、全条件をwhenで記述したってのもある。結果的にはC言語よりもすんなりいったね。
では、テストベンチを作る。
float2uint_tb.vhd
LIBRARY ieee; USE ieee.std_logic_1164.ALL; USE ieee.std_logic_unsigned.ALL; ENTITY float2uint_tb IS END float2uint_tb; ARCHITECTURE SIM OF float2uint_tb IS COMPONENT Float2UI32 IS port ( i_src : in std_logic_vector(31 downto 0); o_ui32 : out std_logic_vector(31 downto 0) ); END COMPONENT; SIGNAL src : std_logic_vector(31 DOWNTO 0) := X"00000000"; SIGNAL ui32 : std_logic_vector(31 DOWNTO 0); SIGNAL clksys : STD_LOGIC:='0'; CONSTANT STEPSYS : TIME := 20 ns; SIGNAL clk_count : INTEGER RANGE 0 TO 1048575:=0; CONSTANT clk_max : INTEGER := 101; subtype ui32_value is std_logic_vector(31 downto 0); type ui32_values_type is array(0 to 100) of ui32_value; constant ui32_values : ui32_values_type:=( X"3F1B5976",X"3E09B808",X"3EAF9985",X"3F65D328",X"3F1E7567",X"3F5C86C9",X"3F496F94",X"3F59E4F8",X"3F74CF79",X"3F038873", X"3F14BC71",X"3DCD6D8B",X"3E684348",X"3ECBC497",X"3ECBF63C",X"3F4A92D0",X"3E30C466",X"3E01EF5B",X"3ED009D4",X"3EA9F195", X"3EFC3D2B",X"3EF9B064",X"3E42504F",X"3DA95955",X"3F33B1AE",X"3F648BEB",X"3F573486",X"3F14545A",X"3DF1EE69",X"3DC46A1A", X"3D43D250",X"3F399743",X"3E6BED15",X"3EC813CF",X"3F1F6A6B",X"3F5970AC",X"3E812161",X"3ED1B3FE",X"3F3355A4",X"3E5580A7", X"3F6C6272",X"3E90242A",X"3E9E1BB6",X"3E19CD11",X"3F2DF460",X"3F3508F9",X"3F710614",X"3F5A257A",X"3F5584D0",X"3EB215FC", X"3E3C7910",X"3EA746CB",X"3F55E330",X"3EBF64B0",X"3ED19D21",X"3F0994DE",X"3E887C85",X"3E80062C",X"3DEF49C4",X"3EC4F820", X"3EB120B3",X"3E289976",X"3DE09A98",X"3F138B9F",X"3F0E3045",X"3F3B7DBE",X"3ED9F896",X"3F4EC0F5",X"3E115EF4",X"3E0147BC", X"3C8423DC",X"3D85D17A",X"3ED0C808",X"3EA65DF4",X"3E5CB5CD",X"3DB2C322",X"3D037F35",X"3E20CE1E",X"3F707DDE",X"3F5DBCC3", X"3F013E86",X"3DFCE10F",X"3E4580A4",X"3EAE436C",X"3EFE9CF3",X"3F1A2EB9",X"3F60B694",X"3F438CBC",X"3F5A31D0",X"3F7E9FCD", X"3E182331",X"3E4B08A4",X"3E2318A8",X"3E84383E",X"3F464DC8",X"3F36F66F",X"3F7D99DD",X"3E4D284B",X"3F05B764",X"3E07C668", X"3EA73804" ); BEGIN Float2UI32_inst : COMPONENT Float2UI32 PORT MAP( i_src=>src, o_ui32=>ui32 ); clksys <= not(clksys) after STEPSYS/2; process(clksys)begin if clksys'event and clksys='0' then if clk_count=clk_max then assert false report "Finish" severity failure; end if; src<=ui32_values(clk_count); clk_count<=clk_count+1; end if; end process; END;
Makefile
VHDLC=/usr/bin/ghdl SRC=float2uint.vhd TBSRC=float2uint_tb.vhd VCD=float2uint.vcd SIMRTL=float2uint_tb all : @make comp @make tb @make tb_e @make vcd comp : $(VHDLC) -a --ieee=synopsys -fexplicit $(SRC) tb : $(VHDLC) -a --ieee=synopsys -fexplicit $(TBSRC) tb_e : $(VHDLC) -e --ieee=synopsys -fexplicit $(SIMRTL) vcd : $(VHDLC) -r --ieee=synopsys -fexplicit $(SIMRTL) --vcd=$(VCD)
テストベンチでは1clockごとに入力を変えていくが、この入力値はどう作ったかというと、
#include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <time.h> typedef struct{ uint32_t frac:23; uint32_t expn:8; uint32_t sign:1; }tIEEE754; typedef union{ uint8_t asui8[4]; uint32_t asui32; float asFloat; tIEEE754 asIEEE754; }uFloat; int main(int argc,char** argv){ uint32_t nbit=16; uint32_t nshift; uint32_t ans; uFloat ufloat; int rnd; float rnd_as_float,rnd_max_as_float; rnd_max_as_float=(float)RAND_MAX; srand((unsigned)time(NULL)); for(uint32_t i=0;i<101;i++){ rnd=rand(); rnd_as_float=((float)rnd)/rnd_max_as_float; ufloat.asFloat=rnd_as_float; printf("%d\t",i); printf("%08X\t",ufloat.asui32); printf("%.12f\t",ufloat.asFloat); printf("%.12f\t",ufloat.asFloat*(1<<nbit)); if(ufloat.asIEEE754.expn+nbit>=127){ nshift=ufloat.asIEEE754.expn+nbit-127; ans=(1<<(nshift))+(ufloat.asIEEE754.frac>>(23-nshift)); }else{ ans=0; } printf("%d\t",ans); printf("%08X\t",ans); printf("X\"%08X\",\n",ufloat.asui32); } }
これを実行して出てきたものを加工した。
で、テストベンチを走らせた結果はこうなる。
答え合わせをすると、
あってるっぽい!クロックなしの論理回路だけで実現できたー!ktkr!がんばれば報われる。いや、報われないことも多い。たくさんの報われないことがあって、少しの報われることがある。どんだけがんばればいいんやー
コメントをお書きください