· 

VHDLで1次IIRフィルタ(2)

VHDLでフィルタを作ってみます。これまでやってきたことを組み合わせて、こんな感じで実装するのを目指します。

実はこいつら(中身の部品)はほぼこれまでに作ってきたものをそのまま持ってきています。

my_1iir_elementはVHDLで1次IIRフィルタ(1)で作ったものですが、i_resetってのを追加しています。i_resetのrising edgeでy(n-1), x(n-1)を強制的にそれぞれ0.0, 0にします。

clk_genはi_en='1'になった瞬間o_resetにパルスを出力し、その後32クロックごとにo_startにパルスを出します。

 

clk_gen.vhd(今回作成)

library ieee;
use ieee.std_logic_1164.all;
-- use ieee.std_logic_unsigned.all;
 
entity clk_gen is
    port (
        i_en : in std_logic;
        o_start : out std_logic;
        o_reset : out std_logic;
        i_clk : in std_logic
    );
end clk_gen;
 
architecture ppl_type of clk_gen is
    type t_state is (STATE_IDLE,STATE_RESET,STATE_RUN);
    signal state : t_state := STATE_IDLE;
    signal state_num : integer range 0 to 7 := 0;
    signal i_en_pres : std_logic:='0';
    signal i_en_prev : std_logic:='0';
    signal i_en_rising : std_logic:='0';
    signal i_en_falling : std_logic:='0';
    constant clk_count_max : integer range 0 to 63:=31;
    signal clk_counter : integer range 0 to 63:=0;
    signal outp_reset : std_logic:='0';
    signal outp_start : std_logic:='0';

begin
    i_en_pres<=i_en;
    i_en_rising<='1' when (i_en_prev='0' and i_en_pres='1') else '0';
    i_en_falling<='1' when (i_en_prev='1' and i_en_pres='0') else '0';
    o_reset<=outp_reset;
    o_start<=outp_start;

    process(i_clk,i_en_rising,state)begin
        if i_clk'event and i_clk='1' then
            i_en_prev<=i_en;
            if i_en_falling='1' then
                state<=STATE_IDLE;
                state_num<=0;
                outp_reset<='0';
                outp_start<='0';
            end if;
            if i_en_rising='1' and state=STATE_IDLE then
                outp_reset<='1';
                state<=STATE_RESET;
                state_num<=1;
            end if;
            if state=STATE_RESET then
                outp_reset<='0';
                clk_counter<=0;
                state<=STATE_RUN;
                state_num<=2;
            end if;
            if state=STATE_RUN then
                if clk_counter=clk_count_max then
                    clk_counter<=0;
                    outp_start<='1';
                else
                    clk_counter<=clk_counter+1;
                    outp_start<='0';
                end if;
            end if;
        end if;
    end process;
end;

my_1iir_element.vhd(前回から修正あり)

library ieee;
use ieee.std_logic_1164.all;
-- use ieee.std_logic_unsigned.all;
 
