VHDLでフィルタを作ってみます。PythonでやったことをVHDLに落とし込むってだけ。いやーそれがしんどいんだよねー。
まず1次IIRフィルタの式はこちら。
フィルタ係数は等間隔でサンプリングすることを前提としているので、計算も一定の時間で完了することが求められる。が、面倒なので、所要より長い時間カウントして、オーバーフローで次のサンプリングと結果の出力をすることになる。FPGAでFPU(1)でためしたIPは足し算に10クロック、掛け算に15クロックかかっていた。式(1)を実行するには掛け算1回+足し算1回分すなわち最低25クロックが必要。IPの実体を複数インプリメントすることで計算を並列化して計算時間を短縮できるってこともあるけど、今回はどうにもならん。余裕を見て32クロックで1サンプリングってすると、5MS/sを実現するにはコアクロックは160MHz。う~ん、実現でけんなー。PLL内蔵タイプじゃないと、、、後で考えよう。
で、さっそく、
iir1_try1.vhd
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity iir1_try1 is port ( i_1bit : in std_logic; o_32bit : out std_logic_vector(31 downto 0); i_start : in std_logic; o_complete : out std_logic; i_a0 : in std_logic_vector(31 downto 0); i_a1 : in std_logic_vector(31 downto 0); i_b0 : in std_logic_vector(31 downto 0); i_b1 : in std_logic_vector(31 downto 0); i_clk : in std_logic ); end iir1_try1; architecture ppl_type of iir1_try1 is signal xn : std_logic; signal xnm1 : std_logic:='0'; signal yn : std_logic_vector(31 downto 0):=X"00000000"; signal ynm1 : std_logic_vector(31 downto 0):=X"00000000"; constant fzero : std_logic_vector(31 downto 0):=X"00000000"; constant fone : std_logic_vector(31 downto 0):=X"3F800000"; signal b_index : std_logic_vector(31 downto 0):=X"00000000"; signal a_index : std_logic_vector(31 downto 0):=X"00000000"; signal i_start_prev : std_logic:='0'; signal i_start_pres : std_logic:='0'; signal i_start_rising : std_logic:='0'; signal indicator_complete : std_logic:='0'; type t_state is (STATE_IDLE,STATE_SET1, STATE_WAIT1,STATE_SET2, STATE_WAIT2, STATE_WAIT3, STATE_DONE); signal state : t_state := STATE_IDLE; signal state_num : integer range 0 to 15 := 0; constant fpu_add : std_logic_vector(2 downto 0):="000"; constant fpu_sub : std_logic_vector(2 downto 0):="001"; constant fpu_mul : std_logic_vector(2 downto 0):="010"; constant fpu_rmode : std_logic_vector(1 downto 0):="00"; component fpu port ( clk_i : in std_logic; opa_i : in std_logic_vector(31 downto 0); opb_i : in std_logic_vector(31 downto 0); fpu_op_i : in std_logic_vector(2 downto 0); rmode_i : in std_logic_vector(1 downto 0); output_o : out std_logic_vector(31 downto 0); ine_o : out std_logic; overflow_o : out std_logic; underflow_o : out std_logic; div_zero_o : out std_logic; inf_o : out std_logic; zero_o : out std_logic; qnan_o : out std_logic; snan_o : out std_logic; start_i : in std_logic; ready_o : out std_logic ); end component; signal opa_i_1, opb_i_1 : std_logic_vector(31 downto 0); signal fpu_op_i_1 : std_logic_vector(2 downto 0); signal output_o_1 : std_logic_vector(31 downto 0); signal start_i_1 : std_logic:='0'; signal ready_o_1 : std_logic ; signal ine_o_1, overflow_o_1, underflow_o_1, div_zero_o_1, inf_o_1, zero_o_1, qnan_o_1, snan_o_1: std_logic; signal comp_1: std_logic:='0'; signal opa_i_2, opb_i_2 : std_logic_vector(31 downto 0); signal fpu_op_i_2 : std_logic_vector(2 downto 0); signal output_o_2 : std_logic_vector(31 downto 0); signal start_i_2 : std_logic:='0'; signal ready_o_2 : std_logic ; signal ine_o_2, overflow_o_2, underflow_o_2, div_zero_o_2, inf_o_2, zero_o_2, qnan_o_2, snan_o_2: std_logic; signal comp_2: std_logic:='0'; begin -- instantiate fpu i_fpu_1: fpu port map ( clk_i => i_clk, opa_i => opa_i_1, opb_i => opb_i_1, fpu_op_i => fpu_op_i_1, rmode_i => fpu_rmode, output_o => output_o_1, ine_o => ine_o_1, overflow_o => overflow_o_1, underflow_o => underflow_o_1, div_zero_o => div_zero_o_1, inf_o => inf_o_1, zero_o => zero_o_1, qnan_o => qnan_o_1, snan_o => snan_o_1, start_i => start_i_1, ready_o => ready_o_1 ); i_fpu_2: fpu port map ( clk_i => i_clk, opa_i => opa_i_2, opb_i => opb_i_2, fpu_op_i => fpu_op_i_2, rmode_i => fpu_rmode, output_o => output_o_2, ine_o => ine_o_2, overflow_o => overflow_o_2, underflow_o => underflow_o_2, div_zero_o => div_zero_o_2, inf_o => inf_o_2, zero_o => zero_o_2, qnan_o => qnan_o_2, snan_o => snan_o_2, start_i => start_i_2, ready_o => ready_o_2 ); i_start_pres<=i_start; i_start_rising<='1' when (i_start_prev='0' and i_start_pres='1') else '0'; o_complete<=indicator_complete; process (i_clk,i_start_rising,state) begin if i_clk'event and i_clk='1' then i_start_prev<=i_start; if i_start_rising='1' and state=STATE_IDLE then -- b_0+b_1 opa_i_1<=i_b0; opb_i_1<=i_b1; fpu_op_i_1<=fpu_add; start_i_1<='1'; comp_1<='0'; -- a_1*y_{n-1} opa_i_2<=i_a1; opb_i_2<=ynm1; fpu_op_i_2<=fpu_mul; start_i_2<='1'; comp_2<='0'; xn<=i_1bit; state<=STATE_SET1; state_num<=1; end if; if state=STATE_SET1 then start_i_1<='0'; start_i_2<='0'; state<=STATE_WAIT1; state_num<=2; end if; if state=STATE_WAIT1 then if ready_o_1='1' then comp_1<='1'; b_index<=output_o_1; end if; if ready_o_2='1' then comp_2<='1'; a_index<=output_o_2; end if; if comp_1='1' and comp_2='1' then if xnm1='0' and xn='0' then opa_i_1<=fzero; elsif xnm1='0' and xn='1' then opa_i_1<=i_b0; elsif xnm1='1' and xn='0' then opa_i_1<=i_b1; elsif xnm1='1' and xn='1' then opa_i_1<=b_index; end if; opb_i_1<=a_index; fpu_op_i_1<=fpu_sub; start_i_1<='1'; comp_1<='0'; state<=STATE_SET2; state_num<=3; end if; end if; if state=STATE_SET2 then start_i_1<='0'; state<=STATE_WAIT2; state_num<=4; end if; if state=STATE_WAIT2 then if ready_o_1='1' then comp_1<='1'; yn<=output_o_1; o_32bit<=output_o_1; state<=STATE_DONE; state_num<=5; indicator_complete<='1'; end if; end if; if state=STATE_DONE then indicator_complete<='0'; xnm1<=xn; ynm1<=yn; state<=STATE_IDLE; state_num<=0; end if; end if; end process; end;
FPUへの代入に行数がかかっているので、長大に見えるけど、まぁまぁシンプルにできた。またいきなり答えを書いてしまったけど、STATEを変えながら動かしていくってのが、コンピュータ言語使いにとっては安定して作れる。まず、どういうSTATEを作るべきかを考えて、一旦STATEを無条件で遷移させるコードにして(これで、1クロックごとにSTATEを移動していく)、その後各STATEで何を待ってどう処理するかを書いていくだけなので、考え方が簡単。
そして、テストベンチ。
iir1_try1_tb.vhd
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity iir1_try1_tb is end iir1_try1_tb; architecture sim of iir1_try1_tb is component iir1_try1 is port ( i_1bit : in std_logic; o_32bit : out std_logic_vector(31 downto 0); i_start : in std_logic; o_complete : out std_logic; i_a0 : in std_logic_vector(31 downto 0); i_a1 : in std_logic_vector(31 downto 0); i_b0 : in std_logic_vector(31 downto 0); i_b1 : in std_logic_vector(31 downto 0); i_clk : in std_logic ); end component; signal clksys : std_logic:='0'; constant STEPSYS : TIME := 6.25 ns; signal i_1bit_tb : std_logic:='0'; signal o_32bit_tb : std_logic_vector(31 downto 0); signal i_start_tb : std_logic:='0'; signal o_complete_tb : std_logic; signal i_a0_tb : std_logic_vector(31 downto 0):=X"3F800000"; signal i_a1_tb : std_logic_vector(31 downto 0):=X"BF7814CD"; signal i_b0_tb : std_logic_vector(31 downto 0):=X"3C7D6652"; signal i_b1_tb : std_logic_vector(31 downto 0):=X"3C7D6652"; signal clk_counter : integer range 0 to 255:=0; signal clk_counter_bb : integer range 0 to 65535:=0; begin iir1_try1_inst : component iir1_try1 port map( i_1bit=>i_1bit_tb, o_32bit=>o_32bit_tb, i_start=>i_start_tb, o_complete=>o_complete_tb, i_a0=>i_a0_tb, i_a1=>i_a1_tb, i_b0=>i_b0_tb, i_b1=>i_b1_tb, i_clk=>clksys ); clksys <= not(clksys) after STEPSYS/2; process(clksys)begin if clksys'event and clksys='1' then if clk_counter=31 then clk_counter<=0; i_start_tb<='1'; else clk_counter<=clk_counter+1; i_start_tb<='0'; end if; end if; end process; process(clksys)begin if clksys'event and clksys='1' then clk_counter_bb<=clk_counter_bb+1; if clk_counter_bb=8000 then clk_counter_bb<=0; if i_1bit_tb='0' then i_1bit_tb<='1'; else i_1bit_tb<='0'; end if; end if; end if; end process; end;
サンプリング周波数を5MHzにするためにシステムクロックを160MHzにして32クロックでサンプリングしています。フィルタ係数はPythonでフィルタ(5)で使ったbutterによる係数にしています。
Makefile
VHDLC=/usr/bin/ghdl SRC=iir1_try1.vhd fpu_v19/fpupack.vhd fpu_v19/pre_norm_addsub.vhd fpu_v19/addsub_28.vhd fpu_v19/post_norm_addsub.vhd fpu_v19/pre_norm_mul.vhd fpu_v19/mul_24.vhd fpu_v19/serial_mul.vhd fpu_v19/post_norm_mul.vhd fpu_v19/pre_norm_div.vhd fpu_v19/serial_div.vhd fpu_v19/post_norm_div.vhd fpu_v19/pre_norm_sqrt.vhd fpu_v19/sqrt.vhd fpu_v19/post_norm_sqrt.vhd fpu_v19/comppack.vhd fpu_v19/fpu.vhd TBSRC=iir1_try1_tb.vhd VCD=iir1_try1.vcd SIMRTL=iir1_try1_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)
これがまぁなんとも、いとも簡単にそれらしい結果が出るんだわ。
フィルタがかかった波形が得られている。
STATEの遷移も期待通り。
が、なんかちょっとpythonの結果と違っている気がする。、、、あ、シンボルレートがちがうわ、、、
Pythonでフィルタ(5)のコード(一番下のやつ)をもってきて
f_symbol=10e3
を
f_symbol=20e3
にして、シンボルもLスタートに変更して実行したものと比較してみる。
ktkr--!完璧やん。初老のおじさんにこんなことができるようになるなんて、なんて世の中だ。世間の善意(web情報)とテクノロジーの進歩に感謝。
今日は浜松にMJ来てるんやね。
コメントをお書きください