entity my_1iir_element is
    port (
        i_1bit : in std_logic;
        o_32bit : out std_logic_vector(31 downto 0);
        i_start : in std_logic;
        i_reset : 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 my_1iir_element;
 
architecture ppl_type of my_1iir_element is
    signal xn : std_logic:='0';
    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 i_reset_prev : std_logic:='0';
    signal i_reset_pres : std_logic:='0';
    signal i_reset_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";
    signal outp_32bit : std_logic_vector(31 downto 0):=X"00000000";

    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 : std_logic_vector(31 downto 0):=X"00000000";
    signal opb_i_1 : std_logic_vector(31 downto 0):=X"00000000";
    signal fpu_op_i_1           : std_logic_vector(2 downto 0):="000";
    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 : std_logic_vector(31 downto 0):=X"00000000";
    signal opb_i_2 : std_logic_vector(31 downto 0):=X"00000000";
    signal fpu_op_i_2           : std_logic_vector(2 downto 0):="000";
    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;
    i_reset_pres<=i_reset;
    i_reset_rising<='1' when (i_reset_prev='0' and i_reset_pres='1') else '0';
    o_32bit<=outp_32bit;

    process (i_clk,i_start_rising,state) begin
        if i_clk'event and i_clk='1' then
            i_start_prev<=i_start;
            i_reset_prev<=i_reset;
            if i_reset_rising='1' then
                state<=STATE_IDLE;
                xnm1<='0';
                ynm1<=fzero;
            end if;
            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;
                    outp_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;

my_1iir_top.vhd(配線しただけ)

library ieee;
use ieee.std_logic_1164.all;
-- use ieee.std_logic_unsigned.all;
 
entity my_1iir_top is
    port (
        i_1bit : in std_logic;
        i_en : in std_logic;
        cs : in std_logic;
        sck : in std_logic;
        mosi : in std_logic;
        miso : out std_logic;
        o_ui32 : out std_logic_vector(31 downto 0);
        i_clk : in std_logic
    );
end my_1iir_top;
 
architecture ppl_type of my_1iir_top is
    component clk_gen port(
        i_en : in std_logic;
        o_start : out std_logic;
        o_reset : out std_logic;
        i_clk : in std_logic
    );end component;
    component my_spi_slave is port (
        cs : in std_logic;
        sck : in std_logic;
        mosi : in std_logic;
        miso : out std_logic;
        a0 : out std_logic_vector(31 downto 0);
        a1 : out std_logic_vector(31 downto 0);
        b0 : out std_logic_vector(31 downto 0);
        b1 : out std_logic_vector(31 downto 0)
    );end component;
    component my_1iir_element is port (
        i_1bit : in std_logic;
        o_32bit : out std_logic_vector(31 downto 0);
        i_start : in std_logic;
        i_reset : 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;
    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 internal_wire_start : std_logic:='0';
    signal internal_wire_reset : std_logic:='0';
    signal internal_wire_complete : std_logic:='0';
    signal internal_wires_a0 : std_logic_vector(31 downto 0):=X"00000000";
    signal internal_wires_a1 : std_logic_vector(31 downto 0):=X"00000000";
    signal internal_wires_b0 : std_logic_vector(31 downto 0):=X"00000000";
    signal internal_wires_b1 : std_logic_vector(31 downto 0):=X"00000000";
    signal internal_wires_float32 : std_logic_vector(31 downto 0):=X"00000000";

begin
    clk_gen_inst: clk_gen port map(
        i_en=>i_en,
        o_start=>internal_wire_start,
        o_reset=>internal_wire_reset,
        i_clk=>i_clk
    );
    my_spi_slave_inst: my_spi_slave port map(
        cs=>cs,
        sck=>sck,
        mosi=>mosi,
        miso=>miso,
        a0=>internal_wires_a0,
        a1=>internal_wires_a1,
        b0=>internal_wires_b0,
        b1=>internal_wires_b1
    );
    my_1iir_element_inst: my_1iir_element port map(
        i_1bit=>i_1bit,
        o_32bit=>internal_wires_float32,
        i_start=>internal_wire_start,
        i_reset=>internal_wire_reset,
        o_complete=>internal_wire_complete,
        i_a0=>internal_wires_a0,
        i_a1=>internal_wires_a1,
        i_b0=>internal_wires_b0,
        i_b1=>internal_wires_b1,
        i_clk=>i_clk
    );
    Float2UI32_inst: Float2UI32 port map(
        i_src=>internal_wires_float32,
        o_ui32=>o_ui32
    );
end;

my_1iir_tb.vhd(SPIで係数を送って、その後波形を出力)

library ieee;
use ieee.std_logic_1164.all;
-- use ieee.std_logic_unsigned.all;
 
entity my_1iir_tb is
end my_1iir_tb;
 
architecture sim of my_1iir_tb is
    component my_1iir_top is port (
        i_1bit : in std_logic;
        i_en : in std_logic;
        cs : in std_logic;
        sck : in std_logic;
        mosi : in std_logic;
        miso : out std_logic;
        o_ui32 : out 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_en_tb : std_logic:='0';
    signal cs_tb : std_logic:='1';
    signal sck_tb : std_logic:='0';
    signal mosi_tb : std_logic:='0';
    signal miso_tb : std_logic:='0';
    signal a0_tb : std_logic_vector(31 downto 0):=X"3F800000";
    signal a1_tb : std_logic_vector(31 downto 0):=X"BF7814CD";
    signal b0_tb : std_logic_vector(31 downto 0):=X"3C7D6652";
    signal b1_tb : std_logic_vector(31 downto 0):=X"3C7D6652";
    signal clk_counter_bb : integer range 0 to 65535:=0;
    signal spitxbuf : std_logic_vector(39 downto 0):=X"0000000000";
    signal spirxbuf : std_logic_vector(39 downto 0):=X"0000000000";

begin
    my_1iir_inst : component my_1iir_top port map(
        i_1bit=>i_1bit_tb,
        i_en=>i_en_tb,
        cs=>cs_tb,
        sck=>sck_tb,
        mosi=>mosi_tb,
        miso=>miso_tb,
        o_ui32=>o_32bit_tb,
        i_clk=>clksys
    );

    process begin
        cs_tb<='1';

        spitxbuf<=X"80"&a0_tb;
        wait for 3 us;
        cs_tb<='0';
        sck_tb<='0';
        mosi_tb<=spitxbuf(39);
        for i in 0 to 39 loop
            wait for 1 us;
            sck_tb<='1';
            spirxbuf(0)<=miso_tb;
            spitxbuf(39 downto 0)<=spitxbuf(38 downto 0)&'0';
            wait for 1 us;
            sck_tb<='0';
            mosi_tb<=spitxbuf(39);
            spirxbuf(39 downto 0)<=spirxbuf(38 downto 0)&'0';
        end loop;
        wait for 1 us;
        cs_tb<='1';

        spitxbuf<=X"81"&a1_tb;
        wait for 3 us;
        cs_tb<='0';
        sck_tb<='0';
        mosi_tb<=spitxbuf(39);
        for i in 0 to 39 loop
            wait for 1 us;
            sck_tb<='1';
            spirxbuf(0)<=miso_tb;
            spitxbuf(39 downto 0)<=spitxbuf(38 downto 0)&'0';
            wait for 1 us;
            sck_tb<='0';
            mosi_tb<=spitxbuf(39);
            spirxbuf(39 downto 0)<=spirxbuf(38 downto 0)&'0';
        end loop;
        wait for 1 us;
        cs_tb<='1';
        
        spitxbuf<=X"82"&b0_tb;
        wait for 3 us;
        cs_tb<='0';
        sck_tb<='0';
        mosi_tb<=spitxbuf(39);
        for i in 0 to 39 loop
            wait for 1 us;
            sck_tb<='1';
            spirxbuf(0)<=miso_tb;
            spitxbuf(39 downto 0)<=spitxbuf(38 downto 0)&'0';
            wait for 1 us;
            sck_tb<='0';
            mosi_tb<=spitxbuf(39);
            spirxbuf(39 downto 0)<=spirxbuf(38 downto 0)&'0';
        end loop;
        wait for 1 us;
        cs_tb<='1';

        spitxbuf<=X"83"&b1_tb;
        wait for 3 us;
        cs_tb<='0';
        sck_tb<='0';
        mosi_tb<=spitxbuf(39);
        for i in 0 to 39 loop
            wait for 1 us;
            sck_tb<='1';
            spirxbuf(0)<=miso_tb;
            spitxbuf(39 downto 0)<=spitxbuf(38 downto 0)&'0';
            wait for 1 us;
            sck_tb<='0';
            mosi_tb<=spitxbuf(39);
            spirxbuf(39 downto 0)<=spirxbuf(38 downto 0)&'0';
        end loop;
        wait for 1 us;
        cs_tb<='1';

        wait for 3 us;
        i_en_tb<='1';
        wait for 500 us;

        assert false
        report "Finish"
        severity failure;
    end process;

    clksys <= not(clksys) after STEPSYS/2;
    process(clksys)begin
        if clksys'event and clksys='1' then
            if i_en_tb='1' then
                clk_counter_bb<=clk_counter_bb+1;
                if clk_counter_bb=8000 then
                    clk_counter_bb<=0;
                    i_1bit_tb<=not(i_1bit_tb);
                end if;
            end if;
        end if;
    end process;

end;

Makefile(基本的に今までと同じ。ソースが多くなったので分割。)

VHDLC=/usr/bin/ghdl
SRC_FPU=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
SRC=clk_gen.vhd float2uint.vhd my_1iir_element.vhd my_1iir_top.vhd my_spi_slave.vhd
TBSRC=my_1iir_tb.vhd
VCD=my_1iir.vcd
FST=my_1iir.fst
SIMRTL=my_1iir_tb

all :
        @make comp
        @make tb
        @make tb_e
        @make vcd

comp :
        $(VHDLC) -a --ieee=synopsys -fexplicit $(SRC) $(SRC_FPU)

tb :
        $(VHDLC) -a --ieee=synopsys -fexplicit $(TBSRC)

tb_e :
        $(VHDLC) -e --ieee=synopsys -fexplicit $(SIMRTL)

vcd :
        $(VHDLC) -r --ieee=synopsys -fexplicit $(SIMRTL) --vcd=$(VCD)

ということで、あっさりできました。まぁ部品はできていたわけなので。一方、テストベンチはどうしようか悩んだ。普通にデバイスを書くように書くのか、テストベンチっぽく書く(センシティビティなしのprocess)のか、、、結果、なんとなくミックスになっていて、違和感もありますが、素人なのでしょせんこんなもんです。

SPIの部分。テストベンチからの通信が内部の配線に反映されています。

32ビット浮動小数点の16ビット符号なし整数(レジスタとしては32ビット幅にしている)への変換部分。

0.015466*65536=1013.579776

なので、あってます。

全体。それらしい波形が出ています。

 

とはいえ、シミュレーション中に

../../src/synopsys/std_logic_arith.vhdl:255:20:@339271875ps:(assertion warning): There is an 'U'|'X'|'W'|'Z'|'-' in an arithmetic operand, the result will be 'X'(es).

ってwarningが1クロックごとに出てきてた。どこかにやってはいけない計算があるんだと思うけど、このメッセージじゃどこでこれが起こっているのかわからんな。QuartusとかDiamondとかならどこか教えてくれるかも。

 

ここまできたので、この後は実デバイスへの実装を検討します。休みが終わるのでやれるかどうかわからんけど。ていうか、半年くらい休みたい。